1
0
Fork 0
mirror of synced 2025-03-06 20:53:27 +01:00
mislaborate/server/Connection.hpp

125 lines
3.1 KiB
C++

#ifndef _CONNECTION_HPP
#define _CONNECTION_HPP
#include "generic.pb.h"
#include <string>
#include <initializer_list>
#include <vector>
#include <exception>
#include <stdexcept>
#include <memory>
#include <boost/asio/awaitable.hpp>
#include <boost/asio/use_awaitable.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/read.hpp>
namespace World {
struct Room;
}
namespace Connection {
using namespace boost;
class Global;
struct Packet {
Generic::Operation op;
std::unique_ptr<google::protobuf::Message> data = nullptr;
};
struct Error : public std::runtime_error {
Packet packet;
Error(const std::string& description, Generic::Error::Code code) : std::runtime_error(description) {
packet.op.set_code(Generic::Operation::Error);
auto error = std::make_unique<Generic::Error>();
error->set_description(description);
error->set_code(code);
packet.data = std::move(error);
}
};
struct FatalError : public Error {
using Error::Error;
};
class Client : public std::enable_shared_from_this<Client> {
Global& global;
asio::ip::tcp::socket socket;
bool authed = false;
Generic::UserInfo userInfo;
bool good = true;
public:
Client(asio::io_context& ioc, Global& global) : socket(ioc), global(global) {}
asio::awaitable<Packet> receivePacket();
asio::awaitable<void> sendProtobuf(const google::protobuf::Message& buffer);
asio::awaitable<void> sendPacket(const Packet& packet);
asio::awaitable<void> handlePacket(const Packet& packet);
asio::awaitable<void> connect();
template<class Buffer>
asio::awaitable<Buffer> receiveProtobuf(uint16_t maxSize = 1024) {
// Receive size
uint16_t size;
co_await asio::async_read(socket, asio::buffer(&size, sizeof(size)), asio::use_awaitable);
// Check size
if (size > maxSize) {
throw FatalError("Buffer too large ("+std::to_string(size)+" > "+std::to_string(maxSize)+')', Generic::Error::TooLarge);
}
// Receive protobuf
Buffer protobuf;
if (size) {
std::vector<char> rawProtobuf(size);
co_await asio::async_read(socket, asio::buffer(rawProtobuf.data(), rawProtobuf.size()), asio::use_awaitable);
// Parse protobuf
protobuf.ParseFromArray(rawProtobuf.data(), static_cast<int>(size));
}
// Return it
co_return protobuf;
}
asio::awaitable<void> sendOK() {
Packet packet;
packet.op.set_code(Generic::Operation::OK);
co_await sendPacket(packet);
}
auto& getSocket() {
return socket;
}
const auto& getUserInfo() const {
return userInfo;
}
bool isGood() const {
return good;
}
bool isAuthed() const {
return good && authed;
}
};
struct Global {
std::vector<std::shared_ptr<Client>> clients;
std::vector<std::shared_ptr<World::Room>> rooms;
};
class Server {
asio::io_context& ioc;
Global global;
public:
Server(asio::io_context& ioc) : ioc(ioc) {}
asio::awaitable<void> listen(int port);
};
}
#endif