Skip to content

Commands & delays

This page describes how the server parses, represents and schedules the commands sent by AIs, as well as the time cost (delay) of each one.

Parsing — CommandParser

Each line received from an AI is analysed by CommandParser::parse (server/srcs/protocol/CommandParser.cpp:38-67). The parser splits the name (before the first space) and the argument (the rest), then instantiates the matching ACommand subclass:

if (name == "Forward" && arg.empty())      return new CmdForward();
...
if (name == "Broadcast" && !arg.empty())   return new CmdBroadcast(arg);
if (name == "Take" && !arg.empty())        return new CmdTake(arg);
if (name == "Set" && !arg.empty())         return new CmdSet(arg);
if (name == "Incantation" && arg.empty())  return new CmdIncantation();
return nullptr;

Strict argument presence

The parser rejects any command whose argument presence is incorrect: an argument given to Forward, or a missing argument for Take, returns nullptr → the server replies ko. Only Broadcast, Take and Set require an argument; all others must have none.

Representation — one class per command

Each command is a subclass of the ACommand interface (server/srcs/commands/ACommand.hpp:42-69), with one instance per command in server/srcs/commands/. The key interface:

class ACommand {
public:
    virtual void        execute(Player& player, World& world) = 0;
    virtual int         getDelay()    const = 0;   // cost in time units
    virtual std::string getResponse() const { return "ok\n"; }
    virtual CommandType getType() const = 0;
    ...
};

The 12 AI commands are all implemented: Forward, Right, Left, Look, Inventory, Broadcast, Connect_nbr, Fork, Eject, Take, Set, Incantation.

CmdIncantation is a no-op

CmdIncantation::execute does nothing (server/srcs/commands/CmdIncantation.cpp:22-26). All elevation logic is carried by IncantationHandler and IncantationAlgo — see Game mechanics. The command only serves as a type marker in the loop.

Scheduling — one delay per command

The main loop pops one command per idle AI each iteration. An AI is idle if it is not incanting, not busy, and has a queued command (server/srcs/core/Server.cpp:163-217). The command is then scheduled to run after cmd->getDelay() time units:

ai->setBusy(true);
...
int playerId = ai->getPlayer()->id;
_scheduler->schedule([this, playerId, cmd]() {
    ...
    cmd->execute(*ai->getPlayer(), *_world);
    ...
    ai->queueWrite(cmd->getResponse());
    ai->setBusy(false);
    _aiDispatcher->decrementPendingResponses(*ai);
    delete cmd;
}, cmd->getDelay());

While waiting, the AI is busy: it runs no other command, which serialises its actions. Incantation is a special case handled upstream by IncantationHandler::start (see Game mechanics).

Delays in time units

getDelay() returns a cost in time units, converted to milliseconds by the Scheduler: delay_ms = delay × 1000 / frequency. The higher the frequency (-f), the faster the actions. See Game loop & time.

Delay table

Command Delay (time units) Argument
Forward 7
Right 7
Left 7
Look 7
Inventory 1
Broadcast 7 text required
Connect_nbr 0
Fork 42
Eject 7
Take 7 object required
Set 7 object required
Incantation 300

(Values read from the getDelay() of the commands/Cmd*.hpp headers; Connect_nbr: server/srcs/commands/CmdConnectNbr.cpp:29-32.)

Instant Connect_nbr

Connect_nbr has a delay of 0: its response (number of remaining team eggs) is set in the loop just before sending (server/srcs/core/Server.cpp:311-316). See Players, teams & eggs.

flowchart LR
    L["AI line"] --> P["CommandParser::parse"]
    P -->|null| KO["ko"]
    P -->|ACommand*| Q["Per-AI queue"]
    Q --> D["Loop: 1 cmd / idle AI"]
    D --> S["scheduler.schedule(getDelay())"]
    S -->|deadline| E["cmd->execute()<br/>+ response + setBusy(false)"]

See also