Path: blob/main/Server/Server.cpp
817 views
#include <cstdlib>1#include <deque>2#include <iostream>3#include <list>4#include <map>5#include <memory>6#include <set>7#include <utility>8#include "asio.hpp"910#define DATA_MAX 0x10001112typedef unsigned int uint;1314using asio::ip::tcp;1516struct CodedData {17unsigned char header;18uint64_t code;19uint roomcode;20union {21unsigned char bytes[DATA_MAX];22int ints[DATA_MAX / sizeof(int)];23} data;24};2526//----------------------------------------------------------------------2728typedef std::deque<CodedData> data_queue;2930//----------------------------------------------------------------------3132class player33{34public:35virtual ~player() {}36virtual void deliver(const CodedData &msg) = 0;37uint64_t code = 0;38uint roomcode = 0;39bool hasPartner = true; // to prevent ==0 joins40};4142typedef std::shared_ptr<player> player_ptr;43std::set<uint64_t> codes;4445//----------------------------------------------------------------------4647bool debug = false;48bool verbose = false;4950inline uint randomint()51{52uint res = 0;53for (int i = 0; i < 4; i++) {54res |= (rand() & 0xFF) << (i * 8);55}56if (!res)57res++;58return res;59}6061class game_room62{63public:64game_room(){};65game_room(std::string &name) : game_name(name), set(true){};66void join(player_ptr participant)67{68participants.insert(participant);69if (debug)70std::cout << "[" + game_name + "] "71<< "New player " << std::hex << participant->code << std::endl;72}7374void leave(player_ptr participant)75{76participants.erase(participant);77rooms.erase(participant->roomcode);78codes.erase(participant->code);79if (debug)80std::cout << "[" + game_name + "] "81<< "Player " << std::hex << participant->code << " in room " << participant->roomcode << " left" << std::endl;82}8384void deliver(const CodedData &msg, player_ptr sender)85{86for (auto &participant : participants) {87if (participant->code != sender->code && participant->roomcode == msg.roomcode) {88CodedData s(msg);89//s.code = participant->code;90participant->deliver(s);91}92}93}9495bool meet(const CodedData msg, player_ptr searcher)96{97if (searcher->hasPartner)98return true; // YOU HAVE ONE ALREADY99for (auto &participant : participants) {100if (participant->code != searcher->code && participant->roomcode == msg.roomcode && !participant->hasPartner) {101participant->hasPartner = true;102searcher->hasPartner = true;103CodedData send;104send.code = participant->code;105send.header = 0x80;106send.roomcode = msg.roomcode;107participant->deliver(send);108return true;109}110}111return false;112}113114std::set<uint> rooms;115std::set<player_ptr> participants;116std::string game_name = "(NO GAME)";117bool set = false;118};119120game_room blank_room;121std::vector<game_room> rooms;122123//----------------------------------------------------------------------124125class player_connection : public player, public std::enable_shared_from_this<player_connection>126{127public:128player_connection(tcp::socket socket) : socket_(std::move(socket))129{130do {131code = randomint() | ((uint64_t)randomint() << 32);132} while (codes.find(code) != codes.end());133codes.insert(code);134}135136void start()137{138// don't join any room YET139if (debug)140std::cout << "New game-less player " << std::hex << code << std::endl;141do_read();142}143144void deliver(const CodedData &msg)145{146write_msgs_.push_back(msg);147do_write();148}149150private:151inline void send_to_self(const CodedData &msg = CodedData())152{153auto self(shared_from_this());154CodedData send(msg);155send.code = self->code;156send.roomcode = self->roomcode;157deliver(send);158}159inline void send_error(unsigned char code = 0)160{161auto self(shared_from_this());162CodedData send;163send.header = 0x81;164send.data.bytes[0] = code;165send_to_self(send);166}167168game_room &get_room()169{170auto self(shared_from_this());171if (self->roomID == -1)172return blank_room;173return rooms[self->roomID];174}175176void do_read()177{178auto self(shared_from_this());179asio::async_read(socket_, asio::buffer(&read_msg_, sizeof(CodedData)), [this, self](std::error_code ec, std::size_t /*length*/) {180if (ec)181return do_read();182if (debug && verbose)183std::cout << "[" + self->get_room().game_name + "] "184<< "Reading data with header " << std::uppercase << std::hex << (int)read_msg_.header << " from player " << std::hex185<< read_msg_.code << " in room " << std::hex << read_msg_.roomcode << " to player " << self->code << std::endl;186CodedData send;187188switch (read_msg_.header) {189case 0x00: { // give me my codes, set the room190if (self->get_room().set) {191self->send_error(1); // already in a room192break;193}194std::string name((char *)&read_msg_.data.bytes[12], read_msg_.data.ints[2]);195for (int i = 0; i < rooms.size(); i++)196if (rooms[i].game_name == name)197self->roomID = i;198if (!self->get_room().set) {199auto newroom = game_room(name);200rooms.push_back(newroom);201if (debug)202std::cout << "Game " << name << " created" << std::endl;203self->roomID = rooms.size() - 1;204}205self->get_room().join(self);206207if (self->get_room().rooms.find(read_msg_.roomcode) != self->get_room().rooms.end()) {208// joining209read_msg_.header = 0x02; // You can now play as Luigi!210self->roomcode = read_msg_.roomcode;211send_to_self(read_msg_);212}213else if (read_msg_.roomcode) {214send_error(0); // roomcode doesn't exist215}216else {217int roomcodeMask = read_msg_.data.ints[0];218int roomcodeBase = read_msg_.data.ints[1];219int tries = 0;220do {221self->socket_.async_wait(asio::socket_base::wait_read, [](asio::error_code) {});222self->roomcode = (randomint() & ~roomcodeMask) | roomcodeBase;223} while (self->get_room().rooms.find(self->roomcode) != self->get_room().rooms.end());224225send.roomcode = self->roomcode;226send.header = 0x01; // this is code and you are MARIO. NOT LUIGI. YOU **SUCK**227send_to_self(send);228}229self->hasPartner = false;230self->get_room().rooms.insert(self->roomcode);231if (debug)232std::cout << "[" + self->get_room().game_name + "] "233<< "Player " << std::hex << self->code << " is now in room " << std::hex << self->roomcode << std::endl;234break;235}236case 0x01: { // try to join player with code237if (get_room().meet(read_msg_, self)) {238send.header = 0x80;239send_to_self(send);240}241break;242}243case 0x11: // reliable send244case 0x10: { // data245get_room().deliver(read_msg_, self);246break;247}248249case 0xFF: { // leave/quit, sent to both players250get_room().leave(self);251get_room().deliver(read_msg_, self);252break;253}254}255// let's check rooms and get rid of unused ones while we're here256/*std::set<int> codes;257for (auto &p : get_room().participants) {258codes.push_back(p->roomcode);259}260for (auto &c : rooms) {261if (!std::count(codes.begin(), codes.end(), c)) {262rooms.erase(std::find(rooms.begin(), rooms.end(), c));263}264}//*/265if (!write_msgs_.empty()) {266socket_.async_wait(asio::socket_base::wait_write, [this](std::error_code) { do_write(); });267}268do_read();269});270}271272void do_write()273{274auto self(shared_from_this());275if (self->writing || write_msgs_.empty())276return;277asio::async_write(socket_, asio::buffer(&write_msgs_.front(), sizeof(CodedData)), [this, self](std::error_code ec, std::size_t /*length*/) {278self->writing = true;279if (!ec && !write_msgs_.empty()) {280auto msg = write_msgs_.front();281if (debug && verbose)282std::cout << "[" + self->get_room().game_name + "] "283<< "Sending data with header " << std::uppercase << std::hex << (int)msg.header << " to player " << std::hex << msg.code284<< " in room " << std::hex << msg.roomcode << " from player " << self->code << std::endl;285if (msg.header == 0x11) {286CodedData sent;287sent.header = 0x1F;288send_to_self(sent);289}290write_msgs_.pop_front();291self->writing = false;292do_write();293}294else if (ec) {295get_room().leave(shared_from_this());296}297self->writing = false;298});299}300301tcp::socket socket_;302uint roomID = -1;303CodedData read_msg_;304data_queue write_msgs_;305bool writing = false;306};307308//----------------------------------------------------------------------309310class server311{312public:313server(asio::io_context &io_context, const tcp::endpoint &endpoint) : acceptor_(io_context, endpoint) { do_accept(); }314315private:316void do_accept()317{318acceptor_.async_accept([this](std::error_code ec, tcp::socket socket) {319if (!ec) {320std::make_shared<player_connection>(std::move(socket))->start();321}322323do_accept();324});325}326327tcp::acceptor acceptor_;328};329330//----------------------------------------------------------------------331332int main(int argc, char *argv[])333{334srand((uint)time(0));335try {336if (argc < 2) {337std::cerr << "Pass ports as arguments. (Server [port] [port] [port]...)";338return 1;339}340341asio::io_context io_context;342343std::list<server> servers;344for (int i = 1; i < argc; ++i) {345if (std::string(argv[i]) == "debug") {346debug = true;347continue;348}349if (std::string(argv[i]) == "verbose") {350debug = true;351verbose = true;352continue;353}354tcp::endpoint endpoint(tcp::v4(), std::atoi(argv[i]));355servers.emplace_back(io_context, endpoint);356std::cout << "Port " << argv[i] << " ready" << std::endl;357}358359std::cout << "Starting..." << std::endl;360io_context.run();361} catch (std::exception &e) {362std::cerr << "Exception: " << e.what() << "\n";363}364365return 0;366}367368