Joueurs, équipes & œufs¶
Cette page décrit le modèle de données des entités vivantes du serveur : le
joueur (Player), l'équipe (Team) et l'œuf (Egg). Elle explique
aussi le modèle de vie et de faim, ainsi que la relation entre œufs et slots de
connexion qui régit l'arrivée des IA.
Pas de classe Drone
Il n'existe aucune classe Drone côté serveur. Un client IA possède
directement un objet Player (AIClient détient un Player*). « Joueur »,
« drone » et « Trantorien » désignent donc la même entité.
Le joueur — Player¶
Un Player regroupe tout l'état d'un Trantorien sur la carte
(server/srcs/player/Player.hpp:26-47).
enum class Direction { North, East, South, West };
class Player {
public:
int id = 0;
int x = 0;
int y = 0;
Direction direction = Direction::North;
int level = 1;
bool isIncanting = false;
std::string teamName;
bool isDead = false;
std::map<Resource, int> inventory = {
{Resource::Food, 10},
{Resource::Linemate, 0},
...
};
bool consumeFood();
};
| Champ | Rôle |
|---|---|
id |
Identifiant unique incrémental, attribué à l'éclosion. Utilisé par le GUI. |
x, y |
Position sur la carte torique. |
direction |
Orientation, enum class Direction { North, East, South, West }. |
level |
Niveau d'élévation, de 1 à 8. |
isIncanting |
true pendant une incantation : le joueur est gelé, ses commandes sont rejetées. |
teamName |
Nom de l'équipe. |
isDead |
Marqueur de mort (famine ou déconnexion). |
inventory |
map<Resource,int>, initialisé à Food = 10 et toutes les pierres à 0. |
Orientation et coordonnées
Le repère est torique : la carte se referme sur elle‑même.
Le mapping numérique des directions pour le GUI est N=1, E=2, S=3, W=4.
Voir Mécaniques de jeu pour la projection de la vision et
de l'éjection selon la direction.
Modèle de vie et de faim¶
À l'éclosion, l'inventaire contient 10 unités de nourriture
(Player.hpp:38). La faim est gérée par un événement périodique replanifié
toutes les 126 ticks dans scheduleFoodTimeoutForPlayer
(server/srcs/core/Server.cpp:671-701) :
void Server::scheduleFoodTimeoutForPlayer(int playerId)
{
_scheduler->schedule([this, playerId]() {
...
if (!player->consumeFood()) {
ai->queueWrite("dead\n");
ai->markForClose();
...
player->isDead = true;
_world->getTile(player->x, player->y).removePlayer(player);
broadcastToGUIs(... sendPdi(playerId) ...);
return;
}
scheduleFoodTimeoutForPlayer(playerId);
}, 126);
}
consumeFood() décrémente la nourriture et renvoie false quand elle atteint
zéro (Player.hpp:46). Chaque déclenchement consomme donc 1 unité tous les
126 ticks ; les 10 unités de départ tiennent 10 × 126 = 1260 ticks. À la
famine, le serveur :
- envoie
deadà l'IA puis ferme sa connexion ; - marque
isDead = trueet retire le joueur de sa tuile ; - diffuse l'événement
pdi <id>aux GUIs.
flowchart LR
A["Éclosion<br/>Food = 10"] -->|toutes les 126 ticks| B{"consumeFood()"}
B -->|Food > 0| C["Food -= 1"]
C -->|replanifie 126| B
B -->|Food == 0| D["dead + fermeture<br/>isDead = true<br/>diffusion pdi"]
Unité de nourriture incohérente
La commande IA Inventory rapporte la nourriture × 126
(server/srcs/commands/CmdInventory.cpp:12), alors que l'événement GUI pin
rapporte la valeur brute en unités. Le commentaire de Player.hpp:12
(« 10 × 126 = 1260 ticks ») décrit le total en ticks, pas le contenu réel de
l'inventaire. Détails dans Limitations connues.
L'équipe — Team¶
Une Team représente une équipe et son réservoir d'œufs disponibles
(server/srcs/player/Team.hpp:20-30).
class Team {
public:
std::string _name;
int _slotsAvailable = 0;
std::vector<Egg*> _eggs;
void addEgg(Egg* egg); // pousse l'œuf, _slotsAvailable++
Egg* popRandomEgg(); // tire un œuf au hasard, _slotsAvailable--
void removeEgg(Egg *egg);
const std::vector<Egg *> &getEggs() const;
};
addEggajoute un œuf et incrémente_slotsAvailable(server/srcs/player/Team.cpp:8-12).popRandomEggtire un œuf au hasard dans le vecteur, le retire et décrémente_slotsAvailable; renvoienullptrsi vide (Team.cpp:14-23). C'est l'opération utilisée à chaque connexion d'IA.removeEggretire un œuf désigné (utilisé par l'éclosion et parEject).
L'œuf — Egg¶
Un Egg matérialise un slot de connexion : tant qu'il existe, une IA de
l'équipe peut s'y connecter (server/srcs/player/Egg.hpp:18-26).
class Egg {
public:
int _id = 0;
int _x = 0;
int _y = 0;
std::string _teamName;
int _creatorId;
Player hatch();
};
hatch() produit un Player posé sur la tuile de l'œuf, avec une direction
aléatoire (server/srcs/player/Egg.cpp:8-18) :
Player Egg::hatch()
{
Direction dirs[4] = {North, South, East, West};
Player player;
player.x = this->_x;
player.y = this->_y;
player.teamName = this->_teamName;
player.direction = dirs[rand() % 4];
return player;
}
Relation œuf ↔ slot et arrivée des IA¶
L'équivalence côté serveur est simple : un œuf = un slot de connexion
disponible. Le compteur _slotsAvailable et la taille de _eggs évoluent
ensemble.
Œufs initiaux¶
Dans le constructeur du serveur, chaque équipe reçoit -c œufs (option CLI
clientsPerTeam) à des positions aléatoires sur la carte
(server/srcs/core/Server.cpp:53-68) :
for (const std::string &teamName : config.teamNames) {
Team &team = _world->getTeam(teamName);
for (int i = 0; i < config.clientsPerTeam; i++) {
Egg *egg = new Egg();
egg->_id = _nextEggId++;
egg->_x = rand() % config.width;
egg->_y = rand() % config.height;
egg->_teamName = teamName;
egg->_creatorId = 0;
team.addEgg(egg);
_world->getTile(egg->_x, egg->_y).addEgg(egg);
}
}
-c = œufs par équipe
L'option -c fixe le nombre d'œufs initiaux par équipe, donc le nombre
de connexions IA simultanées possibles au démarrage. Voir
Limitations connues.
Connexion d'une IA → éclosion¶
Lors du handshake, si l'équipe est connue et possède un œuf disponible, le
serveur éclot un œuf via popRandomEgg et transforme le PendingClient en
AIClient (server/srcs/core/ClientSessionManager.cpp:104-127) :
Player *player = new Player();
*player = egg->hatch();
player->id = nextPlayerId++;
player->teamName = name;
ai->setPlayer(player);
world.getTile(player->x, player->y).addPlayer(player);
clients[fd] = ai;
scheduleFoodTimeoutForPlayer(player->id);
...
int available = team.getEggs().size();
ai->queueWrite(std::to_string(available) + "\n"); // slots restants
ai->queueWrite(std::to_string(config.width) + " " +
std::to_string(config.height) + "\n"); // dimensions
Le serveur répond donc <slots restants> puis <largeur> <hauteur>,
diffuse ebo (éclosion) et pnw (nouveau joueur) aux GUIs, puis arme le timer
de faim. Si l'équipe est inconnue ou sans œuf, il répond ko et ferme.
Détails du wire format : Handshake.
Fork — nouvel œuf¶
La commande Fork pond un nouvel œuf sur la tuile du joueur après 42 ticks.
À la sortie de file, le serveur pré‑attribue l'id de l'œuf et diffuse pfk,
puis CmdFork::execute crée l'Egg et l'ajoute à l'équipe et à la tuile
(server/srcs/commands/CmdFork.cpp:10-27, Server.cpp:209-266). Le succès
diffuse enw (nouvel œuf). Voir Mécaniques de jeu et
Commandes & délais.
Connect_nbr¶
La commande Connect_nbr renvoie le nombre d'œufs restants de l'équipe,
c'est‑à‑dire les slots de connexion encore libres
(server/srcs/core/Server.cpp:311-316) :
Team &team = _world->getTeam(ai->getPlayer()->teamName);
cmd->setResponse(std::to_string(team.getEggs().size()) + "\n");
Slots non suivis séparément
Connect_nbr renvoie team.getEggs().size(), et non un compteur de slots
libres distinct. Comme « œuf » et « slot » sont confondus, le résultat est
correct, mais le découplage n'existe pas. Voir
Limitations connues.
sequenceDiagram
participant IA
participant Serveur
participant Team
IA->>Serveur: nom d'équipe (handshake)
Serveur->>Team: popRandomEgg()
Team-->>Serveur: Egg (ou nullptr)
alt œuf disponible
Serveur->>Serveur: egg.hatch() -> Player
Serveur-->>IA: <slots restants>
Serveur-->>IA: <largeur> <hauteur>
Serveur-->>IA: (GUIs : ebo + pnw)
else aucun œuf / équipe inconnue
Serveur-->>IA: ko + fermeture
end
Voir aussi¶
- Mécaniques de jeu — vision, incantation, broadcast, éjection.
- Commandes & délais — délais de
Fork,Connect_nbr, etc. - Handshake — séquence exacte de connexion.
- Protocole IA ↔ Serveur — formats fil.