Warring NationsWarring Nations MC 1.21.1.NEOFORGE.JAVA 21
Wiki/Developer/Core API Reference

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.

ADDON DEVS
Consuming, not extendingThis page is about calling into WN Core's public API from another mod. If you want to extend WN's own mechanics instead (new claim rules, new war-script effects, per-nation custom data), see Architecture for the extension-point pattern.

Entry Point

Everything a consuming addon needs lives under a single Java package, plus its claim/data/economy subpackages.

PACKAGE

dev.tacyeet.warringnations.api (plus its .claim, .data, and .economy subpackages). See that package's package-info.java for the full "start here" tour.

WnApi.get()

Returns Optional<WarringNationsAPI>, empty until the server has finished starting. Every query and mutator hangs off the instance this returns.

WnEvents.subscribe(SomeEvent.class, handler)

Reacts to nation, war, claim, diplomacy, and government changes as they happen, instead of polling WarringNationsAPI.

i
Supported surface. Nothing outside those packages is a supported dependency. The rest of the codebase is internal and can change shape between releases without notice.

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'])
!
No published artifact yet. This assumes a WN Core checkout built locally (./gradlew build from wn-core/neoforge). There is currently no published Maven artifact, so an integrator outside the core team's own machine needs their own local WN Core build to compile against.

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:

Old code, new core

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.

New code, old core

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:

Missing data

A missing nation, war, village, or claim always reads as an empty Optional or empty collection, never null, never a thrown exception.

Headless mutators

Every headless mutator (declareWar, createNation, kickMember, ...) returns a definite result value (UNAVAILABLE, false, or similar) when world state isn't ready, instead of throwing.

Intentional exceptions

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.

i
The one documented exception. placeBet is safe to call from any thread, since the actual economy withdrawal it triggers is asynchronous and the result callback is delivered back on the server thread for you.

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.