6 changed files with 892 additions and 887 deletions
@ -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> abstract_net::make_net(provider_t provider) |
||||
{ |
||||
if (provider == 'TCPN') { |
||||
return std::make_unique<tcp_client>(); |
||||
} else if (provider == 'UDPN') { |
||||
return std::make_unique<udp_p2p>(); |
||||
} else if (provider == 'SCBL' || provider == 0) { |
||||
return std::make_unique<loopback>(); |
||||
} 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> abstract_net::make_net(provider_t provider) |
||||
{ |
||||
if (provider == 'TCPN') { |
||||
return std::unique_ptr<abstract_net>(new tcp_client); |
||||
} else if (provider == 'UDPN') { |
||||
return std::unique_ptr<abstract_net>(new udp_p2p); |
||||
} else if (provider == 'SCBL' || provider == 0) { |
||||
return std::unique_ptr<abstract_net>(new loopback); |
||||
} else { |
||||
ABORT(); |
||||
} |
||||
} |
||||
|
||||
} // namespace net
|
||||
} // namespace dvl
|
||||
|
||||
@ -1,260 +1,260 @@
|
||||
#include "dvlnet/base.h" |
||||
|
||||
#include <algorithm> |
||||
#include <cstring> |
||||
|
||||
namespace dvl { |
||||
namespace net { |
||||
|
||||
void base::setup_gameinfo(buffer_t info) |
||||
{ |
||||
game_init_info = std::move(info); |
||||
pktfty = std::make_unique<packet_factory>(); |
||||
} |
||||
|
||||
void base::setup_password(std::string pw) |
||||
{ |
||||
pktfty = std::make_unique<packet_factory>(pw); |
||||
} |
||||
|
||||
void base::run_event_handler(_SNETEVENT& ev) |
||||
{ |
||||
auto f = registered_handlers[static_cast<event_type>(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<unsigned char*>(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<unsigned char*>(&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<char*>(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<unsigned char*>(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<PT_MESSAGE>(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<char*>(&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<PT_TURN>(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<PT_DISCONNECT>(plr_self, PLR_BROADCAST, |
||||
plr_self, type); |
||||
send(*pkt); |
||||
return true; |
||||
} |
||||
|
||||
bool base::SNetDropPlayer(int playerid, DWORD flags) |
||||
{ |
||||
auto pkt = pktfty->make_packet<PT_DISCONNECT>(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 <algorithm> |
||||
#include <cstring> |
||||
|
||||
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<event_type>(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<unsigned char*>(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<unsigned char*>(&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<char*>(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<unsigned char*>(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<PT_MESSAGE>(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<char*>(&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<PT_TURN>(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<PT_DISCONNECT>(plr_self, PLR_BROADCAST, |
||||
plr_self, type); |
||||
send(*pkt); |
||||
return true; |
||||
} |
||||
|
||||
bool base::SNetDropPlayer(int playerid, DWORD flags) |
||||
{ |
||||
auto pkt = pktfty->make_packet<PT_DISCONNECT>(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
|
||||
|
||||
@ -1,274 +1,274 @@
|
||||
#pragma once |
||||
|
||||
#include <string> |
||||
#include <memory> |
||||
#include <array> |
||||
#include <cstring> |
||||
#include <sodium.h> |
||||
|
||||
#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<unsigned char, crypto_secretbox_KEYBYTES> 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 P> class packet_proc : public packet { |
||||
public: |
||||
using packet::packet; |
||||
void process_data(); |
||||
}; |
||||
|
||||
class packet_in : public packet_proc<packet_in> { |
||||
public: |
||||
using packet_proc<packet_in>::packet_proc; |
||||
void create(buffer_t buf); |
||||
void process_element(buffer_t& x); |
||||
template <class T> void process_element(T& x); |
||||
void decrypt(); |
||||
}; |
||||
|
||||
class packet_out : public packet_proc<packet_out> { |
||||
public: |
||||
using packet_proc<packet_out>::packet_proc; |
||||
|
||||
template<packet_type t, typename... Args> |
||||
void create(Args... args); |
||||
|
||||
void process_element(buffer_t& x); |
||||
template<class T> void process_element(T& x); |
||||
template<class T> static const unsigned char* begin(const T& x); |
||||
template<class T> static const unsigned char* end(const T& x); |
||||
void encrypt(); |
||||
}; |
||||
|
||||
template<class P> void packet_proc<P>::process_data() |
||||
{ |
||||
P& self = static_cast<P&>(*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 <class T> 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<PT_MESSAGE>(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<PT_TURN>(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<PT_JOIN_REQUEST>(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<PT_JOIN_ACCEPT>(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<PT_CONNECT>(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<PT_DISCONNECT>(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 <class T> void packet_out::process_element(T& x) |
||||
{ |
||||
encrypted_buffer.insert(encrypted_buffer.end(), begin(x), end(x)); |
||||
} |
||||
|
||||
template <class T> const unsigned char* packet_out::begin(const T& x) |
||||
{ |
||||
return reinterpret_cast<const unsigned char *>(&x); |
||||
} |
||||
|
||||
template <class T> const unsigned char* packet_out::end(const T& x) |
||||
{ |
||||
return reinterpret_cast<const unsigned char *>(&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<packet> make_packet(buffer_t buf); |
||||
template<packet_type t, typename... Args> |
||||
std::unique_ptr<packet> make_packet(Args... args); |
||||
}; |
||||
|
||||
inline std::unique_ptr<packet> packet_factory::make_packet(buffer_t buf) |
||||
{ |
||||
auto ret = std::make_unique<packet_in>(key); |
||||
ret->create(std::move(buf)); |
||||
ret->decrypt(); |
||||
return ret; |
||||
} |
||||
|
||||
template<packet_type t, typename... Args> |
||||
std::unique_ptr<packet> packet_factory::make_packet(Args... args) |
||||
{ |
||||
auto ret = std::make_unique<packet_out>(key); |
||||
ret->create<t>(args...); |
||||
ret->encrypt(); |
||||
return ret; |
||||
} |
||||
|
||||
} // namespace net
|
||||
} // namespace dvl
|
||||
#pragma once |
||||
|
||||
#include <string> |
||||
#include <memory> |
||||
#include <array> |
||||
#include <cstring> |
||||
#include <sodium.h> |
||||
|
||||
#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<unsigned char, crypto_secretbox_KEYBYTES> 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 P> class packet_proc : public packet { |
||||
public: |
||||
using packet::packet; |
||||
void process_data(); |
||||
}; |
||||
|
||||
class packet_in : public packet_proc<packet_in> { |
||||
public: |
||||
using packet_proc<packet_in>::packet_proc; |
||||
void create(buffer_t buf); |
||||
void process_element(buffer_t& x); |
||||
template <class T> void process_element(T& x); |
||||
void decrypt(); |
||||
}; |
||||
|
||||
class packet_out : public packet_proc<packet_out> { |
||||
public: |
||||
using packet_proc<packet_out>::packet_proc; |
||||
|
||||
template<packet_type t, typename... Args> |
||||
void create(Args... args); |
||||
|
||||
void process_element(buffer_t& x); |
||||
template<class T> void process_element(T& x); |
||||
template<class T> static const unsigned char* begin(const T& x); |
||||
template<class T> static const unsigned char* end(const T& x); |
||||
void encrypt(); |
||||
}; |
||||
|
||||
template<class P> void packet_proc<P>::process_data() |
||||
{ |
||||
P& self = static_cast<P&>(*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 <class T> 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<PT_MESSAGE>(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<PT_TURN>(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<PT_JOIN_REQUEST>(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<PT_JOIN_ACCEPT>(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<PT_CONNECT>(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<PT_DISCONNECT>(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 <class T> void packet_out::process_element(T& x) |
||||
{ |
||||
encrypted_buffer.insert(encrypted_buffer.end(), begin(x), end(x)); |
||||
} |
||||
|
||||
template <class T> const unsigned char* packet_out::begin(const T& x) |
||||
{ |
||||
return reinterpret_cast<const unsigned char *>(&x); |
||||
} |
||||
|
||||
template <class T> const unsigned char* packet_out::end(const T& x) |
||||
{ |
||||
return reinterpret_cast<const unsigned char *>(&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<packet> make_packet(buffer_t buf); |
||||
template<packet_type t, typename... Args> |
||||
std::unique_ptr<packet> make_packet(Args... args); |
||||
}; |
||||
|
||||
inline std::unique_ptr<packet> packet_factory::make_packet(buffer_t buf) |
||||
{ |
||||
std::unique_ptr<packet_in> ret(new packet_in(key)); |
||||
ret->create(std::move(buf)); |
||||
ret->decrypt(); |
||||
return std::unique_ptr<packet>(std::move(ret)); |
||||
} |
||||
|
||||
template<packet_type t, typename... Args> |
||||
std::unique_ptr<packet> packet_factory::make_packet(Args... args) |
||||
{ |
||||
std::unique_ptr<packet_out> ret(new packet_out(key)); |
||||
ret->create<t>(args...); |
||||
ret->encrypt(); |
||||
return std::unique_ptr<packet>(std::move(ret)); |
||||
} |
||||
|
||||
} // namespace net
|
||||
} // namespace dvl
|
||||
|
||||
@ -1,109 +1,109 @@
|
||||
#include "dvlnet/tcp_client.h" |
||||
|
||||
#include <functional> |
||||
#include <exception> |
||||
#include <system_error> |
||||
#include <stdexcept> |
||||
#include <sodium.h> |
||||
#include <SDL.h> |
||||
|
||||
namespace dvl { |
||||
namespace net { |
||||
|
||||
int tcp_client::create(std::string addrstr, std::string passwd) |
||||
{ |
||||
try { |
||||
auto port = default_port; |
||||
local_server = std::make_unique<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<unsigned char*>(&cookie_self), |
||||
sizeof(cookie_t)); |
||||
auto pkt = pktfty->make_packet<PT_JOIN_REQUEST>(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<buffer_t>(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 <functional> |
||||
#include <exception> |
||||
#include <system_error> |
||||
#include <stdexcept> |
||||
#include <sodium.h> |
||||
#include <SDL.h> |
||||
|
||||
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<unsigned char*>(&cookie_self), |
||||
sizeof(cookie_t)); |
||||
auto pkt = pktfty->make_packet<PT_JOIN_REQUEST>(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<buffer_t>(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
|
||||
|
||||
@ -1,215 +1,215 @@
|
||||
#include "dvlnet/tcp_server.h" |
||||
|
||||
#include <functional> |
||||
#include <chrono> |
||||
|
||||
#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<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<client_connection>(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<PT_CONNECT>(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<PT_JOIN_ACCEPT>(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<buffer_t>(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<PT_DISCONNECT>(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 <functional> |
||||
#include <chrono> |
||||
|
||||
#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<client_connection>(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<PT_CONNECT>(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<PT_JOIN_ACCEPT>(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<buffer_t>(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<PT_DISCONNECT>(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
|
||||
|
||||
Loading…
Reference in new issue