Network Architecture
OpenWarcraft3 uses the same runtime-dispatch networking model as Quake 2. A
single send/receive API (NET_SendPacket / NET_GetPacket) handles all
communication; only the lowest layer changes path depending on whether the
destination is in the same process (loopback) or a remote machine (UDP).
Address types (netadr_t)
Every packet is addressed with a netadr_t:
typedef enum {
NA_LOOPBACK, // in-process ring buffer (zero copy, zero latency)
NA_IP, // unicast UDP
NA_BROADCAST, // broadcast UDP
} netadrtype_t;
typedef struct {
netadrtype_t type;
unsigned char ip[4]; // network byte order
unsigned short port; // network byte order
} netadr_t;
The type field is the only routing key. ip and port are only
inspected when type is NA_IP or NA_BROADCAST.
Loopback (listen-server / single-process)
When the executable starts with -map= it runs both the server and the local
client in the same process. The local client slot is assigned address type
NA_LOOPBACK; no socket is involved.
Two 256 KiB ring buffers carry traffic in each direction:
bufs[NS_CLIENT] ← client writes (clc_move, clc_connect, …)
bufs[NS_SERVER] ← server writes (svc_packetentities, svc_serverdata, …)
Sending to NA_LOOPBACK appends a 4-byte little-endian length prefix followed
by the payload into the appropriate ring buffer. Receiving polls the other
side's ring buffer.
// common/net.c
void NET_SendPacket(NETSOURCE netsrc, int length, const void *data, netadr_t to) {
switch (to.type) {
case NA_LOOPBACK:
NET_SendLoopPacket(netsrc, length, data);
break;
case NA_IP:
case NA_BROADCAST:
NET_SendUDPPacket(length, data, to);
break;
}
}
int NET_GetPacket(NETSOURCE netsrc, netadr_t *from, LPSIZEBUF msg) {
int r = NET_GetLoopPacket(netsrc, from, msg); // loopback first
if (r) return r;
return NET_GetUDPPacket(netsrc, from, msg); // then real socket
}
UDP (remote client)
When the executable starts with -connect= it binds to an ephemeral port
(port 0; the OS assigns one) and connects to the specified server over UDP.
The server always listens on PORT_SERVER (27910).
The UDP path shares the same framing as the loopback path: every datagram
starts with a 4-byte little-endian length prefix, followed by the payload.
The length is encoded/decoded with memcpy into a uint32_t to avoid
strict-aliasing and alignment issues:
// Encode length prefix (little-endian, no aliasing UB)
uint32_t prefix = (uint32_t)length;
unsigned char sendbuf[4 + MAX_MSGLEN];
sendbuf[0] = (prefix ) & 0xff;
sendbuf[1] = (prefix >> 8) & 0xff;
sendbuf[2] = (prefix >> 16) & 0xff;
sendbuf[3] = (prefix >> 24) & 0xff;
memcpy(sendbuf + 4, data, length);
sendto(udp_socket, sendbuf, 4 + length, 0, ...);
Initialisation
NET_Init(port) is called once from main.c before any server or client
code runs:
| Mode | Port argument | Effect |
|---|---|---|
Listen server (-map=) |
PORT_SERVER (27910) |
Bind UDP socket to 27910 so remote clients can find it |
Remote client (-connect=) |
0 |
Bind UDP socket to OS-assigned ephemeral port |
If socket creation fails, NET_Init returns 0 and the process exits.
NET_Shutdown() is called on exit to close the socket.
Connection handshake
Remote client → server
- Client calls
CL_Connect(host, port)which resolves the hostname viaNET_StringToAdrand sends an out-of-band"connect"datagram to the server. - The server's
SV_ReadPacketsreads the datagram, checks that the payload starts with"connect", and callsSV_DirectConnect(from)to allocate a new client slot withNA_IPtype. - From this point the normal
clc_*/svc_*message exchange proceeds over UDP, identical to the loopback exchange.
Local client (loopback)
SV_Map calls SV_ClientConnect() which allocates slot 0 with type
NA_LOOPBACK. No network handshake is required; the client sends a
clc_connect message through the ring buffer and the server responds with
svc_serverdata.
Command-line interface
# Listen server + local client (loopback; no real socket traffic)
openwarcraft3 -mpq=/path/to/War3.mpq -map=Maps\Campaign\Human02.w3m
# Remote client, default port (PORT_SERVER = 27910)
openwarcraft3 -mpq=/path/to/War3.mpq -connect=192.168.1.10
# Remote client, explicit port
openwarcraft3 -mpq=/path/to/War3.mpq -connect=192.168.1.10:27910
| Argument | Description |
|---|---|
-mpq=<path> |
Absolute path to War3.mpq (required for asset loading in both modes) |
-map=<path> |
Internal MPQ path of the map to load; starts a listen server |
-connect=<host[:port]> |
Hostname or IP of the server to join; starts a remote-client session |
-map and -connect are mutually exclusive. Omitting both (with a valid
-mpq) prints the usage message.
Key files
| File | Purpose |
|---|---|
common/net.c |
NET_SendPacket, NET_GetPacket, loopback buffers, UDP socket |
common/net.h |
netadr_t, netchan_t, NETSOURCE, public API |
common/main.c |
CLI parsing, NET_Init, mode selection |
server/sv_init.c |
SV_ClientConnect (loopback slot), SV_DirectConnect (UDP slot) |
server/sv_main.c |
SV_ReadPackets — OOB routing and per-slot dispatch |
client/cl_main.c |
CL_Connect, CL_ReadPackets |