14 changed files with 705 additions and 628 deletions
@ -1,267 +0,0 @@
|
||||
#include "sodium.h" |
||||
#include <asio/ts/buffer.hpp> |
||||
#include <asio/ts/internet.hpp> |
||||
#include <asio/ts/io_context.hpp> |
||||
#include <asio/ts/net.hpp> |
||||
|
||||
class dvlnet { |
||||
public: |
||||
typedef std::vector<unsigned char> buffer_t; |
||||
static std::unique_ptr<dvlnet> inst; |
||||
|
||||
virtual int create(std::string addrstr, std::string passwd) = 0; |
||||
virtual int join(std::string addrstr, std::string passwd) = 0; |
||||
virtual bool SNetReceiveMessage(int *sender, char **data, int *size) = 0; |
||||
virtual bool SNetSendMessage(int dest, void *data, unsigned int size) = 0; |
||||
virtual bool SNetReceiveTurns(char **data, unsigned int *size, DWORD *status) = 0; |
||||
virtual bool SNetSendTurn(char *data, unsigned int size) = 0; |
||||
virtual int SNetGetProviderCaps(struct _SNETCAPS *caps) = 0; |
||||
virtual void *SNetRegisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *)) = 0; |
||||
virtual void *SNetUnregisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *)) = 0; |
||||
virtual ~dvlnet() {} |
||||
}; |
||||
|
||||
class dvlnet_null: public dvlnet { |
||||
private: |
||||
std::queue<buffer_t> message_queue; |
||||
buffer_t message_last; |
||||
const int plr_single = 0; |
||||
|
||||
public: |
||||
virtual int create(std::string addrstr, std::string passwd); |
||||
virtual int join(std::string addrstr, std::string passwd); |
||||
virtual bool SNetReceiveMessage(int *sender, char **data, int *size); |
||||
virtual bool SNetSendMessage(int dest, void *data, unsigned int size); |
||||
virtual bool SNetReceiveTurns(char **data, unsigned int *size, DWORD *status); |
||||
virtual bool SNetSendTurn(char *data, unsigned int size); |
||||
virtual int SNetGetProviderCaps(struct _SNETCAPS *caps); |
||||
virtual void *SNetRegisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *)); |
||||
virtual void *SNetUnregisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *)); |
||||
}; |
||||
|
||||
// exact meaning yet to be worked out
|
||||
#define PS_CONNECTED 0x10000 |
||||
#define PS_TURN_ARRIVED 0x20000 |
||||
#define PS_ACTIVE 0x40000 |
||||
|
||||
class dvlnet_udp : public dvlnet { |
||||
public: |
||||
dvlnet_udp(buffer_t info); |
||||
virtual int create(std::string addrstr, std::string passwd); |
||||
virtual int join(std::string addrstr, std::string passwd); |
||||
|
||||
virtual bool SNetReceiveMessage(int *sender, char **data, int *size); |
||||
virtual bool SNetSendMessage(int dest, void *data, unsigned int size); |
||||
virtual bool SNetReceiveTurns(char **data, unsigned int *size, DWORD *status); |
||||
virtual bool SNetSendTurn(char *data, unsigned int size); |
||||
virtual int SNetGetProviderCaps(struct _SNETCAPS *caps); |
||||
virtual void *SNetRegisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *)); |
||||
virtual void *SNetUnregisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *)); |
||||
|
||||
static constexpr unsigned short max_packet_size = 0xFFFF; |
||||
|
||||
enum packet_type : uint8_t { |
||||
PT_MESSAGE = 0x01, |
||||
PT_TURN = 0x02, |
||||
PT_JOIN_REQUEST = 0x11, |
||||
PT_JOIN_ACCEPT = 0x12, |
||||
PT_LEAVE_GAME = 0x13, |
||||
}; |
||||
typedef uint8_t plr_t; |
||||
typedef uint32_t cookie_t; |
||||
typedef int turn_t; // change int to something else in devilution code later
|
||||
typedef std::array<unsigned char, crypto_secretbox_KEYBYTES> key_t; |
||||
class packet_exception : public std::exception {}; |
||||
typedef asio::ip::udp::endpoint endpoint; |
||||
static const endpoint none; |
||||
|
||||
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; |
||||
plr_t m_oldplr; |
||||
buffer_t m_info; |
||||
|
||||
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(); |
||||
plr_t oldplr(); |
||||
const buffer_t &info(); |
||||
}; |
||||
|
||||
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; |
||||
void create(packet_type t, plr_t s, plr_t d, buffer_t m); |
||||
void create(packet_type t, plr_t s, plr_t d, turn_t u); |
||||
void create(packet_type t, plr_t s, plr_t d, cookie_t c); |
||||
void create(packet_type t, plr_t s, plr_t d, cookie_t c, plr_t n, buffer_t i); |
||||
void create(packet_type t, plr_t s, plr_t d, plr_t o); |
||||
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(); |
||||
}; |
||||
|
||||
typedef std::unique_ptr<packet> upacket; |
||||
upacket make_packet(buffer_t buf); |
||||
template<typename T, typename... Args> upacket make_packet(T t, Args... args); |
||||
|
||||
private: |
||||
static constexpr unsigned short default_port = 6112; |
||||
static constexpr unsigned short try_ports = 512; |
||||
static constexpr daddr_t ADDR_BROADCAST = 0xFF; |
||||
static constexpr daddr_t ADDR_MASTER = 0xFE; |
||||
static constexpr int ACTIVE = 60; |
||||
|
||||
std::map<event_type, void(__stdcall *)(struct _SNETEVENT *)> registered_handlers; |
||||
buffer_t game_init_info; |
||||
|
||||
struct message_t { |
||||
int sender; // change int to something else in devilution code later
|
||||
buffer_t payload; |
||||
message_t() : sender(-1), payload({}) {}; |
||||
message_t(int s, buffer_t p) : sender(s), payload(p) {}; |
||||
}; |
||||
|
||||
message_t message_last; |
||||
std::queue<message_t> message_queue; |
||||
std::array<turn_t, MAX_PLRS> turn_last = { 0 }; |
||||
std::array<std::queue<turn_t>, MAX_PLRS> turn_queue; |
||||
|
||||
plr_t plr_self = ADDR_BROADCAST; |
||||
unsigned short udpport_self = 0; |
||||
cookie_t cookie_self = 0; |
||||
|
||||
key_t key = { 0 }; |
||||
endpoint master; |
||||
|
||||
std::set<endpoint> connection_requests_pending; |
||||
std::array<endpoint, MAX_PLRS> nexthop_table; |
||||
std::array<int, MAX_PLRS> active_table = { 0 }; |
||||
asio::io_context context; |
||||
asio::ip::udp::socket sock = asio::ip::udp::socket(context); |
||||
|
||||
unsigned short bind(); |
||||
void setup_password(std::string pw); |
||||
|
||||
void handle_join_request(upacket &pkt, endpoint sender); |
||||
void handle_accept(upacket &pkt); |
||||
void recv(); |
||||
void send(upacket &pkt, endpoint sender = none); |
||||
void recv_decrypted(upacket &pkt, endpoint sender); |
||||
std::set<endpoint> dests_for_addr(plr_t dest, endpoint sender); |
||||
void run_event_handler(_SNETEVENT &ev); |
||||
}; |
||||
|
||||
template<class P> void dvlnet_udp::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); |
||||
break; |
||||
case PT_JOIN_ACCEPT: |
||||
self.process_element(m_cookie); |
||||
self.process_element(m_newplr); |
||||
self.process_element(m_info); |
||||
break; |
||||
case PT_LEAVE_GAME: |
||||
break; |
||||
} |
||||
} |
||||
|
||||
inline dvlnet_udp::upacket dvlnet_udp::make_packet(buffer_t buf) |
||||
{ |
||||
auto ret = std::make_unique<packet_in>(key); |
||||
ret->create(std::move(buf)); |
||||
ret->decrypt(); |
||||
return ret; |
||||
} |
||||
|
||||
template<typename T, typename... Args> dvlnet_udp::upacket dvlnet_udp::make_packet(T t, Args... args) |
||||
{ |
||||
auto ret = std::make_unique<packet_out>(key); |
||||
ret->create(t, args...); |
||||
ret->encrypt(); |
||||
return ret; |
||||
} |
||||
|
||||
inline void dvlnet_udp::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 dvlnet_udp::packet_in::process_element(T &x) |
||||
{ |
||||
if (decrypted_buffer.size() < sizeof(T)) |
||||
throw packet_exception(); |
||||
x = *reinterpret_cast<T *>(decrypted_buffer.data()); |
||||
decrypted_buffer.erase(decrypted_buffer.begin(), decrypted_buffer.begin() + sizeof(T)); |
||||
} |
||||
|
||||
inline void dvlnet_udp::packet_out::process_element(buffer_t &x) |
||||
{ |
||||
encrypted_buffer.insert(encrypted_buffer.end(), x.begin(), x.end()); |
||||
} |
||||
|
||||
template <class T> void dvlnet_udp::packet_out::process_element(T &x) |
||||
{ |
||||
encrypted_buffer.insert(encrypted_buffer.end(), begin(x), end(x)); |
||||
} |
||||
|
||||
template <class T> const unsigned char *dvlnet_udp::packet_out::begin(const T &x) |
||||
{ |
||||
return reinterpret_cast<const unsigned char *>(&x); |
||||
} |
||||
|
||||
template <class T> const unsigned char *dvlnet_udp::packet_out::end(const T &x) |
||||
{ |
||||
return reinterpret_cast<const unsigned char *>(&x) + sizeof(T); |
||||
} |
||||
@ -0,0 +1,135 @@
|
||||
#include "../types.h" |
||||
|
||||
using namespace dvlnet; |
||||
|
||||
void base::setup_password(std::string pw) |
||||
{ |
||||
//pw.resize(std::min(pw.size(), crypto_pwhash_PASSWD_MAX));
|
||||
//pw.resize(std::max(pw.size(), crypto_pwhash_PASSWD_MIN), 0);
|
||||
std::string salt("devilution-salt"); |
||||
salt.resize(crypto_pwhash_SALTBYTES, 0); |
||||
if (crypto_pwhash(key.data(), crypto_secretbox_KEYBYTES, |
||||
pw.data(), pw.size(), |
||||
reinterpret_cast<const unsigned char *>(salt.data()), |
||||
crypto_pwhash_OPSLIMIT_INTERACTIVE, |
||||
crypto_pwhash_MEMLIMIT_INTERACTIVE, |
||||
crypto_pwhash_ALG_DEFAULT)) |
||||
ABORT(); |
||||
} |
||||
|
||||
void base::run_event_handler(_SNETEVENT& ev) |
||||
{ |
||||
auto f = registered_handlers[static_cast<event_type>(ev.eventid)]; |
||||
if(f) { |
||||
f(&ev); |
||||
} |
||||
} |
||||
|
||||
void base::recv_local(upacket& pkt) |
||||
{ |
||||
switch (pkt->type()) { |
||||
case PT_MESSAGE: |
||||
message_queue.push(message_t(pkt->src(), pkt->message())); |
||||
break; |
||||
case PT_TURN: |
||||
turn_queue[pkt->src()].push(pkt->turn()); |
||||
break; |
||||
case PT_LEAVE_GAME: |
||||
// todo
|
||||
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(); |
||||
*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(message_t(plr_self, message)); |
||||
plr_t dest; |
||||
if (playerID == SNPLAYER_ALL || playerID == SNPLAYER_OTHERS) |
||||
dest = ADDR_BROADCAST; |
||||
else |
||||
dest = playerID; |
||||
upacket pkt = make_packet(PT_MESSAGE, plr_self, dest, message); |
||||
send(pkt); |
||||
return true; |
||||
} |
||||
|
||||
bool base::SNetReceiveTurns(char **data, unsigned int *size, DWORD *status) |
||||
{ |
||||
poll(); |
||||
for (auto i = 0; i < MAX_PLRS; ++i) { |
||||
status[i] = 0; |
||||
if (i == plr_self || active(i)) { |
||||
status[i] |= PS_ACTIVE; |
||||
} |
||||
if (i == plr_self || connected(i)) { |
||||
status[i] |= PS_CONNECTED; |
||||
} |
||||
if (!turn_queue[i].empty()) { |
||||
size[i] = sizeof(turn_t); |
||||
status[i] |= PS_TURN_ARRIVED; |
||||
turn_last[i] = turn_queue[i].front(); |
||||
turn_queue[i].pop(); |
||||
data[i] = reinterpret_cast<char *>(&turn_last[i]); |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
bool base::SNetSendTurn(char* data, unsigned int size) |
||||
{ |
||||
if (size != sizeof(turn_t)) |
||||
ABORT(); |
||||
upacket pkt = make_packet(PT_TURN, plr_self, ADDR_BROADCAST, *reinterpret_cast<turn_t*>(data)); |
||||
send(pkt); |
||||
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; |
||||
} |
||||
|
||||
void* base::SNetUnregisterEventHandler(event_type evtype, void(__stdcall* func)(struct _SNETEVENT *)) |
||||
{ |
||||
registered_handlers.erase(evtype); |
||||
return (void*)func; |
||||
} |
||||
|
||||
void* base::SNetRegisterEventHandler(event_type evtype, void(__stdcall* func)(struct _SNETEVENT *)) |
||||
{ |
||||
registered_handlers[evtype] = func; |
||||
return (void*)func; |
||||
// need to handle:
|
||||
// EVENT_TYPE_PLAYER_LEAVE_GAME
|
||||
// EVENT_TYPE_PLAYER_CREATE_GAME (raised during SNetCreateGame?)
|
||||
// EVENT_TYPE_PLAYER_MESSAGE
|
||||
// all by the same function
|
||||
} |
||||
@ -0,0 +1,73 @@
|
||||
// exact meaning yet to be worked out
|
||||
#define PS_CONNECTED 0x10000 |
||||
#define PS_TURN_ARRIVED 0x20000 |
||||
#define PS_ACTIVE 0x40000 |
||||
|
||||
namespace dvlnet { |
||||
class base : public dvlnet { |
||||
public: |
||||
virtual int create(std::string addrstr, std::string passwd) = 0; |
||||
virtual int join(std::string addrstr, std::string passwd) = 0; |
||||
|
||||
virtual bool SNetReceiveMessage(int* sender, char** data, int* size); |
||||
virtual bool SNetSendMessage(int dest, void* data, unsigned int size); |
||||
virtual bool SNetReceiveTurns(char** data, unsigned int* size, DWORD* status); |
||||
virtual bool SNetSendTurn(char* data, unsigned int size); |
||||
virtual int SNetGetProviderCaps(struct _SNETCAPS* caps); |
||||
virtual void* SNetRegisterEventHandler(event_type evtype, void(__stdcall* func)(struct _SNETEVENT*)); |
||||
virtual void* SNetUnregisterEventHandler(event_type evtype, void(__stdcall* func)(struct _SNETEVENT*)); |
||||
|
||||
virtual void poll() = 0; |
||||
virtual void send(upacket& pkt) = 0; |
||||
virtual bool connected(plr_t p) = 0; |
||||
virtual bool active(plr_t p) = 0; |
||||
|
||||
static constexpr unsigned short max_packet_size = 0xFFFF; |
||||
upacket make_packet(buffer_t buf); |
||||
template<typename T, typename... Args> upacket make_packet(T t, Args... args); |
||||
|
||||
protected: |
||||
static constexpr daddr_t ADDR_BROADCAST = 0xFF; |
||||
static constexpr daddr_t ADDR_MASTER = 0xFE; |
||||
|
||||
std::map<event_type, void(__stdcall*)(struct _SNETEVENT*)> registered_handlers; |
||||
buffer_t game_init_info; |
||||
|
||||
struct message_t { |
||||
int sender; // change int to something else in devilution code later
|
||||
buffer_t payload; |
||||
message_t() : sender(-1), payload({}) {} |
||||
message_t(int s, buffer_t p) : sender(s), payload(p) {} |
||||
}; |
||||
|
||||
message_t message_last; |
||||
std::queue<message_t> message_queue; |
||||
std::array<turn_t, MAX_PLRS> turn_last = { 0 }; |
||||
std::array<std::queue<turn_t>, MAX_PLRS> turn_queue; |
||||
|
||||
plr_t plr_self = ADDR_BROADCAST; |
||||
cookie_t cookie_self = 0; |
||||
|
||||
key_t key = { 0 }; |
||||
|
||||
void setup_password(std::string pw); |
||||
void recv_local(upacket &pkt); |
||||
void run_event_handler(_SNETEVENT &ev); |
||||
}; |
||||
|
||||
inline upacket base::make_packet(buffer_t buf) |
||||
{ |
||||
auto ret = std::make_unique<packet_in>(key); |
||||
ret->create(std::move(buf)); |
||||
ret->decrypt(); |
||||
return ret; |
||||
} |
||||
|
||||
template<typename T, typename... Args> upacket base::make_packet(T t, Args... args) |
||||
{ |
||||
auto ret = std::make_unique<packet_out>(key); |
||||
ret->create(t, args...); |
||||
ret->encrypt(); |
||||
return ret; |
||||
} |
||||
} |
||||
@ -0,0 +1,27 @@
|
||||
#pragma once |
||||
#include "sodium.h" |
||||
#include <asio/ts/buffer.hpp> |
||||
#include <asio/ts/internet.hpp> |
||||
#include <asio/ts/io_context.hpp> |
||||
#include <asio/ts/net.hpp> |
||||
|
||||
namespace dvlnet { |
||||
class dvlnet { |
||||
public: |
||||
virtual int create(std::string addrstr, std::string passwd) = 0; |
||||
virtual int join(std::string addrstr, std::string passwd) = 0; |
||||
virtual bool SNetReceiveMessage(int *sender, char **data, int *size) = 0; |
||||
virtual bool SNetSendMessage(int dest, void *data, unsigned int size) = 0; |
||||
virtual bool SNetReceiveTurns(char **data, unsigned int *size, DWORD *status) = 0; |
||||
virtual bool SNetSendTurn(char *data, unsigned int size) = 0; |
||||
virtual int SNetGetProviderCaps(struct _SNETCAPS *caps) = 0; |
||||
virtual void *SNetRegisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *)) = 0; |
||||
virtual void *SNetUnregisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *)) = 0; |
||||
virtual ~dvlnet() {} |
||||
}; |
||||
} |
||||
|
||||
#include "dvlnet/packet.h" |
||||
#include "dvlnet/loopback.h" |
||||
#include "dvlnet/base.h" |
||||
#include "dvlnet/udp_p2p.h" |
||||
@ -0,0 +1,21 @@
|
||||
#pragma once |
||||
|
||||
namespace dvlnet { |
||||
class loopback : public dvlnet { |
||||
private: |
||||
std::queue<buffer_t> message_queue; |
||||
buffer_t message_last; |
||||
const int plr_single = 0; |
||||
|
||||
public: |
||||
virtual int create(std::string addrstr, std::string passwd); |
||||
virtual int join(std::string addrstr, std::string passwd); |
||||
virtual bool SNetReceiveMessage(int *sender, char **data, int *size); |
||||
virtual bool SNetSendMessage(int dest, void *data, unsigned int size); |
||||
virtual bool SNetReceiveTurns(char **data, unsigned int *size, DWORD *status); |
||||
virtual bool SNetSendTurn(char *data, unsigned int size); |
||||
virtual int SNetGetProviderCaps(struct _SNETCAPS *caps); |
||||
virtual void *SNetRegisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *)); |
||||
virtual void *SNetUnregisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *)); |
||||
}; |
||||
} |
||||
@ -0,0 +1,145 @@
|
||||
#pragma once |
||||
|
||||
namespace dvlnet { |
||||
enum packet_type : uint8_t { |
||||
PT_MESSAGE = 0x01, |
||||
PT_TURN = 0x02, |
||||
PT_JOIN_REQUEST = 0x11, |
||||
PT_JOIN_ACCEPT = 0x12, |
||||
PT_LEAVE_GAME = 0x13, |
||||
}; |
||||
|
||||
typedef uint8_t plr_t; |
||||
typedef uint32_t cookie_t; |
||||
typedef int turn_t; // change int to something else in devilution code later
|
||||
typedef std::vector<unsigned char> buffer_t; |
||||
typedef std::array<unsigned char, crypto_secretbox_KEYBYTES> key_t; |
||||
|
||||
class packet_exception : public std::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; |
||||
plr_t m_oldplr; |
||||
buffer_t m_info; |
||||
|
||||
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(); |
||||
plr_t oldplr(); |
||||
const buffer_t &info(); |
||||
}; |
||||
|
||||
template<class P> class packet_proc : public packet { |
||||
public: |
||||
using packet::packet; |
||||
void process_data(); |
||||
}; |
||||
|
||||
typedef std::unique_ptr<packet> upacket; |
||||
|
||||
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; |
||||
void create(packet_type t, plr_t s, plr_t d, buffer_t m); |
||||
void create(packet_type t, plr_t s, plr_t d, turn_t u); |
||||
void create(packet_type t, plr_t s, plr_t d, cookie_t c); |
||||
void create(packet_type t, plr_t s, plr_t d, cookie_t c, plr_t n, buffer_t i); |
||||
void create(packet_type t, plr_t s, plr_t d, plr_t o); |
||||
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); |
||||
break; |
||||
case PT_JOIN_ACCEPT: |
||||
self.process_element(m_cookie); |
||||
self.process_element(m_newplr); |
||||
self.process_element(m_info); |
||||
break; |
||||
case PT_LEAVE_GAME: |
||||
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(); |
||||
x = *reinterpret_cast<T *>(decrypted_buffer.data()); |
||||
decrypted_buffer.erase(decrypted_buffer.begin(), decrypted_buffer.begin() + sizeof(T)); |
||||
} |
||||
|
||||
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); |
||||
} |
||||
} |
||||
@ -0,0 +1,191 @@
|
||||
#include "../types.h" |
||||
|
||||
using namespace dvlnet; |
||||
|
||||
const udp_p2p::endpoint udp_p2p::none; |
||||
|
||||
udp_p2p::udp_p2p(buffer_t info) |
||||
{ |
||||
if (sodium_init() < 0) |
||||
abort(); |
||||
game_init_info = std::move(info); |
||||
} |
||||
|
||||
|
||||
int udp_p2p::create(std::string addrstr, std::string passwd) |
||||
{ |
||||
sock = asio::ip::udp::socket(io_context); // to be removed later
|
||||
setup_password(passwd); |
||||
auto ipaddr = asio::ip::make_address(addrstr); |
||||
if (ipaddr.is_v4()) |
||||
sock.open(asio::ip::udp::v4()); |
||||
else if (ipaddr.is_v6()) |
||||
sock.open(asio::ip::udp::v6()); |
||||
sock.non_blocking(true); |
||||
unsigned short port = default_port; |
||||
/*
|
||||
while(port <= default_port+try_ports) { |
||||
try { |
||||
sock.bind(asio::ip::udp::endpoint(asio::ip::address_v6(), port)); |
||||
} catch (std::exception e) { |
||||
eprintf("bind: %s, %s\n", asio::ip::address_v6().to_string(), e.what()); |
||||
} |
||||
++port; |
||||
} |
||||
*/ |
||||
try { |
||||
sock.bind(endpoint(ipaddr, port)); |
||||
} catch (std::exception e) { |
||||
return -1; |
||||
} |
||||
plr_self = 0; |
||||
return plr_self; |
||||
} |
||||
|
||||
int udp_p2p::join(std::string addrstr, std::string passwd) |
||||
{ |
||||
setup_password(passwd); |
||||
auto ipaddr = asio::ip::make_address(addrstr); |
||||
endpoint themaster(ipaddr, default_port); |
||||
sock.connect(themaster); |
||||
master = themaster; |
||||
{ // hack: try to join for 5 seconds
|
||||
randombytes_buf(reinterpret_cast<unsigned char *>(&cookie_self), |
||||
sizeof(cookie_t)); |
||||
upacket pkt = make_packet(PT_JOIN_REQUEST, ADDR_BROADCAST, ADDR_MASTER, cookie_self); |
||||
send(pkt); |
||||
for (auto i = 0; i < 5; ++i) { |
||||
recv(); |
||||
if (plr_self != ADDR_BROADCAST) |
||||
break; // join successful
|
||||
sleep(1); |
||||
} |
||||
} |
||||
return (plr_self == ADDR_BROADCAST ? 4 : plr_self); |
||||
} |
||||
|
||||
void udp_p2p::poll() |
||||
{ |
||||
recv(); |
||||
} |
||||
|
||||
void udp_p2p::send(upacket& pkt) |
||||
{ |
||||
send_internal(pkt, none); |
||||
} |
||||
|
||||
bool udp_p2p::connected(plr_t p) |
||||
{ |
||||
return active_table[p]; |
||||
} |
||||
|
||||
bool udp_p2p::active(plr_t p) |
||||
{ |
||||
return active_table[p]; |
||||
} |
||||
|
||||
void udp_p2p::recv() |
||||
{ |
||||
try { |
||||
while (1) { // read until kernel buffer is empty?
|
||||
try { |
||||
endpoint sender; |
||||
buffer_t pkt_buf(max_packet_size); |
||||
size_t pkt_len; |
||||
pkt_len = sock.receive_from(asio::buffer(pkt_buf), sender); |
||||
pkt_buf.resize(pkt_len); |
||||
upacket pkt = make_packet(pkt_buf); |
||||
recv_decrypted(pkt, sender); |
||||
} catch (packet_exception e) { |
||||
// drop packet
|
||||
} |
||||
} |
||||
} catch (std::exception e) { |
||||
return; |
||||
} |
||||
} |
||||
|
||||
void udp_p2p::send_internal(upacket &pkt, endpoint sender) |
||||
{ |
||||
for (auto &dest : dests_for_addr(pkt->dest(), sender)) { |
||||
sock.send_to(asio::buffer(pkt->data()), dest); |
||||
} |
||||
} |
||||
|
||||
std::set<udp_p2p::endpoint> udp_p2p::dests_for_addr(plr_t dest, endpoint sender) |
||||
{ |
||||
auto ret = std::set<endpoint>(); |
||||
if (dest == plr_self) |
||||
return ret; |
||||
|
||||
if (0 <= dest && dest < MAX_PLRS) { |
||||
if (active_table[dest]) |
||||
ret.insert(nexthop_table[dest]); |
||||
} else if (dest == ADDR_BROADCAST) { |
||||
for (auto i = 0; i < MAX_PLRS; ++i) |
||||
if (i != plr_self && active_table[i]) |
||||
ret.insert(nexthop_table[i]); |
||||
ret.insert(connection_requests_pending.begin(), connection_requests_pending.end()); |
||||
} else if (dest == ADDR_MASTER) { |
||||
if (master != none) |
||||
ret.insert(master); |
||||
} |
||||
ret.erase(sender); |
||||
return ret; |
||||
} |
||||
|
||||
void udp_p2p::handle_join_request(upacket &pkt, endpoint sender) |
||||
{ |
||||
plr_t i; |
||||
for (i = 0; i < MAX_PLRS; ++i) { |
||||
if (i != plr_self && nexthop_table[i] == none) { |
||||
nexthop_table[i] = sender; |
||||
break; |
||||
} |
||||
} |
||||
upacket reply = make_packet(PT_JOIN_ACCEPT, plr_self, ADDR_BROADCAST, pkt->cookie(), i, game_init_info); |
||||
send(reply); |
||||
} |
||||
|
||||
void udp_p2p::recv_decrypted(upacket &pkt, endpoint sender) |
||||
{ |
||||
// 1. route
|
||||
send_internal(pkt, sender); |
||||
// 2. handle local
|
||||
if (pkt->src() == ADDR_BROADCAST && pkt->dest() == ADDR_MASTER) { |
||||
connection_requests_pending.insert(sender); |
||||
if (master == none) { |
||||
handle_join_request(pkt, sender); |
||||
} |
||||
} |
||||
// normal packets
|
||||
if (pkt->src() < 0 || pkt->src() >= MAX_PLRS) |
||||
return; //drop packet
|
||||
if (active_table[pkt->src()]) { //WRONG?!?
|
||||
if (sender != nexthop_table[pkt->src()]) |
||||
return; //rpfilter fail: drop packet
|
||||
} else { |
||||
nexthop_table[pkt->src()] = sender; // new connection: accept
|
||||
} |
||||
active_table[pkt->src()] = ACTIVE; |
||||
if (pkt->dest() != plr_self && pkt->dest() != ADDR_BROADCAST) |
||||
return; //packet not for us, drop
|
||||
if(pkt->type() == PT_JOIN_ACCEPT) |
||||
handle_accept(pkt); |
||||
else |
||||
recv_local(pkt); |
||||
} |
||||
|
||||
void udp_p2p::handle_accept(upacket &pkt) |
||||
{ |
||||
if (plr_self != ADDR_BROADCAST) |
||||
return; // already have player id
|
||||
if (pkt->cookie() == cookie_self) |
||||
plr_self = pkt->newplr(); |
||||
_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); |
||||
} |
||||
@ -0,0 +1,38 @@
|
||||
namespace dvlnet { |
||||
class udp_p2p : public base { |
||||
public: |
||||
udp_p2p(buffer_t info); |
||||
virtual int create(std::string addrstr, std::string passwd); |
||||
virtual int join(std::string addrstr, std::string passwd); |
||||
virtual void poll(); |
||||
virtual void send(upacket& pkt); |
||||
virtual bool connected(plr_t p); |
||||
virtual bool active(plr_t p); |
||||
|
||||
private: |
||||
typedef asio::ip::udp::endpoint endpoint; |
||||
static const endpoint none; |
||||
|
||||
unsigned short udpport_self = 0; |
||||
|
||||
static constexpr unsigned short default_port = 6112; |
||||
static constexpr unsigned short try_ports = 512; |
||||
static constexpr int ACTIVE = 60; |
||||
|
||||
asio::io_context io_context; |
||||
endpoint master; |
||||
|
||||
std::set<endpoint> connection_requests_pending; |
||||
std::array<endpoint, MAX_PLRS> nexthop_table; |
||||
std::array<int, MAX_PLRS> active_table = { 0 }; |
||||
|
||||
asio::ip::udp::socket sock = asio::ip::udp::socket(io_context); |
||||
|
||||
void recv(); |
||||
void handle_join_request(upacket &pkt, endpoint sender); |
||||
void handle_accept(upacket &pkt); |
||||
void send_internal(upacket& pkt, endpoint sender = none); |
||||
std::set<endpoint> dests_for_addr(plr_t dest, endpoint sender); |
||||
void recv_decrypted(upacket &pkt, endpoint sender); |
||||
}; |
||||
} |
||||
@ -1,292 +0,0 @@
|
||||
#include "../types.h" |
||||
|
||||
const dvlnet_udp::endpoint dvlnet_udp::none; |
||||
|
||||
dvlnet_udp::dvlnet_udp(buffer_t info) |
||||
{ |
||||
if (sodium_init() < 0) |
||||
abort(); |
||||
game_init_info = std::move(info); |
||||
} |
||||
|
||||
int dvlnet_udp::create(std::string addrstr, std::string passwd) |
||||
{ |
||||
sock = asio::ip::udp::socket(context); // to be removed later
|
||||
setup_password(passwd); |
||||
auto ipaddr = asio::ip::make_address(addrstr); |
||||
if (ipaddr.is_v4()) |
||||
sock.open(asio::ip::udp::v4()); |
||||
else if (ipaddr.is_v6()) |
||||
sock.open(asio::ip::udp::v6()); |
||||
sock.non_blocking(true); |
||||
unsigned short port = default_port; |
||||
/*
|
||||
while(port <= default_port+try_ports) { |
||||
try { |
||||
sock.bind(asio::ip::udp::endpoint(asio::ip::address_v6(), port)); |
||||
} catch (std::exception e) { |
||||
eprintf("bind: %s, %s\n", asio::ip::address_v6().to_string(), e.what()); |
||||
} |
||||
++port; |
||||
} |
||||
*/ |
||||
try { |
||||
sock.bind(endpoint(ipaddr, port)); |
||||
} catch (std::exception e) { |
||||
return -1; |
||||
} |
||||
plr_self = 0; |
||||
return plr_self; |
||||
} |
||||
|
||||
int dvlnet_udp::join(std::string addrstr, std::string passwd) |
||||
{ |
||||
setup_password(passwd); |
||||
auto ipaddr = asio::ip::make_address(addrstr); |
||||
endpoint themaster(ipaddr, default_port); |
||||
sock.connect(themaster); |
||||
master = themaster; |
||||
{ // hack: try to join for 5 seconds
|
||||
randombytes_buf(reinterpret_cast<unsigned char *>(&cookie_self), |
||||
sizeof(cookie_t)); |
||||
upacket pkt = make_packet(PT_JOIN_REQUEST, ADDR_BROADCAST, ADDR_MASTER, cookie_self); |
||||
send(pkt); |
||||
for (auto i = 0; i < 5; ++i) { |
||||
recv(); |
||||
if (plr_self != ADDR_BROADCAST) |
||||
break; // join successful
|
||||
sleep(1); |
||||
} |
||||
} |
||||
return (plr_self == ADDR_BROADCAST ? 4 : plr_self); |
||||
} |
||||
|
||||
void dvlnet_udp::setup_password(std::string pw) |
||||
{ |
||||
//pw.resize(std::min(pw.size(), crypto_pwhash_PASSWD_MAX));
|
||||
//pw.resize(std::max(pw.size(), crypto_pwhash_PASSWD_MIN), 0);
|
||||
std::string salt("devilution-salt"); |
||||
salt.resize(crypto_pwhash_SALTBYTES, 0); |
||||
if (crypto_pwhash(key.data(), crypto_secretbox_KEYBYTES, |
||||
pw.data(), pw.size(), |
||||
reinterpret_cast<const unsigned char *>(salt.data()), |
||||
crypto_pwhash_OPSLIMIT_INTERACTIVE, |
||||
crypto_pwhash_MEMLIMIT_INTERACTIVE, |
||||
crypto_pwhash_ALG_DEFAULT)) |
||||
ABORT(); |
||||
} |
||||
|
||||
void dvlnet_udp::recv() |
||||
{ |
||||
try { |
||||
while (1) { // read until kernel buffer is empty?
|
||||
try { |
||||
endpoint sender; |
||||
buffer_t pkt_buf(max_packet_size); |
||||
size_t pkt_len; |
||||
pkt_len = sock.receive_from(asio::buffer(pkt_buf), sender); |
||||
pkt_buf.resize(pkt_len); |
||||
upacket pkt = make_packet(pkt_buf); |
||||
recv_decrypted(pkt, sender); |
||||
} catch (packet_exception e) { |
||||
// drop packet
|
||||
} |
||||
} |
||||
} catch (std::exception e) { |
||||
return; |
||||
} |
||||
} |
||||
|
||||
void dvlnet_udp::send(dvlnet_udp::upacket &pkt, endpoint sender) |
||||
{ |
||||
for (auto &dest : dests_for_addr(pkt->dest(), sender)) { |
||||
sock.send_to(asio::buffer(pkt->data()), dest); |
||||
} |
||||
} |
||||
|
||||
std::set<dvlnet_udp::endpoint> dvlnet_udp::dests_for_addr(plr_t dest, endpoint sender) |
||||
{ |
||||
auto ret = std::set<endpoint>(); |
||||
if (dest == plr_self) |
||||
return ret; |
||||
|
||||
if (0 <= dest && dest < MAX_PLRS) { |
||||
if (active_table[dest]) |
||||
ret.insert(nexthop_table[dest]); |
||||
} else if (dest == ADDR_BROADCAST) { |
||||
for (auto i = 0; i < MAX_PLRS; ++i) |
||||
if (i != plr_self && active_table[i]) |
||||
ret.insert(nexthop_table[i]); |
||||
ret.insert(connection_requests_pending.begin(), connection_requests_pending.end()); |
||||
} else if (dest == ADDR_MASTER) { |
||||
if (master != none) |
||||
ret.insert(master); |
||||
} |
||||
ret.erase(sender); |
||||
return ret; |
||||
} |
||||
|
||||
void dvlnet_udp::handle_join_request(upacket &pkt, endpoint sender) |
||||
{ |
||||
plr_t i; |
||||
for (i = 0; i < MAX_PLRS; ++i) { |
||||
if (i != plr_self && nexthop_table[i] == none) { |
||||
nexthop_table[i] = sender; |
||||
break; |
||||
} |
||||
} |
||||
upacket reply = make_packet(PT_JOIN_ACCEPT, plr_self, ADDR_BROADCAST, pkt->cookie(), i, game_init_info); |
||||
send(reply); |
||||
} |
||||
|
||||
void dvlnet_udp::run_event_handler(_SNETEVENT &ev) |
||||
{ |
||||
auto f = registered_handlers[static_cast<event_type>(ev.eventid)]; |
||||
if(f) { |
||||
f(&ev); |
||||
} |
||||
} |
||||
|
||||
void dvlnet_udp::handle_accept(upacket &pkt) |
||||
{ |
||||
if (plr_self != ADDR_BROADCAST) |
||||
return; // already have player id
|
||||
if (pkt->cookie() == cookie_self) |
||||
plr_self = pkt->newplr(); |
||||
_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 dvlnet_udp::recv_decrypted(upacket &pkt, endpoint sender) |
||||
{ |
||||
// 1. route
|
||||
send(pkt, sender); |
||||
// 2. handle local
|
||||
if (pkt->src() == ADDR_BROADCAST && pkt->dest() == ADDR_MASTER) { |
||||
connection_requests_pending.insert(sender); |
||||
if (master == none) { |
||||
handle_join_request(pkt, sender); |
||||
} |
||||
} |
||||
// normal packets
|
||||
if (pkt->src() < 0 || pkt->src() >= MAX_PLRS) |
||||
return; //drop packet
|
||||
if (active_table[pkt->src()]) { //WRONG?!?
|
||||
if (sender != nexthop_table[pkt->src()]) |
||||
return; //rpfilter fail: drop packet
|
||||
} else { |
||||
nexthop_table[pkt->src()] = sender; // new connection: accept
|
||||
} |
||||
active_table[pkt->src()] = ACTIVE; |
||||
if (pkt->dest() != plr_self && pkt->dest() != ADDR_BROADCAST) |
||||
return; //packet not for us, drop
|
||||
switch (pkt->type()) { |
||||
case PT_MESSAGE: |
||||
message_queue.push(message_t(pkt->src(), pkt->message())); |
||||
break; |
||||
case PT_TURN: |
||||
turn_queue[pkt->src()].push(pkt->turn()); |
||||
break; |
||||
case PT_JOIN_ACCEPT: |
||||
handle_accept(pkt); |
||||
break; |
||||
case PT_LEAVE_GAME: |
||||
// todo
|
||||
break; |
||||
// otherwise drop
|
||||
} |
||||
} |
||||
|
||||
bool dvlnet_udp::SNetReceiveMessage(int *sender, char **data, int *size) |
||||
{ |
||||
recv(); |
||||
if (message_queue.empty()) |
||||
return false; |
||||
message_last = message_queue.front(); |
||||
message_queue.pop(); |
||||
*sender = message_last.sender; |
||||
*size = message_last.payload.size(); |
||||
*data = reinterpret_cast<char *>(message_last.payload.data()); |
||||
return true; |
||||
} |
||||
|
||||
bool dvlnet_udp::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(message_t(plr_self, message)); |
||||
plr_t dest; |
||||
if (playerID == SNPLAYER_ALL || playerID == SNPLAYER_OTHERS) |
||||
dest = ADDR_BROADCAST; |
||||
else |
||||
dest = playerID; |
||||
upacket pkt = make_packet(PT_MESSAGE, plr_self, dest, message); |
||||
send(pkt); |
||||
return true; |
||||
} |
||||
|
||||
bool dvlnet_udp::SNetReceiveTurns(char **data, unsigned int *size, DWORD *status) |
||||
{ |
||||
recv(); |
||||
for (auto i = 0; i < MAX_PLRS; ++i) { |
||||
status[i] = 0; |
||||
if (i == plr_self || nexthop_table[i] != none) { |
||||
status[i] |= (PS_ACTIVE | PS_CONNECTED); |
||||
} |
||||
if (!turn_queue[i].empty()) { |
||||
size[i] = sizeof(turn_t); |
||||
status[i] |= PS_TURN_ARRIVED; |
||||
turn_last[i] = turn_queue[i].front(); |
||||
turn_queue[i].pop(); |
||||
data[i] = reinterpret_cast<char *>(&turn_last[i]); |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
bool dvlnet_udp::SNetSendTurn(char *data, unsigned int size) |
||||
{ |
||||
if (size != sizeof(turn_t)) |
||||
ABORT(); |
||||
upacket pkt = make_packet(PT_TURN, plr_self, ADDR_BROADCAST, *reinterpret_cast<turn_t *>(data)); |
||||
send(pkt); |
||||
return true; |
||||
} |
||||
|
||||
int dvlnet_udp::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; |
||||
} |
||||
|
||||
void *dvlnet_udp::SNetUnregisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *)) |
||||
{ |
||||
registered_handlers.erase(evtype); |
||||
return (void*)func; |
||||
} |
||||
|
||||
void *dvlnet_udp::SNetRegisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *)) |
||||
{ |
||||
registered_handlers[evtype] = func; |
||||
return (void*)func; |
||||
// need to handle:
|
||||
// EVENT_TYPE_PLAYER_LEAVE_GAME
|
||||
// EVENT_TYPE_PLAYER_CREATE_GAME (raised during SNetCreateGame?)
|
||||
// EVENT_TYPE_PLAYER_MESSAGE
|
||||
// all by the same function
|
||||
} |
||||
Loading…
Reference in new issue