From 94f62177f1f42cfead0a6a7ca01537cf1a1fe70f Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Wed, 25 Sep 2019 21:09:47 +0100 Subject: [PATCH] Make SourceX/dvlnet C++11 compatible std::make_unique is only available in C++17 and above --- CMakeLists.txt | 5 + SourceX/dvlnet/abstract_net.cpp | 58 ++-- SourceX/dvlnet/base.cpp | 520 +++++++++++++++--------------- SourceX/dvlnet/packet.h | 548 ++++++++++++++++---------------- SourceX/dvlnet/tcp_client.cpp | 218 ++++++------- SourceX/dvlnet/tcp_server.cpp | 430 ++++++++++++------------- 6 files changed, 892 insertions(+), 887 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 775f37a38..153ab7ba0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -291,7 +291,12 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") target_compile_options(devilution PRIVATE "/W0") endif() +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +endif() + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") # Style issues target_compile_options(devilutionx PRIVATE -Wno-parentheses -Wno-logical-op-parentheses -Wno-bitwise-op-parentheses) # Silence warnings about __int64 alignment hack not always being applicable diff --git a/SourceX/dvlnet/abstract_net.cpp b/SourceX/dvlnet/abstract_net.cpp index afb601b20..27082d221 100644 --- a/SourceX/dvlnet/abstract_net.cpp +++ b/SourceX/dvlnet/abstract_net.cpp @@ -1,29 +1,29 @@ -#include "dvlnet/abstract_net.h" - -#include "stubs.h" -#include "dvlnet/tcp_client.h" -#include "dvlnet/udp_p2p.h" -#include "dvlnet/loopback.h" - -namespace dvl { -namespace net { - -abstract_net::~abstract_net() -{ -} - -std::unique_ptr abstract_net::make_net(provider_t provider) -{ - if (provider == 'TCPN') { - return std::make_unique(); - } else if (provider == 'UDPN') { - return std::make_unique(); - } else if (provider == 'SCBL' || provider == 0) { - return std::make_unique(); - } else { - ABORT(); - } -} - -} // namespace net -} // namespace dvl +#include "dvlnet/abstract_net.h" + +#include "stubs.h" +#include "dvlnet/tcp_client.h" +#include "dvlnet/udp_p2p.h" +#include "dvlnet/loopback.h" + +namespace dvl { +namespace net { + +abstract_net::~abstract_net() +{ +} + +std::unique_ptr abstract_net::make_net(provider_t provider) +{ + if (provider == 'TCPN') { + return std::unique_ptr(new tcp_client); + } else if (provider == 'UDPN') { + return std::unique_ptr(new udp_p2p); + } else if (provider == 'SCBL' || provider == 0) { + return std::unique_ptr(new loopback); + } else { + ABORT(); + } +} + +} // namespace net +} // namespace dvl diff --git a/SourceX/dvlnet/base.cpp b/SourceX/dvlnet/base.cpp index 08dc158b7..da18d0f82 100644 --- a/SourceX/dvlnet/base.cpp +++ b/SourceX/dvlnet/base.cpp @@ -1,260 +1,260 @@ -#include "dvlnet/base.h" - -#include -#include - -namespace dvl { -namespace net { - -void base::setup_gameinfo(buffer_t info) -{ - game_init_info = std::move(info); - pktfty = std::make_unique(); -} - -void base::setup_password(std::string pw) -{ - pktfty = std::make_unique(pw); -} - -void base::run_event_handler(_SNETEVENT& ev) -{ - auto f = registered_handlers[static_cast(ev.eventid)]; - if (f) { - f(&ev); - } -} - -void base::handle_accept(packet& pkt) -{ - if (plr_self != PLR_BROADCAST) { - return; // already have player id - } - if (pkt.cookie() == cookie_self) { - plr_self = pkt.newplr(); - connected_table[plr_self] = true; - } - if (game_init_info != pkt.info()) { - // we joined and did not create - _SNETEVENT ev; - ev.eventid = EVENT_TYPE_PLAYER_CREATE_GAME; - ev.playerid = plr_self; - ev.data = const_cast(pkt.info().data()); - ev.databytes = pkt.info().size(); - run_event_handler(ev); - } -} - -void base::clear_msg(plr_t plr) -{ - message_queue.erase(std::remove_if(message_queue.begin(), - message_queue.end(), - [&](message_t& msg) - { - return msg.sender == plr; - }), - message_queue.end()); -} - -void base::recv_local(packet& pkt) -{ - if (pkt.src() < MAX_PLRS) { - connected_table[pkt.src()] = true; - } - switch (pkt.type()) { - case PT_MESSAGE: - message_queue.push_back(message_t(pkt.src(), pkt.message())); - break; - case PT_TURN: - turn_queue[pkt.src()].push_back(pkt.turn()); - break; - case PT_JOIN_ACCEPT: - handle_accept(pkt); - break; - case PT_CONNECT: - connected_table[pkt.newplr()] = true; // this can probably be removed - break; - case PT_DISCONNECT: - if (pkt.newplr() != plr_self) { - if (connected_table[pkt.newplr()]) { - auto leaveinfo = pkt.leaveinfo(); - _SNETEVENT ev; - ev.eventid = EVENT_TYPE_PLAYER_LEAVE_GAME; - ev.playerid = pkt.newplr(); - ev.data = reinterpret_cast(&leaveinfo); - ev.databytes = sizeof(leaveinfo_t); - run_event_handler(ev); - connected_table[pkt.newplr()] = false; - clear_msg(pkt.newplr()); - turn_queue[pkt.newplr()].clear(); - } - } else { - ABORT(); // we were dropped by the owner?!? - } - break; - default: - break; - // otherwise drop - } -} - -bool base::SNetReceiveMessage(int* sender, char** data, int* size) -{ - poll(); - if (message_queue.empty()) - return false; - message_last = message_queue.front(); - message_queue.pop_front(); - *sender = message_last.sender; - *size = message_last.payload.size(); - *data = reinterpret_cast(message_last.payload.data()); - return true; -} - -bool base::SNetSendMessage(int playerID, void* data, unsigned int size) -{ - if (playerID != SNPLAYER_ALL && playerID != SNPLAYER_OTHERS - && (playerID < 0 || playerID >= MAX_PLRS)) - abort(); - auto raw_message = reinterpret_cast(data); - buffer_t message(raw_message, raw_message + size); - if (playerID == plr_self || playerID == SNPLAYER_ALL) - message_queue.push_back(message_t(plr_self, message)); - plr_t dest; - if (playerID == SNPLAYER_ALL || playerID == SNPLAYER_OTHERS) - dest = PLR_BROADCAST; - else - dest = playerID; - if (dest != plr_self) { - auto pkt = pktfty->make_packet(plr_self, dest, message); - send(*pkt); - } - return true; -} - -bool base::SNetReceiveTurns(char** data, unsigned int* size, DWORD* status) -{ - poll(); - bool all_turns_arrived = true; - for (auto i = 0; i < MAX_PLRS; ++i) { - status[i] = 0; - if (connected_table[i]) { - status[i] |= PS_CONNECTED; - if (turn_queue[i].empty()) - all_turns_arrived = false; - } - } - if (all_turns_arrived) { - for (auto i = 0; i < MAX_PLRS; ++i) { - if (connected_table[i]) { - size[i] = sizeof(turn_t); - status[i] |= PS_ACTIVE; - status[i] |= PS_TURN_ARRIVED; - turn_last[i] = turn_queue[i].front(); - turn_queue[i].pop_front(); - data[i] = reinterpret_cast(&turn_last[i]); - } - } - return true; - } else { - for (auto i = 0; i < MAX_PLRS; ++i) { - if (connected_table[i]) { - if (!turn_queue[i].empty()) { - status[i] |= PS_ACTIVE; - } - } - } - return false; - } -} - -bool base::SNetSendTurn(char* data, unsigned int size) -{ - if (size != sizeof(turn_t)) - ABORT(); - turn_t turn; - std::memcpy(&turn, data, sizeof(turn)); - auto pkt = pktfty->make_packet(plr_self, PLR_BROADCAST, turn); - send(*pkt); - turn_queue[plr_self].push_back(pkt->turn()); - return true; -} - -int base::SNetGetProviderCaps(struct _SNETCAPS* caps) -{ - caps->size = 0; // engine writes only ?!? - caps->flags = 0; // unused - caps->maxmessagesize = 512; // capped to 512; underflow if < 24 - caps->maxqueuesize = 0; // unused - caps->maxplayers = MAX_PLRS; // capped to 4 - caps->bytessec = 1000000; // ? - caps->latencyms = 0; // unused - caps->defaultturnssec = 10; // ? - caps->defaultturnsintransit = 1; // maximum acceptable number - // of turns in queue? - return 1; -} - -bool base::SNetUnregisterEventHandler(event_type evtype, SEVTHANDLER func) -{ - registered_handlers.erase(evtype); - return true; -} - -bool base::SNetRegisterEventHandler(event_type evtype, SEVTHANDLER func) -{ - /* - engine registers handler for: - EVENT_TYPE_PLAYER_LEAVE_GAME - EVENT_TYPE_PLAYER_CREATE_GAME (should be raised during SNetCreateGame - for non-creating player) - EVENT_TYPE_PLAYER_MESSAGE (for bnet? not implemented) - (engine uses same function for all three) - */ - registered_handlers[evtype] = func; - return true; -} - -bool base::SNetLeaveGame(int type) -{ - auto pkt = pktfty->make_packet(plr_self, PLR_BROADCAST, - plr_self, type); - send(*pkt); - return true; -} - -bool base::SNetDropPlayer(int playerid, DWORD flags) -{ - auto pkt = pktfty->make_packet(plr_self, - PLR_BROADCAST, - (plr_t)playerid, - (leaveinfo_t)flags); - send(*pkt); - recv_local(*pkt); - return true; -} - -plr_t base::get_owner() -{ - for (auto i = 0; i < MAX_PLRS; ++i) { - if (connected_table[i]) { - return i; - } - } - return PLR_BROADCAST; // should be unreachable -} - -bool base::SNetGetOwnerTurnsWaiting(DWORD *turns) -{ - *turns = turn_queue[get_owner()].size(); - return true; -} - -bool base::SNetGetTurnsInTransit(int *turns) -{ - *turns = turn_queue[plr_self].size(); - return true; -} - -} // namespace net -} // namespace dvl +#include "dvlnet/base.h" + +#include +#include + +namespace dvl { +namespace net { + +void base::setup_gameinfo(buffer_t info) +{ + game_init_info = std::move(info); + pktfty.reset(new packet_factory()); +} + +void base::setup_password(std::string pw) +{ + pktfty.reset(new packet_factory(pw)); +} + +void base::run_event_handler(_SNETEVENT& ev) +{ + auto f = registered_handlers[static_cast(ev.eventid)]; + if (f) { + f(&ev); + } +} + +void base::handle_accept(packet& pkt) +{ + if (plr_self != PLR_BROADCAST) { + return; // already have player id + } + if (pkt.cookie() == cookie_self) { + plr_self = pkt.newplr(); + connected_table[plr_self] = true; + } + if (game_init_info != pkt.info()) { + // we joined and did not create + _SNETEVENT ev; + ev.eventid = EVENT_TYPE_PLAYER_CREATE_GAME; + ev.playerid = plr_self; + ev.data = const_cast(pkt.info().data()); + ev.databytes = pkt.info().size(); + run_event_handler(ev); + } +} + +void base::clear_msg(plr_t plr) +{ + message_queue.erase(std::remove_if(message_queue.begin(), + message_queue.end(), + [&](message_t& msg) + { + return msg.sender == plr; + }), + message_queue.end()); +} + +void base::recv_local(packet& pkt) +{ + if (pkt.src() < MAX_PLRS) { + connected_table[pkt.src()] = true; + } + switch (pkt.type()) { + case PT_MESSAGE: + message_queue.push_back(message_t(pkt.src(), pkt.message())); + break; + case PT_TURN: + turn_queue[pkt.src()].push_back(pkt.turn()); + break; + case PT_JOIN_ACCEPT: + handle_accept(pkt); + break; + case PT_CONNECT: + connected_table[pkt.newplr()] = true; // this can probably be removed + break; + case PT_DISCONNECT: + if (pkt.newplr() != plr_self) { + if (connected_table[pkt.newplr()]) { + auto leaveinfo = pkt.leaveinfo(); + _SNETEVENT ev; + ev.eventid = EVENT_TYPE_PLAYER_LEAVE_GAME; + ev.playerid = pkt.newplr(); + ev.data = reinterpret_cast(&leaveinfo); + ev.databytes = sizeof(leaveinfo_t); + run_event_handler(ev); + connected_table[pkt.newplr()] = false; + clear_msg(pkt.newplr()); + turn_queue[pkt.newplr()].clear(); + } + } else { + ABORT(); // we were dropped by the owner?!? + } + break; + default: + break; + // otherwise drop + } +} + +bool base::SNetReceiveMessage(int* sender, char** data, int* size) +{ + poll(); + if (message_queue.empty()) + return false; + message_last = message_queue.front(); + message_queue.pop_front(); + *sender = message_last.sender; + *size = message_last.payload.size(); + *data = reinterpret_cast(message_last.payload.data()); + return true; +} + +bool base::SNetSendMessage(int playerID, void* data, unsigned int size) +{ + if (playerID != SNPLAYER_ALL && playerID != SNPLAYER_OTHERS + && (playerID < 0 || playerID >= MAX_PLRS)) + abort(); + auto raw_message = reinterpret_cast(data); + buffer_t message(raw_message, raw_message + size); + if (playerID == plr_self || playerID == SNPLAYER_ALL) + message_queue.push_back(message_t(plr_self, message)); + plr_t dest; + if (playerID == SNPLAYER_ALL || playerID == SNPLAYER_OTHERS) + dest = PLR_BROADCAST; + else + dest = playerID; + if (dest != plr_self) { + auto pkt = pktfty->make_packet(plr_self, dest, message); + send(*pkt); + } + return true; +} + +bool base::SNetReceiveTurns(char** data, unsigned int* size, DWORD* status) +{ + poll(); + bool all_turns_arrived = true; + for (auto i = 0; i < MAX_PLRS; ++i) { + status[i] = 0; + if (connected_table[i]) { + status[i] |= PS_CONNECTED; + if (turn_queue[i].empty()) + all_turns_arrived = false; + } + } + if (all_turns_arrived) { + for (auto i = 0; i < MAX_PLRS; ++i) { + if (connected_table[i]) { + size[i] = sizeof(turn_t); + status[i] |= PS_ACTIVE; + status[i] |= PS_TURN_ARRIVED; + turn_last[i] = turn_queue[i].front(); + turn_queue[i].pop_front(); + data[i] = reinterpret_cast(&turn_last[i]); + } + } + return true; + } else { + for (auto i = 0; i < MAX_PLRS; ++i) { + if (connected_table[i]) { + if (!turn_queue[i].empty()) { + status[i] |= PS_ACTIVE; + } + } + } + return false; + } +} + +bool base::SNetSendTurn(char* data, unsigned int size) +{ + if (size != sizeof(turn_t)) + ABORT(); + turn_t turn; + std::memcpy(&turn, data, sizeof(turn)); + auto pkt = pktfty->make_packet(plr_self, PLR_BROADCAST, turn); + send(*pkt); + turn_queue[plr_self].push_back(pkt->turn()); + return true; +} + +int base::SNetGetProviderCaps(struct _SNETCAPS* caps) +{ + caps->size = 0; // engine writes only ?!? + caps->flags = 0; // unused + caps->maxmessagesize = 512; // capped to 512; underflow if < 24 + caps->maxqueuesize = 0; // unused + caps->maxplayers = MAX_PLRS; // capped to 4 + caps->bytessec = 1000000; // ? + caps->latencyms = 0; // unused + caps->defaultturnssec = 10; // ? + caps->defaultturnsintransit = 1; // maximum acceptable number + // of turns in queue? + return 1; +} + +bool base::SNetUnregisterEventHandler(event_type evtype, SEVTHANDLER func) +{ + registered_handlers.erase(evtype); + return true; +} + +bool base::SNetRegisterEventHandler(event_type evtype, SEVTHANDLER func) +{ + /* + engine registers handler for: + EVENT_TYPE_PLAYER_LEAVE_GAME + EVENT_TYPE_PLAYER_CREATE_GAME (should be raised during SNetCreateGame + for non-creating player) + EVENT_TYPE_PLAYER_MESSAGE (for bnet? not implemented) + (engine uses same function for all three) + */ + registered_handlers[evtype] = func; + return true; +} + +bool base::SNetLeaveGame(int type) +{ + auto pkt = pktfty->make_packet(plr_self, PLR_BROADCAST, + plr_self, type); + send(*pkt); + return true; +} + +bool base::SNetDropPlayer(int playerid, DWORD flags) +{ + auto pkt = pktfty->make_packet(plr_self, + PLR_BROADCAST, + (plr_t)playerid, + (leaveinfo_t)flags); + send(*pkt); + recv_local(*pkt); + return true; +} + +plr_t base::get_owner() +{ + for (auto i = 0; i < MAX_PLRS; ++i) { + if (connected_table[i]) { + return i; + } + } + return PLR_BROADCAST; // should be unreachable +} + +bool base::SNetGetOwnerTurnsWaiting(DWORD *turns) +{ + *turns = turn_queue[get_owner()].size(); + return true; +} + +bool base::SNetGetTurnsInTransit(int *turns) +{ + *turns = turn_queue[plr_self].size(); + return true; +} + +} // namespace net +} // namespace dvl diff --git a/SourceX/dvlnet/packet.h b/SourceX/dvlnet/packet.h index dcca79ca0..65a06484a 100644 --- a/SourceX/dvlnet/packet.h +++ b/SourceX/dvlnet/packet.h @@ -1,274 +1,274 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "dvlnet/abstract_net.h" -#include "stubs.h" - -namespace dvl { -namespace net { - -enum packet_type : uint8_t { - PT_MESSAGE = 0x01, - PT_TURN = 0x02, - PT_JOIN_REQUEST = 0x11, - PT_JOIN_ACCEPT = 0x12, - PT_CONNECT = 0x13, - PT_DISCONNECT = 0x14, -}; - -typedef uint8_t plr_t; -typedef uint32_t cookie_t; -typedef int turn_t; // change int to something else in devilution code later -typedef int leaveinfo_t; // also change later -typedef std::array key_t; - -static constexpr plr_t PLR_MASTER = 0xFE; -static constexpr plr_t PLR_BROADCAST = 0xFF; - -class packet_exception : public dvlnet_exception {}; - -class packet { -protected: - packet_type m_type; - plr_t m_src; - plr_t m_dest; - buffer_t m_message; - turn_t m_turn; - cookie_t m_cookie; - plr_t m_newplr; - buffer_t m_info; - leaveinfo_t m_leaveinfo; - - const key_t& key; - bool have_encrypted = false; - bool have_decrypted = false; - buffer_t encrypted_buffer; - buffer_t decrypted_buffer; - -public: - packet(const key_t& k) : key(k) {}; - - const buffer_t& data(); - - packet_type type(); - plr_t src(); - plr_t dest(); - const buffer_t& message(); - turn_t turn(); - cookie_t cookie(); - plr_t newplr(); - const buffer_t& info(); - leaveinfo_t leaveinfo(); -}; - -template class packet_proc : public packet { -public: - using packet::packet; - void process_data(); -}; - -class packet_in : public packet_proc { -public: - using packet_proc::packet_proc; - void create(buffer_t buf); - void process_element(buffer_t& x); - template void process_element(T& x); - void decrypt(); -}; - -class packet_out : public packet_proc { -public: - using packet_proc::packet_proc; - - template - void create(Args... args); - - void process_element(buffer_t& x); - template void process_element(T& x); - template static const unsigned char* begin(const T& x); - template static const unsigned char* end(const T& x); - void encrypt(); -}; - -template void packet_proc

::process_data() -{ - P& self = static_cast(*this); - self.process_element(m_type); - self.process_element(m_src); - self.process_element(m_dest); - switch (m_type) { - case PT_MESSAGE: - self.process_element(m_message); - break; - case PT_TURN: - self.process_element(m_turn); - break; - case PT_JOIN_REQUEST: - self.process_element(m_cookie); - self.process_element(m_info); - break; - case PT_JOIN_ACCEPT: - self.process_element(m_cookie); - self.process_element(m_newplr); - self.process_element(m_info); - break; - case PT_CONNECT: - self.process_element(m_newplr); - break; - case PT_DISCONNECT: - self.process_element(m_newplr); - self.process_element(m_leaveinfo); - break; - } -} - -inline void packet_in::process_element(buffer_t& x) -{ - x.insert(x.begin(), decrypted_buffer.begin(), decrypted_buffer.end()); - decrypted_buffer.resize(0); -} - -template void packet_in::process_element(T& x) -{ - if (decrypted_buffer.size() < sizeof(T)) - throw packet_exception(); - std::memcpy(&x, decrypted_buffer.data(), sizeof(T)); - decrypted_buffer.erase(decrypted_buffer.begin(), - decrypted_buffer.begin() + sizeof(T)); -} - -template<> -inline void packet_out::create(plr_t s, plr_t d, buffer_t m) -{ - if (have_encrypted || have_decrypted) - ABORT(); - have_decrypted = true; - m_type = PT_MESSAGE; - m_src = s; - m_dest = d; - m_message = std::move(m); -} - -template<> -inline void packet_out::create(plr_t s, plr_t d, turn_t u) -{ - if (have_encrypted || have_decrypted) - ABORT(); - have_decrypted = true; - m_type = PT_TURN; - m_src = s; - m_dest = d; - m_turn = u; -} - -template<> -inline void packet_out::create(plr_t s, plr_t d, - cookie_t c, buffer_t i) -{ - if (have_encrypted || have_decrypted) - ABORT(); - have_decrypted = true; - m_type = PT_JOIN_REQUEST; - m_src = s; - m_dest = d; - m_cookie = c; - m_info = i; -} - -template<> -inline void packet_out::create(plr_t s, plr_t d, cookie_t c, - plr_t n, buffer_t i) -{ - if (have_encrypted || have_decrypted) - ABORT(); - have_decrypted = true; - m_type = PT_JOIN_ACCEPT; - m_src = s; - m_dest = d; - m_cookie = c; - m_newplr = n; - m_info = i; -} - -template<> -inline void packet_out::create(plr_t s, plr_t d, plr_t n) -{ - if (have_encrypted || have_decrypted) - ABORT(); - have_decrypted = true; - m_type = PT_CONNECT; - m_src = s; - m_dest = d; - m_newplr = n; -} - -template<> -inline void packet_out::create(plr_t s, plr_t d, plr_t n, - leaveinfo_t l) -{ - if (have_encrypted || have_decrypted) - ABORT(); - have_decrypted = true; - m_type = PT_DISCONNECT; - m_src = s; - m_dest = d; - m_newplr = n; - m_leaveinfo = l; -} - -inline void packet_out::process_element(buffer_t& x) -{ - encrypted_buffer.insert(encrypted_buffer.end(), x.begin(), x.end()); -} - -template void packet_out::process_element(T& x) -{ - encrypted_buffer.insert(encrypted_buffer.end(), begin(x), end(x)); -} - -template const unsigned char* packet_out::begin(const T& x) -{ - return reinterpret_cast(&x); -} - -template const unsigned char* packet_out::end(const T& x) -{ - return reinterpret_cast(&x) + sizeof(T); -} - -class packet_factory { - key_t key = {}; - -public: - static constexpr unsigned short max_packet_size = 0xFFFF; - - packet_factory(std::string pw = ""); - std::unique_ptr make_packet(buffer_t buf); - template - std::unique_ptr make_packet(Args... args); -}; - -inline std::unique_ptr packet_factory::make_packet(buffer_t buf) -{ - auto ret = std::make_unique(key); - ret->create(std::move(buf)); - ret->decrypt(); - return ret; -} - -template -std::unique_ptr packet_factory::make_packet(Args... args) -{ - auto ret = std::make_unique(key); - ret->create(args...); - ret->encrypt(); - return ret; -} - -} // namespace net -} // namespace dvl +#pragma once + +#include +#include +#include +#include +#include + +#include "dvlnet/abstract_net.h" +#include "stubs.h" + +namespace dvl { +namespace net { + +enum packet_type : uint8_t { + PT_MESSAGE = 0x01, + PT_TURN = 0x02, + PT_JOIN_REQUEST = 0x11, + PT_JOIN_ACCEPT = 0x12, + PT_CONNECT = 0x13, + PT_DISCONNECT = 0x14, +}; + +typedef uint8_t plr_t; +typedef uint32_t cookie_t; +typedef int turn_t; // change int to something else in devilution code later +typedef int leaveinfo_t; // also change later +typedef std::array key_t; + +static constexpr plr_t PLR_MASTER = 0xFE; +static constexpr plr_t PLR_BROADCAST = 0xFF; + +class packet_exception : public dvlnet_exception {}; + +class packet { +protected: + packet_type m_type; + plr_t m_src; + plr_t m_dest; + buffer_t m_message; + turn_t m_turn; + cookie_t m_cookie; + plr_t m_newplr; + buffer_t m_info; + leaveinfo_t m_leaveinfo; + + const key_t& key; + bool have_encrypted = false; + bool have_decrypted = false; + buffer_t encrypted_buffer; + buffer_t decrypted_buffer; + +public: + packet(const key_t& k) : key(k) {}; + + const buffer_t& data(); + + packet_type type(); + plr_t src(); + plr_t dest(); + const buffer_t& message(); + turn_t turn(); + cookie_t cookie(); + plr_t newplr(); + const buffer_t& info(); + leaveinfo_t leaveinfo(); +}; + +template class packet_proc : public packet { +public: + using packet::packet; + void process_data(); +}; + +class packet_in : public packet_proc { +public: + using packet_proc::packet_proc; + void create(buffer_t buf); + void process_element(buffer_t& x); + template void process_element(T& x); + void decrypt(); +}; + +class packet_out : public packet_proc { +public: + using packet_proc::packet_proc; + + template + void create(Args... args); + + void process_element(buffer_t& x); + template void process_element(T& x); + template static const unsigned char* begin(const T& x); + template static const unsigned char* end(const T& x); + void encrypt(); +}; + +template void packet_proc

::process_data() +{ + P& self = static_cast(*this); + self.process_element(m_type); + self.process_element(m_src); + self.process_element(m_dest); + switch (m_type) { + case PT_MESSAGE: + self.process_element(m_message); + break; + case PT_TURN: + self.process_element(m_turn); + break; + case PT_JOIN_REQUEST: + self.process_element(m_cookie); + self.process_element(m_info); + break; + case PT_JOIN_ACCEPT: + self.process_element(m_cookie); + self.process_element(m_newplr); + self.process_element(m_info); + break; + case PT_CONNECT: + self.process_element(m_newplr); + break; + case PT_DISCONNECT: + self.process_element(m_newplr); + self.process_element(m_leaveinfo); + break; + } +} + +inline void packet_in::process_element(buffer_t& x) +{ + x.insert(x.begin(), decrypted_buffer.begin(), decrypted_buffer.end()); + decrypted_buffer.resize(0); +} + +template void packet_in::process_element(T& x) +{ + if (decrypted_buffer.size() < sizeof(T)) + throw packet_exception(); + std::memcpy(&x, decrypted_buffer.data(), sizeof(T)); + decrypted_buffer.erase(decrypted_buffer.begin(), + decrypted_buffer.begin() + sizeof(T)); +} + +template<> +inline void packet_out::create(plr_t s, plr_t d, buffer_t m) +{ + if (have_encrypted || have_decrypted) + ABORT(); + have_decrypted = true; + m_type = PT_MESSAGE; + m_src = s; + m_dest = d; + m_message = std::move(m); +} + +template<> +inline void packet_out::create(plr_t s, plr_t d, turn_t u) +{ + if (have_encrypted || have_decrypted) + ABORT(); + have_decrypted = true; + m_type = PT_TURN; + m_src = s; + m_dest = d; + m_turn = u; +} + +template<> +inline void packet_out::create(plr_t s, plr_t d, + cookie_t c, buffer_t i) +{ + if (have_encrypted || have_decrypted) + ABORT(); + have_decrypted = true; + m_type = PT_JOIN_REQUEST; + m_src = s; + m_dest = d; + m_cookie = c; + m_info = i; +} + +template<> +inline void packet_out::create(plr_t s, plr_t d, cookie_t c, + plr_t n, buffer_t i) +{ + if (have_encrypted || have_decrypted) + ABORT(); + have_decrypted = true; + m_type = PT_JOIN_ACCEPT; + m_src = s; + m_dest = d; + m_cookie = c; + m_newplr = n; + m_info = i; +} + +template<> +inline void packet_out::create(plr_t s, plr_t d, plr_t n) +{ + if (have_encrypted || have_decrypted) + ABORT(); + have_decrypted = true; + m_type = PT_CONNECT; + m_src = s; + m_dest = d; + m_newplr = n; +} + +template<> +inline void packet_out::create(plr_t s, plr_t d, plr_t n, + leaveinfo_t l) +{ + if (have_encrypted || have_decrypted) + ABORT(); + have_decrypted = true; + m_type = PT_DISCONNECT; + m_src = s; + m_dest = d; + m_newplr = n; + m_leaveinfo = l; +} + +inline void packet_out::process_element(buffer_t& x) +{ + encrypted_buffer.insert(encrypted_buffer.end(), x.begin(), x.end()); +} + +template void packet_out::process_element(T& x) +{ + encrypted_buffer.insert(encrypted_buffer.end(), begin(x), end(x)); +} + +template const unsigned char* packet_out::begin(const T& x) +{ + return reinterpret_cast(&x); +} + +template const unsigned char* packet_out::end(const T& x) +{ + return reinterpret_cast(&x) + sizeof(T); +} + +class packet_factory { + key_t key = {}; + +public: + static constexpr unsigned short max_packet_size = 0xFFFF; + + packet_factory(std::string pw = ""); + std::unique_ptr make_packet(buffer_t buf); + template + std::unique_ptr make_packet(Args... args); +}; + +inline std::unique_ptr packet_factory::make_packet(buffer_t buf) +{ + std::unique_ptr ret(new packet_in(key)); + ret->create(std::move(buf)); + ret->decrypt(); + return std::unique_ptr(std::move(ret)); +} + +template +std::unique_ptr packet_factory::make_packet(Args... args) +{ + std::unique_ptr ret(new packet_out(key)); + ret->create(args...); + ret->encrypt(); + return std::unique_ptr(std::move(ret)); +} + +} // namespace net +} // namespace dvl diff --git a/SourceX/dvlnet/tcp_client.cpp b/SourceX/dvlnet/tcp_client.cpp index 37f043a20..cafe68755 100644 --- a/SourceX/dvlnet/tcp_client.cpp +++ b/SourceX/dvlnet/tcp_client.cpp @@ -1,109 +1,109 @@ -#include "dvlnet/tcp_client.h" - -#include -#include -#include -#include -#include -#include - -namespace dvl { -namespace net { - -int tcp_client::create(std::string addrstr, std::string passwd) -{ - try { - auto port = default_port; - local_server = std::make_unique(ioc, addrstr, port, passwd); - return join(local_server->localhost_self(), passwd); - } catch(std::system_error &e) { - return -1; - } -} - -int tcp_client::join(std::string addrstr, std::string passwd) -{ - constexpr int ms_sleep = 10; - constexpr int no_sleep = 250; - - setup_password(passwd); - try { - auto ipaddr = asio::ip::make_address(addrstr); - sock.connect(asio::ip::tcp::endpoint(ipaddr, default_port)); - asio::ip::tcp::no_delay option(true); - sock.set_option(option); - } catch(std::exception &e) { - return -1; - } - start_recv(); - { - randombytes_buf(reinterpret_cast(&cookie_self), - sizeof(cookie_t)); - auto pkt = pktfty->make_packet(PLR_BROADCAST, - PLR_MASTER, cookie_self, - game_init_info); - send(*pkt); - for (auto i = 0; i < no_sleep; ++i) { - try { - poll(); - } catch (const std::runtime_error &e) { - return -1; - } - if (plr_self != PLR_BROADCAST) - break; // join successful - SDL_Delay(ms_sleep); - } - } - return (plr_self == PLR_BROADCAST ? -1 : plr_self); -} - -void tcp_client::poll() -{ - ioc.poll(); -} - -void tcp_client::handle_recv(const asio::error_code& error, size_t bytes_read) -{ - if (error) { - // error in recv from server - // returning and doing nothing should be the same - // as if all connections to other clients were lost - return; - } - if (bytes_read == 0) { - throw std::runtime_error("error: read 0 bytes from server"); - } - recv_buffer.resize(bytes_read); - recv_queue.write(std::move(recv_buffer)); - recv_buffer.resize(frame_queue::max_frame_size); - while (recv_queue.packet_ready()) { - auto pkt = pktfty->make_packet(recv_queue.read_packet()); - recv_local(*pkt); - } - start_recv(); -} - -void tcp_client::start_recv() -{ - sock.async_receive(asio::buffer(recv_buffer), - std::bind(&tcp_client::handle_recv, this, - std::placeholders::_1, std::placeholders::_2)); -} - -void tcp_client::handle_send(const asio::error_code& error, size_t bytes_sent) -{ - // empty for now -} - -void tcp_client::send(packet& pkt) -{ - auto frame = std::make_shared(frame_queue::make_frame(pkt.data())); - auto buf = asio::buffer(*frame); - asio::async_write(sock, buf, [this, frame = std::move(frame)] - (const asio::error_code &error, size_t bytes_sent) { - handle_send(error, bytes_sent); - }); -} - -} // namespace net -} // namespace dvl +#include "dvlnet/tcp_client.h" + +#include +#include +#include +#include +#include +#include + +namespace dvl { +namespace net { + +int tcp_client::create(std::string addrstr, std::string passwd) +{ + try { + auto port = default_port; + local_server.reset(new tcp_server(ioc, addrstr, port, passwd)); + return join(local_server->localhost_self(), passwd); + } catch(std::system_error &e) { + return -1; + } +} + +int tcp_client::join(std::string addrstr, std::string passwd) +{ + constexpr int ms_sleep = 10; + constexpr int no_sleep = 250; + + setup_password(passwd); + try { + auto ipaddr = asio::ip::make_address(addrstr); + sock.connect(asio::ip::tcp::endpoint(ipaddr, default_port)); + asio::ip::tcp::no_delay option(true); + sock.set_option(option); + } catch(std::exception &e) { + return -1; + } + start_recv(); + { + randombytes_buf(reinterpret_cast(&cookie_self), + sizeof(cookie_t)); + auto pkt = pktfty->make_packet(PLR_BROADCAST, + PLR_MASTER, cookie_self, + game_init_info); + send(*pkt); + for (auto i = 0; i < no_sleep; ++i) { + try { + poll(); + } catch (const std::runtime_error &e) { + return -1; + } + if (plr_self != PLR_BROADCAST) + break; // join successful + SDL_Delay(ms_sleep); + } + } + return (plr_self == PLR_BROADCAST ? -1 : plr_self); +} + +void tcp_client::poll() +{ + ioc.poll(); +} + +void tcp_client::handle_recv(const asio::error_code& error, size_t bytes_read) +{ + if (error) { + // error in recv from server + // returning and doing nothing should be the same + // as if all connections to other clients were lost + return; + } + if (bytes_read == 0) { + throw std::runtime_error("error: read 0 bytes from server"); + } + recv_buffer.resize(bytes_read); + recv_queue.write(std::move(recv_buffer)); + recv_buffer.resize(frame_queue::max_frame_size); + while (recv_queue.packet_ready()) { + auto pkt = pktfty->make_packet(recv_queue.read_packet()); + recv_local(*pkt); + } + start_recv(); +} + +void tcp_client::start_recv() +{ + sock.async_receive(asio::buffer(recv_buffer), + std::bind(&tcp_client::handle_recv, this, + std::placeholders::_1, std::placeholders::_2)); +} + +void tcp_client::handle_send(const asio::error_code& error, size_t bytes_sent) +{ + // empty for now +} + +void tcp_client::send(packet& pkt) +{ + auto frame = std::make_shared(frame_queue::make_frame(pkt.data())); + auto buf = asio::buffer(*frame); + asio::async_write(sock, buf, [this, frame = std::move(frame)] + (const asio::error_code &error, size_t bytes_sent) { + handle_send(error, bytes_sent); + }); +} + +} // namespace net +} // namespace dvl diff --git a/SourceX/dvlnet/tcp_server.cpp b/SourceX/dvlnet/tcp_server.cpp index 9afbc1f8e..9d02075fa 100644 --- a/SourceX/dvlnet/tcp_server.cpp +++ b/SourceX/dvlnet/tcp_server.cpp @@ -1,215 +1,215 @@ -#include "dvlnet/tcp_server.h" - -#include -#include - -#include "dvlnet/base.h" - -namespace dvl { -namespace net { - -tcp_server::tcp_server(asio::io_context& ioc, std::string bindaddr, - unsigned short port, std::string pw) : - ioc(ioc), pktfty(pw) -{ - auto addr = asio::ip::address::from_string(bindaddr); - auto ep = asio::ip::tcp::endpoint(addr, port); - acceptor = std::make_unique(ioc, ep); - start_accept(); -} - -std::string tcp_server::localhost_self() -{ - auto addr = acceptor->local_endpoint().address(); - if (addr.is_unspecified()) { - if (addr.is_v4()) { - return asio::ip::address_v4::loopback().to_string(); - } else if (addr.is_v6()) { - return asio::ip::address_v6::loopback().to_string(); - } else { - ABORT(); - } - } else { - return addr.to_string(); - } -} - -tcp_server::scc tcp_server::make_connection() -{ - return std::make_shared(ioc); -} - -plr_t tcp_server::next_free() -{ - for (plr_t i = 0; i < MAX_PLRS; ++i) - if (!connections[i]) - return i; - return PLR_BROADCAST; -} - -bool tcp_server::empty() -{ - for (plr_t i = 0; i < MAX_PLRS; ++i) - if (connections[i]) - return false; - return true; -} - -void tcp_server::start_recv(scc con) -{ - con->socket.async_receive(asio::buffer(con->recv_buffer), - std::bind(&tcp_server::handle_recv, this, con, - std::placeholders::_1, - std::placeholders::_2)); -} - -void tcp_server::handle_recv(scc con, const asio::error_code& ec, - size_t bytes_read) -{ - if (ec || bytes_read == 0) { - drop_connection(con); - return; - } - con->recv_buffer.resize(bytes_read); - con->recv_queue.write(std::move(con->recv_buffer)); - con->recv_buffer.resize(frame_queue::max_frame_size); - while (con->recv_queue.packet_ready()) { - try { - auto pkt = pktfty.make_packet(con->recv_queue.read_packet()); - if (con->plr == PLR_BROADCAST) { - handle_recv_newplr(con, *pkt); - } else { - con->timeout = timeout_active; - handle_recv_packet(*pkt); - } - } catch (dvlnet_exception &e) { - drop_connection(con); - return; - } - } - start_recv(con); -} - -void tcp_server::send_connect(scc con) -{ - auto pkt = pktfty.make_packet(PLR_MASTER, PLR_BROADCAST, - con->plr); - send_packet(*pkt); -} - -void tcp_server::handle_recv_newplr(scc con, packet& pkt) -{ - auto newplr = next_free(); - if (newplr == PLR_BROADCAST) - throw server_exception(); - if (empty()) - game_init_info = pkt.info(); - auto reply = pktfty.make_packet(PLR_MASTER, PLR_BROADCAST, - pkt.cookie(), newplr, - game_init_info); - start_send(con, *reply); - con->plr = newplr; - connections[newplr] = con; - con->timeout = timeout_active; - send_connect(con); -} - -void tcp_server::handle_recv_packet(packet& pkt) -{ - send_packet(pkt); -} - -void tcp_server::send_packet(packet& pkt) -{ - if (pkt.dest() == PLR_BROADCAST) { - for (auto i = 0; i < MAX_PLRS; ++i) - if (i != pkt.src() && connections[i]) - start_send(connections[i], pkt); - } else { - if (pkt.dest() >= MAX_PLRS) - throw server_exception(); - if ((pkt.dest() != pkt.src()) && connections[pkt.dest()]) - start_send(connections[pkt.dest()], pkt); - } -} - -void tcp_server::start_send(scc con, packet& pkt) -{ - auto frame = std::make_shared(frame_queue::make_frame(pkt.data())); - auto buf = asio::buffer(*frame); - asio::async_write(con->socket, buf, - [this, con, frame = std::move(frame)] - (const asio::error_code &ec, size_t bytes_sent) { - handle_send(con, ec, bytes_sent); - }); -} - -void tcp_server::handle_send(scc con, const asio::error_code& ec, - size_t bytes_sent) -{ - // empty for now -} - -void tcp_server::start_accept() -{ - auto nextcon = make_connection(); - acceptor->async_accept(nextcon->socket, - std::bind(&tcp_server::handle_accept, - this, nextcon, - std::placeholders::_1)); -} - -void tcp_server::handle_accept(scc con, const asio::error_code& ec) -{ - if (next_free() == PLR_BROADCAST) { - drop_connection(con); - } else { - asio::ip::tcp::no_delay option(true); - con->socket.set_option(option); - con->timeout = timeout_connect; - start_recv(con); - start_timeout(con); - } - start_accept(); -} - -void tcp_server::start_timeout(scc con) -{ - con->timer.expires_after(std::chrono::seconds(1)); - con->timer.async_wait(std::bind(&tcp_server::handle_timeout, this, con, - std::placeholders::_1)); -} - -void tcp_server::handle_timeout(scc con, const asio::error_code& ec) -{ - if (ec) { - drop_connection(con); - return; - } - if (con->timeout > 0) - con->timeout -= 1; - if (con->timeout < 0) - con->timeout = 0; - if (!con->timeout) { - drop_connection(con); - return; - } - start_timeout(con); -} - -void tcp_server::drop_connection(scc con) -{ - if (con->plr != PLR_BROADCAST) { - auto pkt = pktfty.make_packet(PLR_MASTER, PLR_BROADCAST, - con->plr, LEAVE_DROP); - connections[con->plr] = nullptr; - send_packet(*pkt); - // TODO: investigate if it is really ok for the server to - // drop a client directly. - } - con->timer.cancel(); - con->socket.close(); -} - -} // namespace net -} // namespace dvl +#include "dvlnet/tcp_server.h" + +#include +#include + +#include "dvlnet/base.h" + +namespace dvl { +namespace net { + +tcp_server::tcp_server(asio::io_context& ioc, std::string bindaddr, + unsigned short port, std::string pw) : + ioc(ioc), pktfty(pw) +{ + auto addr = asio::ip::address::from_string(bindaddr); + auto ep = asio::ip::tcp::endpoint(addr, port); + acceptor.reset(new asio::ip::tcp::acceptor(ioc, ep)); + start_accept(); +} + +std::string tcp_server::localhost_self() +{ + auto addr = acceptor->local_endpoint().address(); + if (addr.is_unspecified()) { + if (addr.is_v4()) { + return asio::ip::address_v4::loopback().to_string(); + } else if (addr.is_v6()) { + return asio::ip::address_v6::loopback().to_string(); + } else { + ABORT(); + } + } else { + return addr.to_string(); + } +} + +tcp_server::scc tcp_server::make_connection() +{ + return std::make_shared(ioc); +} + +plr_t tcp_server::next_free() +{ + for (plr_t i = 0; i < MAX_PLRS; ++i) + if (!connections[i]) + return i; + return PLR_BROADCAST; +} + +bool tcp_server::empty() +{ + for (plr_t i = 0; i < MAX_PLRS; ++i) + if (connections[i]) + return false; + return true; +} + +void tcp_server::start_recv(scc con) +{ + con->socket.async_receive(asio::buffer(con->recv_buffer), + std::bind(&tcp_server::handle_recv, this, con, + std::placeholders::_1, + std::placeholders::_2)); +} + +void tcp_server::handle_recv(scc con, const asio::error_code& ec, + size_t bytes_read) +{ + if (ec || bytes_read == 0) { + drop_connection(con); + return; + } + con->recv_buffer.resize(bytes_read); + con->recv_queue.write(std::move(con->recv_buffer)); + con->recv_buffer.resize(frame_queue::max_frame_size); + while (con->recv_queue.packet_ready()) { + try { + auto pkt = pktfty.make_packet(con->recv_queue.read_packet()); + if (con->plr == PLR_BROADCAST) { + handle_recv_newplr(con, *pkt); + } else { + con->timeout = timeout_active; + handle_recv_packet(*pkt); + } + } catch (dvlnet_exception &e) { + drop_connection(con); + return; + } + } + start_recv(con); +} + +void tcp_server::send_connect(scc con) +{ + auto pkt = pktfty.make_packet(PLR_MASTER, PLR_BROADCAST, + con->plr); + send_packet(*pkt); +} + +void tcp_server::handle_recv_newplr(scc con, packet& pkt) +{ + auto newplr = next_free(); + if (newplr == PLR_BROADCAST) + throw server_exception(); + if (empty()) + game_init_info = pkt.info(); + auto reply = pktfty.make_packet(PLR_MASTER, PLR_BROADCAST, + pkt.cookie(), newplr, + game_init_info); + start_send(con, *reply); + con->plr = newplr; + connections[newplr] = con; + con->timeout = timeout_active; + send_connect(con); +} + +void tcp_server::handle_recv_packet(packet& pkt) +{ + send_packet(pkt); +} + +void tcp_server::send_packet(packet& pkt) +{ + if (pkt.dest() == PLR_BROADCAST) { + for (auto i = 0; i < MAX_PLRS; ++i) + if (i != pkt.src() && connections[i]) + start_send(connections[i], pkt); + } else { + if (pkt.dest() >= MAX_PLRS) + throw server_exception(); + if ((pkt.dest() != pkt.src()) && connections[pkt.dest()]) + start_send(connections[pkt.dest()], pkt); + } +} + +void tcp_server::start_send(scc con, packet& pkt) +{ + auto frame = std::make_shared(frame_queue::make_frame(pkt.data())); + auto buf = asio::buffer(*frame); + asio::async_write(con->socket, buf, + [this, con, frame = std::move(frame)] + (const asio::error_code &ec, size_t bytes_sent) { + handle_send(con, ec, bytes_sent); + }); +} + +void tcp_server::handle_send(scc con, const asio::error_code& ec, + size_t bytes_sent) +{ + // empty for now +} + +void tcp_server::start_accept() +{ + auto nextcon = make_connection(); + acceptor->async_accept(nextcon->socket, + std::bind(&tcp_server::handle_accept, + this, nextcon, + std::placeholders::_1)); +} + +void tcp_server::handle_accept(scc con, const asio::error_code& ec) +{ + if (next_free() == PLR_BROADCAST) { + drop_connection(con); + } else { + asio::ip::tcp::no_delay option(true); + con->socket.set_option(option); + con->timeout = timeout_connect; + start_recv(con); + start_timeout(con); + } + start_accept(); +} + +void tcp_server::start_timeout(scc con) +{ + con->timer.expires_after(std::chrono::seconds(1)); + con->timer.async_wait(std::bind(&tcp_server::handle_timeout, this, con, + std::placeholders::_1)); +} + +void tcp_server::handle_timeout(scc con, const asio::error_code& ec) +{ + if (ec) { + drop_connection(con); + return; + } + if (con->timeout > 0) + con->timeout -= 1; + if (con->timeout < 0) + con->timeout = 0; + if (!con->timeout) { + drop_connection(con); + return; + } + start_timeout(con); +} + +void tcp_server::drop_connection(scc con) +{ + if (con->plr != PLR_BROADCAST) { + auto pkt = pktfty.make_packet(PLR_MASTER, PLR_BROADCAST, + con->plr, LEAVE_DROP); + connections[con->plr] = nullptr; + send_packet(*pkt); + // TODO: investigate if it is really ok for the server to + // drop a client directly. + } + con->timer.cancel(); + con->socket.close(); +} + +} // namespace net +} // namespace dvl