Server — Overview¶
The Zappy server is the authoritative core of the simulation. It — and it alone — holds the truth of the game: the state of the map, the resources, the position and level of every player, the progress of incantations. Clients (AI and graphical interface) merely observe that state and submit actions; every decision is arbitrated by the server.
One arbiter, two audiences
The server serves two kinds of clients over TCP:
- the AI clients (the autonomous drones), which play the game by sending text commands;
- the graphical client (GUI), which receives a stream of events to render the world in real time.
The server never trusts a client: every command is validated against the real state of the world.
Technical characteristics¶
- Language: C++17 (
g++ -Wall -Wextra -std=c++17). - Produced binary:
build/zappy_server. - Static library: the network sources are first compiled into
build/libnetwork.a, then linked into the final binary. - Execution model: single-threaded, event-driven — a single
poll()multiplexes all sockets, and aSchedulerorders deferred actions.
Internal architecture¶
The code is organised into layers under server/srcs/, each with a clear responsibility:
flowchart TD
main["core/main.cpp<br/>entry point, signals"]
config["core/Config<br/>CLI parsing"]
server["core/Server<br/>poll() loop + orchestration"]
subgraph net["network/ (→ libnetwork.a)"]
sock["SocketUtils<br/>socket creation"]
clients["AClient / PendingClient<br/>AIClient / GUIClient"]
buf["NetworkBuffer"]
end
sched["scheduler/<br/>Scheduler + Event"]
subgraph world["world/"]
wmap["World / Tile"]
spawn["RessourceSpawner"]
end
subgraph player["player/"]
pl["Player / Team / Egg"]
end
subgraph proto["protocol/"]
cp["CommandParser (AI)"]
gp["GUIProtocol (GUI)"]
end
cmds["commands/<br/>one class per AI command"]
algos["algorithms/<br/>VisionAlgo / BroadcastAlgo / IncantationAlgo"]
main --> config --> server
server --> net
server --> sched
server --> world
server --> proto
server --> cmds
cmds --> algos
world --> spawn
world --> player
cp --> cmds
Role of each layer¶
| Layer | Directory | Responsibility |
|---|---|---|
| Core | core/ |
Entry point, configuration parsing, main loop, dispatchers (AI, GUI, incantation, sessions). |
| Network | network/ |
Socket creation, client hierarchy, buffering of input/output. Compiled into libnetwork.a. |
| World | world/ |
Toroidal map, tiles, resources, resource generation. |
| Player | player/ |
Players, teams, eggs. |
| Scheduler | scheduler/ |
Queue of timed events (the game's "clock"). |
| Protocol | protocol/ |
Parsing of AI commands (CommandParser) and serialisation of GUI events (GUIProtocol). |
| Commands | commands/ |
One ACommand class per AI command (Forward, Look, Incantation…), each carrying its delay and effect. |
| Algorithms | algorithms/ |
Shared game logic: vision cone, sound propagation (broadcast), elevation. |
Source tree¶
server/srcs/
├── core/ main.cpp, Server, Config, AICommandDispatcher,
│ ClientSessionManager, GuiCommandHandler, IncantationHandler
├── network/ AClient, PendingClient, AIClient, GUIClient,
│ NetworkBuffer, SocketUtils
├── world/ World, Tile, RessourceSpawner
├── player/ Player, Team, Egg
├── scheduler/ Scheduler, Event
├── protocol/ CommandParser (AI), GUIProtocol (GUI)
├── commands/ Cmd* (Forward, Right, Left, Look, Inventory,
│ Broadcast, ConnectNbr, Fork, Eject, Take, Set, Incantation)
└── algorithms/ VisionAlgo, BroadcastAlgo, IncantationAlgo
Event-driven design¶
The server has no fixed-rate game loop in the classic sense (no global "tick" continuously incremented). It rests on two complementary mechanisms:
poll()multiplexes all sockets — listening for new connections, client reads and client writes — in a single blocking call.- The
Scheduleris a binary heap (min-heap) of dated events. Every deferred action (the completion of a command after its delay, death by starvation, resource respawn) is scheduled there.
On each wake-up, the server handles the ready sockets, then runs through Scheduler::tick() every event whose deadline has passed. The poll() timeout is exactly the time until the next event (Scheduler::nextDeadlineMs()). The server therefore sleeps precisely until the moment something must happen: no busy-waiting, no needless latency.
// core/Server.cpp:114-115
int timeout = _scheduler->nextDeadlineMs();
int pollResult = poll(_pollFds.data(), _pollFds.size(), timeout);
Why this is elegant
By tying the poll() timeout to the next deadline, you get a server that consumes zero CPU when it has nothing to do, while remaining responsive to the millisecond for game actions. This is the detail explored in depth in Game loop & time.
Pages in this section¶
- Network & sockets — socket creation, the
poll()loop, client hierarchy, buffers, the 10-command limit. - Game loop & time — the
Scheduler, the ticks → milliseconds conversion, the effect of-f, the absence of busy-waiting. - World & resources — the toroidal map, tiles, the resource enumeration, densities and respawn.
For the detail of commands and their delays, see Commands & delays; for the format of exchanged messages, see Protocol.