GitBook Assistant Ask chevron-down Architecture Documentation High-level architecture overview for maintainers and contributors.
System Overview
zCrates is a modular Minecraft crate plugin built on Paper 1.21+ with Java 21. The architecture emphasizes:
Separation of Concerns - API, implementation, and extensions are distinct
Extensibility - Hook system for seamless plugin integration
Security - Sandboxed JavaScript execution with restricted API surface
Performance - Async database operations, in-memory caching, efficient registries
Type Safety - Polymorphic YAML deserialization, strong typing throughout
High-Level Architecture
Copy ┌─────────────────────────────────────────────────────────────────┐
│ USER INTERFACE │
│ (Commands, GUIs, Chat Messages, Placed Crates) │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ PRESENTATION LAYER │
│ (Commands, Listeners, zMenu Integration) │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ BUSINESS LOGIC LAYER │
│ (CratesManager, UsersManager, AnimationExecutor) │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ DOMAIN MODEL LAYER │
│ (Crate, Reward, Key, Animation, Algorithm, User) │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ INFRASTRUCTURE LAYER │
│ (Registries, Script Engine, Storage, Serialization) │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ EXTERNAL SERVICES │
│ (Database, zMenu, Bukkit API, Custom Item Plugins) │
└─────────────────────────────────────────────────────────────────┘ Module Structure
1. API Module (api/)
Purpose: Public contract for external plugins
Responsibilities:
Define interfaces for all core models
Define extension points (Hook, ItemsProvider, etc.)
Key Characteristics:
No implementation details
Minimal dependencies (only Bukkit API, annotations)
Backward compatibility guarantees
Exported Packages:
fr.traqueur.crates.api - Core interfaces
fr.traqueur.crates.api.events - Event system
fr.traqueur.crates.api.models - Model interfaces
fr.traqueur.crates.api.registries - Registry system
fr.traqueur.crates.api.managers - Manager interfaces
fr.traqueur.crates.api.hooks - Hook system
fr.traqueur.crates.api.providers - Provider interfaces
2. Common Module (common/)
Purpose: Shared implementations used across main plugin and hooks
Responsibilities:
Block/Entity display implementations
Dependencies:
3. Main Plugin Module (src/)
Purpose: Core plugin implementation
Responsibilities:
Implement all API interfaces
Orchestrate business logic
Dependencies:
Rhino (JavaScript engine)
Structura (YAML serialization)
CommandsAPI (command framework)
Reflections (classpath scanning)
4. Hooks Module (hooks/)
Purpose: Optional plugin integrations
Structure:
Characteristics:
Each hook is a separate Gradle subproject
Isolated dependencies (only required hook targets)
Auto-discovered via @AutoHook annotation
Gracefully disabled if target plugin missing
Core Subsystems
1. Registry System
Architecture:
Design Decisions:
Static Access via ClassToInstanceMap
Avoids dependency injection complexity
Type-safe registry retrieval
Single source of truth for all registries
FileBasedRegistry Pattern
Automatic resource copying from JAR
Folder hierarchy with metadata
Consistent loading behavior
Generic ID Type
String IDs for file-based registries
Enum IDs for runtime registries (DisplayType)
Type safety at compile time
2. Manager System
Architecture:
Design Decisions:
ServicesManager Integration
Automatic lifecycle management
Accessible from any plugin
Single Responsibility
CratesManager: crate operations
Clear separation of concerns
In-Memory State
Active openings in CratesManager
User cache in UsersManager
Placed crates loaded per chunk
3. Script Engine System
Architecture:
Design Decisions:
Single Engine Instance
Shared Rhino Context across all scripts
Consistent security settings
Isolated Scopes
Each script execution uses new scope
Null parent (no prototype chain access)
Prevents cross-script interference
Wrapper Pattern
Type-safe method exposure
No direct Java object access
Security First
Interpreter mode (no bytecode)
Method blacklist via WrapFactory
4. Animation System
Architecture:
Design Decisions:
Phase-Based Execution
Sequential phase execution
Independent timing per phase
SpeedCurve Integration
Mathematical easing functions
Applied to progress value
Smooth visual transitions
BukkitRunnable Scheduling
Sync with server tick rate
5. Storage System
Architecture:
Design Decisions:
Sarah ORM
Multiple database support
Repository Pattern
Separation of persistence logic
DTO Pattern
Database schema independence
Clear domain/persistence boundary
Async Operations
Non-blocking database access
6. Display System
Architecture:
Design Decisions:
Factory Pattern
Encapsulates creation logic
Validation before creation
Chunk PDC Storage
Persistent across restarts
Automatic chunk association
No separate database table
Lazy Loading
Displays created on chunk load
Crate Opening Flow
Configuration Loading Flow
Plugin Lifecycle
Initialization Sequence
Shutdown Sequence
Reload Sequence
Design Decisions
1. Why Rhino Instead of Nashorn/GraalVM?
Decision: Use Rhino JavaScript engine
Rationale:
Security: Fine-grained control over Java access
Compatibility: Works on all Java versions (11+)
Sandbox: Built-in sandboxing with initSafeStandardObjects()
Performance: Interpreter mode sufficient for animation/algorithm scripts
Trade-offs:
Not ES2015+ compliant (limited ES6 support)
Slower than GraalVM for compute-intensive tasks
No native Promise support
2. Why Static Registry Access?
Decision: Use ClassToInstanceMap for static registry access
Rationale:
Simplicity: No dependency injection framework needed
Type Safety: Compile-time verification of registry types
Accessibility: Accessible from any plugin code
Singleton: Single source of truth
Trade-offs:
Global state (harder to test in isolation)
Cannot swap implementations at runtime
Requires manual registration
3. Why Chunk PDC for Placed Crates?
Decision: Store placed crates in Chunk PDC
Rationale:
Automatic Association: Chunks own their crates
Persistence: Survives server restarts
Performance: No database queries for crate lookup
Simplicity: No separate placed_crates table
Trade-offs:
Limited to chunk-sized queries (can't query all crates in world easily)
PDC size limits (unlikely to hit in practice)
Chunk load required for data access
4. Why ServicesManager for Managers?
Decision: Register managers via Bukkit ServicesManager
Rationale:
Standard Pattern: Follows Bukkit conventions
Discovery: Other plugins can find managers
Lifecycle: Bukkit manages service lifecycle
Type Safety: Retrieval by class
Trade-offs:
Limited lifecycle control
No priority/ordering guarantees
5. Why Polymorphic YAML with Structura?
Decision: Use Structura for YAML deserialization
Rationale:
Type Safety: Compile-time validation of YAML structure
Polymorphism: @Polymorphic annotation for type field
Validation: Built-in validation (@Options, @DefaultInt, etc.)
Maintainability: YAML changes reflected in code
Trade-offs:
Learning curve for contributors
Less flexible than manual parsing
1. Memory Management
User Cache:
Cleared on player quit (prevents memory leaks)
ConcurrentHashMap for thread-safe access
Registry Storage:
HashMap-based (O(1) lookup)
Immutable after loading (no synchronization needed)
Cleared on reload (prevents memory bloat)
Animation State:
Removed immediately after completion
No persistent animation history
Bounded by concurrent player count
2. Database Optimization
Connection Pooling:
HikariCP for connection management
Async Operations:
All DB operations use CompletableFuture
Batch operations where possible
Caching:
User data cached in memory
Keys stored locally (no query per check)
Opening history loaded on-demand
Interpreter Mode:
No bytecode compilation overhead
Acceptable performance for animations/algorithms
Scope Reuse:
Scopes created per execution (not per phase)
Shared Rhino Context across all scripts
Wrapper Objects:
Lightweight wrappers (no heavy computation)
Direct method calls (no reflection)
Cached references where possible
4. Event System
Listener Registration:
Single listener instances (not per crate)
Ignore if not handling event
Event Frequency:
CratePreOpenEvent: Once per open attempt
CrateOpenEvent: Once per successful open
RewardGeneratedEvent: Once per opening + rerolls
RewardGivenEvent: Once per completion
Security Architecture
1. JavaScript Sandbox
Threat Model:
Malicious server admin uploading dangerous scripts
Exploiting Java reflection to bypass security
Accessing file system or network
Escaping sandbox via prototype pollution
Mitigations:
No Java Package Access: initSafeStandardObjects() blocks java.*
Method Blacklist: Custom WrapFactory blocks dangerous methods
Null Parent Scopes: Prevents prototype chain manipulation
Interpreter Mode: No bytecode execution (no JIT exploits)
Wrapper Objects: Controlled API surface
Residual Risks:
Infinite loops (no timeout mechanism)
Memory exhaustion (no heap limits)
Malicious animation logic (e.g., inventory spam)
2. Inventory Security
Threat Model:
Script manipulating unauthorized slots
Duplication exploits via inventory manipulation
Item theft via inventory swaps
Mitigations:
Slot Authorization: Only authorized slots can be modified
Wrapper Methods: Controlled inventory access
State Validation: Animation state tracked by manager
3. Data Security
Threat Model:
SQL injection via user input
Race conditions in user data
Mitigations:
Prepared Statements: Sarah ORM uses prepared statements
Concurrent Collections: Thread-safe user cache
Transactions: Database operations in transactions
Async Saves: Non-blocking persistence
4. Permission System
Threat Model:
Unauthorized crate access
Permission bypass via exploits
Mitigations:
PermissionCondition: Checks before opening
Event Cancellation: External plugins can prevent opens
Key Validation: Keys verified before consumption
Future Considerations
Potential Bottlenecks:
User cache growth (many concurrent players)
Database connection pool exhaustion
Animation executor (many concurrent animations)
Mitigation Strategies:
Queue system for concurrent animations
Migration to separate placed_crates table if needed
Extension Points:
Custom OpenConditions via polymorphic registry
Custom Rewards via polymorphic registry
Custom DisplayTypes via factory registry
Custom Hooks via @AutoHook annotation
Custom ItemsProviders via provider registry
API Stability:
Semantic versioning for API module
Deprecation warnings before removal
Backward compatibility for configuration
Observability:
Debug logging for troubleshooting
Event system for external tracking
Metrics (opening counts, reward distribution)
Health Checks:
Database connection validation
Registry population verification
Script engine health checks
The zCrates architecture prioritizes:
Modularity - Clear separation between API, implementation, and extensions
Security - Sandboxed JavaScript, controlled API surface
Performance - Async operations, efficient caching, optimized data structures
Extensibility - Hook system, polymorphic types, provider pattern
Maintainability - Type safety, clear abstractions, comprehensive documentation