134 lines
3.3 KiB
C++
134 lines
3.3 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:
|
|
World::Room *currentRoom = nullptr;
|
|
|
|
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;
|
|
uint64_t highestTemporaryId = 0;
|
|
|
|
std::shared_ptr<World::Room> getRandomRoom() const;
|
|
|
|
uint64_t getTemporaryId() {
|
|
return ++highestTemporaryId;
|
|
}
|
|
};
|
|
|
|
|
|
class Server {
|
|
asio::io_context& ioc;
|
|
Global global;
|
|
|
|
public:
|
|
Server(asio::io_context& ioc) : ioc(ioc) {}
|
|
|
|
asio::awaitable<void> listen(int port);
|
|
};
|
|
}
|
|
#endif
|