Serveur — Vue d'ensemble¶
Le serveur Zappy est le cœur autoritaire de la simulation. C'est lui — et lui seul — qui détient la vérité du jeu : l'état de la carte, les ressources, la position et le niveau de chaque joueur, le déroulement des incantations. Les clients (IA et interface graphique) ne font qu'observer cet état et soumettre des actions ; toute décision est arbitrée par le serveur.
Un seul arbitre, deux publics
Le serveur sert deux types de clients via TCP :
- les clients IA (les drones autonomes), qui jouent la partie en envoyant des commandes texte ;
- le client graphique (GUI), qui reçoit un flux d'événements pour afficher le monde en temps réel.
Le serveur ne fait jamais confiance à un client : chaque commande est validée contre l'état réel du monde.
Caractéristiques techniques¶
- Langage : C++17 (
g++ -Wall -Wextra -std=c++17). - Binaire produit :
build/zappy_server. - Bibliothèque statique : les sources réseau sont d'abord compilées dans
build/libnetwork.a, puis liées au binaire final. - Modèle d'exécution : mono-thread, événementiel — un unique
poll()multiplexe toutes les sockets, et unSchedulerordonnance les actions différées.
Architecture interne¶
Le code est organisé en couches sous server/srcs/, chacune avec une responsabilité claire :
flowchart TD
main["core/main.cpp<br/>point d'entrée, signaux"]
config["core/Config<br/>parsing CLI"]
server["core/Server<br/>boucle poll() + orchestration"]
subgraph net["network/ (→ libnetwork.a)"]
sock["SocketUtils<br/>création socket"]
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 (IA)"]
gp["GUIProtocol (GUI)"]
end
cmds["commands/<br/>une classe par commande IA"]
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
Rôle de chaque couche¶
| Couche | Répertoire | Responsabilité |
|---|---|---|
| Core | core/ |
Point d'entrée, parsing de la configuration, boucle principale, dispatchers (IA, GUI, incantation, sessions). |
| Network | network/ |
Création des sockets, hiérarchie de clients, mise en mémoire tampon des entrées/sorties. Compilée dans libnetwork.a. |
| World | world/ |
Carte torique, tuiles, ressources, génération des ressources. |
| Player | player/ |
Joueurs, équipes, œufs. |
| Scheduler | scheduler/ |
File d'événements temporisés (le « temps » du jeu). |
| Protocol | protocol/ |
Analyse des commandes IA (CommandParser) et sérialisation des événements GUI (GUIProtocol). |
| Commands | commands/ |
Une classe ACommand par commande IA (Forward, Look, Incantation…), chacune portant son délai et son effet. |
| Algorithms | algorithms/ |
Logique métier partagée : cône de vision, propagation du son (broadcast), élévation. |
Arborescence des sources¶
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 (IA), GUIProtocol (GUI)
├── commands/ Cmd* (Forward, Right, Left, Look, Inventory,
│ Broadcast, ConnectNbr, Fork, Eject, Take, Set, Incantation)
└── algorithms/ VisionAlgo, BroadcastAlgo, IncantationAlgo
Conception événementielle¶
Le serveur n'a pas de boucle de jeu cadencée au sens classique (pas de « tick » global incrémenté en continu). Il repose sur deux mécanismes complémentaires :
poll()multiplexe toutes les sockets — l'écoute des nouvelles connexions, les lectures et les écritures clients — en un seul appel bloquant.- Le
Schedulerest un tas binaire (min-heap) d'événements datés. Toute action différée (l'achèvement d'une commande après son délai, la mort par faim, le respawn des ressources) y est planifiée.
À chaque réveil, le serveur traite les sockets prêtes, puis exécute via Scheduler::tick() tous les événements dont l'échéance est dépassée. Le délai d'attente de poll() est exactement la durée avant le prochain événement (Scheduler::nextDeadlineMs()). Le serveur dort donc précisément jusqu'au moment utile : ni attente active, ni latence inutile.
// core/Server.cpp:114-115
int timeout = _scheduler->nextDeadlineMs();
int pollResult = poll(_pollFds.data(), _pollFds.size(), timeout);
Pourquoi c'est élégant
En liant le timeout de poll() à la prochaine échéance, on obtient un serveur qui consomme zéro CPU lorsqu'il n'a rien à faire, tout en restant réactif à la milliseconde près pour les actions de jeu. C'est le détail décrit en profondeur dans Boucle de jeu & temps.
Pages de cette section¶
- Réseau & sockets — création de socket, boucle
poll(), hiérarchie de clients, tampons, limite des 10 commandes. - Boucle de jeu & temps — le
Scheduler, la conversion ticks → millisecondes, l'effet de-f, l'absence d'attente active. - Monde & ressources — la carte torique, les tuiles, l'énumération des ressources, les densités et le respawn.
Pour le détail des commandes et de leurs délais, voir Commandes & délais ; pour le format des messages échangés, voir Protocole.