Tick modes
Warning
The regionized modes are not implemented in this build. exec.mode exists
and compat is the only value with any effect; the regionized machinery below
is design, not a feature you can turn on.
MeowMC is single-threaded today, and that is the compatibility product: a regionized mode is designed — Folia-style, each world's loaded chunks partitioned into exclusive regions, each region ticked by its own thread, behind one server-wide key read once at startup.
The switch is restart-time and web-panel-only, and it is never an in-game setting.
The honest tradeoff, first
Multi-threading and any-plugin compatibility are a tradeoff, not both at once — a structural property of regionized ticking, not a limitation of any one implementation.
| | Single-thread (default, compat) | Regionized (regionized) |
| --- | --- | --- |
| What a Paper-lineage plugin sees | The world it was written for: one tick thread, Bukkit.isPrimaryThread() true wherever it expects, every sync task serial, every teleport and scoreboard contract intact. | A world where its listeners may fire on a region thread it never heard of, where "the main thread" no longer ticks the chunk its event happened in, and where the global state it touches is genuinely shared across threads. |
| What the server does about it | Nothing needed — correct by construction. | Pins every plugin that has not declared region support to the global region, where it stays correct — and gains nothing from the parallelism. |
An unadapted plugin pinned to the global region is not running in a degraded mode — it is running in Paper mode, the correct default for code written against the Paper contract. The regionized mode is not claimed to be faster: the benefit is workload-dependent, and the crowd case — all players standing in one region — is a named risk, not a solved one.
The mode key
exec.mode, in config/meowmc-global.yml — one value, server-wide, resolved
once at boot.
| Value | Runtime semantics | Status |
| --- | --- | --- |
| compat | One tick thread; the regionizer sits inert, one region per world. The whole server as it is today. | Default |
| regionized | Region machinery live: a region pool, per-region ticking, the global tick barrier on. | Declared, no reader |
| regionized_aggressive | regionized with the barrier off and the Tier-2 offloads enabled, behind an acknowledgement checklist. Never the default. | Declared, no reader |
| diagnostic | compat execution with verification forced on and the linter's full per-plugin report. | Declared, no reader |
| vertical | Reserved and inert: parallel world ticking, which is not part of this design. | Declared, no reader |
Why restart-time only
Three pieces of live server state are mode-shaped and cannot be rebuilt while the server runs:
- The region graph. Every loaded chunk holder belongs to exactly one
region. Going to
regionizedmeans constructing that partition over every loaded chunk in every world; going back means dissolving it — while those same holders are the objects the chunk system is asynchronously generating, loading and saving. - Entity ownership. Under
regionized, every entity is owned by the region that owns its chunk. A dissolution would have to drain every region's entity work, rewrite every owner, and resume — a full stop-the-world more failure-prone than a restart, because the failure mode is a half-migrated entity visible to players. - The scheduler. Sync tasks, region queues and the global region's deadline map all carry work bound to a thread contract. Switching the contract under a live queue orphans tasks or runs them on the wrong thread.
The effective mode is resolved once at boot into an immutable process-level value, so a config file edited mid-run cannot produce a half-applied mode even by accident.
Why web-panel-only
The mode is a restart-time, server-wide execution-model change; in-game settings are the runtime-suitable surface. Two arguments close it:
- An in-game flip would be a lying command.
exec.mode's readers run at boot./meow config set exec.mode regionizedon a live server would answer "done" and change nothing until reboot. - The tradeoff belongs in front of the operator, with the file open — see the web panel.
Consequences:
- the in-game
/meow config setpath refusesexec.mode, naming the web panel as the only supported UI; - in the panel, the toggle is a first-class control with a compat warning and a
typed confirmation, badged
restart; - direct YAML editing still works — the boot parses whatever you wrote and fails loudly if it is wrong. "Web-panel-only" is a statement about the provided UIs, not a file lock.
Server-wide, enforced
Modes and the region grid are server-wide by enforcement, not by convention.
The config system fails loudly at startup if a world file declares a
concrete exec.mode that differs from the global one, naming the offending
path. There is no per-world mode mixing: a mixed server runs the whole server
at one mode and relies on per-plugin pinning.
Back-pressure: the global tick barrier
With regions decoupled onto separate threads, a producer region (a farm in one dimension) runs regardless of consumer-region load; once a consumer's tick overruns, its backlog grows beyond what the tick can clear, and the loop is irreversible — a failure mode reported against regionized servers with no plugins at all. Single-threaded servers cannot produce it, because every dimension slows together.
The design's answer is the global tick barrier, on by default at regionized:
regions still execute in parallel within a tick, but no region begins tick
N+1 until every region has finished tick N — so a tick is bounded by its
slowest region, the honest price of the property. Opting out is
regionized_aggressive, behind an acknowledgement checklist; portal-linked
region affinity (paired regions preferring the same thread) is layered on top
of the barrier, never offered instead of it.
Plugin tiers under each mode
The tier model in plugin policies gets its meaning from the modes.
| Tier | At compat | At regionized |
| --- | --- | --- |
| pinned (default; any unmarked plugin) | Everything on the one tick thread. | All sync tasks, commands and listeners on the global region, strictly serially. Correct, never wrong, never refused. |
| regionized | Runs correctly and serially — the declaration is a request, not a requirement. | Real region dispatch; its events fire on the owning region. |
| agnostic | As pinned. | Region dispatch, with the linter verifying its claim to touch no region-owned state. The right tier for library plugins. |
The sharpest rule is about events: if even one unmarked plugin listens to a hot event, the whole event class runs on the global region — correct, and visible, with the linter naming the plugin holding it serial. The compatible choice is structural and the parallel choice is earned, per event, by declaration.
The keys the design reads
Already declared (see global keys — exec):
| Key | Default | Purpose in the design |
| --- | --- | --- |
| exec.region-threads | -1 | -1 selects the reserve-then-fill sizing, which subtracts the other configured pools from the core count instead of double-booking them. |
| exec.region-threads-max | 16 | The clamp on the fill policy. |
| exec.region-scheduler | work_stealing | The region pool policy. work_stealing stays A/B-gated before it is settled. |
| exec.global-tick-barrier | true | The tick-boundary barrier above. |
| exec.region-tick-budget-ms | 0 | A soft per-region budget. 0 = unlimited; exceeding it logs and the region does not skip work — the barrier protects everyone else. |
| exec.teleport-handoff-timeout-ms | 500 | The cross-region teleport handoff, set above worst-case region-merge duration. |
| exec.region-grid-exponent | 4 | The section size (16×16 chunks). Server-wide. |
| exec.verification-mode | false | The boundary asserts; the mode that produces the per-plugin report. |
Proposed by the design, not implemented: exec.region-task-queue-depth
(384, a bound on per-region task backlog), exec.handoff-max-per-tick (64, a
bound on per-tick handoff work), exec.affinity-portal-linked (true),
profiling.region-sample-hz (1) and profiling.budget-warn-fraction (0.8).
Every one of these is a bound, not a tuning promise.
Firmly not proposed: a per-world mode (the config forbids it), a new mode value (the five-value enum is the stable surface), and any knob named after a goal.
What this page does not claim
- No speedup, in either direction — both are hypotheses until the design's measurement gates clear, and those gates are named, hard and may fail.
- No plugin compatibility matrix — none exists; no plugin has ever been measured on a MeowMC build.
- No mode flip while the server runs, and no in-game toggle — the restart-time and web-panel-only rules are design properties, not v1 limitations.
Related
- The web admin panel — the only UI the mode toggle lives in.
- In-game settings — the runtime-suitable half, and the negative list that keeps the mode out of chat.
- Global keys — exec — where the keys above are declared today, with their status.
- Plugin policies — the per-plugin tier file.