API Integration
Other plugins integrate against the small, versioned FlagForgeApi interface
(flagforge-api module), obtained via Bukkit's services manager:
FlagForgeApi api = Bukkit.getServicesManager().load(FlagForgeApi.class);
if (api == null) {
return; // FlagForge isn't installed/enabled
}
// Declare a plugin-granted reward before mutating inventory, so reconciliation doesn't flag it
// as an unexplained gain.
TransactionSpec spec = new TransactionSpec(
"ECONOMY_REWARD", player.getUniqueId(), TransactionSpec.Direction.CREDIT,
"minecraft:diamond", 100, Duration.ofSeconds(30));
TransactionHandle handle = api.beginItemTransaction(this, spec);
giveDiamonds(player, 100);
api.completeItemTransaction(handle);
// Grant a temporary, scoped exemption (e.g. inside your own arena/minigame plugin).
ExemptionSpec exemption = new ExemptionSpec(
ExemptionSpec.ScopeType.CATEGORY, "COMBAT", "PvP arena zone", Duration.ofMinutes(10));
ExemptionHandle exemptionHandle = api.exempt(this, player.getUniqueId(), exemption);
// api.revoke(exemptionHandle); once the arena match ends, to end it early.
// Submit a finding from your own detection logic into FlagForge's pipeline/alerting.
api.submitFinding(this, new ExternalFinding(
"myplugin.suspicious_teleport", Category.MOVEMENT,
EvidenceClass.HEURISTIC, player.getUniqueId(), 5, 0.6,
"Teleported across dimensions with no known API cause", Map.of()));
// Read a player's current aggregated risk (available once at least one finding has fired them
// through the policy engine this session).
Optional<PlayerRiskView> risk = api.risk(player.getUniqueId());
// Authorize your own plugin's items to exceed a vanilla limit, so inventory.illegal_item.a /
// inventory.illegal_stack.a never flag them - e.g. a custom enchant plugin's own Efficiency 10
// pickaxes. Server-wide and permanent (not player- or time-scoped) until revoked or this plugin
// disables, at which point every authorization it issued is revoked automatically.
ItemAuthorizationSpec authorization = new ItemAuthorizationSpec(
"minecraft:diamond_pickaxe", ItemAuthorizationKind.ENCHANTMENT_LEVEL,
"minecraft:efficiency", 10, "MyEnchantPlugin's crate-exclusive pickaxes");
ItemAuthorizationHandle authorizationHandle = api.authorizeItem(this, authorization);
// api.revokeItemAuthorization(authorizationHandle); if you stop handing these out.
Every handle validates the calling plugin's ownership (a handle from one plugin cannot be
completed/revoked by another) and expires — an abandoned or expired handle is ignored, never
silently trusted. ItemAuthorizationHandle is the one exception to expiry: it stays active until
explicitly revoked or the issuing plugin disables. See the Javadoc on each flagforge-api class
for full method contracts.
Worked example: a shop-gated build tool ("printer" mode)
A common pattern: a custom Creative-like mode where players place blocks rapidly through a
plugin-controlled tool (charged per block via a shop, no commands allowed). The block-placement
rate this produces is exactly what world.fastplace.a/world.nuker.a/world.impossible_action.a
exist to catch for an ordinary player — but here it's expected and legitimate, since every
placement is already gated by your own shop logic.
Don't work around this by intercepting or cancelling packets before FlagForge sees them - that fights the anti-cheat instead of telling it what's actually going on, and breaks the moment FlagForge's internals change. Do grant a scoped exemption for exactly as long as the player is in that mode:
// When a player enters your build tool:
ExemptionSpec printerMode = new ExemptionSpec(
ExemptionSpec.ScopeType.CATEGORY, "WORLD",
"Shop-gated build tool - placement rate is expected here", Duration.ofHours(2));
ExemptionHandle handle = api.exempt(this, player.getUniqueId(), printerMode);
activeExemptions.put(player.getUniqueId(), handle); // track it yourself, keyed by player
// When they leave (or disconnect) - revoke immediately rather than waiting out the duration:
ExemptionHandle handle = activeExemptions.remove(player.getUniqueId());
if (handle != null) {
api.revoke(handle);
}
CATEGORY/"WORLD" covers every world check at once (placement rate, break rate, reach, etc.) for
that one player, only while the exemption is active. If you only need to exempt placement
specifically and still want break/reach checks active, scope it narrower instead:
ExemptionSpec.ScopeType.CHECK with "world.fastplace.a" (one exempt() call per check ID you
need). The duration is a safety net, not the primary mechanism - always revoke() on exit so a
disconnect or a bug in your own mode-tracking can't leave a player permanently exempted.
Events
Beyond the interface, flagforge-api also ships three plain Bukkit events other plugins can listen
for instead of polling:
FlagForgeFindingEvent— fired for every finding, right after it is durably recorded. Cancellable; cancelling suppresses only the policy action that would follow, never the recorded finding itself.FlagForgeActionEvent— fired immediately before a preventable response (CANCEL,SETBACK,KICK,COMMAND) actually executes against a player. Cancellable; cancelling blocks just that one action.FlagForgeRiskChangeEvent— fired when a player's global risk changes by a meaningful amount, rate-limited per player. Informational only, not cancellable.