| name | command-implementation |
| description | Implement a new player-facing game command in gb/commands/. Use when adding a command, registering a command alias, validating scope/permissions, parsing argv, or producing player output. Covers the command file template, registration in GB_server.cc, CMakeLists wiring, and the standard validate/parse/act/respond structure. |
| user-invocable | false |
Command Implementation
Every player-facing action is a free function in gb/commands/. Commands look uniform across the codebase. New commands must match that shape exactly.
Signature
void commandname(const command_t& argv, GameObj& g);
argv[0] is the command name; argv[1..] are user arguments.
g is the GameObj execution context (player, governor, scope, output stream, EntityManager).
File Template
module;
import gblib;
import std;
module commands;
namespace GB::commands {
void commandname(const command_t& argv, GameObj& g) {
}
}
Prefer import std; over import std.compat; in new files. Use #include only for legacy constants from gb/files.h or gb/buffers.h.
Standard Body Pattern
void give(const command_t& argv, GameObj& g) {
if (g.level() != ScopeLevel::LEVEL_SHIP &&
g.level() != ScopeLevel::LEVEL_PLAN) {
g.out << "Must be at ship or planet scope.\n";
return;
}
if (argv.size() < 3) {
g.out << "Usage: give <ship> <player>\n";
return;
}
if (!g.race->God && g.race->Guest) {
g.out << "Guests cannot do this.\n";
return;
}
auto ship_handle = g.entity_manager.get_ship(target_ship);
auto& ship = *ship_handle;
ship.owner = new_owner;
g.out << std::format("Ship {} given to player {}.\n", target_ship, new_owner);
}
Output Rules
Registering a New Command
After implementing, do all three:
-
Export in gb/commands/commands.cppm:
export void commandname(const command_t&, GameObj&);
-
Add source in gb/CMakeLists.txt under the commands target:
PRIVATE commands/commandname.cc
-
Wire in gb/GB_server.cc::getCommands():
{"commandname", GB::commands::commandname},
{"cn", GB::commands::commandname},
Then ninja -C build and (cd build && ctest) from the workspace root.
Anti-Patterns
- ❌ Direct console I/O (
printf, std::cout, std::print).
- ❌ Repository or
getstar()/putship() calls — go through g.entity_manager.
- ❌ Inline
*g.entity_manager.get_xxx(...) — use the two-step handle pattern.
- ❌ Null-checking
peek_star/peek_planet/peek_sectormap results — they throw.
- ❌ Hardcoded paths or magic numbers — use
gb/files.h constants and gblib:tweakables.
- ❌
using namespace std; or other namespace-pollution shortcuts.
Checklist