Browse Source

Restructure networking

pull/25/head
Xadhoom 7 years ago
parent
commit
3ad2c379e3
  1. 8
      CMakeLists.txt
  2. 267
      Stub/dvlnet.h
  3. 135
      Stub/dvlnet/base.cpp
  4. 73
      Stub/dvlnet/base.h
  5. 27
      Stub/dvlnet/dvlnet.h
  6. 20
      Stub/dvlnet/loopback.cpp
  7. 21
      Stub/dvlnet/loopback.h
  8. 90
      Stub/dvlnet/packet.cpp
  9. 145
      Stub/dvlnet/packet.h
  10. 191
      Stub/dvlnet/udp_p2p.cpp
  11. 38
      Stub/dvlnet/udp_p2p.h
  12. 292
      Stub/dvlnet_udp.cpp
  13. 24
      Stub/storm_net.cpp
  14. 2
      types.h

8
CMakeLists.txt

@ -140,9 +140,11 @@ set(STUB_SOURCES
Stub/sound.cpp
Stub/storm.cpp
Stub/storm_net.cpp
Stub/dvlnet_null.cpp
Stub/dvlnet_udp.cpp
Stub/dvlnet_udp_packet.cpp
Stub/dvlnet/loopback.cpp
Stub/dvlnet/packet.cpp
Stub/dvlnet/base.cpp
Stub/dvlnet/udp_p2p.cpp
Stub/DiabloUI/credits.cpp
Stub/DiabloUI/diabloui.cpp

267
Stub/dvlnet.h

@ -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);
}

135
Stub/dvlnet/base.cpp

@ -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
}

73
Stub/dvlnet/base.h

@ -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;
}
}

27
Stub/dvlnet/dvlnet.h

@ -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"

20
Stub/dvlnet_null.cpp → Stub/dvlnet/loopback.cpp

@ -1,18 +1,18 @@
#include "../types.h"
std::unique_ptr<dvlnet> dvlnet::inst;
using namespace dvlnet;
int dvlnet_null::create(std::string addrstr, std::string passwd)
int loopback::create(std::string addrstr, std::string passwd)
{
return plr_single;
}
int dvlnet_null::join(std::string addrstr, std::string passwd)
int loopback::join(std::string addrstr, std::string passwd)
{
ABORT();
}
bool dvlnet_null::SNetReceiveMessage(int *sender, char **data, int *size)
bool loopback::SNetReceiveMessage(int *sender, char **data, int *size)
{
if (message_queue.empty())
return false;
@ -24,7 +24,7 @@ bool dvlnet_null::SNetReceiveMessage(int *sender, char **data, int *size)
return true;
}
bool dvlnet_null::SNetSendMessage(int dest, void *data, unsigned int size)
bool loopback::SNetSendMessage(int dest, void *data, unsigned int size)
{
if (dest == plr_single || dest == SNPLAYER_ALL) {
auto raw_message = reinterpret_cast<unsigned char *>(data);
@ -34,19 +34,19 @@ bool dvlnet_null::SNetSendMessage(int dest, void *data, unsigned int size)
return true;
}
bool dvlnet_null::SNetReceiveTurns(char **data, unsigned int *size, DWORD *status)
bool loopback::SNetReceiveTurns(char **data, unsigned int *size, DWORD *status)
{
// todo: check that this is safe
return true;
}
bool dvlnet_null::SNetSendTurn(char *data, unsigned int size)
bool loopback::SNetSendTurn(char *data, unsigned int size)
{
// todo: check that this is safe
return true;
}
int dvlnet_null::SNetGetProviderCaps(struct _SNETCAPS *caps)
int loopback::SNetGetProviderCaps(struct _SNETCAPS *caps)
{
caps->size = 0; // engine writes only ?!?
caps->flags = 0; // unused
@ -60,14 +60,14 @@ int dvlnet_null::SNetGetProviderCaps(struct _SNETCAPS *caps)
return 1;
}
void *dvlnet_null::SNetRegisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *))
void *loopback::SNetRegisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *))
{
// not called in real singleplayer mode
// not needed in pseudo multiplayer mode (?)
return this;
}
void *dvlnet_null::SNetUnregisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *))
void *loopback::SNetUnregisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *))
{
// not called in real singleplayer mode
// not needed in pseudo multiplayer mode (?)

21
Stub/dvlnet/loopback.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 *));
};
}

90
Stub/dvlnet_udp_packet.cpp → Stub/dvlnet/packet.cpp

@ -1,90 +1,92 @@
#include "../types.h"
using namespace dvlnet;
static constexpr bool disable_encryption = false;
const dvlnet_udp::buffer_t &dvlnet_udp::packet::data()
const buffer_t& packet::data()
{
if (!have_decrypted || !have_encrypted)
ABORT();
return encrypted_buffer;
}
dvlnet_udp::packet_type dvlnet_udp::packet::type()
packet_type packet::type()
{
if (!have_decrypted)
ABORT();
return m_type;
}
dvlnet_udp::plr_t dvlnet_udp::packet::src()
plr_t packet::src()
{
if (!have_decrypted)
ABORT();
return m_src;
}
dvlnet_udp::plr_t dvlnet_udp::packet::dest()
plr_t packet::dest()
{
if (!have_decrypted)
ABORT();
return m_dest;
}
const dvlnet_udp::buffer_t &dvlnet_udp::packet::message()
const buffer_t &packet::message()
{
if (!have_decrypted)
ABORT();
if (m_type != PT_MESSAGE)
throw dvlnet_udp::packet_exception();
throw packet_exception();
return m_message;
}
dvlnet_udp::turn_t dvlnet_udp::packet::turn()
turn_t packet::turn()
{
if (!have_decrypted)
ABORT();
if (m_type != PT_TURN)
throw dvlnet_udp::packet_exception();
throw packet_exception();
return m_turn;
}
dvlnet_udp::cookie_t dvlnet_udp::packet::cookie()
cookie_t packet::cookie()
{
if (!have_decrypted)
ABORT();
if (m_type != PT_JOIN_REQUEST && m_type != PT_JOIN_ACCEPT)
throw dvlnet_udp::packet_exception();
throw packet_exception();
return m_cookie;
}
dvlnet_udp::plr_t dvlnet_udp::packet::newplr()
plr_t packet::newplr()
{
if (!have_decrypted)
ABORT();
if (m_type != PT_JOIN_ACCEPT)
throw dvlnet_udp::packet_exception();
throw packet_exception();
return m_newplr;
}
dvlnet_udp::plr_t dvlnet_udp::packet::oldplr()
plr_t packet::oldplr()
{
if (!have_decrypted)
ABORT();
if (m_type != PT_LEAVE_GAME)
throw dvlnet_udp::packet_exception();
throw packet_exception();
return m_oldplr;
}
const dvlnet_udp::buffer_t &dvlnet_udp::packet::info()
const buffer_t &packet::info()
{
if (!have_decrypted)
ABORT();
if (m_type != PT_JOIN_ACCEPT)
throw dvlnet_udp::packet_exception();
throw packet_exception();
return m_info;
}
void dvlnet_udp::packet_in::create(dvlnet_udp::buffer_t buf)
void packet_in::create(buffer_t buf)
{
if (have_encrypted || have_decrypted)
ABORT();
@ -92,7 +94,7 @@ void dvlnet_udp::packet_in::create(dvlnet_udp::buffer_t buf)
have_encrypted = true;
}
void dvlnet_udp::packet_in::decrypt()
void packet_in::decrypt()
{
if (!have_encrypted)
ABORT();
@ -101,7 +103,7 @@ void dvlnet_udp::packet_in::decrypt()
if (!disable_encryption) {
if (encrypted_buffer.size() < crypto_secretbox_NONCEBYTES +
crypto_secretbox_MACBYTES + sizeof(packet_type) + 2 * sizeof(plr_t))
throw dvlnet_udp::packet_exception();
throw packet_exception();
auto pktlen = encrypted_buffer.size() - crypto_secretbox_NONCEBYTES - crypto_secretbox_MACBYTES;
decrypted_buffer.resize(pktlen);
if (crypto_secretbox_open_easy(decrypted_buffer.data(),
@ -109,10 +111,10 @@ void dvlnet_udp::packet_in::decrypt()
encrypted_buffer.size() - crypto_secretbox_NONCEBYTES,
encrypted_buffer.data(),
key.data()))
throw dvlnet_udp::packet_exception();
throw packet_exception();
} else {
if (encrypted_buffer.size() < sizeof(packet_type) + 2 * sizeof(plr_t))
throw dvlnet_udp::packet_exception();
throw packet_exception();
decrypted_buffer = encrypted_buffer;
}
@ -121,10 +123,10 @@ void dvlnet_udp::packet_in::decrypt()
have_decrypted = true;
}
void dvlnet_udp::packet_out::create(dvlnet_udp::packet_type t,
dvlnet_udp::plr_t s,
dvlnet_udp::plr_t d,
dvlnet_udp::buffer_t m)
void packet_out::create(packet_type t,
plr_t s,
plr_t d,
buffer_t m)
{
if (have_encrypted || have_decrypted)
ABORT();
@ -137,10 +139,10 @@ void dvlnet_udp::packet_out::create(dvlnet_udp::packet_type t,
m_message = std::move(m);
}
void dvlnet_udp::packet_out::create(dvlnet_udp::packet_type t,
dvlnet_udp::plr_t s,
dvlnet_udp::plr_t d,
dvlnet_udp::turn_t u)
void packet_out::create(packet_type t,
plr_t s,
plr_t d,
turn_t u)
{
if (have_encrypted || have_decrypted)
ABORT();
@ -153,10 +155,10 @@ void dvlnet_udp::packet_out::create(dvlnet_udp::packet_type t,
m_turn = u;
}
void dvlnet_udp::packet_out::create(dvlnet_udp::packet_type t,
dvlnet_udp::plr_t s,
dvlnet_udp::plr_t d,
dvlnet_udp::cookie_t c)
void packet_out::create(packet_type t,
plr_t s,
plr_t d,
cookie_t c)
{
if (have_encrypted || have_decrypted)
ABORT();
@ -169,12 +171,12 @@ void dvlnet_udp::packet_out::create(dvlnet_udp::packet_type t,
m_cookie = c;
}
void dvlnet_udp::packet_out::create(dvlnet_udp::packet_type t,
dvlnet_udp::plr_t s,
dvlnet_udp::plr_t d,
dvlnet_udp::cookie_t c,
dvlnet_udp::plr_t n,
dvlnet_udp::buffer_t i)
void packet_out::create(packet_type t,
plr_t s,
plr_t d,
cookie_t c,
plr_t n,
buffer_t i)
{
if (have_encrypted || have_decrypted)
ABORT();
@ -189,10 +191,10 @@ void dvlnet_udp::packet_out::create(dvlnet_udp::packet_type t,
m_info = i;
}
void dvlnet_udp::packet_out::create(dvlnet_udp::packet_type t,
dvlnet_udp::plr_t s,
dvlnet_udp::plr_t d,
dvlnet_udp::plr_t o)
void packet_out::create(packet_type t,
plr_t s,
plr_t d,
plr_t o)
{
if (have_encrypted || have_decrypted)
ABORT();
@ -205,7 +207,7 @@ void dvlnet_udp::packet_out::create(dvlnet_udp::packet_type t,
m_oldplr = o;
}
void dvlnet_udp::packet_out::encrypt()
void packet_out::encrypt()
{
if (!have_decrypted)
ABORT();

145
Stub/dvlnet/packet.h

@ -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);
}
}

191
Stub/dvlnet/udp_p2p.cpp

@ -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);
}

38
Stub/dvlnet/udp_p2p.h

@ -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);
};
}

292
Stub/dvlnet_udp.cpp

@ -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
}

24
Stub/storm_net.cpp

@ -1,8 +1,10 @@
#include "../types.h"
static std::unique_ptr<dvlnet::dvlnet> dvlnet_inst;
BOOL STORMAPI SNetReceiveMessage(int *senderplayerid, char **data, int *databytes)
{
if (!dvlnet::inst->SNetReceiveMessage(senderplayerid, data, databytes)) {
if (!dvlnet_inst->SNetReceiveMessage(senderplayerid, data, databytes)) {
SErrSetLastError(STORM_ERROR_NO_MESSAGES_WAITING);
return FALSE;
}
@ -11,7 +13,7 @@ BOOL STORMAPI SNetReceiveMessage(int *senderplayerid, char **data, int *databyte
BOOL STORMAPI SNetSendMessage(int playerID, void *data, unsigned int databytes)
{
return dvlnet::inst->SNetSendMessage(playerID, data, databytes);
return dvlnet_inst->SNetSendMessage(playerID, data, databytes);
}
BOOL STORMAPI SNetReceiveTurns(int a1, int arraysize, char **arraydata, unsigned int *arraydatabytes,
@ -21,27 +23,27 @@ BOOL STORMAPI SNetReceiveTurns(int a1, int arraysize, char **arraydata, unsigned
UNIMPLEMENTED();
if (arraysize != MAX_PLRS)
UNIMPLEMENTED();
return dvlnet::inst->SNetReceiveTurns(arraydata, arraydatabytes, arrayplayerstatus);
return dvlnet_inst->SNetReceiveTurns(arraydata, arraydatabytes, arrayplayerstatus);
}
BOOL STORMAPI SNetSendTurn(char *data, unsigned int databytes)
{
return dvlnet::inst->SNetSendTurn(data, databytes);
return dvlnet_inst->SNetSendTurn(data, databytes);
}
int __stdcall SNetGetProviderCaps(struct _SNETCAPS *caps)
{
return dvlnet::inst->SNetGetProviderCaps(caps);
return dvlnet_inst->SNetGetProviderCaps(caps);
}
void *__stdcall SNetUnregisterEventHandler(int evtype, void(__stdcall *func)(struct _SNETEVENT *))
{
return dvlnet::inst->SNetUnregisterEventHandler(evtype, func);
return dvlnet_inst->SNetUnregisterEventHandler(evtype, func);
}
void *__stdcall SNetRegisterEventHandler(int evtype, void(__stdcall *func)(struct _SNETEVENT *))
{
return dvlnet::inst->SNetRegisterEventHandler(evtype, func);
return dvlnet_inst->SNetRegisterEventHandler(evtype, func);
}
BOOL STORMAPI SNetDestroy()
@ -85,9 +87,9 @@ int __stdcall SNetInitializeProvider(unsigned long provider, struct _SNETPROGRAM
if (provider == 'UDPN') {
dvlnet::buffer_t game_init_info((char*)client_info->initdata,
(char*)client_info->initdata + client_info->initdatabytes);
dvlnet::inst = std::make_unique<dvlnet_udp>(std::move(game_init_info));
dvlnet_inst = std::make_unique<dvlnet::udp_p2p>(std::move(game_init_info));
} else if (provider == 'SCBL' || provider == 0) {
dvlnet::inst = std::make_unique<dvlnet_null>();
dvlnet_inst = std::make_unique<dvlnet::loopback>();
} else {
ABORT();
}
@ -105,8 +107,8 @@ BOOL STORMAPI SNetCreateGame(const char *pszGameName, const char *pszGamePasswor
// hack: cannot create game until UI is ready
// first instance will create, second will join
int ret;
if (ret = dvlnet::inst->create("0.0.0.0", "mypass") == -1)
ret = dvlnet::inst->join("127.0.0.1", "mypass");
if (ret = dvlnet_inst->create("0.0.0.0", "mypass") == -1)
ret = dvlnet_inst->join("127.0.0.1", "mypass");
*playerID = ret;
return TRUE;
}

2
types.h

@ -56,7 +56,7 @@
#include "3rdParty/PKWare/pkware.h"
#ifdef MINIWIN
#include "dvlnet.h"
#include "dvlnet/dvlnet.h"
#endif
// If defined, use copy protection [Default -> Defined]

Loading…
Cancel
Save