Files
core-minigames/docs/architecture.md
T

6.2 KiB

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.

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:

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<String>.

Runtime events dispatch through:

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=<id>, MapId=<map>, optional VariantId=<variant>, 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.