MeowMC

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:

  1. The region graph. Every loaded chunk holder belongs to exactly one region. Going to regionized means 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.
  2. 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.
  3. 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 regionized on 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 set path refuses exec.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.