# Architecture ## Overview core-minigames extends Hytale's existing asset, trigger-volume, and event systems. It does not run a separate minigame logic registry beside Hytale's trigger-volume registry. ```text Server/Minigames/*.json | v MinigameDefinition asset store | v MinigameRuntimeService | +--> Hytale event bus: MinigameEvent | +--> TriggerEffect.CODEC entries | `--> TriggerCondition.CODEC entries ``` ## Asset Store `MinigameDefinition` is registered as a Hytale asset store in `MinigameCorePlugin.setup()`. Important details: - Path: `Server/Minigames/` - Codec: `MinigameDefinition.CODEC` - Key function: `MinigameDefinition::getId` - Editor dropdown source: `MinigameDefinition` The trigger-volume editor can use this asset source for fields named `MinigameId`, so minigame trigger effects and conditions can pick from loaded minigame assets instead of free-typing IDs. ## Runtime `DefaultMinigameRuntimeService` owns active `MinigameRuntime` instances keyed by runtime ID. A single minigame ID can have multiple active runtimes when multiple discovered maps or variants are started. Runtime state includes: - Current `MinigamePhase` - Player state, score, lives, team, tags, and status - Team membership and team score - Objective state - Counter and variable state - Spectator and eliminated-player sets - Runtime ID, map ID, and variant ID `DefaultMinigameService` handles definition-level actions and delegates live actions to the runtime service. ## Lifecycle Contract The built-in lifecycle is intentionally minimal — most progression is **volume-driven** so map designers control exactly when things happen: ```text WAITING_FOR_PLAYERS set automatically on start; countdown runs (CountdownSeconds) | v STARTING set automatically when the countdown finishes | v ACTIVE set by a volume: SetMinigamePhase(ACTIVE) <-- REQUIRED | v (rounds: a SetRound effect starts round 1 and the $round timer) OVERTIME / SUDDEN_DEATH automatic after the final round, when enabled (timed) | v ENDING -> RESETTING via EndMinigame effect, /minigame stop, or round completion ``` Hard rules: - **A game refuses to start when no trigger volume carries a `SetMinigamePhase(ACTIVE)` effect for its minigame/map/variant ids.** Queued players are told why. This catches maps whose lifecycle wiring is incomplete instead of leaving the game stuck in `STARTING`. - The round system only runs after a `SetRound` effect fires (typically from the `SignalPhaseStart=ACTIVE` volume). `TotalRounds`/`RoundLengthSeconds` alone do nothing. - `SetMinigamePhase(ENDING)` and `(RESETTING)` route through the full end/reset lifecycle (cleanup, rewards, inventory restore) — they are equivalent to `EndMinigame`/`ResetMinigame`. - With `MapSelectionMode: VOTE` and more than one voteable map, reaching `MinPlayers` opens a 30-second vote window. The game starts when every queued player has voted or the window expires. ## Threading Model Each Hytale world ticks on its own thread. The rules this module follows: - Every `MinigameRuntime` records a **home world** at start (adopted on first pregame tick if unknown). All mutation of runtime state happens on that world's thread. - Ticking systems (pregame, HUD, queue UI, menu) run per world and only touch runtimes homed in their world. - The signal-service scheduler thread never mutates state directly — it hops onto the home world via `world.execute(...)`. - Queue sessions are shared across worlds and synchronized internally. - Adapter calls that need a result (`saveInventory`) only run on the player's own world thread; cross-world void calls (give item, teleport, restore) are forwarded with `world.execute` and never block the calling thread. ## Events `MinigameEvent` implements Hytale's `IEvent`. Runtime events dispatch through: ```java HytaleServer.get() .getEventBus() .dispatchFor(MinigameEvent.class, event.minigameId()) .dispatch(event); ``` This keeps minigame events available to Hytale's event system instead of routing them through a custom `EventRegistry`. ## Trigger Conditions Minigame trigger conditions extend Hytale's `TriggerCondition` and are registered directly into `TriggerCondition.CODEC`. Current condition type IDs: - `MinigamePhase` - `PlayerInMinigame` - `PlayerStatus` - `PlayerScore` - `TeamScore` - `Counter` - `CurrentRound` - `PlayerLives` - `TeamCondition` - `TimerElapsed` - `ObjectiveStatus` - `WinConditionMet` ## Trigger Effects Minigame trigger effects extend Hytale's `TriggerEffect` and are registered directly into `TriggerEffect.CODEC`. Current effect type IDs: - `StartMinigame` - `EndMinigame` - `ResetMinigame` - `SetMinigamePhase` - `ResetScores` - `ModifyScore` - `ModifyCounter` - `ModifyLives` - `SetPlayerStatus` - `JoinMinigameQueue` - `LeaveMinigameQueue` - `VoteForMap` - `StartQueuedMinigame` - `SpawnItem` - `SpawnNpc` - `MountPlayer` - `PlaceBlocks` - `SetRound` - `StartTimer` - `StopTimer` - `SetSpawnPoint` - `RespawnPlayer` - `SendMinigameMessage` - `AssignTeam` - `SaveInventory` - `RestoreInventory` - `ClearInventory` - `SetObjectiveStatus` - `ModifyObjectiveProgress` - `AwardKillScore` ## Volumes There are no custom minigame volume classes after the refactor. Hytale trigger volumes provide the region shape, enter/exit behavior, tags, and trigger effect assets. To define a minigame area, create a normal Hytale trigger volume and tag it with `MinigameId=`, `MapId=`, optional `VariantId=`, and optionally `MapRole=MainArena`. Then attach minigame trigger conditions and effects to that trigger volume or to reusable Hytale trigger effect assets. ## Removed Layer The following old architecture pieces were removed because they duplicated Hytale systems: - `BuiltinRegistries` - `ConditionRegistry` - `EventRegistry` - `MinigameRegistries` - `TemplateRegistry` - `VolumeRegistry` - `VolumeTypeDefinition` Future minigame features should keep following the same pattern: use Hytale asset stores, Hytale trigger codec registration, and Hytale event dispatch unless there is a concrete engine gap that requires a local adapter.