Skip to main content

Architecture

Architecture overview​

Lasers-Enigma is a Spigot/Paper Minecraft plugin. All source code lives in core/src/main/java/eu/lasersenigma/.

The codebase follows a strict layered architecture. Each layer has a single responsibility and calls only the layer(s) below it.

Layer call stacks​

Command → Controller → Service → Manager / (Repository → Entity/Mapper)
Inventory → Controller → Service → Manager / (Repository → Entity/Mapper)
Listener → Controller → Service → ...
Task → Service → ...

Layer responsibilities​

LayerResponsibilityPattern
CommandEntry point for /lasers sub-commands. Parses arguments, delegates to a Controller. Extends LasersCommand<E>.One class per sub-command
InventoryGUI menus (chest UIs) and shortcut bars (hotbar UIs). Handles click events, delegates to Controllers. Extends AOpenableInventory or AShortcutBarInventory.Class name ends with *Inventory
ControllerOrchestration layer. Checks permissions, calls Services, sends user feedback via TranslationUtils. Static utility methods (private constructor).*Controller
ServicePure business logic. No permission checks, no player messages. Singleton (getInstance()).*Service
ManagerIn-memory state, caching, data structures. No database access, no business logic. Singleton (getInstance()).*Manager
RepositoryDatabase access (raw SQL queries, JDBC). Static methods. Supports SQLite and MySQL.*Repository
EntityPOJO for persistence. Maps 1:1 to a database table row.*Entity
MapperConverts between Entity ↔ Domain objects. Static methods.*Mapper
ListenerBukkit event handlers. Thin: delegates to Controllers/Services.*EventsListener
EventCustom Bukkit events for decoupling between modules or for third-party plugins.Extends ABeforeActionEvent (cancellable) or AAfterActionEvent
TaskPeriodic game loops (BukkitRunnable). Delegates to a *PeriodicUpdateController.*Task
ExceptionBusiness exceptions with a translation code for user-facing messages.Extends AbstractLasersException

Reference controller: area/controller/AreaController.java. Reference service: area/service/area/AreaLifecycleService.java.

Package organization​

eu.lasersenigma/
├── area/ # Puzzle areas: CRUD, configuration, victory, reset, schematics, stats
│ ├── command/ # Commands for area management
│ ├── controller/ # AreaController, AreaCreationController, ComponentController, ...
│ ├── service/area/ # AreaCreationService, AreaQueryService, AreaVictoryService, ...
│ ├── service/component/ # CacheUpdateService, LaserParticleUpdateService, LightingUpdateService
│ ├── manager/ # AreaManager, WorldAreaManager
│ ├── repository/ # AreaRepository
│ ├── entity/ # AreaEntity, UnloadedAreaEntityView
│ ├── mapper/ # AreaMapper, UnloadedAreaMapper
│ ├── listener/ # AreaEventsListener
│ ├── event/ # AreaCreatedEvent, AreaDeletedEvent, ...
│ ├── exception/ # AreaOverlapException, NoAreaFoundException, ...
│ ├── entitydisplay/ # Display entity rendering for area boundaries
│ ├── schematic/ # WorldEdit clipboard/schematic import & export
│ ├── stats/ # Player & server statistics (area/global/puzzle)
│ ├── task/ # AreaTask (periodic area updates)
│ └── inventory/ # Area-related GUI menus
├── component/ # All puzzle components
│ ├── lasersender/ # Reference component implementation (LaserSender)
│ ├── laserreceiver/ # LaserReceiver
│ ├── mirrorsupport/ # MirrorSupport (+ MirrorSupportMode)
│ ├── concentrator/ # Concentrator (merges lasers)
│ ├── bonus/ # Bonus collectibles
│ ├── elevator/ # Elevator and CallButton
│ ├── lock/ # Lock
│ ├── lockkeychest/ # KeyChest (+ playerskeys sub-package)
│ ├── mirrorchest/ # MirrorChest
│ ├── burnableblock/ # BurnableBlock
│ ├── filteringsphere/# FilteringSphere
│ ├── gravitationalsphere/ # GravitationalSphere
│ ├── reflectingsphere/ # ReflectingSphere
│ ├── prism/ # Prism (color splitting)
│ ├── redstonesensor/ # RedstoneSensor
│ ├── conditionnalwinnerblock/ # ConditionalWinnerBlock
│ ├── musicblock/ # MusicBlock
│ ├── leaderboard/ # Leaderboard
│ ├── lasersolidifier/# LaserSolidifier — solidifies laser beams into temporary blocks
│ ├── sync/ # Bidirectional sync links between components (controller, service, repository, entity, mapper, event, visualization)
│ ├── clipboard/ # Component copy/paste (ComponentClipboardManager)
│ ├── scheduledactions/ # Timed component actions
│ ├── common/ # Shared component utilities (inventories, laser reception)
│ ├── command/ # Component commands
│ ├── repository/ # ComponentRepository
│ ├── entity/ # ComponentEntity
│ ├── mapper/ # ComponentMapper
│ └── event/ # Component events and event producers
├── laser/ # Laser beam system
│ ├── LaserParticle.java # Core beam logic (movement, collisions, color mixing)
│ ├── entitydisplay/ # Display Entity rendering for laser segments
│ ├── event/ # Laser-specific events (hit block, hit component, hit entity)
│ ├── sound/ # Laser ambient sound (LaserAmbientSoundService)
│ └── task/ # LaserTask (periodic laser updates)
├── player/ # Player system
│ ├── LEPlayer.java # Plugin's player wrapper
│ ├── LEPlayers.java # Player container singleton
│ ├── PlayerInventoryManager.java # Manages GUI menus & shortcut bars for a player
│ ├── inventory/ # Player-specific GUI menus
│ ├── checkpoint/ # Player checkpoint persistence
│ └── listener/ # Player event listeners
├── commands/ # Top-level commands (HelpCommand)
├── componentinfo/ # Component inspector: ray-cast targeting, map display, chat info
│ # (InfoCommand, ComponentInfoController, ComponentInfoRayTracingService)
├── common/ # Shared infrastructure
│ ├── command/ # LasersCommand<E> base class
│ ├── config/ # ConfigData (plugin config keys), ConfigManager
│ ├── database/ # Database, SQLDatabase (connection + migrations)
│ ├── event/ # AEvent, ABeforeActionEvent, AAfterActionEvent
│ ├── exception/ # AbstractLasersException
│ ├── inventory/ # AInventory, AOpenableInventory, AShortcutBarInventory
│ │ └── saving/ # Player inventory save/restore on editor mode toggle
│ ├── items/ # ItemsFactory, ComponentType, LasersColor
│ ├── logger/ # Logger (wrapper with level filtering)
│ ├── message/ # TranslationUtils
│ ├── task/ # ColorChangeTask, ITaskComponent
│ ├── temporaryblock/ # Temporary block placement & automatic restoration (entity, repository, service)
│ ├── util/ # BlockUtils and other utilities
│ └── dependency/ # Optional dependency integrations
│ ├── worldedit/ # WorldEditHelper, WECopy, WEPaste
│ ├── protocollib/# ProtocolLibIntegration, ProtocolLibEntityVisibility
│ └── mapengine/ # MapEngineIntegration
├── checkpoint/ # Checkpoint system (respawn on join/death/world change)
├── editor/ # Editor mode commands
├── permission/ # Permission enum and checks
├── sound/ # Sound effects (SoundLauncher, PlaySoundCause)
├── songs/ # NoteBlock song system (optional)
├── betonquest/ # BetonQuest integration (quests)
└── updatenotifier/ # Plugin update checker

Key classes​

ClassPurpose
LasersEnigmaPluginMain plugin entry point (JavaPlugin). Initializes database, commands, listeners, tasks. Access via getInstance().
Area / AArea / UnloadedAreaLoaded puzzle area (with components) / abstract base / lightweight unloaded representation.
LEPlayer / LEPlayersPlayer wrapper (inventory state, checkpoints, current area) / singleton container for all players.
PlayerInventoryManagerPer-player manager for GUI menus (AOpenableInventory) and shortcut bars (AShortcutBarInventory). Handles item clicks, component placement, color selection. Owned by LEPlayer.
LaserParticleCore laser beam: movement step-by-step, collision detection (blocks, components, entities), color mixing, light block placement. Created by LaserSender/Concentrator, moved by LaserTask.
LaserBeamGroups particles sharing the same origin into an ordered sequence for segment rendering.
LaserTask / AreaTaskPeriodic game loops (BukkitRunnable). LaserTask moves all laser particles every tick. AreaTask checks area entry/exit every 10 ticks. Both delegate to *PeriodicUpdateController.
IComponent / AComponent / AArmorStandComponent / ATextDisplayComponentComponent interface / abstract base / armor-stand-based components / TextDisplay-based components.
ConfigData / ConfigManagerConfigData defines all config key constants from config.yml. ConfigManager handles loading.
SQLDatabaseDatabase connection (SQLite or MySQL) + schema migrations. Migrations are versioned methods (update_X_Y_Z_to_A_B_C()). Schema version tracked in database_configuration table. See Database-migrations.
ABeforeActionEvent / AAfterActionEventBase classes for custom events. Before events are Cancellable (can prevent action). After events are informational. Both extend AEvent (→ Bukkit Event).
AbstractLasersExceptionBase exception. Constructor takes a translation code + params. Message is auto-resolved via TranslationUtils. All business exceptions must extend this.
LoggerWrapper around Bukkit logger with level filtering (finest to severe). Level configurable via debug_level in config.yml. Access via LasersEnigmaPlugin.getInstance().getLasersEnigmaLogger().
TranslationUtilsSends translated messages to players. Resolves translation codes from core/src/main/resources/lang/*.json. Supports per-player language.
WorldEditHelper / WECopy / WEPasteOptional WorldEdit integration for area clipboard/schematic operations.
ProtocolLibIntegration / ProtocolLibEntityVisibilityOptional ProtocolLib integration for per-player entity visibility (rotation preview arrows).
laser.entitydisplay.*Display Entity-based laser rendering: LaserSegment, LaserTrajectory, SegmentRenderer, SegmentRoutingService, SegmentMergeService, MergeOverlaySegment.

Command tree​

Commands are organized in containers registered in LasersEnigmaPlugin.registerCommands():

/lasers (/le)
├── area/ → eu.lasersenigma.area.command.AreaCommandContainer
├── component/ → eu.lasersenigma.component.command.ComponentCommandContainer
├── schematic/ → eu.lasersenigma.area.schematic.command.SchematicCommandContainer
├── clipboard/ → eu.lasersenigma.area.schematic.command.clipboard.ClipboardCommandContainer
├── stats/ → eu.lasersenigma.area.stats.command.StatsCommandContainer
├── editor → eu.lasersenigma.editor.command.EditorCommand (also the root default command)
├── help → eu.lasersenigma.commands.HelpCommand
└── info → eu.lasersenigma.componentinfo.InfoCommand

When creating a new command:

  1. Create a class extending LasersCommand<Player> (or CommandSender).
  2. In the constructor: set permission via super.setPermission(), add arguments via super.addArgument(name, required, ArgumentType.xxx()).
  3. Register in the parent container via super.registerSubCommand(new MyCommand()).
  4. Add a translation code for the description in core/src/main/resources/lang/commands/.

Optional dependencies​

Declared in gradle/libs.versions.toml. Integrations in common/dependency/:

DependencyIntegration classPurpose
WorldEditworldedit/WorldEditHelperArea clipboard & schematic operations
ProtocolLibprotocollib/ProtocolLibIntegrationPer-player entity visibility
MapEnginemapengine/MapEngineIntegrationComponent info map rendering