Theme
Game server protocol
The WildM game server (game-server/server.js) exposes two ports, configured in server.json:
- HTTP on
httpPort(default30120) — used by the launcher; - TCP on
gamePort(default30121) — used by the client in game (wildm_net).
HTTP
GET /info
json
{
"name": "WildM LAN",
"desc": "Serveur de test WildM en réseau local",
"tags": ["roleplay", "dev"],
"locale": "FR",
"players": 0,
"maxPlayers": 32,
"host": "192.168.2.3",
"httpPort": 30120,
"gamePort": 30121,
"resources": { "count": 2, "totalSize": 1536, "hash": "3f1c2a9b0d4e5f61" }
}The launcher requires an integer gamePort (otherwise gamePort manquant). resources.hash is the first 16 hex characters of the sha256 of the manifest's file list. /info does not currently include spawn; the launcher accepts one (info.spawn) if a future server sends it.
GET /resources/manifest
json
{
"files": [
{ "path": "loadscreen/index.html", "size": 1012, "sha256": "…64 hex…" },
{ "path": "wildm-base/resource.json", "size": 524, "sha256": "…" }
],
"totalSize": 1536,
"hash": "3f1c2a9b0d4e5f61"
}Paths are relative to resources/, with / separators. The manifest is built once at server start.
GET /resources/file/<path>
Streams one file (application/octet-stream, with Content-Length). <path> is URL-encoded per segment. Only paths present in the manifest are served; anything else is 404.
Any other route answers 404.
What the launcher checks
- Each manifest entry must have a safe relative path (no
./.., no characters invalid on Windows, ≤ 240 characters), a 64-hexsha256and a non-negative integersize— otherwise the whole join fails. - Downloads go to
<file>.wildm-part, must match the exact size and sha256, and are then renamed.
TCP: JSON lines
One JSON object per line, UTF-8, terminated by \n. Lines that do not parse are ignored. A connection that accumulates more than 64 KB without a newline is closed. TCP_NODELAY is set on the server side.
Client → server
| Message | When | Notes |
|---|---|---|
{"t":"hello","name":"Joueur"} | First message after connecting | name is truncated to 24 characters (default Joueur). The client adds "token":"…" when wildm_connect.json has a root token field; the current server ignores it. Only the first hello of a connection counts. |
{"t":"pos","x":…,"y":…,"z":…,"h":…} | 10 Hz while in game (configurable pos_hz, 1–30) | Metres (x east, y north, z up); h = heading (yaw) in radians. Non-finite numbers are ignored per field. Ignored before hello. |
Server → client
| Message | When |
|---|---|
{"t":"welcome","id":3,"spawn":{"x":…,"y":…,"z":…},"name":"WildM LAN"} | Reply to hello. id is a per-process counter starting at 1; name is the server name; spawn comes from server.json. |
{"t":"players","list":[{"id":1,"name":"…","x":…,"y":…,"z":…,"h":…}]} | Broadcast to every connected player every 100 ms (10 Hz). The list includes the receiving player. Positions are 0 until a player's first pos. |
{"t":"error","msg":"Serveur plein"} | Sent instead of welcome when maxPlayers is reached; the connection is then closed. |
When the socket closes or errors, the player is removed from the list.
Scripts (resources with Lua scripts)
See Scripting (Lua) for the API. On the wire:
| Message | Direction | Notes |
|---|---|---|
{"t":"sres","list":[{"name":"example-scripts","dir":"example-scripts","scripts":[{"path":"example-scripts/client/main.lua","sha256":"…"}]}]} | S → C | Right after welcome: client scripts (shared first) to run, in order. path is relative to the resource cache; the client refuses a file whose sha256 differs. |
{"t":"ev","n":"example:ping","a":[123],"c":1} | both | Network event: name, JSON arguments, argument count (c, so trailing nils survive). Keys may come in any order. Ignored unless the receiving side called RegisterNetEvent(n). Client → server: 32 KB per line, 50 events/s per player (burst 100). |
{"t":"cmd","raw":"npc 2"} | C → S | A command no client script handled (256 characters max). Runs the server command with source = the player. |
{"t":"notify","msg":"Commande inconnue : /x"} | S → C | Message shown to the player (unknown command…). |
Network entities and state bags
See Entities, state bags & permissions for the Lua API. h is in radians; o is the owner's player id (0 = server).
| Message | Direction | Notes |
|---|---|---|
{"t":"ent","op":"new","id":3,"k":"veh","m":"kamaz","x":…,"y":…,"z":…,"h":…,"o":1,"sb":{"fuel":80}} | S → C | Entity entered your zone (400 m). k: veh, ped, obj. pl = this is player J's ped (already drawn, just map the id). ref only for the client that created it. |
{"t":"ent","op":"out"|"del","id":3} | S → C | Left your zone (remove locally) / deleted for everyone. |
{"t":"ent","op":"own","id":3,"o":2} | S → C | New owner. |
{"t":"ent","op":"tp","id":3,"x":…,"y":…,"z":…,"h":…} | S → owner | A server script moved the entity. |
{"t":"ents","ts":T,"list":[[id,x,y,z,h,pt],…]} | S → C | 10 Hz, entities in your zone (player peds excluded). Same clock as players. |
{"t":"ent","op":"new","ref":7,"k":"veh","m":"quad","x":…,"y":…,"z":…,"h":…} | C → S | A client script created an entity; answered with new + ref, or {"t":"ent","op":"refused","ref":7}. |
{"t":"epos","list":[[id,x,y,z,h],…]} | C → S | 10 Hz, positions of entities you own (ignored for others). |
{"t":"ent","op":"drive"|"leave"|"del","id":3} | C → S | You drive it (become owner if ≤ 15 m) / got out / delete (owner only). |
{"t":"sb","b":"player:2","k":"job","v":{…}} | both | State bag value (v missing = removed). Clients may only write their own player: bag and entities they own, never srv_* keys. |
Before welcome, the server verifies hello.token (a wft_… ticket from the master, see Master server API) and may answer {"t":"error","msg":…} and close (banned, or account required).
Session example
text
C: {"t":"hello","name":"Alex"}
S: {"t":"welcome","id":1,"spawn":{"x":-3112.6,"y":5535.8,"z":956},"name":"WildM LAN"}
C: {"t":"pos","x":-3110.2,"y":5531.0,"z":957.4,"h":1.57}
S: {"t":"players","list":[{"id":1,"name":"Alex","x":-3110.2,"y":5531,"z":957.4,"h":1.57}]}
…You can test the TCP side by hand with any line-based tool, e.g. ncat 127.0.0.1 30121 and typing a hello line.
Heartbeat
The game server also calls the master every 30 s — see Heartbeat & server list and POST /heartbeat.