diff --git a/README.md b/README.md index 1297874..38fc801 100644 --- a/README.md +++ b/README.md @@ -68,16 +68,22 @@ 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) ### 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 +- `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 `` +- `d` `` `\n` - unit of id `` mined a resource +- `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`) @@ -85,9 +91,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: @@ -109,3 +115,19 @@ Structure as follows: ... Numbers are represented as strings of characters (97 ---> "97" not 'a'). + +### 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** 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 3c7bfe7..039cb0f 100644 --- a/src/rts/board.cpp +++ b/src/rts/board.cpp @@ -83,13 +83,19 @@ 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 resFields = resourceFields(false); for (unsigned int i = 0; i < amount; ++i) { - 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); } } diff --git a/src/rts/board.hpp b/src/rts/board.hpp index 070b223..88e4dd5 100644 --- a/src/rts/board.hpp +++ b/src/rts/board.hpp @@ -25,7 +25,7 @@ namespace rts { field* randomResourceField(bool resource); field* closestEmptyField(const field* source); - void spawnResource(unsigned int hp); + field* spawnResource(unsigned int hp); void spawnResources(unsigned int amount, unsigned int hp); unsigned int getXdim() const; diff --git a/src/rts/field.cpp b/src/rts/field.cpp index d0e31c4..f7017d0 100644 --- a/src/rts/field.cpp +++ b/src/rts/field.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -16,13 +17,16 @@ 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; + printf("spawned resource at: %d, %d\n", x, y); + 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..3c42cd3 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 2486c47..ac29ac2 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, ' '); @@ -72,7 +74,6 @@ std::vector rts::game::boardStateMessage() const { buff.push_back(';'); } buff.push_back('\n'); - // resources buff.push_back('r'); std::vector resources = _board.constResourceFields(true); @@ -86,6 +87,32 @@ std::vector rts::game::boardStateMessage() const { return buff; } +std::vector rts::game::newPlayerMessage(const player* p) const{ + std::vector buff = {'j'}; + + message::appendStringWDelim(buff, p->getName(), '\n'); // player name + + 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, ' '); + 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_); allPlayers.insert(pl); @@ -93,12 +120,8 @@ void rts::game::handleNewClient(client* client_) { } void rts::game::loopLogic(){ - // spawn resource - if (rand() % 10 == 0) _board.spawnResource(resourceHp); - - - // sent updates to clients - sendToPlayers(activePlayers, boardStateMessage()); + // spawn resource and inform players + if (rand() % 10 == 0) sendToPlayers(activePlayers, newResourceMessage(_board.spawnResource(resourceHp))); // allow units to move again for (player* p : activePlayers) { @@ -116,11 +139,14 @@ void rts::game::clearRoom() { pl->removeAllUnits(); } 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(); @@ -130,9 +156,10 @@ void rts::game::startGame() { void rts::game::addPlayerToRoom(player* pl) { assert(activePlayers.size() < maxPlayers); - activePlayers.insert(pl); + sendToPlayers(activePlayers, newPlayerMessage(pl)); pl->newUnit(_board.randomEmptyField(true)); // add first unit for the player to control - pl->getClient()->sendToClient({'g', '\n'}); // joined active group + activePlayers.insert(pl); + pl->getClient()->sendToClient(boardStateMessage()); } void rts::game::addPlayerToQueue(player* pl) { @@ -151,6 +178,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(); } @@ -170,6 +198,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); } @@ -216,6 +248,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/game.hpp b/src/rts/game.hpp index 0a16777..adeb639 100644 --- a/src/rts/game.hpp +++ b/src/rts/game.hpp @@ -31,6 +31,9 @@ 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; void handleNewClient(client* client_); void loopLogic(); @@ -48,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 71b40a8..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; @@ -29,7 +30,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); @@ -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..a47f08d 100644 --- a/src/rts/unit.cpp +++ b/src/rts/unit.cpp @@ -6,11 +6,13 @@ #include #include #include +#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()); @@ -19,6 +21,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 +36,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 +53,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; } 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_);