Network mode & deployment shapes
TeaCore supports three deployment shapes. Pick the one that matches how you actually want to run your fleet, then copy the config block into plugins/TeaCore/config.yml.
- Standalone - one box runs lobby + games. No proxy needed.
- Central - proxy (BungeeCord or Velocity) + lobby + multiple arena servers. Leader balances allocations.
- External - one game per pod, external orchestrator (k8s HPA) owns scaling.
Each shape is independent — you don't need to understand all three to run one. Pick yours and skim the others later.
Standalone
Use this when: You're running a single Paper (or Spigot) server that hosts the lobby and the games together. No proxy, one JVM, one box. Most dev setups and small communities.
What happens
- Players join the single server directly.
- TeaCore uses an in-memory coordination backend (no Redis).
- The arena pool runs locally on this one server and allocates new arenas up to
max-total.
Config
# plugins/TeaCore/config.yml
network:
mode: standalone
server-id: auto
lobby-server: lobby # ignored in standalone
database:
type: sqlite
sqlite-file: plugins/TeaCore/tea.db
arena-pool:
mode: standalone
games:
mygame:
min-available: 1 # keep at least 1 waiting arena around
max-total: 4 # never exceed 4 concurrent arenas
preload-on-startup: 1 # spin up 1 arena at plugin enable
Running
- Drop the
TeaCorejar and each game jar intoplugins/. - Set
lobby.enabled: trueinplugins/TeaCore/config.yml(this single server hosts both the lobby UI and the arenas). - Start the server.
/tea game listshould show every registered game. /tea map create <game> <map>to author your first map./play <game>to join. Pool automatically spins arenas up tomax-total.
Central
Use this when: You run a proxy (BungeeCord or Velocity) in front of a lobby server plus one or more arena servers. Multiple arenas per arena server. You want the fleet to self-balance — when a new arena is needed, it should land on the least-loaded server.
What happens
- Players connect via the proxy. The proxy sends them to the lobby first.
- Every server (lobby included) publishes arena state and "hosted games" to Redis.
- One lobby server holds the matchmaker leader lock. That server's
ArenaPoolManageris the only one that actually makes allocation decisions. - When an allocation is needed, the leader looks at who hosts the game in question, picks the server with the fewest current arenas for that game, and sends an
ALLOCATE_ARENAcontrol command over Redis pub/sub. The target server creates the arena locally. - On match end,
/play <game>re-joins through the lobby, which routes to whichever server has a free slot.
Config
Apply to every server (lobby + each arena server):
# plugins/TeaCore/config.yml
network:
mode: bungeecord # activates the Redis-backed coordination backend
server-id: arena-1 # set per server: "lobby", "arena-1", "arena-2", ...
lobby-server: lobby # name of the lobby server in the proxy's server list
eligible-matchmaker: false # only the lobby is eligible; arena servers stay false
redis:
host: redis.internal
port: 6379
password: ""
database:
type: mysql # all servers share one MySQL
mysql:
host: mysql.internal
port: 3306
database: tea
user: tea
password: ""
arena-pool:
mode: central
games:
# In central mode, the LOBBY's arena-pool.games config is the single
# source of truth for fleet-wide pool sizing. Arena servers don't need
# this section populated — they just declare themselves as hosts and
# accept ALLOCATE_ARENA control commands from the lobby. Put this block
# on the lobby only, with one entry per game id you want auto-scaled.
mygame:
# These are NETWORK-WIDE totals in central mode, not per-server.
min-available: 2 # keep 2 joinable arenas across the whole fleet
max-total: 16 # never exceed 16 total arenas
preload-on-startup: 0 # ignored in central mode (leader handles it)
Running
- Install the
TeaCorejar + every game jar on every server. - On the lobby server, set
lobby.enabled: trueinplugins/TeaCore/config.yml. Leave itfalse(the default) on arena servers. - Start Redis + MySQL.
- Start all servers. Each one registers its hosted games in Redis.
- Set
network.eligible-matchmaker: trueon the lobby andfalseon every arena server. Only eligible servers compete for the matchmaker leader lock and run the cluster-wide pool manager. The lobby'sarena-pool.gamesconfig is then the only sizing source for the fleet. - Copy your map data into each arena server's
plugins/TeaCore/maps/directory. (Shared storage like NFS or S3 sync is the usual prod approach.) /play <game>from the proxy joins a players-needed arena on whichever arena server has one.
Gotcha: maps must exist on each hosting server
When the leader sends ALLOCATE_ARENA to arena-2, arena-2 reads its local plugins/TeaCore/maps/ to find maps. If no maps are authored on that server, the allocation silently no-ops. Three options:
- Author maps on every server.
- Mount a shared volume at
plugins/TeaCore/maps/. - Use an image layer that pre-bakes maps into every arena server.
External
Use this when: Kubernetes (or similar) boots a fresh pod per match. Each pod hosts exactly one arena, runs the match, then terminates. An external scaler (HPA on queue depth, or a custom controller) decides when to spawn more pods.
What happens
- TeaCore's pool manager is a no-op — it never allocates.
- Each pod boots with
end-mode: SHUTDOWN, so when the match ends the JVM exits and the pod dies. - Your orchestrator watches queue depth via Redis and scales the pod count up.
Config
On each arena pod:
network:
mode: bungeecord
server-id: auto # let k8s inject TEA_SERVER_ID from the pod name
lobby-server: lobby
redis:
host: redis.internal
database:
type: mysql
mysql: { ... }
arena-pool:
mode: external # core never allocates; each pod hosts one arena
On the lobby:
arena-pool:
mode: central # lobby still uses central mode for discovery
# or standalone if the lobby is the only server; external only applies to arena pods.
Per-game end mode (example — every game plugin exposes the same field in its own config):
# plugins/<YourGame>/config.yml
game:
end-mode: SHUTDOWN # JVM exits after the ENDING phase
Running
- Build your pod image with TeaCore + game plugins + a single map baked in.
- Kubernetes deployment with
replicas: Ncontrolled by an HPA (queue-depth custom metric). - Lobby runs normally. Queue-based routing still works — the lobby sees a new arena snapshot appear each time a pod boots and routes queued players into it.
- After match end, the pod terminates; k8s replaces it per the deployment's replica count.
Reload
/tea reload # TeaCore: config, theme, all locale bundles
/tea lobby reload # Reload connection signs + lobby spawn settings (the lobby UI inside TeaCore)
Neither command reconnects Redis or the DB pool — restart the server for those.
Also see
- Installing — first-time setup for all three shapes.
- Arenas and pool internals — how allocations + control commands work under the hood.