Browse Source

Add ZeroTier support

pull/1415/head
Xadhoom 5 years ago committed by Anders Jenbo
parent
commit
867dd96a73
  1. 49
      3rdParty/libzt/CMakeLists.txt
  2. 2
      CMake/mingwcc.cmake
  3. 2
      CMake/mingwcc64.cmake
  4. 10
      CMakeLists.txt
  5. 8
      SourceX/DiabloUI/selconn.cpp
  6. 9
      SourceX/dvlnet/abstract_net.cpp
  7. 17
      SourceX/dvlnet/abstract_net.h
  8. 9
      SourceX/dvlnet/base.cpp
  9. 6
      SourceX/dvlnet/base.h
  10. 14
      SourceX/dvlnet/base_protocol.cpp
  11. 293
      SourceX/dvlnet/base_protocol.h
  12. 17
      SourceX/dvlnet/cdwrap.h
  13. 11
      SourceX/dvlnet/frame_queue.h
  14. 11
      SourceX/dvlnet/loopback.cpp
  15. 3
      SourceX/dvlnet/loopback.h
  16. 6
      SourceX/dvlnet/packet.cpp
  17. 46
      SourceX/dvlnet/packet.h
  18. 304
      SourceX/dvlnet/protocol_zt.cpp
  19. 99
      SourceX/dvlnet/protocol_zt.h
  20. 5
      SourceX/dvlnet/tcp_client.cpp
  21. 3
      SourceX/dvlnet/tcp_client.h
  22. 2
      SourceX/dvlnet/tcp_server.h
  23. 171
      SourceX/dvlnet/udp_p2p.cpp
  24. 45
      SourceX/dvlnet/udp_p2p.h
  25. 40
      SourceX/dvlnet/zerotier_lwip.cpp
  26. 8
      SourceX/dvlnet/zerotier_lwip.h
  27. 71
      SourceX/dvlnet/zerotier_native.cpp
  28. 17
      SourceX/dvlnet/zerotier_native.h
  29. 5
      SourceX/storm/storm_dvlnet.h
  30. 88
      SourceX/storm/storm_net.cpp
  31. 2
      enums.h

49
3rdParty/libzt/CMakeLists.txt vendored

@ -0,0 +1,49 @@
include(FetchContent_MakeAvailableExcludeFromAll)
include(FetchContent)
FetchContent_Declare(libzt
GIT_REPOSITORY https://github.com/diasurgical/libzt.git
GIT_TAG 97a405529d64d9589e9fc785d6d1d2c597f92478)
FetchContent_MakeAvailableExcludeFromAll(libzt)
# External library, ignore all warnings
target_compile_options(zto_obj PRIVATE -fpermissive -w)
target_compile_options(libnatpmp_obj PRIVATE -w)
target_compile_options(libzt_obj PRIVATE -fpermissive -w)
target_compile_options(lwip_obj PRIVATE -w)
target_compile_options(miniupnpc_obj PRIVATE -w)
target_compile_options(zt-static PRIVATE -fpermissive -w)
target_include_directories(zt-static INTERFACE
"${libzt_SOURCE_DIR}/include"
"${libzt_SOURCE_DIR}/src"
"${libzt_SOURCE_DIR}/ext/lwip/src/include")
if(WIN32)
target_include_directories(zt-static INTERFACE
"${libzt_SOURCE_DIR}/ext/lwip-contrib/ports/win32/include")
else()
target_include_directories(zt-static INTERFACE
"${libzt_SOURCE_DIR}/ext/lwip-contrib/ports/unix/port/include")
endif()
if(MINGW_CROSS)
option(MINGW_STDTHREADS_GENERATE_STDHEADERS "" ON)
FetchContent_Declare(mingw-std-threads
GIT_REPOSITORY https://github.com/meganz/mingw-std-threads
GIT_TAG bee085c0a6cb32c59f0b55c7bba976fe6dcfca7f)
FetchContent_MakeAvailableExcludeFromAll(mingw-std-threads)
target_compile_definitions(libnatpmp_obj PRIVATE -D_WIN32_WINNT=0x601 -DSTATICLIB)
target_compile_definitions(zto_obj PRIVATE -D_WIN32_WINNT=0x601 -DZT_SALSA20_SSE=0)
target_compile_definitions(libzt_obj PRIVATE -D_WIN32_WINNT=0x601)
target_link_libraries(libzt_obj PRIVATE mingw_stdthreads)
target_link_libraries(zt-static PUBLIC iphlpapi shlwapi wsock32 ws2_32 wininet mingw_stdthreads)
target_include_directories(zt-static INTERFACE "${libzt_SOURCE_DIR}/include/mingw-fixes")
endif()
if(MSVC)
target_compile_definitions(libnatpmp_obj PRIVATE -DSTATICLIB)
target_link_libraries(zt-static PUBLIC iphlpapi shlwapi wsock32 ws2_32 wininet)
endif()

2
CMake/mingwcc.cmake

@ -1,5 +1,7 @@
cmake_minimum_required(VERSION 3.10)
SET(MINGW_CROSS TRUE)
SET(CROSS_PREFIX "/usr" CACHE STRING "crosstool-NG prefix")
SET(CMAKE_SYSTEM_NAME Windows)

2
CMake/mingwcc64.cmake

@ -1,5 +1,7 @@
cmake_minimum_required(VERSION 3.10)
SET(MINGW_CROSS TRUE)
SET(CROSS_PREFIX "/usr" CACHE STRING "crosstool-NG prefix")
SET(CMAKE_SYSTEM_NAME Windows)

10
CMakeLists.txt

@ -343,7 +343,10 @@ if(NOT NONET)
list(APPEND devilutionx_SRCS
SourceX/dvlnet/tcp_client.cpp
SourceX/dvlnet/tcp_server.cpp
SourceX/dvlnet/udp_p2p.cpp)
SourceX/dvlnet/protocol_zt.cpp
SourceX/dvlnet/base_protocol.cpp
SourceX/dvlnet/zerotier_native.cpp
SourceX/dvlnet/zerotier_lwip.cpp)
endif()
set(BIN_TARGET devilutionx)
@ -888,3 +891,8 @@ if(CPACK)
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
include(CPack)
endif()
if(NOT NONET)
add_subdirectory(3rdParty/libzt)
target_link_libraries(devilutionx PRIVATE zt-static)
endif()

8
SourceX/DiabloUI/selconn.cpp

@ -26,10 +26,8 @@ void selconn_Load()
LoadBackgroundArt("ui_art\\selconn.pcx");
#ifndef NONET
vecConnItems.push_back(new UiListItem("Zerotier", SELCONN_ZT));
vecConnItems.push_back(new UiListItem("Client-Server (TCP)", SELCONN_TCP));
#ifdef BUGGY
vecConnItems.push_back(new UiListItem("Peer-to-Peer (UDP)", SELCONN_UDP));
#endif
#endif
vecConnItems.push_back(new UiListItem("Loopback", SELCONN_LOOPBACK));
@ -103,8 +101,8 @@ void selconn_Focus(int value)
strncpy(selconn_Description, "All computers must be connected to a TCP-compatible network.", sizeof(selconn_Description) - 1);
players = MAX_PLRS;
break;
case SELCONN_UDP:
strncpy(selconn_Description, "All computers must be connected to a UDP-compatible network.", sizeof(selconn_Description) - 1);
case SELCONN_ZT:
strncpy(selconn_Description, "All computers must be connected to the internet.", sizeof(selconn_Description) - 1);
players = MAX_PLRS;
break;
case SELCONN_LOOPBACK:

9
SourceX/dvlnet/abstract_net.cpp

@ -4,7 +4,8 @@
#ifndef NONET
#include "dvlnet/cdwrap.h"
#include "dvlnet/tcp_client.h"
#include "dvlnet/udp_p2p.h"
#include "dvlnet/base_protocol.h"
#include "dvlnet/protocol_zt.h"
#endif
#include "dvlnet/loopback.h"
@ -19,10 +20,8 @@ std::unique_ptr<abstract_net> abstract_net::make_net(provider_t provider)
switch (provider) {
case SELCONN_TCP:
return std::unique_ptr<abstract_net>(new cdwrap<tcp_client>);
#ifdef BUGGY
case SELCONN_UDP:
return std::unique_ptr<abstract_net>(new cdwrap<udp_p2p>);
#endif
case SELCONN_ZT:
return std::unique_ptr<abstract_net>(new cdwrap<base_protocol<protocol_zt>>);
case SELCONN_LOOPBACK:
return std::unique_ptr<abstract_net>(new loopback);
default:

17
SourceX/dvlnet/abstract_net.h

@ -49,8 +49,23 @@ public:
virtual void setup_gameinfo(buffer_t info) = 0;
virtual ~abstract_net() = default;
virtual std::string make_default_gamename() = 0;
virtual void setup_password(std::string passwd)
{
}
virtual void send_info_request()
{
}
virtual std::vector<std::string> get_gamelist()
{
return std::vector<std::string>();
}
static std::unique_ptr<abstract_net> make_net(provider_t provider);
};
} // namespace net
} // namespace dvl
} // namespace devilution

9
SourceX/dvlnet/base.cpp

@ -24,6 +24,10 @@ void base::run_event_handler(_SNETEVENT &ev)
}
}
void base::disconnect_net(plr_t plr)
{
}
void base::handle_accept(packet &pkt)
{
if (plr_self != PLR_BROADCAST) {
@ -34,7 +38,11 @@ void base::handle_accept(packet &pkt)
connected_table[plr_self] = true;
}
if (game_init_info != pkt.info()) {
if(pkt.info().size() != sizeof(GameData)) {
ABORT();
}
// we joined and did not create
game_init_info = pkt.info();
_SNETEVENT ev;
ev.eventid = EVENT_TYPE_PLAYER_CREATE_GAME;
ev.playerid = plr_self;
@ -83,6 +91,7 @@ void base::recv_local(packet &pkt)
ev.databytes = sizeof(leaveinfo_t);
run_event_handler(ev);
connected_table[pkt.newplr()] = false;
disconnect_net(pkt.newplr());
clear_msg(pkt.newplr());
turn_queue[pkt.newplr()].clear();
}

6
SourceX/dvlnet/base.h

@ -35,9 +35,12 @@ public:
virtual void poll() = 0;
virtual void send(packet &pkt) = 0;
virtual void disconnect_net(plr_t plr);
void setup_gameinfo(buffer_t info);
virtual void setup_password(std::string pw);
virtual ~base() = default;
protected:
@ -70,7 +73,6 @@ protected:
std::unique_ptr<packet_factory> pktfty;
void setup_password(std::string pw);
void handle_accept(packet &pkt);
void recv_local(packet &pkt);
void run_event_handler(_SNETEVENT &ev);
@ -81,4 +83,4 @@ private:
};
} // namespace net
} // namespace dvl
} // namespace devilution

14
SourceX/dvlnet/base_protocol.cpp

@ -0,0 +1,14 @@
#include "dvlnet/base_protocol.h"
#include <algorithm>
#include <SDL.h>
#ifdef USE_SDL1
#include "sdl2_to_1_2_backports.h"
#endif
namespace devilution {
namespace net {
} // namespace net
} // namespace devilution

293
SourceX/dvlnet/base_protocol.h

@ -0,0 +1,293 @@
#pragma once
#include <string>
#include <set>
#include <map>
#include <memory>
#include "dvlnet/packet.h"
#include "dvlnet/base.h"
namespace devilution {
namespace net {
template<class P>
class base_protocol : public base {
public:
virtual int create(std::string addrstr, std::string passwd);
virtual int join(std::string addrstr, std::string passwd);
virtual void poll();
virtual void send(packet &pkt);
virtual void disconnect_net(plr_t plr);
virtual bool SNetLeaveGame(int type);
virtual std::string make_default_gamename();
virtual void send_info_request();
virtual std::vector<std::string> get_gamelist();
virtual ~base_protocol() = default;
private:
P proto;
typedef typename P::endpoint endpoint;
endpoint firstpeer;
std::string gamename;
std::map<std::string, endpoint> game_list;
std::array<endpoint, MAX_PLRS> peers;
plr_t get_master();
void recv();
void handle_join_request(packet &pkt, endpoint sender);
void recv_decrypted(packet &pkt, endpoint sender);
void recv_ingame(packet &pkt, endpoint sender);
bool wait_network();
bool wait_firstpeer();
void wait_join();
};
template<class P>
plr_t base_protocol<P>::get_master()
{
plr_t ret = plr_self;
for (plr_t i = 0; i < MAX_PLRS; ++i)
if(peers[i])
ret = std::min(ret, i);
return ret;
}
template<class P>
bool base_protocol<P>::wait_network()
{
// wait for ZeroTier for 5 seconds
for (auto i = 0; i < 500; ++i) {
if (proto.network_online())
break;
SDL_Delay(10);
}
return proto.network_online();
}
template<class P>
void base_protocol<P>::disconnect_net(plr_t plr)
{
proto.disconnect(peers[plr]);
peers[plr] = endpoint();
}
template<class P>
bool base_protocol<P>::wait_firstpeer()
{
// wait for peer for 5 seconds
for (auto i = 0; i < 500; ++i) {
if (game_list.count(gamename)) {
firstpeer = game_list[gamename];
break;
}
send_info_request();
recv();
SDL_Delay(10);
}
return (bool)firstpeer;
}
template<class P>
void base_protocol<P>::send_info_request()
{
auto pkt = pktfty->make_packet<PT_INFO_REQUEST>(PLR_BROADCAST,
PLR_MASTER);
proto.send_oob_mc(pkt->data());
}
template<class P>
void base_protocol<P>::wait_join()
{
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);
proto.send(firstpeer, pkt->data());
for (auto i = 0; i < 500; ++i) {
recv();
if (plr_self != PLR_BROADCAST)
break; // join successful
SDL_Delay(10);
}
}
template<class P>
int base_protocol<P>::create(std::string addrstr, std::string passwd)
{
setup_password(passwd);
gamename = addrstr;
if(wait_network()) {
plr_self = 0;
connected_table[plr_self] = true;
}
return (plr_self == PLR_BROADCAST ? MAX_PLRS : plr_self);
}
template<class P>
int base_protocol<P>::join(std::string addrstr, std::string passwd)
{
//addrstr = "fd80:56c2:e21c:0:199:931d:b14:c4d2";
setup_password(passwd);
gamename = addrstr;
if(wait_network())
if(wait_firstpeer())
wait_join();
return (plr_self == PLR_BROADCAST ? MAX_PLRS : plr_self);
}
template<class P>
void base_protocol<P>::poll()
{
recv();
}
template<class P>
void base_protocol<P>::send(packet &pkt)
{
if(pkt.dest() < MAX_PLRS) {
if(pkt.dest() == myplr)
return;
if(peers[pkt.dest()])
proto.send(peers[pkt.dest()], pkt.data());
} else if(pkt.dest() == PLR_BROADCAST) {
for (auto &peer : peers)
if(peer)
proto.send(peer, pkt.data());
} else if(pkt.dest() == PLR_MASTER) {
throw dvlnet_exception();
} else {
throw dvlnet_exception();
}
}
template<class P>
void base_protocol<P>::recv()
{
try {
buffer_t pkt_buf;
endpoint sender;
while (proto.recv(sender, pkt_buf)) { // read until kernel buffer is empty?
try {
auto pkt = pktfty->make_packet(pkt_buf);
recv_decrypted(*pkt, sender);
} catch (packet_exception &e) {
// drop packet
proto.disconnect(sender);
SDL_Log(e.what());
}
}
} catch (std::exception &e) {
SDL_Log(e.what());
return;
}
}
template<class P>
void base_protocol<P>::handle_join_request(packet &pkt, endpoint sender)
{
plr_t i;
for (i = 0; i < MAX_PLRS; ++i) {
if (i != plr_self && !peers[i]) {
peers[i] = sender;
break;
}
}
if(i >= MAX_PLRS) {
//already full
return;
}
for (plr_t j = 0; j < MAX_PLRS; ++j) {
if ((j != plr_self) && (j != i) && peers[j]) {
auto infopkt = pktfty->make_packet<PT_CONNECT>(PLR_MASTER, PLR_BROADCAST, j, peers[j].serialize());
proto.send(sender, infopkt->data());
break;
}
}
auto reply = pktfty->make_packet<PT_JOIN_ACCEPT>(plr_self, PLR_BROADCAST,
pkt.cookie(), i,
game_init_info);
proto.send(sender, reply->data());
}
template<class P>
void base_protocol<P>::recv_decrypted(packet &pkt, endpoint sender)
{
if (pkt.src() == PLR_BROADCAST && pkt.dest() == PLR_MASTER && pkt.type() == PT_INFO_REPLY) {
std::string pname;
pname.resize(pkt.info().size());
std::memcpy(&pname[0], pkt.info().data(), pkt.info().size());
game_list[pname] = sender;
return;
}
recv_ingame(pkt, sender);
}
template<class P>
void base_protocol<P>::recv_ingame(packet &pkt, endpoint sender)
{
if (pkt.src() == PLR_BROADCAST && pkt.dest() == PLR_MASTER) {
if(pkt.type() == PT_JOIN_REQUEST) {
handle_join_request(pkt, sender);
} else if(pkt.type() == PT_INFO_REQUEST) {
if((plr_self != PLR_BROADCAST) && (get_master() == plr_self)) {
buffer_t buf;
buf.resize(gamename.size());
std::memcpy(buf.data(), &gamename[0], gamename.size());
auto reply = pktfty->make_packet<PT_INFO_REPLY>(PLR_BROADCAST,
PLR_MASTER,
buf);
proto.send_oob(sender, reply->data());
}
}
return;
} else if (pkt.src() == PLR_MASTER && pkt.type() == PT_CONNECT) {
// addrinfo packets
connected_table[pkt.newplr()] = true;
peers[pkt.newplr()].unserialize(pkt.info());
return;
} else if (pkt.src() >= MAX_PLRS) {
// normal packets
ABORT();
}
connected_table[pkt.src()] = true;
peers[pkt.src()] = sender;
if (pkt.dest() != plr_self && pkt.dest() != PLR_BROADCAST)
return; //packet not for us, drop
recv_local(pkt);
}
template<class P>
std::vector<std::string> base_protocol<P>::get_gamelist()
{
recv();
std::vector<std::string> ret;
for (auto& s : game_list) {
ret.push_back(s.first);
}
return ret;
}
template<class P>
bool base_protocol<P>::SNetLeaveGame(int type)
{
auto ret = base::SNetLeaveGame(type);
recv();
return ret;
}
template<class P>
std::string base_protocol<P>::make_default_gamename()
{
return proto.make_default_gamename();
}
} // namespace net
} // namespace devilution

17
SourceX/dvlnet/cdwrap.h

@ -41,10 +41,18 @@ public:
virtual bool SNetGetOwnerTurnsWaiting(DWORD *turns);
virtual bool SNetGetTurnsInTransit(DWORD *turns);
virtual void setup_gameinfo(buffer_t info);
virtual std::string make_default_gamename();
cdwrap();
virtual ~cdwrap() = default;
};
template <class T>
cdwrap<T>::cdwrap()
{
reset();
}
template <class T>
void cdwrap<T>::reset()
{
@ -65,6 +73,7 @@ int cdwrap<T>::create(std::string addrstr, std::string passwd)
template <class T>
int cdwrap<T>::join(std::string addrstr, std::string passwd)
{
game_init_info = buffer_t();
reset();
return dvlnet_wrap->join(addrstr, passwd);
}
@ -151,5 +160,11 @@ bool cdwrap<T>::SNetGetTurnsInTransit(DWORD *turns)
return dvlnet_wrap->SNetGetTurnsInTransit(turns);
}
template <class T>
std::string cdwrap<T>::make_default_gamename()
{
return dvlnet_wrap->make_default_gamename();
}
} // namespace net
} // namespace dvl
} // namespace devilution

11
SourceX/dvlnet/frame_queue.h

@ -1,13 +1,16 @@
#pragma once
#include <deque>
#include "dvlnet/abstract_net.h"
#include <exception>
#include <vector>
#include <cstdint>
namespace devilution {
namespace net {
class frame_queue_exception : public dvlnet_exception {
typedef std::vector<unsigned char> buffer_t;
class frame_queue_exception : public std::exception {
public:
const char *what() const throw() override
{
@ -38,4 +41,4 @@ public:
};
} // namespace net
} // namespace dvl
} // namespace devilution

11
SourceX/dvlnet/loopback.cpp

@ -38,13 +38,15 @@ bool loopback::SNetSendMessage(int dest, void *data, unsigned int size)
bool loopback::SNetReceiveTurns(char **data, unsigned int *size, DWORD *status)
{
// todo: check that this is safe
for (auto i = 0; i < MAX_PLRS; ++i) {
size[i] = 0;
data[i] = nullptr;
}
return true;
}
bool loopback::SNetSendTurn(char *data, unsigned int size)
{
// todo: check that this is safe
return true;
}
@ -105,5 +107,10 @@ bool loopback::SNetGetTurnsInTransit(DWORD *turns)
return true;
}
std::string loopback::make_default_gamename()
{
return std::string("loopback");
}
} // namespace net
} // namespace devilution

3
SourceX/dvlnet/loopback.h

@ -38,7 +38,8 @@ public:
virtual bool SNetGetOwnerTurnsWaiting(DWORD *turns);
virtual bool SNetGetTurnsInTransit(DWORD *turns);
virtual void setup_gameinfo(buffer_t info);
virtual std::string make_default_gamename();
};
} // namespace net
} // namespace dvl
} // namespace devilution

6
SourceX/dvlnet/packet.cpp

@ -22,6 +22,10 @@ const char *packet_type_to_string(uint8_t packet_type)
return "PT_CONNECT";
case PT_DISCONNECT:
return "PT_DISCONNECT";
case PT_INFO_REQUEST:
return "PT_INFO_REQUEST";
case PT_INFO_REPLY:
return "PT_INFO_REPLY";
default:
return NULL;
}
@ -124,7 +128,7 @@ const buffer_t &packet::info()
{
if (!have_decrypted)
ABORT();
CheckPacketTypeOneOf({ PT_JOIN_REQUEST, PT_JOIN_ACCEPT }, m_type);
CheckPacketTypeOneOf({ PT_JOIN_REQUEST, PT_JOIN_ACCEPT, PT_CONNECT, PT_INFO_REPLY }, m_type);
return m_info;
}

46
SourceX/dvlnet/packet.h

@ -21,6 +21,8 @@ enum packet_type : uint8_t {
PT_JOIN_ACCEPT = 0x12,
PT_CONNECT = 0x13,
PT_DISCONNECT = 0x14,
PT_INFO_REQUEST = 0x21,
PT_INFO_REPLY = 0x22
};
// Returns NULL for an invalid packet type.
@ -152,11 +154,17 @@ void packet_proc<P>::process_data()
break;
case PT_CONNECT:
self.process_element(m_newplr);
self.process_element(m_info);
break;
case PT_DISCONNECT:
self.process_element(m_newplr);
self.process_element(m_leaveinfo);
break;
case PT_INFO_REPLY:
self.process_element(m_info);
break;
case PT_INFO_REQUEST:
break;
}
}
@ -176,6 +184,29 @@ void packet_in::process_element(T &x)
decrypted_buffer.begin() + sizeof(T));
}
template <>
inline void packet_out::create<PT_INFO_REQUEST>(plr_t s, plr_t d)
{
if (have_encrypted || have_decrypted)
ABORT();
have_decrypted = true;
m_type = PT_INFO_REQUEST;
m_src = s;
m_dest = d;
}
template <>
inline void packet_out::create<PT_INFO_REPLY>(plr_t s, plr_t d, buffer_t i)
{
if (have_encrypted || have_decrypted)
ABORT();
have_decrypted = true;
m_type = PT_INFO_REPLY;
m_src = s;
m_dest = d;
m_info = std::move(i);
}
template <>
inline void packet_out::create<PT_MESSAGE>(plr_t s, plr_t d, buffer_t m)
{
@ -229,6 +260,19 @@ inline void packet_out::create<PT_JOIN_ACCEPT>(plr_t s, plr_t d, cookie_t c,
m_info = i;
}
template <>
inline void packet_out::create<PT_CONNECT>(plr_t s, plr_t d, plr_t n, buffer_t i)
{
if (have_encrypted || have_decrypted)
ABORT();
have_decrypted = true;
m_type = PT_CONNECT;
m_src = s;
m_dest = d;
m_newplr = n;
m_info = i;
}
template <>
inline void packet_out::create<PT_CONNECT>(plr_t s, plr_t d, plr_t n)
{
@ -308,4 +352,4 @@ std::unique_ptr<packet> packet_factory::make_packet(Args... args)
}
} // namespace net
} // namespace dvl
} // namespace devilution

304
SourceX/dvlnet/protocol_zt.cpp

@ -0,0 +1,304 @@
#include "dvlnet/protocol_zt.h"
#include <random>
#include <SDL.h>
#ifdef USE_SDL1
#include "sdl2_to_1_2_backports.h"
#else
#include "sdl2_backports.h"
#endif
#include <lwip/sockets.h>
#include <lwip/tcpip.h>
#include <lwip/mld6.h>
#include <lwip/igmp.h>
#include "dvlnet/zerotier_native.h"
namespace devilution {
namespace net {
protocol_zt::protocol_zt()
{
zerotier_network_start();
}
void protocol_zt::set_nonblock(int fd)
{
static_assert(O_NONBLOCK == 1, "O_NONBLOCK == 1 not satisfied");
auto mode = lwip_fcntl(fd, F_GETFL, 0);
mode |= O_NONBLOCK;
lwip_fcntl(fd, F_SETFL, mode);
}
void protocol_zt::set_nodelay(int fd)
{
const int yes = 1;
lwip_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&yes, sizeof(yes));
}
void protocol_zt::set_reuseaddr(int fd)
{
const int yes = 1;
lwip_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(yes));
}
bool protocol_zt::network_online()
{
if(!zerotier_network_ready())
return false;
struct sockaddr_in6 in6{};
in6.sin6_port = htons(default_port);
in6.sin6_family = AF_INET6;
in6.sin6_addr = in6addr_any;
if(fd_udp == -1) {
fd_udp = lwip_socket(AF_INET6, SOCK_DGRAM, 0);
set_reuseaddr(fd_udp);
auto ret = lwip_bind(fd_udp, (struct sockaddr *)&in6, sizeof(in6));
if(ret < 0) {
SDL_Log("lwip, (udp) bind: %s\n", strerror(errno));
throw protocol_exception();
}
set_nonblock(fd_udp);
}
if(fd_tcp == -1) {
fd_tcp = lwip_socket(AF_INET6, SOCK_STREAM, 0);
set_reuseaddr(fd_tcp);
auto r1 = lwip_bind(fd_tcp, (struct sockaddr *)&in6, sizeof(in6));
if(r1 < 0) {
SDL_Log("lwip, (tcp) bind: %s\n", strerror(errno));
throw protocol_exception();
}
auto r2 = lwip_listen(fd_tcp, 10);
if(r2 < 0) {
SDL_Log("lwip, listen: %s\n", strerror(errno));
throw protocol_exception();
}
set_nonblock(fd_tcp);
set_nodelay(fd_tcp);
}
return true;
}
bool protocol_zt::send(const endpoint& peer, const buffer_t& data)
{
peer_list[peer].send_queue.push_back(frame_queue::make_frame(data));
return true;
}
bool protocol_zt::send_oob(const endpoint& peer, const buffer_t& data)
{
struct sockaddr_in6 in6{};
in6.sin6_port = htons(default_port);
in6.sin6_family = AF_INET6;
std::copy(peer.addr.begin(), peer.addr.end(), in6.sin6_addr.s6_addr);
lwip_sendto(fd_udp, data.data(), data.size(), 0, (const struct sockaddr *)&in6, sizeof(in6));
return true;
}
bool protocol_zt::send_oob_mc(const buffer_t& data)
{
endpoint mc;
std::copy(dvl_multicast_addr, dvl_multicast_addr+16, mc.addr.begin());
return send_oob(mc, data);
}
bool protocol_zt::send_queued_peer(const endpoint& peer)
{
if(peer_list[peer].fd == -1) {
peer_list[peer].fd = lwip_socket(AF_INET6, SOCK_STREAM, 0);
set_nodelay(peer_list[peer].fd);
set_nonblock(peer_list[peer].fd);
struct sockaddr_in6 in6{};
in6.sin6_port = htons(default_port);
in6.sin6_family = AF_INET6;
std::copy(peer.addr.begin(), peer.addr.end(), in6.sin6_addr.s6_addr);
lwip_connect(peer_list[peer].fd, (const struct sockaddr *)&in6, sizeof(in6));
}
while(!peer_list[peer].send_queue.empty()) {
auto len = peer_list[peer].send_queue.front().size();
auto r = lwip_send(peer_list[peer].fd, peer_list[peer].send_queue.front().data(), len, 0);
if(r < 0) {
// handle error
return false;
} else if (decltype(len)(r) < len) {
// partial send
auto it = peer_list[peer].send_queue.front().begin();
peer_list[peer].send_queue.front().erase(it, it+r);
return true;
} else if (decltype(len)(r) == len) {
peer_list[peer].send_queue.pop_front();
} else {
throw protocol_exception();
}
}
return true;
}
bool protocol_zt::recv_peer(const endpoint& peer)
{
unsigned char buf[PKTBUF_LEN];
while(true) {
auto len = lwip_recv(peer_list[peer].fd, buf, sizeof(buf), 0);
if(len >= 0) {
peer_list[peer].recv_queue.write(buffer_t(buf, buf+len));
} else {
if(errno == EAGAIN || errno == EWOULDBLOCK) {
return true;
} else {
return false;
}
}
}
}
bool protocol_zt::send_queued_all()
{
for(auto& peer : peer_list) {
if(!send_queued_peer(peer.first)) {
// disconnect this peer
}
}
return true;
}
bool protocol_zt::recv_from_peers()
{
for(auto& peer : peer_list) {
if(peer.second.fd != -1) {
if(!recv_peer(peer.first)) {
// error, disconnect?
}
}
}
return true;
}
bool protocol_zt::recv_from_udp()
{
unsigned char buf[PKTBUF_LEN];
struct sockaddr_in6 in6{};
socklen_t addrlen = sizeof(in6);
auto len = lwip_recvfrom(fd_udp, buf, sizeof(buf), 0, (struct sockaddr *)&in6, &addrlen);
if(len < 0)
return false;
buffer_t data(buf, buf+len);
endpoint ep;
std::copy(in6.sin6_addr.s6_addr, in6.sin6_addr.s6_addr+16, ep.addr.begin());
oob_recv_queue.push_back(std::make_pair(std::move(ep), std::move(data)));
return true;
}
bool protocol_zt::accept_all()
{
struct sockaddr_in6 in6{};
socklen_t addrlen = sizeof(in6);
while(true) {
auto newfd = lwip_accept(fd_tcp, (struct sockaddr *)&in6, &addrlen);
if(newfd < 0)
break;
endpoint ep;
std::copy(in6.sin6_addr.s6_addr, in6.sin6_addr.s6_addr+16, ep.addr.begin());
if(peer_list[ep].fd != -1) {
SDL_Log("protocol_zt::accept_all: WARNING: overwriting connection\n");
lwip_close(peer_list[ep].fd);
}
set_nonblock(newfd);
set_nodelay(newfd);
peer_list[ep].fd = newfd;
}
return true;
}
bool protocol_zt::recv(endpoint& peer, buffer_t& data)
{
accept_all();
send_queued_all();
recv_from_peers();
recv_from_udp();
if(!oob_recv_queue.empty()) {
peer = oob_recv_queue.front().first;
data = oob_recv_queue.front().second;
oob_recv_queue.pop_front();
return true;
}
for(auto& p : peer_list) {
if(p.second.recv_queue.packet_ready()) {
peer = p.first;
data = p.second.recv_queue.read_packet();
return true;
}
}
return false;
}
void protocol_zt::disconnect(const endpoint& peer)
{
if(peer_list.count(peer)) {
if(peer_list[peer].fd != -1) {
if(lwip_close(peer_list[peer].fd) < 0) {
SDL_Log("lwip_close: %s\n", strerror(errno));
}
}
peer_list.erase(peer);
}
}
void protocol_zt::close_all()
{
if(fd_tcp != -1) {
lwip_close(fd_tcp);
fd_tcp = -1;
}
if(fd_udp != -1) {
lwip_close(fd_udp);
fd_udp = -1;
}
for(auto& i : peer_list) {
if(i.second.fd != -1)
lwip_close(i.second.fd);
}
peer_list.clear();
}
protocol_zt::~protocol_zt()
{
close_all();
}
void protocol_zt::endpoint::from_string(const std::string& str)
{
ip_addr_t a;
if(!ipaddr_aton(str.c_str(), &a))
return;
if(!IP_IS_V6_VAL(a))
return;
const unsigned char* r = reinterpret_cast<const unsigned char*>(a.u_addr.ip6.addr);
std::copy(r, r+16, addr.begin());
}
uint64_t protocol_zt::current_ms()
{
return 0;
}
std::string protocol_zt::make_default_gamename()
{
std::string ret;
std::string allowed_chars = "abcdefghkopqrstuvwxyz";
std::random_device rd;
std::uniform_int_distribution<int> dist(0, allowed_chars.size() - 1);
for(int i = 0; i < 5; ++i) {
ret += allowed_chars.at(dist(rd));
}
return ret;
}
} // namespace net
} // namespace devilution

99
SourceX/dvlnet/protocol_zt.h

@ -0,0 +1,99 @@
#pragma once
#include <string>
#include <set>
#include <atomic>
#include <deque>
#include <map>
#include <exception>
#include <array>
#include <algorithm>
#include "dvlnet/frame_queue.h"
namespace devilution {
namespace net {
class protocol_exception : public std::exception {
public:
const char *what() const throw() override
{
return "Protocol error";
}
};
class protocol_zt {
public:
class endpoint {
public:
std::array<unsigned char, 16> addr = {};
explicit operator bool() const
{
auto empty = std::array<unsigned char, 16>{};
return (addr != empty);
}
bool operator<(const endpoint& rhs) const
{
return addr < rhs.addr;
}
buffer_t serialize() const
{
return buffer_t(addr.begin(), addr.end());
}
void unserialize(const buffer_t& buf)
{
if(buf.size() != 16)
throw protocol_exception();
std::copy(buf.begin(), buf.end(), addr.begin());
}
void from_string(const std::string& str);
};
protocol_zt();
~protocol_zt();
void disconnect(const endpoint& peer);
bool send(const endpoint& peer, const buffer_t& data);
bool send_oob(const endpoint& peer, const buffer_t& data);
bool send_oob_mc(const buffer_t& data);
bool recv(endpoint& peer, buffer_t& data);
bool network_online();
std::string make_default_gamename();
private:
static constexpr uint32_t PKTBUF_LEN = 65536;
static constexpr uint16_t default_port = 6112;
struct peer_state {
int fd = -1;
std::deque<buffer_t> send_queue;
frame_queue recv_queue;
};
std::deque<std::pair<endpoint, buffer_t>> oob_recv_queue;
std::map<endpoint, peer_state> peer_list;
int fd_tcp = -1;
int fd_udp = -1;
uint64_t current_ms();
void close_all();
void set_nonblock(int fd);
void set_nodelay(int fd);
void set_reuseaddr(int fd);
bool send_queued_peer(const endpoint& peer);
bool recv_peer(const endpoint& peer);
bool send_queued_all();
bool recv_from_peers();
bool recv_from_udp();
bool accept_all();
};
} // namespace net
} // namespace devilution

5
SourceX/dvlnet/tcp_client.cpp

@ -127,6 +127,11 @@ bool tcp_client::SNetLeaveGame(int type)
return ret;
}
std::string tcp_client::make_default_gamename()
{
return std::string(sgOptions.Network.szBindAddress);
}
tcp_client::~tcp_client()
{
}

3
SourceX/dvlnet/tcp_client.h

@ -27,6 +27,7 @@ public:
virtual ~tcp_client();
virtual std::string make_default_gamename();
private:
frame_queue recv_queue;
buffer_t recv_buffer = buffer_t(frame_queue::max_frame_size);
@ -42,4 +43,4 @@ private:
};
} // namespace net
} // namespace dvl
} // namespace devilution

2
SourceX/dvlnet/tcp_server.h

@ -76,4 +76,4 @@ private:
};
} //namespace net
} //namespace dvl
} //namespace devilution

171
SourceX/dvlnet/udp_p2p.cpp

@ -1,171 +0,0 @@
#include "dvlnet/udp_p2p.h"
#include "options.h"
#include <SDL.h>
#ifdef USE_SDL1
#include "sdl2_to_1_2_backports.h"
#endif
namespace devilution {
namespace net {
const udp_p2p::endpoint udp_p2p::none;
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);
try {
sock.bind(endpoint(ipaddr, sgOptions.Network.nPort));
} catch (std::exception &e) {
SDL_SetError(e.what());
return -1;
}
plr_self = 0;
return plr_self;
}
int udp_p2p::join(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);
endpoint themaster(ipaddr, sgOptions.Network.nPort);
sock.connect(themaster);
master = themaster;
{ // hack: try to join for 5 seconds
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 < 5; ++i) {
recv();
if (plr_self != PLR_BROADCAST)
break; // join successful
SDL_Delay(1000);
}
}
return (plr_self == PLR_BROADCAST ? MAX_PLRS : plr_self);
}
void udp_p2p::poll()
{
recv();
}
void udp_p2p::send(packet &pkt)
{
send_internal(pkt, none);
}
void udp_p2p::recv()
{
try {
while (1) { // read until kernel buffer is empty?
try {
endpoint sender;
buffer_t pkt_buf(packet_factory::max_packet_size);
size_t pkt_len;
pkt_len = sock.receive_from(asio::buffer(pkt_buf), sender);
pkt_buf.resize(pkt_len);
auto pkt = pktfty->make_packet(pkt_buf);
recv_decrypted(*pkt, sender);
} catch (packet_exception &e) {
SDL_Log(e.what());
// drop packet
}
}
} catch (std::exception &e) {
SDL_Log(e.what());
return;
}
}
void udp_p2p::send_internal(packet &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 (dest < MAX_PLRS) {
if (connected_table[dest])
ret.insert(nexthop_table[dest]);
} else if (dest == PLR_BROADCAST) {
for (auto i = 0; i < MAX_PLRS; ++i)
if (i != plr_self && connected_table[i])
ret.insert(nexthop_table[i]);
ret.insert(connection_requests_pending.begin(),
connection_requests_pending.end());
} else if (dest == PLR_MASTER) {
if (master != none)
ret.insert(master);
}
ret.erase(sender);
return ret;
}
void udp_p2p::handle_join_request(packet &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;
}
}
auto reply = pktfty->make_packet<PT_JOIN_ACCEPT>(plr_self, PLR_BROADCAST,
pkt.cookie(), i,
game_init_info);
send(*reply);
}
void udp_p2p::recv_decrypted(packet &pkt, endpoint sender)
{
// 1. route
send_internal(pkt, sender);
// 2. handle local
if (pkt.src() == PLR_BROADCAST && pkt.dest() == PLR_MASTER) {
connection_requests_pending.insert(sender);
if (master == none) {
handle_join_request(pkt, sender);
}
}
// normal packets
if (pkt.src() >= MAX_PLRS)
return; //drop packet
if (connected_table[pkt.src()]) { //WRONG?!?
if (sender != nexthop_table[pkt.src()])
return; //rpfilter fail: drop packet
} else {
nexthop_table[pkt.src()] = sender; // new connection: accept
}
connected_table[pkt.src()] = true;
if (pkt.dest() != plr_self && pkt.dest() != PLR_BROADCAST)
return; //packet not for us, drop
recv_local(pkt);
}
} // namespace net
} // namespace devilution

45
SourceX/dvlnet/udp_p2p.h

@ -1,45 +0,0 @@
#pragma once
#include <string>
#include <set>
#include <asio/ts/buffer.hpp>
#include <asio/ts/internet.hpp>
#include <asio/ts/io_context.hpp>
#include <asio/ts/net.hpp>
#include "dvlnet/packet.h"
#include "dvlnet/base.h"
namespace devilution {
namespace net {
class udp_p2p : public base {
public:
virtual int create(std::string addrstr, std::string passwd);
virtual int join(std::string addrstr, std::string passwd);
virtual void poll();
virtual void send(packet &pkt);
private:
typedef asio::ip::udp::endpoint endpoint;
static const endpoint none;
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;
asio::ip::udp::socket sock = asio::ip::udp::socket(io_context);
void recv();
void handle_join_request(packet &pkt, endpoint sender);
void send_internal(packet &pkt, endpoint sender = none);
std::set<endpoint> dests_for_addr(plr_t dest, endpoint sender);
void recv_decrypted(packet &pkt, endpoint sender);
};
} // namespace net
} // namespace dvl

40
SourceX/dvlnet/zerotier_lwip.cpp

@ -0,0 +1,40 @@
#include "dvlnet/zerotier_lwip.h"
#include <lwip/sockets.h>
#include <lwip/tcpip.h>
#include <lwip/mld6.h>
#include <lwip/igmp.h>
#include <SDL.h>
#ifdef USE_SDL1
#include "sdl2_to_1_2_backports.h"
#else
#include "sdl2_backports.h"
#endif
#include "dvlnet/zerotier_native.h"
namespace devilution {
namespace net {
void print_ip6_addr(void* x)
{
char ipstr[INET6_ADDRSTRLEN];
struct sockaddr_in6 *in = (struct sockaddr_in6*)x;
lwip_inet_ntop(AF_INET6, &(in->sin6_addr), ipstr, INET6_ADDRSTRLEN);
SDL_Log("ZeroTier: ZTS_EVENT_ADDR_NEW_IP6, addr=%s\n", ipstr);
}
void zt_ip6setup()
{
ip6_addr_t mcaddr;
memcpy(mcaddr.addr, dvl_multicast_addr, 16);
mcaddr.zone = 0;
LOCK_TCPIP_CORE();
mld6_joingroup(IP6_ADDR_ANY6, &mcaddr);
UNLOCK_TCPIP_CORE();
}
}
}

8
SourceX/dvlnet/zerotier_lwip.h

@ -0,0 +1,8 @@
namespace devilution {
namespace net {
void print_ip6_addr(void* x);
void zt_ip6setup();
}
}

71
SourceX/dvlnet/zerotier_native.cpp

@ -0,0 +1,71 @@
#include "dvlnet/zerotier_native.h"
#include <atomic>
#include <SDL.h>
#ifdef USE_SDL1
#include "sdl2_to_1_2_backports.h"
#else
#include "sdl2_backports.h"
#endif
#include <cstdlib>
#include <ZeroTierSockets.h>
#include "paths.h"
#include "dvlnet/zerotier_lwip.h"
namespace devilution {
namespace net {
//static constexpr uint64_t zt_earth = 0x8056c2e21c000001;
static constexpr uint64_t zt_network = 0xaf78bf943649eb12;
static std::atomic_bool zt_network_ready(false);
static std::atomic_bool zt_node_online(false);
static std::atomic_bool zt_started(false);
static std::atomic_bool zt_joined(false);
static void callback(struct zts_callback_msg *msg)
{
//printf("callback %i\n", msg->eventCode);
if(msg->eventCode == ZTS_EVENT_NODE_ONLINE) {
SDL_Log("ZeroTier: ZTS_EVENT_NODE_ONLINE, nodeId=%llx\n", (unsigned long long)msg->node->address);
zt_node_online = true;
if(!zt_joined) {
zts_join(zt_network);
zt_joined = true;
}
} else if(msg->eventCode == ZTS_EVENT_NODE_OFFLINE) {
SDL_Log("ZeroTier: ZTS_EVENT_NODE_OFFLINE\n");
zt_node_online = false;
} else if(msg->eventCode == ZTS_EVENT_NETWORK_READY_IP6) {
SDL_Log("ZeroTier: ZTS_EVENT_NETWORK_READY_IP6, networkId=%llx\n", (unsigned long long)msg->network->nwid);
zt_ip6setup();
zt_network_ready = true;
} else if(msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) {
print_ip6_addr(&(msg->addr->addr));
}
}
bool zerotier_network_ready()
{
return zt_network_ready && zt_node_online;
}
void zerotier_network_stop()
{
zts_stop();
}
void zerotier_network_start()
{
if(zt_started)
return;
zts_start(GetPrefPath().c_str(), (void(*)(void*))callback, 0);
std::atexit(zerotier_network_stop);
}
}
}

17
SourceX/dvlnet/zerotier_native.h

@ -0,0 +1,17 @@
#pragma once
namespace devilution {
namespace net {
bool zerotier_network_ready();
void zerotier_network_start();
void zerotier_network_stop();
// NOTE: We have patched our libzt to have the corresponding multicast
// MAC hardcoded, since libzt is still missing the proper handling.
const unsigned char dvl_multicast_addr[16] =
{0xff, 0x0e, 0xa8, 0xa9, 0xb6, 0x11, 0x58, 0xce,
0x04, 0x12, 0xfd, 0x73, 0x37, 0x86, 0x6f, 0xb7};
}
}

5
SourceX/storm/storm_dvlnet.h

@ -0,0 +1,5 @@
#pragma once
void DvlNet_SendInfoRequest();
std::vector<std::string> DvlNet_GetGamelist();
void DvlNet_SetPassword(std::string pw);

88
SourceX/storm/storm_net.cpp

@ -1,9 +1,14 @@
#include <memory>
#ifndef NONET
#include <mutex>
#include <thread>
#endif
#include "all.h"
#include "options.h"
#include "stubs.h"
#include "dvlnet/abstract_net.h"
#include "storm/storm_dvlnet.h"
namespace devilution {
@ -11,8 +16,15 @@ static std::unique_ptr<net::abstract_net> dvlnet_inst;
static char gpszGameName[128] = {};
static char gpszGamePassword[128] = {};
#ifndef NONET
static std::mutex storm_net_mutex;
#endif
BOOL SNetReceiveMessage(int *senderplayerid, char **data, int *databytes)
{
#ifndef NONET
std::lock_guard<std::mutex> lg(storm_net_mutex);
#endif
if (!dvlnet_inst->SNetReceiveMessage(senderplayerid, data, databytes)) {
SErrSetLastError(STORM_ERROR_NO_MESSAGES_WAITING);
return false;
@ -22,12 +34,18 @@ BOOL SNetReceiveMessage(int *senderplayerid, char **data, int *databytes)
BOOL SNetSendMessage(int playerID, void *data, unsigned int databytes)
{
#ifndef NONET
std::lock_guard<std::mutex> lg(storm_net_mutex);
#endif
return dvlnet_inst->SNetSendMessage(playerID, data, databytes);
}
BOOL SNetReceiveTurns(int a1, int arraysize, char **arraydata, unsigned int *arraydatabytes,
DWORD *arrayplayerstatus)
{
#ifndef NONET
std::lock_guard<std::mutex> lg(storm_net_mutex);
#endif
if (a1 != 0)
UNIMPLEMENTED();
if (arraysize != MAX_PLRS)
@ -41,36 +59,57 @@ BOOL SNetReceiveTurns(int a1, int arraysize, char **arraydata, unsigned int *arr
BOOL SNetSendTurn(char *data, unsigned int databytes)
{
#ifndef NONET
std::lock_guard<std::mutex> lg(storm_net_mutex);
#endif
return dvlnet_inst->SNetSendTurn(data, databytes);
}
int SNetGetProviderCaps(struct _SNETCAPS *caps)
{
#ifndef NONET
std::lock_guard<std::mutex> lg(storm_net_mutex);
#endif
return dvlnet_inst->SNetGetProviderCaps(caps);
}
bool SNetUnregisterEventHandler(event_type evtype, SEVTHANDLER func)
{
#ifndef NONET
std::lock_guard<std::mutex> lg(storm_net_mutex);
#endif
return dvlnet_inst->SNetUnregisterEventHandler(evtype, func);
}
bool SNetRegisterEventHandler(event_type evtype, SEVTHANDLER func)
{
#ifndef NONET
std::lock_guard<std::mutex> lg(storm_net_mutex);
#endif
return dvlnet_inst->SNetRegisterEventHandler(evtype, func);
}
BOOL SNetDestroy()
{
#ifndef NONET
std::lock_guard<std::mutex> lg(storm_net_mutex);
#endif
return true;
}
BOOL SNetDropPlayer(int playerid, DWORD flags)
{
#ifndef NONET
std::lock_guard<std::mutex> lg(storm_net_mutex);
#endif
return dvlnet_inst->SNetDropPlayer(playerid, flags);
}
BOOL SNetGetGameInfo(int type, void *dst, unsigned int length)
{
#ifndef NONET
std::lock_guard<std::mutex> lg(storm_net_mutex);
#endif
switch (type) {
case GAMEINFO_NAME:
strncpy((char *)dst, gpszGameName, length);
@ -85,6 +124,9 @@ BOOL SNetGetGameInfo(int type, void *dst, unsigned int length)
BOOL SNetLeaveGame(int type)
{
#ifndef NONET
std::lock_guard<std::mutex> lg(storm_net_mutex);
#endif
if (dvlnet_inst == NULL)
return true;
return dvlnet_inst->SNetLeaveGame(type);
@ -96,6 +138,9 @@ BOOL SNetLeaveGame(int type)
*/
int SNetInitializeProvider(Uint32 provider, struct GameData *gameData)
{
#ifndef NONET
std::lock_guard<std::mutex> lg(storm_net_mutex);
#endif
dvlnet_inst = net::abstract_net::make_net(provider);
return mainmenu_select_hero_dialog(gameData);
}
@ -107,20 +152,32 @@ BOOL SNetCreateGame(const char *pszGameName, const char *pszGamePassword, const
DWORD dwGameType, char *GameTemplateData, int GameTemplateSize, int playerCount,
const char *creatorName, const char *a11, int *playerID)
{
#ifndef NONET
std::lock_guard<std::mutex> lg(storm_net_mutex);
#endif
if (GameTemplateSize != sizeof(GameData))
ABORT();
net::buffer_t game_init_info(GameTemplateData, GameTemplateData + GameTemplateSize);
dvlnet_inst->setup_gameinfo(std::move(game_init_info));
strncpy(gpszGameName, sgOptions.Network.szBindAddress, sizeof(gpszGameName) - 1);
std::string default_name;
if(!pszGameName) {
default_name = dvlnet_inst->make_default_gamename();
pszGameName = default_name.c_str();
}
strncpy(gpszGameName, pszGameName, sizeof(gpszGameName) - 1);
if (pszGamePassword)
strncpy(gpszGamePassword, pszGamePassword, sizeof(gpszGamePassword) - 1);
*playerID = dvlnet_inst->create(sgOptions.Network.szBindAddress, pszGamePassword);
*playerID = dvlnet_inst->create(pszGameName, pszGamePassword);
return *playerID != -1;
}
BOOL SNetJoinGame(int id, char *pszGameName, char *pszGamePassword, char *playerName, char *userStats, int *playerID)
{
#ifndef NONET
std::lock_guard<std::mutex> lg(storm_net_mutex);
#endif
if (pszGameName)
strncpy(gpszGameName, pszGameName, sizeof(gpszGameName) - 1);
if (pszGamePassword)
@ -134,11 +191,17 @@ BOOL SNetJoinGame(int id, char *pszGameName, char *pszGamePassword, char *player
*/
BOOL SNetGetOwnerTurnsWaiting(DWORD *turns)
{
#ifndef NONET
std::lock_guard<std::mutex> lg(storm_net_mutex);
#endif
return dvlnet_inst->SNetGetOwnerTurnsWaiting(turns);
}
BOOL SNetGetTurnsInTransit(DWORD *turns)
{
#ifndef NONET
std::lock_guard<std::mutex> lg(storm_net_mutex);
#endif
return dvlnet_inst->SNetGetTurnsInTransit(turns);
}
@ -147,6 +210,9 @@ BOOL SNetGetTurnsInTransit(DWORD *turns)
*/
BOOLEAN SNetSetBasePlayer(int)
{
#ifndef NONET
std::lock_guard<std::mutex> lg(storm_net_mutex);
#endif
return true;
}
@ -155,7 +221,25 @@ BOOLEAN SNetSetBasePlayer(int)
*/
BOOL SNetPerformUpgrade(DWORD *upgradestatus)
{
#ifndef NONET
std::lock_guard<std::mutex> lg(storm_net_mutex);
#endif
UNIMPLEMENTED();
}
void DvlNet_SendInfoRequest()
{
dvlnet_inst->send_info_request();
}
std::vector<std::string> DvlNet_GetGamelist()
{
return dvlnet_inst->get_gamelist();
}
void DvlNet_SetPassword(std::string pw)
{
dvlnet_inst->setup_password(pw);
}
} // namespace devilution

2
enums.h

@ -2338,8 +2338,8 @@ typedef enum dlrg_flag {
} dlrg_flag;
typedef enum conn_type {
SELCONN_ZT,
SELCONN_TCP,
SELCONN_UDP,
SELCONN_LOOPBACK,
} conn_type;

Loading…
Cancel
Save