Core API Reference
For a developer building a mod that consumes Warring Nations' public API, the way WN Siege, WN Discord, and WN BlueMap already do, rather than someone running or playing on a server.
Entry Point
Everything a consuming addon needs lives under a single Java package, plus its claim/data/economy subpackages.
dev.tacyeet.warringnations.api (plus its .claim, .data, and .economy subpackages). See that package's package-info.java for the full "start here" tour.
Returns Optional<WarringNationsAPI>, empty until the server has finished starting. Every query and mutator hangs off the instance this returns.
Reacts to nation, war, claim, diplomacy, and government changes as they happen, instead of polling WarringNationsAPI.
Compiling Against It
WN Core is a hard runtime dependency for an addon that uses this API, not a soft or optional one, unlike WN Siege's soft dependency on WN Discord (which is guarded with a mod-loaded check and reflection since that integration is genuinely optional). Because it's hard, no reflection or version-probing is needed to call WarringNationsAPI itself, see Compatibility below for why.
On the Gradle side, compileOnly against a built warring-nations-neoforge-*.jar, and declare WN Core as a required dependency in your mod metadata (neoforge.mods.toml or equivalent). The existing addons all do this as a glob against WN Core's own build output, not a pinned filename, since WN Core's version changes independently of theirs:
compileOnly fileTree(dir: '/path/to/wn-core/neoforge/build/libs',
include: ['warring-nations-neoforge-*.jar'], exclude: ['*-sources.jar'])
Compatibility Across Versions
Every WarringNationsAPI accessor added after the interface's original release is a default method with a documented, safe fallback (an empty Optional, an empty list, false, an UNAVAILABLE-shaped result, and so on). Concretely, this means:
Code compiled against an older WN Core jar stays binary-compatible if the server ends up running a newer WN Core: it simply never calls the newer methods it doesn't know about.
Code compiled against a newer WN Core jar degrades gracefully if the server is on an older build that predates a given accessor. The interface itself is never implemented by the caller, so there's no AbstractMethodError risk from a missing override; the fallback is WN Core's own concern, not the addon's.
There is no shared version-guard helper in the API for this, because none of the three existing addons need one: the graceful-default shape above already covers the "core predates this method" case, and WN Core presence itself is a single WnApi.get().isPresent() (or Optional chaining) check, not something worth its own reflection ceremony.
Error Behavior
WnApi.get() returning empty (core not installed, or the server hasn't finished starting) is the only "is the API even there" check worth doing up front. After that:
A missing nation, war, village, or claim always reads as an empty Optional or empty collection, never null, never a thrown exception.
Every headless mutator (declareWar, createNation, kickMember, ...) returns a definite result value (UNAVAILABLE, false, or similar) when world state isn't ready, instead of throwing.
A handful of methods document a genuine thrown exception as intentional on bad input, for example setTier with a tier ordinal out of NationTier.values() range, so a misconfigured addon fails loudly at the call site instead of silently corrupting state. These are called out explicitly in each method's own Javadoc; if a method's doc doesn't mention throwing, assume it degrades to a safe default instead.
Threading
Every mutator on WarringNationsAPI must be called on the server thread unless its Javadoc says otherwise.
Worked Example
A minimal read and a minimal event subscription, in the two shapes every addon call site ends up using.
WnApi.get().ifPresent(wn -> {
wn.nationById(nationId).ifPresent(nation ->
System.out.println(nation.name() + " has " + nation.memberIds().size() + " members"));
});
WnEvents.subscribe(WnEvents.WarStarted.class, e ->
System.out.println("War " + e.warId() + " declared: " + e.attacker() + " vs " + e.defender()));
See WnAccess in WN BlueMap, or WnLookup/WnFeeds in WN Discord, for a fuller real-world wrapper pattern: a thin, null-safe facade over WnApi.get() so call sites never need to null-check or Optional-chain themselves.
