From 516e259318d63e78da57fdb5ec510771cf36a539 Mon Sep 17 00:00:00 2001 From: pwalig Date: Sat, 21 Dec 2024 15:29:32 +0100 Subject: [PATCH 01/10] Send whole configuration to clients --- README.md | 5 +++-- src/rts/board.cpp | 14 ++++++++++++-- src/rts/board.hpp | 7 ++++--- src/rts/game.cpp | 49 +++++++++++++++++++++++++++++++++-------------- src/rts/game.hpp | 9 ++++++--- 5 files changed, 60 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 7b0b62e..eb1152a 100644 --- a/README.md +++ b/README.md @@ -31,12 +31,12 @@ make ### Available values: -* `maxPlayers` - how many players can play at once [default: 16] * `millis` - duration of one game step [default: 200] +* `maxPlayers` - how many players can play at once [default: 16] * `boardX` - x dimention of the board [default: 256] * `boardY` - y dimention of the board[default: 256] -* `startResources` - how many resources to spawn at the start of the game [default: 25] * `unitsToWin` - how many units player has to aquire to win the game [default: 50] +* `startResources` - how many resources to spawn at the start of the game [default: 25] * `resourceHp` - starting hit points of every new resource [default: 100] * `unitHp` - starting hit points of every new unit [default: 100] * `unitDamage` - how much damage do units deal on every attack [default: 10] @@ -77,6 +77,7 @@ Some messages consist of only type character others contain more data. ### From server - `g` `` ` ` `` ` ` `` `\n` - player was sent to game room (in response to: `j`), ``, `` and `` same as in `[config file]` +- `c` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` `\n` - whole server configuration sent to newly joined clients - `q` `\n` - player was sent to queue (in response to: `j`) - `y` `\n` - client request accepted (in response to: `n`) - `n` `\n` - client request denied (in response to: `j` or `n`) diff --git a/src/rts/board.cpp b/src/rts/board.cpp index 69cd5e5..3c7bfe7 100644 --- a/src/rts/board.cpp +++ b/src/rts/board.cpp @@ -12,12 +12,12 @@ rts::board::board(unsigned int X, unsigned int Y) : gen(std::random_device()()) } } -rts::field* rts::board::getField(const unsigned int& xpos, const unsigned int& ypos) { +rts::field* rts::board::getField(unsigned int xpos, unsigned int ypos) { if (xpos < getXdim() && ypos < getYdim()) return &fields[xpos][ypos]; else return nullptr; } -const rts::field* rts::board::getConstField(const unsigned int& xpos, const unsigned int& ypos) const { +const rts::field* rts::board::getConstField(unsigned int xpos, unsigned int ypos) const { if (xpos < getXdim() && ypos < getYdim()) return &fields[xpos][ypos]; else return nullptr; @@ -33,6 +33,16 @@ std::vector rts::board::resourceFields(bool resource) { return out; } +std::vector rts::board::constResourceFields(bool resource) const { + std::vector out; + for (const std::vector& row : fields){ + for (const field& f : row) { + if (f.hasResource() == resource) out.push_back(&f); + } + } + return out; +} + std::vector rts::board::emptyFields(bool empty) { std::vector out; for (std::vector& row : fields){ diff --git a/src/rts/board.hpp b/src/rts/board.hpp index 9dbcb00..070b223 100644 --- a/src/rts/board.hpp +++ b/src/rts/board.hpp @@ -14,10 +14,11 @@ namespace rts { public: board(unsigned int x = 256, unsigned int y = 256); - field* getField(const unsigned int& xpos, const unsigned int& ypos); - const field* getConstField(const unsigned int& xpos, const unsigned int& ypos) const; - + field* getField(unsigned int xpos, unsigned int ypos); + const field* getConstField(unsigned int xpos, unsigned int ypos) const; + std::vector resourceFields(bool resource); + std::vector constResourceFields(bool resource) const; std::vector emptyFields(bool empty); field* randomField(); field* randomEmptyField(bool empty); diff --git a/src/rts/game.cpp b/src/rts/game.cpp index be0d651..315ab80 100644 --- a/src/rts/game.cpp +++ b/src/rts/game.cpp @@ -13,13 +13,13 @@ std::unordered_map> {"maxPlayers", [](rts::game* g, std::ifstream& f){ f >> g->maxPlayers; }}, {"boardX", [](rts::game* g, std::ifstream& f){ f >> g->boardX; }}, {"boardY", [](rts::game* g, std::ifstream& f){ f >> g->boardY; }}, + {"unitsToWin", [](rts::game* g, std::ifstream& f){ f >> g->unitsToWin; }}, {"startResources", [](rts::game* g, std::ifstream& f){ f >> g->startResources; }}, {"resourceHp", [](rts::game* g, std::ifstream& f){ f >> g->resourceHp; }}, {"unitHp", [](rts::game* g, std::ifstream& f){ f >> g->unitHp; }}, {"unitDamage", [](rts::game* g, std::ifstream& f){ f >> g->unitDamage; }}, - {"unitsToWin", [](rts::game* g, std::ifstream& f){ f >> g->unitsToWin; }}, {"allowedNameCharacters", [](rts::game* g, std::ifstream& f){ f >> g->allowedNameCharacters; }} -}; // i can only dream of reflection system in c++ +}; // i wish there was reflection system in c++ rts::game::game(const char *port, const char* configFile) : _server(port) { _server.onNewClient = std::bind(&rts::game::handleNewClient, this, std::placeholders::_1); @@ -38,17 +38,22 @@ rts::game::game(const char *port, const char* configFile) : _server(port) { } } -void rts::game::handleNewClient(client* client_) { - player* pl = new player(this, client_); - allPlayers.insert(pl); +std::vector rts::game::configMessage() const { + std::vector buff = {'c'}; + message::appendNumberWDelim(buff, millis, ' '); + message::appendNumberWDelim(buff, maxPlayers, ' '); + message::appendNumberWDelim(buff, boardX, ' '); + message::appendNumberWDelim(buff, boardY, ' '); + message::appendNumberWDelim(buff, unitsToWin, ' '); + message::appendNumberWDelim(buff, startResources, ' '); + message::appendNumberWDelim(buff, resourceHp, ' '); + message::appendNumberWDelim(buff, unitHp, ' '); + message::appendNumberWDelim(buff, unitDamage, ' '); + message::appendStringWDelim(buff, allowedNameCharacters, '\n'); + return buff; } -void rts::game::loopLogic(){ - // spawn resource - if (rand() % 10 == 0) _board.spawnResource(resourceHp); - - - // sent updates to clients +std::vector rts::game::boardStateMessage() const { std::vector buff = {'p'}; message::appendNumberWDelim(buff, activePlayers.size(), ';'); // amount of players @@ -70,17 +75,33 @@ void rts::game::loopLogic(){ // resources buff.push_back('r'); - std::vector resources = _board.resourceFields(true); + std::vector resources = _board.constResourceFields(true); message::appendNumberWDelim(buff, resources.size(), ';'); // amount of resources - for (field* f : resources) { + for (const field* f : resources) { message::appendNumberWDelim(buff, f->x, ' '); message::appendNumberWDelim(buff, f->y, ' '); message::appendNumberWDelim(buff, f->getHp(), ';'); } buff.push_back('\n'); + return buff; +} + +void rts::game::handleNewClient(client* client_) { + player* pl = new player(this, client_); + allPlayers.insert(pl); + pl->getClient()->sendToClient(configMessage()); +} + +void rts::game::loopLogic(){ + // spawn resource + if (rand() % 10 == 0) _board.spawnResource(resourceHp); + + + // sent updates to clients + std::vector msg = boardStateMessage(); for (player* p : activePlayers){ - p->getClient()->sendToClient(buff); + p->getClient()->sendToClient(msg); } diff --git a/src/rts/game.hpp b/src/rts/game.hpp index fdad88b..050df2d 100644 --- a/src/rts/game.hpp +++ b/src/rts/game.hpp @@ -12,23 +12,26 @@ namespace rts { class game { private: server _server; - unsigned int maxPlayers = 16; std::unordered_set allPlayers; std::unordered_set activePlayers; std::deque queuedPlayers; unsigned int millis = 1000; - unsigned int startResources = 25; + unsigned int maxPlayers = 16; unsigned int boardX = 256; unsigned int boardY = 256; + unsigned int unitsToWin = 50; + unsigned int startResources = 25; unsigned int resourceHp = 100; unsigned int unitHp = 100; unsigned int unitDamage = 10; - unsigned int unitsToWin = 50; std::string allowedNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"; unsigned int nextUnitId = 0; + std::vector configMessage() const; + std::vector boardStateMessage() const; + void handleNewClient(client* client_); void loopLogic(); void clearRoom(); From d1f1c097ad66c3ce6f99340d4f68944ff29d454a Mon Sep 17 00:00:00 2001 From: pwalig Date: Sat, 21 Dec 2024 16:02:33 +0100 Subject: [PATCH 02/10] Send tick end message and shorten g message --- README.md | 3 ++- src/rts/game.cpp | 22 +++++++++++----------- src/rts/game.hpp | 1 + 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index eb1152a..1297874 100644 --- a/README.md +++ b/README.md @@ -76,8 +76,9 @@ Some messages consist of only type character others contain more data. ### From server -- `g` `` ` ` `` ` ` `` `\n` - player was sent to game room (in response to: `j`), ``, `` and `` same as in `[config file]` +- `g` `\n` - player was sent to game room (in response to: `j`), ``, `` and `` same as in `[config file]` - `c` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` `\n` - whole server configuration sent to newly joined clients +- `t` `\n` - sent to all players in game room in regular time intervals, marks the and of each tick and a start of the next one - `q` `\n` - player was sent to queue (in response to: `j`) - `y` `\n` - client request accepted (in response to: `n`) - `n` `\n` - client request denied (in response to: `j` or `n`) diff --git a/src/rts/game.cpp b/src/rts/game.cpp index 315ab80..2486c47 100644 --- a/src/rts/game.cpp +++ b/src/rts/game.cpp @@ -98,12 +98,7 @@ void rts::game::loopLogic(){ // sent updates to clients - std::vector msg = boardStateMessage(); - - for (player* p : activePlayers){ - p->getClient()->sendToClient(msg); - } - + sendToPlayers(activePlayers, boardStateMessage()); // allow units to move again for (player* p : activePlayers) { @@ -111,6 +106,9 @@ void rts::game::loopLogic(){ u->movedThisRound = false; } } + + // send next tick message to room players + sendToPlayers(activePlayers, {'t', '\n'}); // next game tick } void rts::game::clearRoom() { @@ -134,11 +132,7 @@ void rts::game::addPlayerToRoom(player* pl) { assert(activePlayers.size() < maxPlayers); activePlayers.insert(pl); pl->newUnit(_board.randomEmptyField(true)); // add first unit for the player to control - std::vector buff = {'g'}; - message::appendNumberWDelim(buff, _board.getXdim(), ' '); - message::appendNumberWDelim(buff, _board.getYdim(), ' '); - message::appendNumberWDelim(buff, unitsToWin, '\n'); - pl->getClient()->sendToClient(buff); // joined active group + pl->getClient()->sendToClient({'g', '\n'}); // joined active group } void rts::game::addPlayerToQueue(player* pl) { @@ -170,6 +164,12 @@ void rts::game::removePlayerFromRoomOrQueue(player* pl) { } } +void rts::game::sendToPlayers(const std::unordered_set& players, const std::vector& message) { + for (player* p : players){ + p->getClient()->sendToClient(message); + } +} + void rts::game::run() { _server.loop(millis); } diff --git a/src/rts/game.hpp b/src/rts/game.hpp index 050df2d..0a16777 100644 --- a/src/rts/game.hpp +++ b/src/rts/game.hpp @@ -42,6 +42,7 @@ namespace rts { void moveQueuedPlayerToRoom(); static std::unordered_map> configValueHandlers; + static void sendToPlayers(const std::unordered_set& players, const std::vector& message); public: board _board; From ce883bb7588cff54220aa8fe6fe607b415794b2b Mon Sep 17 00:00:00 2001 From: pwalig Date: Sat, 21 Dec 2024 17:34:58 +0100 Subject: [PATCH 03/10] Send player joined message and propose new message scheme --- README.md | 12 ++++++++++-- src/rts/game.cpp | 19 +++++++++++++++++++ src/rts/game.hpp | 5 +++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1297874..a8663f2 100644 --- a/README.md +++ b/README.md @@ -68,8 +68,8 @@ Some messages consist of only type character others contain more data. ### From client - `n` `` `\n` - set name or rename self -- `j` `\n` - request join (player will be sent to game room or queue) -- `q` `\n` - request quit (player will be removed from game room or queue, can still rejoin with `j`) +- `j` - request join (player will be sent to game room or queue) +- `q` - request quit (player will be removed from game room or queue, can still rejoin with `j`) - `m` `` ` ` `` ` ` `` ` ` `` `\n` - request unit to move (`` are coordinates of the unit, `` designate destination) - `a` `` ` ` `` ` ` `` ` ` `` `\n` - request unit to move (`` are coordinates of the unit, `` coordinates of the target unit) (possible to attack own units) - `d` `` ` ` `` `\n` - request unit to mine the resource (`` are coordinates of the unit) (unit can only mine resource that it is standing on) @@ -78,6 +78,14 @@ Some messages consist of only type character others contain more data. - `g` `\n` - player was sent to game room (in response to: `j`), ``, `` and `` same as in `[config file]` - `c` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` `\n` - whole server configuration sent to newly joined clients +- `p` `` ` ` `` `;` +`` ` ` `` ` ` `` ` ` `` `;` +... +`` ` ` `` ` ` `` ` ` `` `;` `\n` - message explaining current state of the player, is sent to everyone when new player joins or to new player to inform about other players +- `m` `` ` ` `` ` ` `` `\n` - unit of id `` has moved to `;` +- `a` `` ` ` `` `\n` - unit of id `` attacked unit of id `` +- `d` `` `\n` - unit of id `` mined a resource +- `r` `` ` ` `` ` ` `` `\n` - there is a resource on field `;`, sent to everyone when resource spawns or to new player for every resource that is already on the board - `t` `\n` - sent to all players in game room in regular time intervals, marks the and of each tick and a start of the next one - `q` `\n` - player was sent to queue (in response to: `j`) - `y` `\n` - client request accepted (in response to: `n`) diff --git a/src/rts/game.cpp b/src/rts/game.cpp index 2486c47..5eacc79 100644 --- a/src/rts/game.cpp +++ b/src/rts/game.cpp @@ -86,6 +86,24 @@ std::vector rts::game::boardStateMessage() const { return buff; } +std::vector rts::game::newPlayerMessage(const player* p) const{ + std::vector buff = {'p'}; + + message::appendStringWDelim(buff, p->getName(), ' '); // player name + message::appendNumberWDelim(buff, p->units.size(), ';'); // amount of units + + for (unit* u : p->units) { + message::appendNumberWDelim(buff, u->id, ' '); + message::appendNumberWDelim(buff, u->f->x, ' '); + message::appendNumberWDelim(buff, u->f->y, ' '); + message::appendNumberWDelim(buff, u->hp, ';'); + } + + buff.push_back('\n'); + + return buff; +} + void rts::game::handleNewClient(client* client_) { player* pl = new player(this, client_); allPlayers.insert(pl); @@ -133,6 +151,7 @@ void rts::game::addPlayerToRoom(player* pl) { activePlayers.insert(pl); pl->newUnit(_board.randomEmptyField(true)); // add first unit for the player to control pl->getClient()->sendToClient({'g', '\n'}); // joined active group + sendToPlayers(activePlayers, newPlayerMessage(pl)); } void rts::game::addPlayerToQueue(player* pl) { diff --git a/src/rts/game.hpp b/src/rts/game.hpp index 0a16777..49943ad 100644 --- a/src/rts/game.hpp +++ b/src/rts/game.hpp @@ -31,6 +31,11 @@ namespace rts { std::vector configMessage() const; std::vector boardStateMessage() const; + std::vector newPlayerMessage(const player* p) const; + std::vector newResourceMessage(const field* f) const; + std::vector moveMessage() const; + std::vector attackMessage() const; + std::vector mineMessage() const; void handleNewClient(client* client_); void loopLogic(); From c421dbd502cc216dcf1c8f101be4d9d89bf5afde Mon Sep 17 00:00:00 2001 From: pwalig Date: Sat, 21 Dec 2024 17:56:49 +0100 Subject: [PATCH 04/10] Send new resource message to clients --- src/rts/board.cpp | 10 ++++++---- src/rts/board.hpp | 4 ++-- src/rts/field.cpp | 6 ++++-- src/rts/field.hpp | 8 ++++++-- src/rts/game.cpp | 26 ++++++++++++++++++++------ src/rts/player.cpp | 2 +- 6 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/rts/board.cpp b/src/rts/board.cpp index 3c7bfe7..257dc37 100644 --- a/src/rts/board.cpp +++ b/src/rts/board.cpp @@ -83,14 +83,16 @@ rts::field* rts::board::closestEmptyField(const field* source) { return candidate; } -void rts::board::spawnResource(unsigned int hp) { - randomResourceField(false)->spawnResource(hp); +rts::field* rts::board::spawnResource(unsigned int hp) { + return randomResourceField(false)->spawnResource(hp); } -void rts::board::spawnResources(unsigned int amount, unsigned int hp) { +std::vector rts::board::spawnResources(unsigned int amount, unsigned int hp) { + std::vector fs; for (unsigned int i = 0; i < amount; ++i) { - spawnResource(hp); + fs.push_back(spawnResource(hp)); } + return fs; } unsigned int rts::board::getXdim() const { return fields.size(); } diff --git a/src/rts/board.hpp b/src/rts/board.hpp index 070b223..ffddc04 100644 --- a/src/rts/board.hpp +++ b/src/rts/board.hpp @@ -25,8 +25,8 @@ namespace rts { field* randomResourceField(bool resource); field* closestEmptyField(const field* source); - void spawnResource(unsigned int hp); - void spawnResources(unsigned int amount, unsigned int hp); + field* spawnResource(unsigned int hp); + std::vector spawnResources(unsigned int amount, unsigned int hp); unsigned int getXdim() const; unsigned int getYdim() const; diff --git a/src/rts/field.cpp b/src/rts/field.cpp index d0e31c4..8380b4c 100644 --- a/src/rts/field.cpp +++ b/src/rts/field.cpp @@ -16,13 +16,15 @@ bool rts::field::hasResource() const { return (resourceHp > 0); } -void rts::field::spawnResource(unsigned int hp) { +rts::field* rts::field::spawnResource(unsigned int hp) { assert(!hasResource()); resourceHp = (int)hp; + return this; } -void rts::field::mine(int dmg) { +rts::field* rts::field::mine(int dmg) { resourceHp -= dmg; + return this; } int rts::field::getHp() const { diff --git a/src/rts/field.hpp b/src/rts/field.hpp index 3da9359..2c7f034 100644 --- a/src/rts/field.hpp +++ b/src/rts/field.hpp @@ -11,8 +11,12 @@ namespace rts { bool empty() const; bool hasResource() const; - void spawnResource(unsigned int hp); - void mine(int dmg); + + // @returns this field + rts::field* spawnResource(unsigned int hp); + + // @returns this field + rts::field* mine(int dmg); int getHp() const; diff --git a/src/rts/game.cpp b/src/rts/game.cpp index 5eacc79..fabfd45 100644 --- a/src/rts/game.cpp +++ b/src/rts/game.cpp @@ -38,6 +38,8 @@ rts::game::game(const char *port, const char* configFile) : _server(port) { } } +// ========== MESSAGES ========= + std::vector rts::game::configMessage() const { std::vector buff = {'c'}; message::appendNumberWDelim(buff, millis, ' '); @@ -87,7 +89,7 @@ std::vector rts::game::boardStateMessage() const { } std::vector rts::game::newPlayerMessage(const player* p) const{ - std::vector buff = {'p'}; + std::vector buff = {'j'}; message::appendStringWDelim(buff, p->getName(), ' '); // player name message::appendNumberWDelim(buff, p->units.size(), ';'); // amount of units @@ -103,6 +105,15 @@ std::vector rts::game::newPlayerMessage(const player* p) const{ return buff; } +std::vector rts::game::newResourceMessage(const field* f) const{ + std::vector buff = {'f'}; + message::appendNumberWDelim(buff, f->x, ' '); + message::appendNumberWDelim(buff, f->y, ' '); + message::appendNumberWDelim(buff, f->getHp(), '\n'); + return buff; +} + +// ========== GAME LOGIC ========== void rts::game::handleNewClient(client* client_) { player* pl = new player(this, client_); @@ -111,12 +122,12 @@ void rts::game::handleNewClient(client* client_) { } void rts::game::loopLogic(){ - // spawn resource - if (rand() % 10 == 0) _board.spawnResource(resourceHp); + // spawn resource and inform players + if (rand() % 10 == 0) sendToPlayers(activePlayers, newResourceMessage(_board.spawnResource(resourceHp))); // sent updates to clients - sendToPlayers(activePlayers, boardStateMessage()); + // sendToPlayers(activePlayers, boardStateMessage()); // allow units to move again for (player* p : activePlayers) { @@ -148,10 +159,11 @@ void rts::game::startGame() { void rts::game::addPlayerToRoom(player* pl) { assert(activePlayers.size() < maxPlayers); - activePlayers.insert(pl); pl->newUnit(_board.randomEmptyField(true)); // add first unit for the player to control - pl->getClient()->sendToClient({'g', '\n'}); // joined active group sendToPlayers(activePlayers, newPlayerMessage(pl)); + activePlayers.insert(pl); + pl->getClient()->sendToClient({'g', '\n'}); // joined active group + pl->getClient()->sendToClient(boardStateMessage()); } void rts::game::addPlayerToQueue(player* pl) { @@ -235,6 +247,8 @@ void rts::game::tryWin(player* pl){ } } +// ========== GETTERS ========== + unsigned int rts::game::getUnitDamage() const {return unitDamage;} unsigned int rts::game::getUnitHp() const {return unitHp;} diff --git a/src/rts/player.cpp b/src/rts/player.cpp index 71b40a8..64e0bec 100644 --- a/src/rts/player.cpp +++ b/src/rts/player.cpp @@ -29,7 +29,7 @@ void rts::player::handleNewMessage(const message::base* msg) { } else if (const message::state* cmsg = dynamic_cast(msg)) { if (cmsg->act == message::state::action::disconnect) { - _game->deletePlayer(this); + _game->deletePlayer(this); } else if (cmsg->act == message::state::action::joinRequest) { _game->tryJoin(this); From 07cd575de38d66463704d02d779b8947ac26154a Mon Sep 17 00:00:00 2001 From: pwalig Date: Sat, 21 Dec 2024 18:48:51 +0100 Subject: [PATCH 05/10] Closes #19; send only changes instead of board state --- README.md | 28 ++++++++++++++++++++++------ src/rts/game.cpp | 16 +++++++++++++++- src/rts/game.hpp | 6 +++--- src/rts/player.cpp | 12 +++++++++++- src/rts/unit.cpp | 20 ++++++++++++++++++++ 5 files changed, 71 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a8663f2..4226952 100644 --- a/README.md +++ b/README.md @@ -76,16 +76,16 @@ Some messages consist of only type character others contain more data. ### From server -- `g` `\n` - player was sent to game room (in response to: `j`), ``, `` and `` same as in `[config file]` - `c` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` `\n` - whole server configuration sent to newly joined clients -- `p` `` ` ` `` `;` +- `j` `` ` ` `` `;` `` ` ` `` ` ` `` ` ` `` `;` ... -`` ` ` `` ` ` `` ` ` `` `;` `\n` - message explaining current state of the player, is sent to everyone when new player joins or to new player to inform about other players +`` ` ` `` ` ` `` ` ` `` `;` `\n` - new player has joined - `m` `` ` ` `` ` ` `` `\n` - unit of id `` has moved to `;` - `a` `` ` ` `` `\n` - unit of id `` attacked unit of id `` - `d` `` `\n` - unit of id `` mined a resource -- `r` `` ` ` `` ` ` `` `\n` - there is a resource on field `;`, sent to everyone when resource spawns or to new player for every resource that is already on the board +- `u` `` ` ` `` ` ` `` ` ` `` - player `` has aquired unit of id `` on field `;` +- `f` `` ` ` `` ` ` `` `\n` - new resource spawned on field `;` - `t` `\n` - sent to all players in game room in regular time intervals, marks the and of each tick and a start of the next one - `q` `\n` - player was sent to queue (in response to: `j`) - `y` `\n` - client request accepted (in response to: `n`) @@ -93,9 +93,9 @@ Some messages consist of only type character others contain more data. - `L` `\n` - client lost the game (and was moved out of game room) - `W` `\n` - client won the game (and was moved out of game room) -### Board state update +### Board state message -Board state update is sent to all players in the game room in regular time intervals +Board state update is sent to every player that joins the game room Structure as follows: @@ -117,3 +117,19 @@ Structure as follows: ... Numbers are represented as strings of characters (97 ---> "97" not 'a'). + +### Communication order + +1. server sends `c` +2. client sends `n` +3. if server responds `n` => go to step 2. +3. else server responds `y` +4. if client sends `n` => go to step 3. +4. else client sends `j` +5. if server responds `q` => wait until server sends `p`, then `r` +5. else server reponds `p`, then `r` +6. server can send multiple: `j` `m` `a` `d` `u` `f` messages +6. client can send multiple: `m` `a` `d` messages +6. if client sends `q` => go to step 4. +6. if server sends `W` or `L` => go to step 4. +7. server sends `t` => go to step 6. diff --git a/src/rts/game.cpp b/src/rts/game.cpp index fabfd45..25f787a 100644 --- a/src/rts/game.cpp +++ b/src/rts/game.cpp @@ -105,6 +105,15 @@ std::vector rts::game::newPlayerMessage(const player* p) const{ return buff; } + +std::vector rts::game::playerLeftMessage(const player* p) const{ + std::vector buff = {'l'}; + + message::appendStringWDelim(buff, p->getName(), '\n'); // player name + + return buff; +} + std::vector rts::game::newResourceMessage(const field* f) const{ std::vector buff = {'f'}; message::appendNumberWDelim(buff, f->x, ' '); @@ -145,6 +154,7 @@ void rts::game::clearRoom() { pl->removeAllUnits(); } activePlayers.clear(); + nextUnitId = 0; _server.loopLogic = [](){}; } @@ -162,7 +172,6 @@ void rts::game::addPlayerToRoom(player* pl) { pl->newUnit(_board.randomEmptyField(true)); // add first unit for the player to control sendToPlayers(activePlayers, newPlayerMessage(pl)); activePlayers.insert(pl); - pl->getClient()->sendToClient({'g', '\n'}); // joined active group pl->getClient()->sendToClient(boardStateMessage()); } @@ -182,6 +191,7 @@ void rts::game::removePlayerFromRoomOrQueue(player* pl) { if (activePlayers.find(pl) != activePlayers.end()) { activePlayers.erase(pl); pl->removeAllUnits(); + sendToPlayers(activePlayers, playerLeftMessage(pl)); if (!queuedPlayers.empty()) moveQueuedPlayerToRoom(); if (activePlayers.empty()) clearRoom(); } @@ -201,6 +211,10 @@ void rts::game::sendToPlayers(const std::unordered_set& players, c } } +void rts::game::sendToPlayers(const std::vector& message) const { + sendToPlayers(activePlayers, message); +} + void rts::game::run() { _server.loop(millis); } diff --git a/src/rts/game.hpp b/src/rts/game.hpp index 49943ad..adeb639 100644 --- a/src/rts/game.hpp +++ b/src/rts/game.hpp @@ -32,10 +32,8 @@ namespace rts { std::vector configMessage() const; std::vector boardStateMessage() const; std::vector newPlayerMessage(const player* p) const; + std::vector playerLeftMessage(const player* p) const; std::vector newResourceMessage(const field* f) const; - std::vector moveMessage() const; - std::vector attackMessage() const; - std::vector mineMessage() const; void handleNewClient(client* client_); void loopLogic(); @@ -53,6 +51,8 @@ namespace rts { board _board; game(const char *port, const char* configFile); + + void sendToPlayers(const std::vector& message) const; void run(); diff --git a/src/rts/player.cpp b/src/rts/player.cpp index 64e0bec..fceb5ae 100644 --- a/src/rts/player.cpp +++ b/src/rts/player.cpp @@ -7,6 +7,7 @@ #include #include #include +#include std::unordered_map rts::player::playersByName; @@ -96,7 +97,16 @@ void rts::player::removeAllUnits(){ } void rts::player::newUnit(field* field_){ - units.insert(new unit(this, field_, _game->getNextUnitId())); + unit* u = new unit(this, field_, _game->getNextUnitId()); + units.insert(u); + + std::vector buff = {'u'}; + message::appendStringWDelim(buff, _name, ' '); + message::appendNumberWDelim(buff, u->id, ' '); + message::appendNumberWDelim(buff, u->f->x, ' '); + message::appendNumberWDelim(buff, u->f->y, '\n'); + _game->sendToPlayers(buff); + _game->tryWin(this); } diff --git a/src/rts/unit.cpp b/src/rts/unit.cpp index 1632354..26d8891 100644 --- a/src/rts/unit.cpp +++ b/src/rts/unit.cpp @@ -6,6 +6,7 @@ #include #include #include +#include rts::unit::unit(player* owner_, field* field_, unsigned int id_) : owner(owner_), @@ -19,6 +20,11 @@ rts::unit::unit(player* owner_, field* field_, unsigned int id_) : void rts::unit::mine(){ if (!movedThisRound && f->hasResource()) { + + std::vector buff = {'d'}; + message::appendNumberWDelim(buff, id, '\n'); + owner->getGame()->sendToPlayers(buff); + f->mine(owner->getGame()->getUnitDamage()); if (f->getHp() <= 0) { field* nf = owner->getGame()->_board.closestEmptyField(f); @@ -29,6 +35,14 @@ void rts::unit::mine(){ } void rts::unit::move(field* field_){ if (!movedThisRound && f->distance(*field_) <= 1 && field_->empty()) { + + std::vector buff = {'m'}; + message::appendNumberWDelim(buff, id, ' '); + message::appendNumberWDelim(buff, field_->x, ' '); + message::appendNumberWDelim(buff, field_->y, '\n'); + owner->getGame()->sendToPlayers(buff); + + this->f->_unit = nullptr; this->f = field_; field_->_unit = this; @@ -38,6 +52,12 @@ void rts::unit::move(field* field_){ void rts::unit::attack(unit* target){ if (target == nullptr) return; if (!movedThisRound && f->distance(*(target->f)) <= 1) { + + std::vector buff = {'a'}; + message::appendNumberWDelim(buff, id, ' '); + message::appendNumberWDelim(buff, target->id, '\n'); + owner->getGame()->sendToPlayers(buff); + target->recvDamage(owner->getGame()->getUnitDamage()); movedThisRound = true; } From bc276b0999994628966944ecafe642fadbf9dfd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Walig=C3=B3ra?= Date: Sat, 21 Dec 2024 17:57:30 +0000 Subject: [PATCH 06/10] finish feature documentation --- README.md | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 4226952..c23526c 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ Some messages consist of only type character others contain more data. `` ` ` `` ` ` `` ` ` `` `;` ... `` ` ` `` ` ` `` ` ` `` `;` `\n` - new player has joined +- `l` `` `\n` - player `` has either left or lost the game - `m` `` ` ` `` ` ` `` `\n` - unit of id `` has moved to `;` - `a` `` ` ` `` `\n` - unit of id `` attacked unit of id `` - `d` `` `\n` - unit of id `` mined a resource @@ -118,18 +119,18 @@ Structure as follows: Numbers are represented as strings of characters (97 ---> "97" not 'a'). -### Communication order - -1. server sends `c` -2. client sends `n` -3. if server responds `n` => go to step 2. -3. else server responds `y` -4. if client sends `n` => go to step 3. -4. else client sends `j` -5. if server responds `q` => wait until server sends `p`, then `r` -5. else server reponds `p`, then `r` -6. server can send multiple: `j` `m` `a` `d` `u` `f` messages -6. client can send multiple: `m` `a` `d` messages -6. if client sends `q` => go to step 4. -6. if server sends `W` or `L` => go to step 4. -7. server sends `t` => go to step 6. +### Communication order (client`s perspective) + +**1:** server sends `c` +**2:** client sends `n` +**3:** if server responds `n` => go to step **2** +**3:** else server responds `y` +**4:** if client sends `n` => go to step **3** +**4:** else client sends `j` +**5:** if server responds `q` => wait until server sends `p`, then `r` +**5:** else server reponds `p`, then `r` +**6:** server can send multiple: `j` `l` `m` `a` `d` `u` `f` messages +**6:** client can send multiple: `m` `a` `d` messages +**6:** if client sends `q` => go to step **4** +**6:** if server sends `W` or `L` => go to step **4** +**7:** server sends `t` => go to step **6** From 92d80b941a40ca8d3d94bdaddb07ae2f595eddc1 Mon Sep 17 00:00:00 2001 From: pwalig Date: Sat, 21 Dec 2024 21:02:26 +0100 Subject: [PATCH 07/10] shorten new player joined message --- README.md | 5 +---- src/rts/board.cpp | 20 +++++++++++++++----- src/rts/board.hpp | 2 +- src/rts/field.cpp | 2 ++ src/rts/field.hpp | 2 +- src/rts/game.cpp | 18 +++++------------- 6 files changed, 25 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index c23526c..38fc801 100644 --- a/README.md +++ b/README.md @@ -77,10 +77,7 @@ Some messages consist of only type character others contain more data. ### From server - `c` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` ` ` `` `\n` - whole server configuration sent to newly joined clients -- `j` `` ` ` `` `;` -`` ` ` `` ` ` `` ` ` `` `;` -... -`` ` ` `` ` ` `` ` ` `` `;` `\n` - new player has joined +- `j` `` `\n` - new player has joined the game room - `l` `` `\n` - player `` has either left or lost the game - `m` `` ` ` `` ` ` `` `\n` - unit of id `` has moved to `;` - `a` `` ` ` `` `\n` - unit of id `` attacked unit of id `` diff --git a/src/rts/board.cpp b/src/rts/board.cpp index 257dc37..360c6c2 100644 --- a/src/rts/board.cpp +++ b/src/rts/board.cpp @@ -27,7 +27,13 @@ std::vector rts::board::resourceFields(bool resource) { std::vector out; for (std::vector& row : fields){ for (field& f : row) { - if (f.hasResource() == resource) out.push_back(&f); + if (f.hasResource() == resource) { + printf("begin\n"); + printf("%d, %d ", (int)out.size(), (int)out.capacity()); + out.push_back(&f); + printf("y\n"); + printf("end\n"); + } } } return out; @@ -87,12 +93,16 @@ rts::field* rts::board::spawnResource(unsigned int hp) { return randomResourceField(false)->spawnResource(hp); } -std::vector rts::board::spawnResources(unsigned int amount, unsigned int hp) { - std::vector fs; +void rts::board::spawnResources(unsigned int amount, unsigned int hp) { + std::vector resFields = resourceFields(false); for (unsigned int i = 0; i < amount; ++i) { - fs.push_back(spawnResource(hp)); + if (resFields.empty()) return; + std::uniform_int_distribution<> distrib(0, resFields.size() - 1); + int fid = distrib(gen); + field* f = resFields[fid]; + f->spawnResource(hp); + resFields.erase(resFields.begin() + fid); } - return fs; } unsigned int rts::board::getXdim() const { return fields.size(); } diff --git a/src/rts/board.hpp b/src/rts/board.hpp index ffddc04..88e4dd5 100644 --- a/src/rts/board.hpp +++ b/src/rts/board.hpp @@ -26,7 +26,7 @@ namespace rts { field* closestEmptyField(const field* source); field* spawnResource(unsigned int hp); - std::vector spawnResources(unsigned int amount, unsigned int hp); + void spawnResources(unsigned int amount, unsigned int hp); unsigned int getXdim() const; unsigned int getYdim() const; diff --git a/src/rts/field.cpp b/src/rts/field.cpp index 8380b4c..f7017d0 100644 --- a/src/rts/field.cpp +++ b/src/rts/field.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -19,6 +20,7 @@ bool rts::field::hasResource() const { rts::field* rts::field::spawnResource(unsigned int hp) { assert(!hasResource()); resourceHp = (int)hp; + printf("spawned resource at: %d, %d\n", x, y); return this; } diff --git a/src/rts/field.hpp b/src/rts/field.hpp index 2c7f034..3c42cd3 100644 --- a/src/rts/field.hpp +++ b/src/rts/field.hpp @@ -14,7 +14,7 @@ namespace rts { // @returns this field rts::field* spawnResource(unsigned int hp); - + // @returns this field rts::field* mine(int dmg); diff --git a/src/rts/game.cpp b/src/rts/game.cpp index 25f787a..37b6eee 100644 --- a/src/rts/game.cpp +++ b/src/rts/game.cpp @@ -73,10 +73,12 @@ std::vector rts::game::boardStateMessage() const { buff.push_back(';'); } + printf("BEGIN\n"); buff.push_back('\n'); - + printf("END1\n"); // resources buff.push_back('r'); + printf("END2\n"); std::vector resources = _board.constResourceFields(true); message::appendNumberWDelim(buff, resources.size(), ';'); // amount of resources for (const field* f : resources) { @@ -91,17 +93,7 @@ std::vector rts::game::boardStateMessage() const { std::vector rts::game::newPlayerMessage(const player* p) const{ std::vector buff = {'j'}; - message::appendStringWDelim(buff, p->getName(), ' '); // player name - message::appendNumberWDelim(buff, p->units.size(), ';'); // amount of units - - for (unit* u : p->units) { - message::appendNumberWDelim(buff, u->id, ' '); - message::appendNumberWDelim(buff, u->f->x, ' '); - message::appendNumberWDelim(buff, u->f->y, ' '); - message::appendNumberWDelim(buff, u->hp, ';'); - } - - buff.push_back('\n'); + message::appendStringWDelim(buff, p->getName(), '\n'); // player name return buff; } @@ -169,8 +161,8 @@ void rts::game::startGame() { void rts::game::addPlayerToRoom(player* pl) { assert(activePlayers.size() < maxPlayers); - pl->newUnit(_board.randomEmptyField(true)); // add first unit for the player to control sendToPlayers(activePlayers, newPlayerMessage(pl)); + pl->newUnit(_board.randomEmptyField(true)); // add first unit for the player to control activePlayers.insert(pl); pl->getClient()->sendToClient(boardStateMessage()); } From 5024d5ae4e6f0298ee1fac08a1595e386dda5721 Mon Sep 17 00:00:00 2001 From: pwalig Date: Sat, 21 Dec 2024 22:46:33 +0100 Subject: [PATCH 08/10] Fix unable to rejoin after finished game bug --- src/rts/unit.cpp | 7 ++++--- src/rts/unit.hpp | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/rts/unit.cpp b/src/rts/unit.cpp index 26d8891..a47f08d 100644 --- a/src/rts/unit.cpp +++ b/src/rts/unit.cpp @@ -9,9 +9,10 @@ #include rts::unit::unit(player* owner_, field* field_, unsigned int id_) : - owner(owner_), - f(field_), id(id_), - hp(owner_->getGame()->getUnitHp()) + id(id_), + hp(owner_->getGame()->getUnitHp()), + f(field_), + owner(owner_) { printf("%s got new unit\n", owner->getName().c_str()); assert(f->empty()); diff --git a/src/rts/unit.hpp b/src/rts/unit.hpp index b3f8f82..4e8e1e3 100644 --- a/src/rts/unit.hpp +++ b/src/rts/unit.hpp @@ -6,11 +6,11 @@ namespace rts { class unit { public: - bool movedThisRound = false; - player* const owner; - field* f; const unsigned int id; unsigned int hp; + field* f; + player* const owner; + bool movedThisRound = false; unit(player* owner_, field* field_, unsigned int id_); From 77bdd5daf15c0f6a040a40df5d49e163ca0f77e8 Mon Sep 17 00:00:00 2001 From: pwalig Date: Sat, 21 Dec 2024 23:08:31 +0100 Subject: [PATCH 09/10] Remove bug tracing comments --- src/net/server.cpp | 3 +-- src/rts/board.cpp | 8 +------- src/rts/game.cpp | 5 ++--- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/net/server.cpp b/src/net/server.cpp index 3eaba2d..2434679 100644 --- a/src/net/server.cpp +++ b/src/net/server.cpp @@ -81,8 +81,7 @@ void server::loop(const int& millis){ else { client* client_ = (client*)(ee.data.ptr); if (ee.events & EPOLLIN) { - std::vector data = client_->receive(); - write(0, data.data(), data.size()); + client_->receive(); } if (ee.events & EPOLLOUT) { client_->sendFromBuffer(); diff --git a/src/rts/board.cpp b/src/rts/board.cpp index 360c6c2..039cb0f 100644 --- a/src/rts/board.cpp +++ b/src/rts/board.cpp @@ -27,13 +27,7 @@ std::vector rts::board::resourceFields(bool resource) { std::vector out; for (std::vector& row : fields){ for (field& f : row) { - if (f.hasResource() == resource) { - printf("begin\n"); - printf("%d, %d ", (int)out.size(), (int)out.capacity()); - out.push_back(&f); - printf("y\n"); - printf("end\n"); - } + if (f.hasResource() == resource) out.push_back(&f); } } return out; diff --git a/src/rts/game.cpp b/src/rts/game.cpp index 37b6eee..c873f3b 100644 --- a/src/rts/game.cpp +++ b/src/rts/game.cpp @@ -73,12 +73,9 @@ std::vector rts::game::boardStateMessage() const { buff.push_back(';'); } - printf("BEGIN\n"); buff.push_back('\n'); - printf("END1\n"); // resources buff.push_back('r'); - printf("END2\n"); std::vector resources = _board.constResourceFields(true); message::appendNumberWDelim(buff, resources.size(), ';'); // amount of resources for (const field* f : resources) { @@ -148,10 +145,12 @@ void rts::game::clearRoom() { activePlayers.clear(); nextUnitId = 0; _server.loopLogic = [](){}; + printf("room cleared\n"); } void rts::game::startGame() { _board = board(boardX, boardY); // reset board + printf("game start\n"); _board.spawnResources(startResources, resourceHp); while(!queuedPlayers.empty() && activePlayers.size() < maxPlayers){ moveQueuedPlayerToRoom(); From c5cabb1f05cb4a6eef3af6b08ee6902ac9de05d9 Mon Sep 17 00:00:00 2001 From: pwalig Date: Sat, 21 Dec 2024 23:31:46 +0100 Subject: [PATCH 10/10] Remove commented code --- src/rts/game.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/rts/game.cpp b/src/rts/game.cpp index c873f3b..ac29ac2 100644 --- a/src/rts/game.cpp +++ b/src/rts/game.cpp @@ -122,10 +122,6 @@ void rts::game::handleNewClient(client* client_) { void rts::game::loopLogic(){ // spawn resource and inform players if (rand() % 10 == 0) sendToPlayers(activePlayers, newResourceMessage(_board.spawnResource(resourceHp))); - - - // sent updates to clients - // sendToPlayers(activePlayers, boardStateMessage()); // allow units to move again for (player* p : activePlayers) {