191 lines
6.2 KiB
Markdown
191 lines
6.2 KiB
Markdown
# 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<String>`.
|
|
|
|
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=<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.
|