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.
- PGM is built around a match runtime that behaves more like infrastructure than a plugin, with maps compiled into live rules rather than handled as loose event callbacks.
- Its XML layer is the real product surface, because regions, kits, filters, and actions are authored declaratively and then bound into gameplay.
- The tri-state filter model gives PGM a rules engine feel, letting conditions compose without forcing every check into a hard yes or no.
- Legacy 1.8 support is not an accident here, but a design commitment that matches the competitive community the engine was built to serve.
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.
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.
| Pattern | Typical plugin logic | PGM logic |
|---|---|---|
| Decision model | Boolean checks at event time | Tri-state filters that can compose |
| Authoring style | Imperative handlers in Java | Declarative XML rules |
| Rule chaining | Manual control flow | Nested filters and regions |
| Failure mode | One handler owns the decision | Multiple filters can defer or override |
| Mental model | Patch behavior around events | Compile 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.
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.
| Concept | Conventional Bukkit plugin | PGM |
|---|---|---|
| World model | One shared event surface | A scoped match runtime |
| Lifecycle | Handlers react continuously | Modules enable and disable by phase |
| Isolation | Loose convention | Explicit match boundaries |
| Concurrency | Usually one game context at a time | Multiple matches can coexist on one JVM |
| Maintenance | Cross-cutting listeners | Module-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.
| Question | Modern-version-first plugin | PGM |
|---|---|---|
| Primary goal | Adopt the newest platform behavior | Preserve a known competitive ruleset |
| Compatibility work | Usually optional | Core to the architecture |
| Cross-version support | Often a thin adapter layer | Built into platform and utility code |
| Product priority | General server convenience | Deterministic 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 choice | Trade-off | Why it fits PGM |
|---|---|---|
| Explicit globals | Less architectural elegance | Easier to trace in a community codebase |
| No heavy DI layer | Fewer indirections | Map and match flow stay visible |
| Strict interfaces | More upfront structure | The engine stays pluggable without becoming opaque |
| Legacy support | More compatibility work | The 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.