Skip to content
This repository was archived by the owner on Sep 11, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions PROVIDER.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,23 @@ Examples cover [anonymous NXS](examples/provider-local.yml),
The [NXS contract](https://github.com/teamziax/NetworkCompatible/blob/nxs-dev/docs/external-signalling/README.md)
and [native build instructions](docs/native-admission.md) describe the maintained
source chain. Native/fixture readiness does not establish stock-client gameplay.

## Warden shared fleet registration and runtime counts

The Warden connection wizard can generate `examples/provider-console.yml` and a
separate token file. Save the token in the extension data directory. Each live
replica needs its own persistent directory; preserving `provider-state` across
restarts preserves its runtime identity.

Warden organisation API keys select initial Signal Servers through enrolment
configuration. One Game Server can attach to several Signal Servers. The host's
`region` and `pool` data fields select that configuration; a Pool is not a separate
operator resource. Fleet completion can omit `publicAddress` and `serviceId`.
The extension logs the runtime identity and directs operators to the attached
Signal Servers for public addresses, without treating that response as a failure.

Heartbeat player telemetry samples Geyser's actual session-manager count and
sample time independently of server-list status. Counts can exceed a lowered
admission capacity; only the legacy load fraction is bounded to one. Public
status overrides never replace this actual-player sample. This consumer requires
the exact NetworkCompatible revision in `registration-network.properties`.
8 changes: 8 additions & 0 deletions examples/provider-console.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
signalling: nxs
nxs:
advertise-addresses: []
token: "file:warden-enrolment.token"
endpoint: "https://agent.warden.cloud"
data:
region: "EU"
pool: "lobby"
4 changes: 2 additions & 2 deletions registration-network.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
repository=teamziax/NetworkCompatible
commit=f34c0b0a37cd5933ac7f5d46febc3a92a6aad381
branch=nxs-dev
commit=5d0063699be6e037dda8013cf3d7962338a2edc2
branch=codex/fleet-control-plane
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ private void startProvider() {
initializingTransport = transport;
transport = new GameOutcomeTransport(transport, gameOutcomes);
ProviderClient client = new ProviderClient(runtime.clientConfiguration(), store, transport,
() -> providerStatusSupplier.get(), () -> new ProviderClient.Health(true, runtime.capacity(), Math.min(1, (double) GeyserImpl.getInstance().getSessionManager().size() / Math.max(1, runtime.capacity())), "nethernet", description().version()), message -> logger().warning(message));
() -> providerStatusSupplier.get(), () -> ProviderRuntimeObservations.health(GeyserImpl.getInstance().getSessionManager().size(), runtime.capacity(), System.currentTimeMillis(), description().version()), message -> logger().warning(message));
store = null; // ProviderClient now owns its lifetime.
initializingTransport = null;
synchronized (providerLifecycle) {
Expand All @@ -168,7 +168,7 @@ private void startProvider() {
}
client.start().whenComplete((registration, failure) -> {
if (failure != null) { logger().error("Provider startup failed: " + providerFailure(failure)); stopProvider(); return; }
logger().info("Provider address: " + registration.get("publicAddress").getAsString() + " (instance " + registration.get("instanceId").getAsString() + ")");
logger().info(ProviderRuntimeObservations.registrationMessage(registration));
WardenClaimAdapter claim = wardenClaim;
if (claim != null) claim.current().thenAccept(action -> action.ifPresent(value -> logger().info(value.message())));
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.geyser.extension.nethernet.provider;

import com.google.gson.JsonObject;
import org.cloudburstmc.netty.signalling.ProviderClient;

/** Host observations are independent of the publicly advertised server-list status. */
public final class ProviderRuntimeObservations {
private ProviderRuntimeObservations() {}

public static ProviderClient.Health health(int connectedPlayers, int capacity, long sampledAt, String build) {
return new ProviderClient.Health(true, capacity,
Math.min(1, (double) connectedPlayers / Math.max(1, capacity)), "nethernet", build,
new ProviderClient.PlayerCount(connectedPlayers, sampledAt));
}

public static String registrationMessage(JsonObject registration) {
String instanceId = registration.get("instanceId").getAsString();
var address = registration.get("publicAddress");
return address != null && !address.isJsonNull()
? "Provider address: " + address.getAsString() + " (instance " + instanceId + ")"
: "Provider instance registered: " + instanceId + "; public addresses are managed on its attached Signal Servers";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ private ProviderRuntimeConfiguration runtime(Path dir, String yaml, Map<String,S
Files.writeString(dir.resolve("config.yml"), yaml);
return ProviderRuntimeConfiguration.resolve(ConfigLoader.loadConfig(dir.resolve("config.yml").toFile(), env), dir, "::", 20000, 40);
}
@Test void loadsTheOperatorConsoleConfigurationWithSeparatePersistentIdentityAndToken(@TempDir Path dir) throws Exception {
Files.writeString(dir.resolve("warden-enrolment.token"), "console-test-token\n");
var config = runtime(dir, Files.readString(Path.of("examples/provider-console.yml")), Map.of());
assertEquals("automatic", config.clientConfiguration().registrationMode());
assertEquals("bearer-token", config.clientConfiguration().authorizationScheme());
assertEquals("console-test-token", config.authorizationToken());
assertEquals("EU", config.region()); assertEquals("lobby", config.pool());
assertEquals(Map.of(), config.tags());
assertEquals(dir.resolve("provider-state"), config.stateDirectory());
assertEquals(20001, config.udpPort()); assertEquals(40, config.capacity());
}

@Test void freshConfigHasOnlyTheFiveOptionsAndInheritsGeyser(@TempDir Path dir) throws Exception {
var config = ConfigLoader.loadConfig(dir.resolve("config.yml").toFile(), Map.of());
assertEquals("hybrid", config.signalling());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.geyser.extension.nethernet.provider;

import com.google.gson.JsonParser;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class ProviderRuntimeObservationsTest {
@Test void acceptsFleetRegistrationsWithoutAnOriginatingPublicAddress() {
var registration = JsonParser.parseString("{\"instanceId\":\"game-one\"}").getAsJsonObject();
assertTrue(ProviderRuntimeObservations.registrationMessage(registration).contains("game-one"));
registration.add("publicAddress", com.google.gson.JsonNull.INSTANCE);
assertTrue(ProviderRuntimeObservations.registrationMessage(registration).contains("attached Signal Servers"));
registration.addProperty("publicAddress", "https://play.example");
assertEquals("Provider address: https://play.example (instance game-one)", ProviderRuntimeObservations.registrationMessage(registration));
}

@Test void reportsActualPlayersEvenWhenAdmissionCapacityHasBeenReduced() {
var health = ProviderRuntimeObservations.health(143, 100, 123456789L, "test");
assertEquals(143, health.playerCount().connectedPlayers());
assertEquals(123456789L, health.playerCount().sampledAt());
assertEquals(100, health.capacity());
assertEquals(1, health.load());
assertEquals(0, ProviderRuntimeObservations.health(0, 100, 123456790L, "test").playerCount().connectedPlayers());
}
}
Loading