-
Notifications
You must be signed in to change notification settings - Fork 1
Server
The server is written in C++ and runs as a single process, single thread. It uses poll() for socket multiplexing so it never blocks waiting for one client.
./zappy_server -p port -x width -y height -n name1 name2 ... -c clientsNb -f freq| Flag | Meaning |
|---|---|
-p |
Port number |
-x |
Map width |
-y |
Map height |
-n |
One or more team names (space-separated) |
-c |
Max clients per team |
-f |
Frequency — reciprocal of time unit (default 100) |
Argument parsing is not yet implemented; the server currently hard-codes port 4242, a 10×10 map, and two teams.
The action duration formula from the spec is: action_time = action_cost / f seconds. Internally the server converts this to a tick duration of (7.0 / f) * 1000 milliseconds.
main.cpp
└── Server
├── run() main loop: game_tick + poll_clients
├── Polling.cpp poll() + accept + per-client read/dispatch
├── server_helper.cpp socket setup, client accept/create/remove
├── client_helpers.cpp move_player, parse_resource
└── game_loop.cpp game_tick, populate_map_resources
Server::run() drives two things in lock-step:
-
Game tick — fires every
time_unitmilliseconds. Every 20 ticks (and at startup) it refills map resources viapopulate_map_resources(). -
poll_clients(timeout)— callspoll()with a timeout calculated as the time remaining until the next tick, so the server never over-sleeps.
All file descriptors — the listening socket and every client socket — are kept in a std::vector<pollfd> _fds. poll() is called with the whole list. When POLLIN fires on the server fd, a new client is accepted. When it fires on a client fd, the client's buffer is read and any complete newline-terminated commands are dispatched.
- Server sends
WELCOME\n. - Client replies with
GRAPHIC\n→ becomes a GUI client, or a team name → becomes a player. - For a player: server validates the team name and checks
spots_left > 0, then sends<client_num>\n<x> <y>\n. - For a GUI: server calls
gui_start()which immediately sends the full game state snapshot.
Client (abstract)
├── Player
└── Gui
Base class for both player and GUI connections. Holds the socket fd (control_fd) and a string buffer (ctrl_buffer) for incomplete commands. Pure virtual parse_command() dispatches incoming lines to the concrete type.
Implements the Observer interface: each Client is attached to a Subject and receives Update(message) calls when the subject notifies.
Represents an AI client. State:
| Field | Type | Description |
|---|---|---|
position |
array<int,2> |
X, Y on the map |
orientation |
enum |
NORTH/EAST/SOUTH/WEST |
level |
int |
Current elevation level (starts at 0, effectively 1 after connect) |
team_name |
string |
Team this player belongs to |
inventory |
Inventory |
7-slot resource array; starts with 10 food |
Every player gets a unique auto-incrementing player_id (static counter).
| Command | Action |
|---|---|
Forward |
Move one tile in facing direction (map wraps) |
Right / Left
|
Rotate 90° |
Look |
Returns a vision grid — 1+3+5+7 = 16 tiles in a pyramid ahead |
Inventory |
Returns current resource counts |
Take <res> / Set <res>
|
Pick up / drop a resource on the current tile |
Eject |
Push all other players on the tile one step in facing direction |
Broadcast <text> |
Send a message to all clients with a direction hint |
Connect_nbr |
Returns open slots in player's team |
Fork |
Lay an egg (egg logic not yet implemented) |
Incantation |
Start an elevation ritual (see below) |
Unknown commands return ko\n.
The look response is [tile0, tile1, ..., tile15]. Tile 0 is the player's own tile. The grid grows one row per level of vision (hardcoded to depth 3 — rows of 1, 3, 5, 7 tiles, oriented relative to the player's facing direction). Each tile lists player tokens for occupants and resource names for items present.
Requirements are checked immediately when the command is received (start check). The server checks players_on_tile >= required and stone quantities on the tile. If met:
- All participating players advance one level.
- Required stones are consumed from the tile.
- GUI is notified (
picthenpie).
The 300/f wait between
Elevation underwayandCurrent level: Kis not yet implemented — the level-up is currently instant.
Receives commands from the GUI client and also receives unsolicited events pushed by the server via the Observer pattern.
| Command | Response | Description |
|---|---|---|
msz |
msz X Y |
Map dimensions |
bct X Y |
bct X Y q0..q6 |
Tile resource counts |
mct |
all tiles as bct lines |
Full map dump |
tna |
tna N per team |
Team names |
ppo #n |
ppo #n X Y O |
Player position + orientation |
plv #n |
plv #n L |
Player level |
pin #n |
pin #n X Y q0..q6 |
Player inventory |
sgt |
sgt T |
Current time unit |
sst T |
sst T |
Set server time unit |
Unknown GUI commands return suc\n.
These are pushed via _gui_subject.Notify(...) whenever the corresponding action happens, without the GUI requesting them:
| Message | Trigger |
|---|---|
pnw #n X Y O L N |
New player connects |
ppo #n X Y O |
Player moves |
plv #n L |
Player levels up |
pin #n X Y q0..q6 |
Player inventory changes |
pex #n |
Player ejects others |
pbc #n M |
Player broadcasts |
pic X Y L #n... |
Incantation starts |
pie X Y R |
Incantation ends |
pfk #n |
Player starts fork |
pdr #n i |
Player drops resource |
pgt #n i |
Player picks up resource |
pdi #n |
Player dies |
enw #e #n X Y |
Egg laid |
ebo #e |
Egg hatches |
edi #e |
Egg dies |
seg N |
Game over, team N wins |
smg M |
Server message |
The server holds two Subject instances:
-
_gui_subject— allGuiclients subscribe to this. Used to push passive events to every connected GUI simultaneously. -
_player_subject— reserved for player-facing broadcast events (not yet wired up to most player actions).
When an action happens (e.g., a player drops a resource), the action handler calls _gui_subject.Notify(lambda) where the lambda casts each observer to Gui* and calls the appropriate passive method.
_map is a vector<vector<Tiles>> indexed as _map[y][x].
Each Tiles holds:
-
Inventory inventory— how many of each resource are on this tile. -
vector<shared_ptr<Player>> players— who is currently standing here. -
shared_ptr<Egg> egg— at most one egg per tile (nullable).
The map is a torus: move_forward and eject compute new coordinates modulo map width/height.
Resource quantities are computed as floor(width * height * density) and distributed randomly across tiles at startup (and every 20 ticks). The current distribution algorithm places a random amount per tile in raster order, subtracting from a running total — this can leave the last tiles with near-zero amounts and is marked for improvement.
Resource densities (Struct.hpp):
| Resource | Density |
|---|---|
| food | 0.50 |
| linemate | 0.30 |
| deraumere | 0.15 |
| sibur | 0.10 |
| mendiane | 0.10 |
| phiras | 0.08 |
| thystame | 0.05 |
- Argument parsing (
handle_arguments) is a stub — all parameters are hardcoded. - The 300/f incantation duration is not implemented; level-up is instant.
- Fork creates no actual
Eggobject and does not update team slots. -
_player_subjectis not used for most player-facing events. - Resource distribution is uneven (last tiles can be starved).
-
seg(end of game) always reports the first team as winner. - Food depletion / player death on starvation is not yet implemented.