forked from Kweebec_Network/core-vitals
init
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
# ServerRegistry
|
||||
|
||||
Every server in the network heartbeats its presence to Redis. Every server keeps a local snapshot of who's online and reacts to changes via Hytale events.
|
||||
|
||||
## API
|
||||
|
||||
```java
|
||||
public interface ServerRegistry {
|
||||
ServerInfo getSelf();
|
||||
Collection<ServerInfo> getServers();
|
||||
Collection<ServerInfo> getServersByRole(String role);
|
||||
@Nullable ServerInfo getServer(String id);
|
||||
}
|
||||
```
|
||||
|
||||
All four are local cache reads. No Redis round-trip per call.
|
||||
|
||||
```java
|
||||
public final class ServerInfo {
|
||||
String id(); // e.g. "lobby-3f2a91bd"
|
||||
String role(); // e.g. "lobby", "bridge_duel"
|
||||
String address(); // currently unused, reserved for future
|
||||
int players(); // last reported player count
|
||||
long startedAt(); // epoch millis when the server booted
|
||||
long lastSeen(); // epoch millis of last heartbeat we observed
|
||||
}
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
On `start()`:
|
||||
|
||||
1. Subscribe to `network:events` (presence channel).
|
||||
2. Write own heartbeat to `network:servers:<id>` (TTL 15s).
|
||||
3. Scan `network:servers:*` to populate the local cache and fire join events for everyone discovered.
|
||||
4. Publish a `hello` on `network:events`.
|
||||
5. Schedule a heartbeat task every 5s. Each tick rewrites the heartbeat (resetting TTL) and re-scans.
|
||||
|
||||
On `stop()` (clean shutdown only):
|
||||
|
||||
1. Publish a `goodbye` on `network:events`.
|
||||
2. Delete the registry key.
|
||||
|
||||
If a server hard-crashes (kill -9, OOM, network partition), no goodbye is sent. Other servers learn it's gone when the 15s TTL expires the key and the next scan doesn't see it.
|
||||
|
||||
## Detection latency
|
||||
|
||||
| Event | When other servers learn |
|
||||
|---|---|
|
||||
| New server boots | Within a few hundred ms (immediate hello over pub/sub) |
|
||||
| Server clean-shutdown | Within a few hundred ms (immediate goodbye over pub/sub) |
|
||||
| Server hard-crash | Up to 20s (TTL 15s + up to 5s for the next scan) |
|
||||
|
||||
## Events
|
||||
|
||||
When the registry's view of the network changes, it dispatches these on the Hytale event bus:
|
||||
|
||||
```java
|
||||
import net.kewwbec.networkcore.api.events.ServerJoinedNetworkEvent;
|
||||
import net.kewwbec.networkcore.api.events.ServerLeftNetworkEvent;
|
||||
|
||||
getEventRegistry().registerGlobal(ServerJoinedNetworkEvent.class, event -> {
|
||||
ServerInfo server = event.getServer();
|
||||
LOGGER.at(Level.INFO).log("Server joined: %s (role=%s)", server.id(), server.role());
|
||||
});
|
||||
|
||||
getEventRegistry().registerGlobal(ServerLeftNetworkEvent.class, event -> {
|
||||
ServerInfo server = event.getServer();
|
||||
LOGGER.at(Level.INFO).log("Server left: %s", server.id());
|
||||
});
|
||||
```
|
||||
|
||||
Event handlers run on the Hytale event-bus dispatch thread. Same world-thread caveat as MessageBus subscribers: if you touch entities, hop to `world.execute(...)`.
|
||||
|
||||
## Server id and role
|
||||
|
||||
The `id` is either the value in `server.id` in `config.json`, or auto-generated as `<role>-<8 hex chars>` on every boot.
|
||||
|
||||
The `role` is a free-form string from `server.role`. Use snake_case. Examples:
|
||||
|
||||
- `lobby` - hub/main world
|
||||
- `bridge_duel` - one Bridge Duel match host
|
||||
- `arena` - PvP arena
|
||||
- `staff` - admin-only world
|
||||
|
||||
Roles are how plugins route. Example: a queue plugin running on a lobby finds an available `bridge_duel` server with:
|
||||
|
||||
```java
|
||||
Collection<ServerInfo> available = core.getServerRegistry()
|
||||
.getServersByRole("bridge_duel")
|
||||
.stream()
|
||||
.filter(s -> s.players() < 20)
|
||||
.toList();
|
||||
```
|
||||
|
||||
## Redis layout
|
||||
|
||||
For reference, what NetworkCore writes:
|
||||
|
||||
```
|
||||
network:servers:<id> HASH server heartbeat record, TTL 15s
|
||||
fields: id, role, players, started_at, last_seen
|
||||
network:events CHANNEL pub/sub for presence (hello/goodbye)
|
||||
```
|
||||
|
||||
`network:` is the default `redis.key_prefix`. Change in config if needed.
|
||||
Reference in New Issue
Block a user