Pull in and hook up libevent
This commit is contained in:
parent
332e6645ff
commit
d2af20daca
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "subprojects/libevent"]
|
||||||
|
path = subprojects/libevent
|
||||||
|
url = https://github.com/libevent/libevent
|
24
meson.build
24
meson.build
|
@ -1,4 +1,4 @@
|
||||||
project('FeatherMC', ['c', 'cpp'], version : '0.0', meson_version : '>= 0.49', default_options : [
|
project('FeatherMC', ['c', 'cpp'], version : '0.0', meson_version : '>= 0.55', default_options : [
|
||||||
'warning_level=2',
|
'warning_level=2',
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -14,8 +14,28 @@ endif
|
||||||
|
|
||||||
threads_dep = dependency('threads')
|
threads_dep = dependency('threads')
|
||||||
|
|
||||||
|
cmake = import('cmake')
|
||||||
|
|
||||||
|
libevent_vars = cmake.subproject_options()
|
||||||
|
libevent_vars.add_cmake_defines({
|
||||||
|
'EVENT__DISABLE_OPENSSL' : true,
|
||||||
|
'EVENT__DISABLE_MBEDTLS' : true,
|
||||||
|
'EVENT__DISABLE_BENCHMARK' : true,
|
||||||
|
'EVENT__DISABLE_TESTS' : true,
|
||||||
|
'EVENT__DISABLE_REGRESS' : true,
|
||||||
|
'EVENT__DISABLE_SAMPLES' : true,
|
||||||
|
'EVENT__LIBRARY_TYPE' : 'STATIC',
|
||||||
|
})
|
||||||
|
|
||||||
|
libevent_proj = cmake.subproject('libevent', options : libevent_vars)
|
||||||
|
libevent_dep = libevent_proj.dependency('event_static')
|
||||||
|
|
||||||
|
feather_deps = [ threads_dep, libevent_dep ]
|
||||||
if feather_platform == 'windows'
|
if feather_platform == 'windows'
|
||||||
ws2_32_dep = feather_compiler.find_library('ws2_32')
|
ws2_32_dep = feather_compiler.find_library('ws2_32')
|
||||||
|
iphlpapi_dep = feather_compiler.find_library('iphlpapi')
|
||||||
|
|
||||||
|
feather_deps += [ ws2_32_dep, iphlpapi_dep ]
|
||||||
endif
|
endif
|
||||||
|
|
||||||
subdir('src')
|
subdir('src')
|
||||||
|
|
|
@ -3,8 +3,12 @@
|
||||||
namespace Feather
|
namespace Feather
|
||||||
{
|
{
|
||||||
DedicatedServer::DedicatedServer(uint16_t port)
|
DedicatedServer::DedicatedServer(uint16_t port)
|
||||||
: m_socket(port)
|
: m_listener(port)
|
||||||
{
|
{
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DedicatedServer::~DedicatedServer()
|
DedicatedServer::~DedicatedServer()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "util/SocketUtil.h"
|
#include "network/TCPListener.h"
|
||||||
|
|
||||||
namespace Feather
|
namespace Feather
|
||||||
{
|
{
|
||||||
|
@ -10,6 +10,6 @@ namespace Feather
|
||||||
DedicatedServer(uint16_t port);
|
DedicatedServer(uint16_t port);
|
||||||
~DedicatedServer();
|
~DedicatedServer();
|
||||||
private:
|
private:
|
||||||
Sockets::TCPSocket m_socket;
|
Network::TCPListener m_listener;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "config/ServerProperties.h"
|
#include "config/ServerProperties.h"
|
||||||
|
#include "DedicatedServer.h"
|
||||||
|
|
||||||
using namespace Feather;
|
using namespace Feather;
|
||||||
|
|
||||||
|
@ -9,5 +10,7 @@ int main()
|
||||||
properties.Save();
|
properties.Save();
|
||||||
printf("Starting server on port %d\n", properties.serverPort.GetValue());
|
printf("Starting server on port %d\n", properties.serverPort.GetValue());
|
||||||
|
|
||||||
|
auto server = DedicatedServer(properties.serverPort.GetValue());
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,24 +2,15 @@ feather_src = [
|
||||||
'Main.cpp',
|
'Main.cpp',
|
||||||
'DedicatedServer.cpp',
|
'DedicatedServer.cpp',
|
||||||
|
|
||||||
|
'network/NetworkManager.cpp',
|
||||||
|
'network/TCPListener.cpp',
|
||||||
|
|
||||||
'util/StringUtil.cpp',
|
'util/StringUtil.cpp',
|
||||||
'util/SocketUtil.cpp',
|
|
||||||
|
|
||||||
'config/Properties.cpp',
|
'config/Properties.cpp',
|
||||||
'config/ServerProperties.cpp',
|
'config/ServerProperties.cpp',
|
||||||
]
|
]
|
||||||
|
|
||||||
if feather_platform == 'windows'
|
|
||||||
feather_deps = [
|
|
||||||
threads_dep,
|
|
||||||
ws2_32_dep,
|
|
||||||
]
|
|
||||||
else
|
|
||||||
feather_deps = [
|
|
||||||
threads_dep,
|
|
||||||
]
|
|
||||||
endif
|
|
||||||
|
|
||||||
executable('FeatherMC', feather_src,
|
executable('FeatherMC', feather_src,
|
||||||
dependencies : feather_deps,
|
dependencies : feather_deps,
|
||||||
include_directories : include_directories('.'),
|
include_directories : include_directories('.'),
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
#include "NetworkManager.h"
|
||||||
|
|
||||||
|
#include <event2/event.h>
|
||||||
|
#include <event2/thread.h>
|
||||||
|
|
||||||
|
namespace Feather::Network
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void LogCallback(int severity, const char* msg)
|
||||||
|
{
|
||||||
|
printf("libevent: %s\n", msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkManager::NetworkManager()
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSADATA wsa_data = {};
|
||||||
|
WSAStartup(MAKEWORD(2, 2), &wsa_data);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
event_set_log_callback(LogCallback);
|
||||||
|
|
||||||
|
#if defined(EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED)
|
||||||
|
evthread_use_windows_threads();
|
||||||
|
#elif defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED)
|
||||||
|
evthread_use_pthreads();
|
||||||
|
#else
|
||||||
|
# error No threading implemented for EVTHREAD
|
||||||
|
#endif
|
||||||
|
m_eventBase = event_base_new();
|
||||||
|
m_eventThread = std::thread([]() { NetworkManager::Instance().EventLoop(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkManager::~NetworkManager() {
|
||||||
|
event_base_loopbreak(m_eventBase);
|
||||||
|
m_eventThread.join();
|
||||||
|
|
||||||
|
event_base_free(m_eventBase);
|
||||||
|
libevent_global_shutdown();
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSACleanup();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static NetworkManager s_networkManager;
|
||||||
|
|
||||||
|
NetworkManager& NetworkManager::Instance()
|
||||||
|
{
|
||||||
|
return s_networkManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkManager::EventLoop()
|
||||||
|
{
|
||||||
|
event_base_loop(m_eventBase, EVLOOP_NO_EXIT_ON_EMPTY);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
struct event_base;
|
||||||
|
|
||||||
|
namespace Feather::Network
|
||||||
|
{
|
||||||
|
class NetworkManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NetworkManager();
|
||||||
|
~NetworkManager();
|
||||||
|
|
||||||
|
static NetworkManager& Instance();
|
||||||
|
|
||||||
|
void EventLoop();
|
||||||
|
|
||||||
|
inline event_base* GetEventBase() const { return m_eventBase; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
event_base* m_eventBase;
|
||||||
|
|
||||||
|
std::thread m_eventThread;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
#include "TCPListener.h"
|
||||||
|
#include "NetworkManager.h"
|
||||||
|
|
||||||
|
#include <event2/event.h>
|
||||||
|
#include <event2/listener.h>
|
||||||
|
|
||||||
|
namespace Feather::Network
|
||||||
|
{
|
||||||
|
class TCPSocket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TCPSocket()
|
||||||
|
{
|
||||||
|
m_socket = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
|
||||||
|
// Can't create IPv6 socket? Create an IPv4 one.
|
||||||
|
if (!(m_ipv6 = IsValid()))
|
||||||
|
m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
}
|
||||||
|
|
||||||
|
~TCPSocket()
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsIPV6() const { return m_ipv6; }
|
||||||
|
|
||||||
|
bool MarkReusable()
|
||||||
|
{
|
||||||
|
return evutil_make_listen_socket_reuseable(m_socket) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MarkDualBind()
|
||||||
|
{
|
||||||
|
constexpr uint32_t zero = 0;
|
||||||
|
return setsockopt(m_socket, IPPROTO_IPV6, IPV6_V6ONLY,
|
||||||
|
reinterpret_cast<const char*>(&zero),
|
||||||
|
sizeof(zero)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Bind(uint16_t port)
|
||||||
|
{
|
||||||
|
sockaddr_in6 name_ipv6 = {};
|
||||||
|
name_ipv6.sin6_family = AF_INET6;
|
||||||
|
name_ipv6.sin6_port = ntohs(port);
|
||||||
|
|
||||||
|
sockaddr_in name_ipv4 = {};
|
||||||
|
name_ipv4.sin_family = AF_INET;
|
||||||
|
name_ipv4.sin_port = ntohs(port);
|
||||||
|
|
||||||
|
return m_ipv6
|
||||||
|
? BindGeneric(name_ipv6)
|
||||||
|
: BindGeneric(name_ipv4);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MarkNonBlocking()
|
||||||
|
{
|
||||||
|
return evutil_make_socket_nonblocking(m_socket) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Close()
|
||||||
|
{
|
||||||
|
if (!IsValid())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return evutil_closesocket(m_socket) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
evconnlistener* Listen(evconnlistener_cb callback, T* self)
|
||||||
|
{
|
||||||
|
if (listen(m_socket, SOMAXCONN) != 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return evconnlistener_new(
|
||||||
|
NetworkManager::Instance().GetEventBase(),
|
||||||
|
callback, reinterpret_cast<void*>(self),
|
||||||
|
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
|
||||||
|
0,
|
||||||
|
m_socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValid() const { return m_socket != EVUTIL_INVALID_SOCKET; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool BindGeneric(const T& name)
|
||||||
|
{
|
||||||
|
return bind(m_socket, reinterpret_cast<const sockaddr*>(&name), sizeof(name)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
evutil_socket_t m_socket;
|
||||||
|
bool m_ipv6;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void TCPListenerCallback(evconnlistener* evListener, evutil_socket_t socket, sockaddr* addr, int len, void* self)
|
||||||
|
{
|
||||||
|
TCPListener* listener = static_cast<TCPListener*>(self);
|
||||||
|
|
||||||
|
listener->Accept();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TCPListener::TCPListener(uint16_t port)
|
||||||
|
{
|
||||||
|
// Setup the listen socket.
|
||||||
|
auto socket = std::make_unique<TCPSocket>();
|
||||||
|
|
||||||
|
if (!socket->IsValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!socket->MarkReusable())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (socket->IsIPV6() && !socket->MarkDualBind())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!socket->Bind(port))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!socket->MarkNonBlocking())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!socket->Listen(TCPListenerCallback, this))
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_socket = std::move(socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
TCPListener::~TCPListener()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TCPListener::Accept()
|
||||||
|
{
|
||||||
|
printf("Frog");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Feather::Network
|
||||||
|
{
|
||||||
|
class TCPSocket;
|
||||||
|
|
||||||
|
class TCPListener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TCPListener(uint16_t port);
|
||||||
|
~TCPListener();
|
||||||
|
|
||||||
|
void Accept();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<TCPSocket> m_socket;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -1,172 +0,0 @@
|
||||||
#include "SocketUtil.h"
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
# include <winsock2.h>
|
|
||||||
# include <Ws2tcpip.h>
|
|
||||||
#else
|
|
||||||
# include <sys/socket.h>
|
|
||||||
# include <arpa/inet.h>
|
|
||||||
# include <netdb.h>
|
|
||||||
# include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Feather::Sockets
|
|
||||||
{
|
|
||||||
class TCPSocketHandle
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TCPSocketHandle()
|
|
||||||
{
|
|
||||||
m_socket = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
|
|
||||||
|
|
||||||
// Can't create IPv6 socket? Create an IPv4 one.
|
|
||||||
if (!(m_ipv6 = IsValid()))
|
|
||||||
m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
||||||
}
|
|
||||||
|
|
||||||
~TCPSocketHandle()
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsIPV6() const { return m_ipv6; }
|
|
||||||
|
|
||||||
bool MarkReusable()
|
|
||||||
{
|
|
||||||
constexpr uint32_t one = 1;
|
|
||||||
return setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR,
|
|
||||||
reinterpret_cast<const char*>(&one),
|
|
||||||
socklen_t(sizeof(one))) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MarkDualBind()
|
|
||||||
{
|
|
||||||
constexpr uint32_t zero = 0;
|
|
||||||
return setsockopt(m_socket, IPPROTO_IPV6, IPV6_V6ONLY,
|
|
||||||
reinterpret_cast<const char*>(&zero),
|
|
||||||
sizeof(zero)) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Bind(uint16_t port)
|
|
||||||
{
|
|
||||||
sockaddr_in6 name_ipv6 = {};
|
|
||||||
name_ipv6.sin6_family = AF_INET6;
|
|
||||||
name_ipv6.sin6_port = ntohs(port);
|
|
||||||
|
|
||||||
sockaddr_in name_ipv4 = {};
|
|
||||||
name_ipv4.sin_family = AF_INET;
|
|
||||||
name_ipv4.sin_port = ntohs(port);
|
|
||||||
|
|
||||||
return m_ipv6
|
|
||||||
? BindGeneric(name_ipv6)
|
|
||||||
: BindGeneric(name_ipv4);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Listen()
|
|
||||||
{
|
|
||||||
return listen(m_socket, SOMAXCONN) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
bool IsValid() const { return m_socket != INVALID_SOCKET; }
|
|
||||||
|
|
||||||
bool MarkNonBlocking()
|
|
||||||
{
|
|
||||||
unsigned long one = 1;
|
|
||||||
return ioctlsocket(m_socket, FIONBIO, &one) != SOCKET_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Close()
|
|
||||||
{
|
|
||||||
return shutdown(m_socket, SD_BOTH) == 0 && closesocket(m_socket) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool InitSocketSystem()
|
|
||||||
{
|
|
||||||
WSADATA wsa_data;
|
|
||||||
return WSAStartup(MAKEWORD(1, 1), &wsa_data) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool CloseSocketSystem()
|
|
||||||
{
|
|
||||||
return WSACleanup() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
using SocketType = SOCKET;
|
|
||||||
#else
|
|
||||||
bool IsValid() const { return m_socket >= 0; }
|
|
||||||
|
|
||||||
bool MarkNonBlocking()
|
|
||||||
{
|
|
||||||
return fcntl(fd, F_SETFL, O_NONBLOCK) != -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Close()
|
|
||||||
{
|
|
||||||
return shutdown(m_socket, SHUT_RDWR) == 0 && close(m_socket) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool InitSocketSystem()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool CloseSocketSystem()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
using SocketType = int;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool BindGeneric(const T& name)
|
|
||||||
{
|
|
||||||
return bind(m_socket, reinterpret_cast<const sockaddr*>(&name), sizeof(name)) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SocketType m_socket;
|
|
||||||
bool m_ipv6;
|
|
||||||
};
|
|
||||||
|
|
||||||
TCPSocket::TCPSocket(uint16_t port)
|
|
||||||
{
|
|
||||||
auto socket = std::make_unique<TCPSocketHandle>();
|
|
||||||
|
|
||||||
if (!socket->IsValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!socket->MarkReusable())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (socket->IsIPV6() && !socket->MarkDualBind())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!socket->Bind(port))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (socket->Listen())
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_socket = std::move(socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
TCPSocket::~TCPSocket()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
class SocketInitializer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SocketInitializer() { TCPSocketHandle::InitSocketSystem(); }
|
|
||||||
~SocketInitializer() { TCPSocketHandle::CloseSocketSystem(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
static SocketInitializer s_socketInitializer;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace Feather::Sockets
|
|
||||||
{
|
|
||||||
class TCPSocketHandle;
|
|
||||||
|
|
||||||
class TCPSocket
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TCPSocket(uint16_t port);
|
|
||||||
~TCPSocket();
|
|
||||||
|
|
||||||
inline bool IsValid() const { return m_socket != nullptr; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<TCPSocketHandle> m_socket;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 948ad3043590a36db6c8299bf0fb00ac3e1235ef
|
Loading…
Reference in New Issue