uWorldGuard

A from-scratch, Folia-ready reimagining of WorldGuard.

javapaperfoliaminecraft
On this page 6 sections
  1. Built for Folia, not bolted onto it
  2. The hot path is the whole point
  3. A dial for the most expensive event in the game
  4. Bypass you have to arm
  5. An API worth depending on
  6. Under the hood

uWorldGuard is a land and region protection plugin for Paper Minecraft servers, built from the ground up as a spiritual successor to the classic WorldGuard. Same core idea (draw a box in the world, then control what’s allowed inside it), rewritten for modern server hardware with a lot of the old rough edges sanded off.

If you’ve never run a server: a region is a named 3D area an admin defines, and flags are the rules attached to it. You pick two corners, give the region a name, and then start flipping flags: no PvP here, no TNT there, this area heals you, that one greets you on the way in. It’s the backbone of how most survival servers stop griefing and carve out spawns, shops, and arenas.

A quick tour of what it does:

  • Four region shapes (cuboid, cylinder, sphere, and polygon), plus a world-wide global region. Old WorldGuard really only gave you cuboids and 2D polygons, so spheres and true cylinders are new toys.
  • Over 165 flags out of the box covering the full WorldGuard set, plus a pile I wanted for myself: end-crystal placement, wind charges, crop trampling, chorus-fruit teleports, chambered ender pearls, per-region healing and potion effects, level-gated entry, teleport-on-entry, run-a-command-on-entry, and more. They’re filed into eight categories (protection 38, environment 26, mobs and explosions 21, player state 24, items and commands 23, entry and exit 15, messages and sound 14, movement 6), and they keep up with modern Minecraft: mannequins, copper golems, vaults, crafters, and breeze charges all have their own flags. hopper-transfer is judged at the destination, so a hopper chain can’t quietly drain a chest across a region border.
  • Flags can be aimed at a group. State flags take -g, so /wg flag spawn pvp deny -g nonmembers stops outsiders fighting while leaving members alone. Groups are all, members, owners, nonmembers, nonowners, and none, and WorldGuard’s non-members and non_members spellings are both accepted so old muscle memory keeps working. Values go beyond allow/deny too: numbers, text, material sets, entity-type sets, string sets, and potion-effect sets.
  • A real in-game GUI. Editing flags and members happens in a clean menu (built on InvUI) so you don’t have to memorize command syntax. Prefer the keyboard? Every action has a command too.
  • Selections your way: it hooks into WorldEdit if you’ve got it, or hands you a built-in wand if you don’t.
  • Owners, members, priorities, and parent/child inheritance, so a big region can set the baseline and smaller ones layer on top.

Already running WorldGuard? There’s a /uwg migrate worldguard command that reads your existing plugins/WorldGuard/worlds/<world>/regions.yml files and pulls them straight in. Because uWorldGuard mirrors WorldGuard’s flag names one-to-one, flags round-trip by name, and the command even shares WorldGuard’s own /wg alias so it can slot in as a drop-in replacement. Regions whose id already exists are reported as conflicts and left untouched, so a migration never quietly clobbers something you’d already set up.

Built for Folia, not bolted onto it

The big architectural bet is Folia compatibility and thread-safety by default. Folia shards the server across many region threads instead of running everything on one main thread, which is fantastic for large servers and merciless toward plugins that assumed single-threading. A region check can fire from any thread at any time.

So uWorldGuard is written for that world from the first line. No BukkitRunnable, no Bukkit.getScheduler(): world and block work goes through the RegionScheduler, entity-following work through the EntityScheduler, and disk or database I/O through the AsyncScheduler so it never blocks a region tick. Every piece of shared state is a concurrent structure, counters use atomic operations, and the region managers are safe to query while a world is still loading its regions in the background.

The hot path is the whole point

Region lookups are the single most frequent thing this plugin does. Every block placed, every step taken, every explosion resolved asks “what regions apply here, and what do they say?” If that answer is slow, the whole server feels it, so performance is treated as a feature rather than an afterthought.

The core trick is a per-chunk candidate cache. The first query in a chunk scans every region for the ones whose bounding box overlaps it and caches that list (usually tiny, very often empty). Every later query in that chunk only tests those few candidates instead of the whole world. Some details I’m proud of:

  • The cache is a fixed-capacity, direct-mapped table of 16,384 slots keyed by primitive long chunk coordinates, using Fibonacci hashing to spread chunks across slots. No boxing, no eviction bookkeeping, bounded memory.
  • The overwhelmingly common case (standing in unprotected wilderness) collapses to a single array-slot read that returns a shared, pre-allocated empty result. Zero allocation on the path that runs millions of times.
  • When a region is added or removed the whole table is swapped out for a fresh one rather than cleared in place, so a query racing the invalidation can never publish a stale candidate list.

A spatial index like an R-tree is the right answer for worlds packed with thousands of huge overlapping regions, but for real servers the chunk cache turns nearly every lookup into a couple of cheap comparisons. Coordinate math leans on bit tricks (x >> 4 for block-to-chunk, for instance) to skip allocating throwaway objects, and the query facade funnels blocks, entities, and locations into one primitive (world, x, y, z) entry point so a lookup costs nothing for the position itself.

A dial for the most expensive event in the game

PlayerMoveEvent is the highest-frequency event a region plugin touches. It fires many times per second per player, and it’s where entry and exit are decided. Rather than pick one strategy for everybody, movement.mode lets an admin trade exactness for throughput:

EVENT (the default) handles the move event directly. It’s exact: a denied entry is cancelled before the player moves, so there’s no visible bounce, and greeting and farewell messages fire the instant the border is crossed. The cost scales with how much your players actually move.

TASK ignores the event and polls every task-interval-ticks instead (4 by default, a fifth of a second). Now the cost scales with player count and the interval rather than with movement, which makes it cheaper and, more importantly, predictable under load: a hundred players sprinting cost exactly what a hundred players standing still do.

The trade-offs are real, and the config file states them plainly rather than burying them. A denied crossing can no longer be cancelled, so the player is teleported back afterwards and sees a brief rubber-band. Entry and exit effects can lag by up to one interval. And a player who crosses into a region and leaves again within a single interval is never seen entering it at all.

TASK mode also does something EVENT mode structurally can’t: about once a second it sweeps for players standing somewhere that refuses them, and moves them out. Not every unwanted presence is a crossing. Logging in inside a no-entry region isn’t one. Neither is an admin drawing a region around somebody who was already there, or flipping an entry flag while they’re stood inside it. A pure movement hook never sees any of those, because nobody moved. Anyone the sweep evicts goes back to the last spot they were accepted at, or world spawn if there isn’t one.

Bypass you have to arm

A small decision I feel strongly about. uworldguard.bypass is declared in the plugin manifest with default: false, including for operators, which is deliberate: an undeclared permission node defaults to OP in Bukkit, and that quietly hands every operator on the server a permanent pass through every region they own or don’t. Plenty of protection plugins ship exactly that hole without meaning to.

Holding the node isn’t enough on its own either. Bypass takes both halves: you have the permission and you ran /wg bypass to arm it. It disarms itself on logout, so nobody leaves it on by accident across sessions, and revoking the node takes effect immediately even on a player who already armed it.

An API worth depending on

The project splits into two Gradle modules: a lean api module (published as com.tricrotism:uworldguard-api) and the plugin that implements it. Other plugins compile against the API and get first-class access at runtime:

  • Register your own flags with Flags.register(...) and they behave exactly like built-ins. They persist, resolve, show up in the flag menu, and appear in command tab-completion. My own GSit seating integration is wired in this exact way.
  • Grab the RegionContainer either as a registered Bukkit service or through a static UWorldGuardApi handle, then ask it region questions from your own code.

Under the hood

A few more things worth mentioning for the curious:

  • Storage is pluggable. YAML files by default, or an optional SQLite backend over plain JDBC. Both write the exact same document, so the two are interchangeable and you can switch without a conversion step. Loading and saving always happen off-thread, and if the SQL backend fails to come up the plugin falls back to YAML with a warning rather than refusing to start.
  • Three optional integrations, all genuinely optional. WorldEdit swaps its selection in for the built-in wand (and is what makes define-polygon possible), and a flag guard enforces the worldedit flag against WorldEdit’s own operations so a region can refuse mass edits. PlaceholderAPI resolves %placeholders% inside message flags and inside the entry-min-level / entry-max-level gates. And GSit picks up four extra flags (sit, playersit, pose, crawl), enforced on uWorldGuard’s side because GSit can’t see region flags on its own. Install them or don’t; nothing breaks either way.
  • A modern stack, kept lean. Commands run on Cloud (annotation-driven, permission-gated), all text is Adventure + MiniMessage (no dusty legacy color codes), and GUIs go through InvUI. Most of that isn’t shaded into the jar: a custom PluginLoader downloads the libraries into a shared classloader at boot, so the plugin ships small and there’s still nothing for you to install. No database to set up, no other plugins required.
  • A per-world EventGate lets an admin tell uWorldGuard to ignore specific Bukkit events in specific worlds (blacklist or whitelist), with the filter check kept off the hot path so switching it on costs effectively nothing. (Credit for that idea goes to @The60th.)
  • Upgrades don’t eat your config. New keys from the jar’s template are merged into your existing config.yml on boot, and the plugin logs exactly which ones it added. Movement mode, poll interval, and autosave cadence are all re-readable with /wg reload.
  • Built on Java 25 against Paper 26.2.

It’s open source (MIT) and lives on GitHub if you want to poke around, run it yourself or even add stuff to it on your own. The README is the full manual: every flag, every command, and every config key.