Further implementation and some documentation
This commit is contained in:
parent
45afcdee07
commit
a76c40d876
6 changed files with 170 additions and 4 deletions
|
@ -17,6 +17,7 @@ message Error {
|
||||||
OutOfRange = 8;
|
OutOfRange = 8;
|
||||||
NotYourTurn = 9;
|
NotYourTurn = 9;
|
||||||
RoomNotAvailable = 10;
|
RoomNotAvailable = 10;
|
||||||
|
RoomFull = 11;
|
||||||
}
|
}
|
||||||
Code code = 2;
|
Code code = 2;
|
||||||
}
|
}
|
||||||
|
@ -32,13 +33,15 @@ message Operation {
|
||||||
OK = 2;
|
OK = 2;
|
||||||
SimpleAuth = 3;
|
SimpleAuth = 3;
|
||||||
_MinAuth = 4;
|
_MinAuth = 4;
|
||||||
SettingsSync = 5;
|
RoomInfoSync = 5;
|
||||||
PlayfieldSync = 6;
|
PlayfieldSync = 6;
|
||||||
RobotUpdate = 7;
|
RobotUpdate = 7;
|
||||||
YourTurn = 8;
|
YourTurn = 8;
|
||||||
MakeRoom = 9;
|
MakeRoom = 9;
|
||||||
JoinRoom = 10;
|
JoinRoom = 10;
|
||||||
JoinRandomRoom = 11;
|
JoinRandomRoom = 11;
|
||||||
|
LeaveRoom = 12;
|
||||||
|
UserInfoSync = 13;
|
||||||
}
|
}
|
||||||
Code code = 1;
|
Code code = 1;
|
||||||
}
|
}
|
||||||
|
@ -58,11 +61,17 @@ message Settings {
|
||||||
optional uint32 protectCooldown = 5;
|
optional uint32 protectCooldown = 5;
|
||||||
optional uint32 healCooldown = 6;
|
optional uint32 healCooldown = 6;
|
||||||
optional uint32 healPerTurn = 7;
|
optional uint32 healPerTurn = 7;
|
||||||
|
optional uint32 maxPlayers = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
message UserReference {
|
||||||
|
uint64 clientId = 2;
|
||||||
|
uint64 userId = 3;
|
||||||
|
}
|
||||||
message PublicUserInfo {
|
message PublicUserInfo {
|
||||||
string name = 1;
|
string name = 1;
|
||||||
|
UserReference reference = 2;
|
||||||
}
|
}
|
||||||
message UserInfo {
|
message UserInfo {
|
||||||
PublicUserInfo publicInfo = 1;
|
PublicUserInfo publicInfo = 1;
|
||||||
|
|
27
generic.txt
Normal file
27
generic.txt
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
Server
|
||||||
|
|
||||||
|
void Disconnect() // client closes connection
|
||||||
|
void Error(Error)
|
||||||
|
void OK()
|
||||||
|
SimpleAuth(SimpleAuth) -> UserInfoSync // client wants to authenticate anonymously
|
||||||
|
RobotUpdate(Robot) -> RobotUpdate // client wants to change robot
|
||||||
|
void RoomInfoSync(RoomInfo) <- TODO // client wants to change room info
|
||||||
|
MakeRoom() -> RoomInfoSync // client creates a room
|
||||||
|
JoinRoom(???) -> RoomInfoSync // client joins specific room
|
||||||
|
JoinRandomRoom() -> RoomInfoSync // client joins random room
|
||||||
|
LeaveRoom() -> LeaveRoom // client leaves current room
|
||||||
|
|
||||||
|
|
||||||
|
Client
|
||||||
|
|
||||||
|
Disconnect() // server closes connection
|
||||||
|
Error(Error)
|
||||||
|
OK()
|
||||||
|
RobotUpdate(Robot) // this robot has changed
|
||||||
|
RoomInfoSync(RoomInfo) // your current rooms info has changed
|
||||||
|
UserInfoSync(UserInfo) // your user info has been changed
|
||||||
|
PlayfielSync(PlayfieldSync) // playfield has been created
|
||||||
|
RoomInfoSync(RoomInfo) // room info has changed
|
||||||
|
YourTurn() -> RobotUpdate // it's your turn
|
||||||
|
JoinRoom(PublicUserInfo) // player joins your current room
|
||||||
|
LeaveRoom(UserReference) // player leaves your current room
|
|
@ -1,4 +1,5 @@
|
||||||
#include "Connection.hpp"
|
#include "Connection.hpp"
|
||||||
|
#include "World.hpp"
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -19,6 +20,12 @@ asio::awaitable<Packet> Client::receivePacket() {
|
||||||
case Generic::Operation::SimpleAuth: {
|
case Generic::Operation::SimpleAuth: {
|
||||||
packet.data = std::make_unique<Generic::SimpleAuth>(co_await receiveProtobuf<Generic::SimpleAuth>(30));
|
packet.data = std::make_unique<Generic::SimpleAuth>(co_await receiveProtobuf<Generic::SimpleAuth>(30));
|
||||||
} break;
|
} break;
|
||||||
|
case Generic::Operation::RobotUpdate: {
|
||||||
|
packet.data = std::make_unique<Generic::Robot>(co_await receiveProtobuf<Generic::Robot>());
|
||||||
|
} break;
|
||||||
|
case Generic::Operation::UserInfoSync:
|
||||||
|
case Generic::Operation::MakeRoom:
|
||||||
|
case Generic::Operation::JoinRandomRoom:
|
||||||
case Generic::Operation::Disconnect:
|
case Generic::Operation::Disconnect:
|
||||||
case Generic::Operation::OK:
|
case Generic::Operation::OK:
|
||||||
break;
|
break;
|
||||||
|
@ -65,14 +72,45 @@ asio::awaitable<void> Client::handlePacket(const Packet& packet) {
|
||||||
//TODO...
|
//TODO...
|
||||||
// Set username
|
// Set username
|
||||||
userInfo.mutable_publicinfo()->set_name(std::move(authData.username()));
|
userInfo.mutable_publicinfo()->set_name(std::move(authData.username()));
|
||||||
|
// Set temporary user and client id
|
||||||
|
auto userRef = userInfo.mutable_publicinfo()->mutable_reference();
|
||||||
|
userRef->set_userid(global.getTemporaryId());
|
||||||
|
userRef->set_clientid(global.getTemporaryId());
|
||||||
// Mark as authed
|
// Mark as authed
|
||||||
authed = true;
|
authed = true;
|
||||||
// Send OK
|
// Send user info
|
||||||
co_await sendOK();
|
Packet userInfoSync;
|
||||||
|
userInfoSync.op.set_code(Generic::Operation::UserInfoSync);
|
||||||
|
userInfoSync.data = std::make_unique<Generic::UserInfo>(userInfo);
|
||||||
|
} break;
|
||||||
|
case Generic::Operation::MakeRoom: {
|
||||||
|
// Create room
|
||||||
|
auto room = std::make_shared<World::Room>(global);
|
||||||
|
global.rooms.push_back(room);
|
||||||
|
co_await room->addClient(shared_from_this());
|
||||||
|
} break;
|
||||||
|
case Generic::Operation::JoinRandomRoom: {
|
||||||
|
// Get random room
|
||||||
|
auto room = global.getRandomRoom();
|
||||||
|
// Check that room was found
|
||||||
|
if (!room) {
|
||||||
|
throw Error("No available room could be found", Generic::Error::RoomNotAvailable);
|
||||||
|
}
|
||||||
|
// Add client to it
|
||||||
|
co_await room->addClient(shared_from_this());
|
||||||
|
} break;
|
||||||
|
case Generic::Operation::LeaveRoom: {
|
||||||
|
// Check that client is in any room
|
||||||
|
if (!currentRoom) {
|
||||||
|
throw Error("You are not in any room", Generic::Error::IllegalOperation);
|
||||||
|
}
|
||||||
|
// Remove client from room
|
||||||
|
co_await currentRoom->removeClient(shared_from_this());
|
||||||
} break;
|
} break;
|
||||||
default: {}
|
default: {}
|
||||||
}
|
}
|
||||||
co_return;
|
// Send OK
|
||||||
|
co_await sendOK();
|
||||||
}
|
}
|
||||||
|
|
||||||
asio::awaitable<void> Client::connect() {
|
asio::awaitable<void> Client::connect() {
|
||||||
|
@ -111,6 +149,16 @@ asio::awaitable<void> Client::connect() {
|
||||||
socket.close(ec);
|
socket.close(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<World::Room> Global::getRandomRoom() const {
|
||||||
|
//TODO: Real rng
|
||||||
|
for (const auto& room : rooms) {
|
||||||
|
if (room->isOpen()) {
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
asio::awaitable<void> Server::listen(int port) {
|
asio::awaitable<void> Server::listen(int port) {
|
||||||
try {
|
try {
|
||||||
// Create acceptor
|
// Create acceptor
|
||||||
|
|
|
@ -56,6 +56,8 @@ class Client : public std::enable_shared_from_this<Client> {
|
||||||
bool good = true;
|
bool good = true;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
World::Room *currentRoom = nullptr;
|
||||||
|
|
||||||
Client(asio::io_context& ioc, Global& global) : socket(ioc), global(global) {}
|
Client(asio::io_context& ioc, Global& global) : socket(ioc), global(global) {}
|
||||||
|
|
||||||
asio::awaitable<Packet> receivePacket();
|
asio::awaitable<Packet> receivePacket();
|
||||||
|
@ -109,6 +111,13 @@ public:
|
||||||
struct Global {
|
struct Global {
|
||||||
std::vector<std::shared_ptr<Client>> clients;
|
std::vector<std::shared_ptr<Client>> clients;
|
||||||
std::vector<std::shared_ptr<World::Room>> rooms;
|
std::vector<std::shared_ptr<World::Room>> rooms;
|
||||||
|
uint64_t highestTemporaryId = 0;
|
||||||
|
|
||||||
|
std::shared_ptr<World::Room> getRandomRoom() const;
|
||||||
|
|
||||||
|
uint64_t getTemporaryId() {
|
||||||
|
return ++highestTemporaryId;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,9 @@ void fillSettings(Generic::Settings& settings) {
|
||||||
if (!settings.has_healperturn()) {
|
if (!settings.has_healperturn()) {
|
||||||
settings.set_healperturn(2);
|
settings.set_healperturn(2);
|
||||||
}
|
}
|
||||||
|
if (!settings.has_maxplayers()) {
|
||||||
|
settings.set_maxplayers(4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::asio::awaitable<void> Playfield::reset() {
|
boost::asio::awaitable<void> Playfield::reset() {
|
||||||
|
@ -138,4 +141,56 @@ Generic::RoomInfo Room::getFullRoomInfo() const {
|
||||||
}
|
}
|
||||||
return fres;
|
return fres;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boost::asio::awaitable<void> Room::addClient(const std::shared_ptr<Connection::Client>& client) {
|
||||||
|
// Check that room is not full
|
||||||
|
if (isFull()) {
|
||||||
|
throw Connection::Error("Room is full", Generic::Error::RoomFull);
|
||||||
|
}
|
||||||
|
// Check that room has no game running
|
||||||
|
if (isRunning()) {
|
||||||
|
throw Connection::Error("Room is in game", Generic::Error::RoomNotAvailable);
|
||||||
|
}
|
||||||
|
// Broadcast join
|
||||||
|
Connection::Packet packet;
|
||||||
|
packet.op.set_code(Generic::Operation::JoinRoom);
|
||||||
|
packet.data = std::make_unique<Generic::PublicUserInfo>(client->getUserInfo().publicinfo());
|
||||||
|
for (auto& client : clients) {
|
||||||
|
co_await client->sendPacket(packet);
|
||||||
|
}
|
||||||
|
// Add client
|
||||||
|
clients.push_back(client);
|
||||||
|
client->currentRoom = this;
|
||||||
|
// Send room info to client
|
||||||
|
packet.op.set_code(Generic::Operation::RoomInfoSync);
|
||||||
|
packet.data = std::make_unique<Generic::RoomInfo>(getFullRoomInfo());
|
||||||
|
co_await client->sendPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::awaitable<void> Room::removeClient(const std::shared_ptr<Connection::Client>& client) {
|
||||||
|
// Broadcast leave
|
||||||
|
Connection::Packet packet;
|
||||||
|
packet.op.set_code(Generic::Operation::LeaveRoom);
|
||||||
|
packet.data = std::make_unique<Generic::UserReference>(client->getUserInfo().publicinfo().reference());
|
||||||
|
for (auto& client : clients) {
|
||||||
|
co_await client->sendPacket(packet);
|
||||||
|
}
|
||||||
|
// Remove client from list
|
||||||
|
clients.erase(std::find(clients.begin(), clients.end(), client));
|
||||||
|
client->currentRoom = nullptr;
|
||||||
|
// Remove room if now empty
|
||||||
|
if (clients.empty()) {
|
||||||
|
removeRoom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Room::removeRoom() {
|
||||||
|
global.rooms.erase(std::find_if(global.rooms.begin(), global.rooms.end(), [this] (auto o) {return o.get() == this;}));
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::awaitable<void> Room::removeAllClients() {
|
||||||
|
for (auto& client : clients) {
|
||||||
|
co_await removeClient(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
namespace Connection {
|
namespace Connection {
|
||||||
class Client;
|
class Client;
|
||||||
|
class Global;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace World {
|
namespace World {
|
||||||
|
@ -92,15 +93,32 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Room {
|
struct Room {
|
||||||
|
Connection::Global &global;
|
||||||
Generic::RoomInfo info;
|
Generic::RoomInfo info;
|
||||||
std::vector<std::shared_ptr<Connection::Client>> clients;
|
std::vector<std::shared_ptr<Connection::Client>> clients;
|
||||||
std::unique_ptr<Playfield> playfield = nullptr;
|
std::unique_ptr<Playfield> playfield = nullptr;
|
||||||
|
|
||||||
|
Room(Connection::Global& global)
|
||||||
|
: global(global) {}
|
||||||
|
|
||||||
Generic::RoomInfo getFullRoomInfo() const;
|
Generic::RoomInfo getFullRoomInfo() const;
|
||||||
|
boost::asio::awaitable<void> addClient(const std::shared_ptr<Connection::Client>& client);
|
||||||
|
boost::asio::awaitable<void> removeClient(const std::shared_ptr<Connection::Client>& client);
|
||||||
|
void removeRoom();
|
||||||
|
boost::asio::awaitable<void> removeAllClients();
|
||||||
|
|
||||||
bool isMasterClient(const Connection::Client *client) const {
|
bool isMasterClient(const Connection::Client *client) const {
|
||||||
return client == clients.front().get();
|
return client == clients.front().get();
|
||||||
}
|
}
|
||||||
|
bool isFull() const {
|
||||||
|
return clients.size() >= info.settings().maxplayers();
|
||||||
|
}
|
||||||
|
bool isRunning() const {
|
||||||
|
return bool(playfield);
|
||||||
|
}
|
||||||
|
bool isOpen() const {
|
||||||
|
return !isFull() && !isRunning();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Reference in a new issue