PGM: The XML Engine That Keeps Minecraft PvP Deterministic

A deep dive into the match runtime, filter logic, and legacy-compatibility tricks that turn a Bukkit plugin into a competitive game manager.

9 to 11 min read • View on GitHub • More from PGMDev

An editorial engraving of an arena control desk where XML tags sit beside a live Minecraft match. The scene explains that PGM turns declarative map data into active game rules, not just static configuration.
PGM treats a map file like source code for a match. XML defines the logic, and the runtime turns it into a controlled arena.
Key Takeaways

Why PGM Feels Like a Game Engine, Not a Plugin

PGM does not read like a typical Bukkit project that listens for events and patches behavior around the edges. It reads like a small runtime for competitive matches, with a clear lifecycle, its own player abstraction, and XML that describes how the game should behave.

That is the surprise. The code is not trying to hide the game in a framework. It is trying to make the game legible, deterministic, and editable by the people building maps.

The key move: map authors define regions, kits, filters, and actions in XML, and PGM compiles that into match behavior. The map is not just content. It is program logic.

PGM compiles XML into runtime objects, then binds those objects into a match lifecycle. The point is not configuration loading. The point is compilation into behavior.

The Map Is the Program

The core architecture starts with factories and parsers, not with a monolithic match handler. The repo’s design centers on a fluent XML parsing pipeline that turns nested tags into game objects the runtime can use.

<map>
  <regions>
    <rectangle id="mid" min="-10,0,-10" max="10,20,10" />
  </regions>
  <kits>
    <kit id="starter">
      <item material="IRON_SWORD" />
    </kit>
  </kits>
  <filters>
    <all>
      <team name="red" />
      <holding material="DIAMOND_SWORD" />
    </all>
  </filters>
  <actions>
    <teleport region="spawn" />
  </actions>
</map>

The important part is not the exact tag names. It is the shape of the system. XML is parsed into region definitions, filter trees, kits, and actions, then those pieces are bound into the match runtime through dedicated parsers such as RegionParser, FilterParser, and KitParser.

That gives the project a clean separation between authored rules and engine behavior. Map makers can change the game without rewriting engine code, and the engine can stay strict about how those rules are interpreted.

Filters, Regions, and Actions: PGM’s Hidden Language

The most revealing detail in PGM is the filter model. Instead of forcing every rule into a binary yes or no, filters can return ALLOW, DENY, or ABSTAIN. That one extra state changes how composition works.

PatternTypical plugin logicPGM logic
Decision modelBoolean checks at event timeTri-state filters that can compose
Authoring styleImperative handlers in JavaDeclarative XML rules
Rule chainingManual control flowNested filters and regions
Failure modeOne handler owns the decisionMultiple filters can defer or override
Mental modelPatch behavior around eventsCompile behavior into the match

ABSTAIN matters because it lets a filter opt out without forcing the whole chain to resolve immediately. A region can narrow scope, a team rule can add context, and a final filter can still make the call. That is why the system feels like a rules engine instead of a pile of ad hoc checks.

A close-up mechanical logic gate with three branches labeled by behavior rather than color. The image explains how PGM composes ALLOW, DENY, and ABSTAIN without collapsing every decision into a simple yes or no.
PGM’s filter chain is composable because a rule can allow, deny, or step aside. That makes it possible to layer gameplay conditions without flattening them too early.

Matches Are Isolated Worlds

PGM treats each match as its own managed scope. The match lifecycle moves through states like LOADED, RUNNING, and FINISHED, and modules enable or disable around those transitions.

That matters because a Bukkit server is normally a shared global space. PGM pushes back against that assumption. It creates a virtual boundary around each match so game logic can stay coherent even when the JVM is doing many things at once.

ConceptConventional Bukkit pluginPGM
World modelOne shared event surfaceA scoped match runtime
LifecycleHandlers react continuouslyModules enable and disable by phase
IsolationLoose conventionExplicit match boundaries
ConcurrencyUsually one game context at a timeMultiple matches can coexist on one JVM
MaintenanceCross-cutting listenersModule-based responsibilities

The MatchModule pattern is doing serious work here. It lets the engine attach behavior to a match phase without making every subsystem know about every other subsystem. That is old-school design in the best sense: explicit, inspectable, and hard to misunderstand.

Why MatchPlayer Exists

PGM wraps Bukkit’s Player in its own match-aware abstraction because the raw API does not model competitive state very well. A player is not just connected or disconnected. They may be participating, dead, spectating, away from keyboard, or carrying match-specific metadata.

public interface MatchPlayer {
  boolean isParticipating();
  boolean isDead();
  boolean isObserving();
  boolean isAfk();
  Match getMatch();
}

That wrapper is not ceremony. It is the engine making a claim about what matters. Competitive matches need state that survives beyond the vanilla Bukkit concept of a player entity, and PGM keeps that state close to the match itself.

Legacy Support Is the Product

PGM’s 1.8 focus is not a compromise at the margins. It is part of the point. The competitive Minecraft scene cares about a specific combat model, specific movement timing, and a specific era of mechanics, so the engine is built to preserve that environment rather than chase newer defaults.

QuestionModern-version-first pluginPGM
Primary goalAdopt the newest platform behaviorPreserve a known competitive ruleset
Compatibility workUsually optionalCore to the architecture
Cross-version supportOften a thin adapter layerBuilt into platform and utility code
Product priorityGeneral server convenienceDeterministic legacy gameplay

That is why utilities like version handling and compatibility layers matter so much. The code is not merely surviving an old version. It is actively protecting a specific game feel.

The Trade-Off: Less Magic, More Legibility

The repository’s style is more explicit than a lot of modern Java codebases. The global access pattern is visible. The engine avoids hiding dependencies behind a large injection framework. That makes the code easier to trace for contributors who need to understand the flow quickly.

In enterprise software, that style might look dated. In a community-maintained game engine, it can be a feature. PGM favors readability over abstraction debt, and the result is a system that more people can reason about when they need to change match logic, parser behavior, or compatibility code.

Design choiceTrade-offWhy it fits PGM
Explicit globalsLess architectural eleganceEasier to trace in a community codebase
No heavy DI layerFewer indirectionsMap and match flow stay visible
Strict interfacesMore upfront structureThe engine stays pluggable without becoming opaque
Legacy supportMore compatibility workThe scene depends on older mechanics

PGM’s philosophy is consistent: keep the runtime disciplined, keep the rules declarative, and keep the code legible enough that someone can extend it without reverse-engineering a framework.

What PGM Preserved, and What It Refused to Change

PGM is interesting because it preserves a competitive ruleset without pretending the surrounding ecosystem moved on. It does not modernize the game by flattening its history. It preserves the parts the scene actually cares about and builds a runtime that can enforce them.

That is a strong editorial lesson too. Some open-source projects win by chasing novelty. Others win by becoming infrastructure for a culture that values consistency. PGM belongs to the second group.