5.6 KiB
5.6 KiB
Overview
Architecture in one diagram
StaffPlugin (entry)
+-- StaffPermissions wraps Hytale PermissionsModule, checks groups from config
+-- PunishmentService issue / revoke / lookup, broadcasts on MessageBus
| +-- PunishmentRepository SQL CRUD
| +-- MuteCache in-memory map of active mutes for online players
+-- StaffChatService cross-server staff chat over MessageBus
+-- PlayerSeenTracker writes to players_seen on join/leave
| +-- PlayerSeenRepository SQL CRUD
+-- BanEnforcer cancels PlayerSetupConnectEvent if banned
+-- ChatEnforcer cancels PlayerChatEvent if muted
External dependencies:
- NetworkCore.getDatabase() - MySQL pool (HikariCP). Required.
- NetworkCore.getMessageBus() - Redis pub/sub. Required.
- NetworkCore.getServerId() - identifies which server an action was taken on.
- PermissionsModule.get() - Hytale's built-in group/permission API.
What lives where
| File | Responsibility |
|---|---|
| StaffPlugin.java | Plugin entry. Reads NetworkCore services, bootstraps schema, registers everything. |
| config/StaffConfig.java | Default config shape. |
| util/StaffPermissions.java | isStaff(uuid) / isAdmin(uuid) checks. |
| util/PlayerResolver.java | Name -> UUID, looks at online players first, then players_seen. |
| util/DurationParser.java | Parse "30m", "2h" etc.; format milliseconds back to human strings. |
| model/Punishment.java, PunishmentType.java, PlayerSeen.java | Record types. |
| db/SchemaBootstrap.java | CREATE TABLE IF NOT EXISTS for both tables. Run on start(). |
| db/PunishmentRepository.java | SQL for punishments. |
| db/PlayerSeenRepository.java | SQL for player presence. |
| service/PunishmentService.java | Issuing/revoking + cross-server broadcast + applying remote actions locally. |
| service/MuteCache.java | Per-server hot cache of active mutes for online players. |
| service/StaffChatService.java | Cross-server staff chat over MessageBus. |
| service/PunishmentBroadcast.java, StaffChatPayload.java | Wire payloads. |
| listener/BanEnforcer.java | PlayerSetupConnectEvent -> cancel + set reason if banned. |
| listener/ChatEnforcer.java | PlayerChatEvent -> cancel if muted. |
| listener/PlayerSeenTracker.java | Connect/disconnect -> update players_seen, load/clear mute cache. |
| command/punish/PunishmentCommands.java | All 8 punishment commands as nested classes. |
| command/chat/StaffChatCommand.java | /sc. |
| command/lookup/LookupCommand.java, HistoryCommand.java | /lookup, /history. |
Why the design is this shape
- One service per concern. Punishments and staff chat are independent. Each owns its own MessageBus subscription and can be modified without touching the other.
- Repositories return models, services orchestrate. Repositories never call services; services call repositories. This keeps SQL out of the punishment-issuing logic.
- MuteCache is per-server, not network-wide. Mute state is loaded on join for that one player and cleared on leave. Network-wide propagation happens via the MessageBus, which updates other servers' caches when their player base intersects.
- BanEnforcer talks to MySQL on connect. No cache. Connects are infrequent enough that a single indexed query is fine; the data is authoritative and we don't want stale-cache surprises letting a banned player in.
- Permission checks live in the command, not in services. That way a future console source can call the same service without re-checking permissions.
Lifecycle
Standard Hytale plugin lifecycle:
setup()- load config, buildStaffPermissionsandMuteCache.start()- require NetworkCore's database + bus, bootstrap schema, build services and repositories, mark this server's stale online flags as offline (crash recovery), register events and commands.shutdown()- stop service subscriptions. The database pool is owned by NetworkCore and closes on its own shutdown.
If MySQL isn't healthy at start(), this plugin logs a severe error and returns. The server keeps running; this plugin just does nothing. Restart once MySQL is back.