From 797530e883fb12887dbc40ff7e0f39b16771b9bc Mon Sep 17 00:00:00 2001 From: NOTPIES Date: Mon, 20 Apr 2026 18:47:13 -0400 Subject: [PATCH] LCEMP v1.1.0 Dedicated Server --- Commands/MeCommand.cpp | 20 +- Core/DedicatedServer.cpp | 19 + Core/DedicatedServer_Main.cpp | 14 + Core/ServerProperties.cpp | 5 +- Core/ServerProperties.h | 1 + Core/ServerThreadPool.cpp | 375 ++++++++++++++ Core/ServerThreadPool.h | 73 +++ Linux/LinuxCompat.h | 18 + Linux/PosixNetLayer.cpp | 830 +++++++++++++++++++++++-------- Linux/PosixNetLayer.h | 52 +- Minecraft.Server.vcxproj | 15 +- Minecraft.Server.vcxproj.filters | 3 + Stubs/ServerStubs2.cpp | 1 + 13 files changed, 1204 insertions(+), 222 deletions(-) create mode 100644 Core/ServerThreadPool.cpp create mode 100644 Core/ServerThreadPool.h diff --git a/Commands/MeCommand.cpp b/Commands/MeCommand.cpp index 79b4e6c..f905898 100644 --- a/Commands/MeCommand.cpp +++ b/Commands/MeCommand.cpp @@ -2,6 +2,7 @@ #include "MeCommand.h" #include "../../Minecraft.Client/MinecraftServer.h" #include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/PlayerConnection.h" #include "../../Minecraft.Client/PlayerList.h" #include "../../Minecraft.World/ChatPacket.h" @@ -12,7 +13,22 @@ void MeCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftS src->warn(L"Usage: /me "); return; } - wstring msg = L"* Server " + args; + wstring senderName = L"Server"; + if (src != NULL) + { + senderName = src->getConsoleName(); + if (senderName.empty()) + { + senderName = L"Server"; + } + } + + wstring msg = L"* " + senderName + L" " + args; server->getPlayers()->broadcastAll(shared_ptr(new ChatPacket(msg))); - src->info(msg); + + // player sources already receive the broadcast chat line above + if (dynamic_cast(src) == NULL) + { + src->info(msg); + } } diff --git a/Core/DedicatedServer.cpp b/Core/DedicatedServer.cpp index be42d9b..83a7188 100644 --- a/Core/DedicatedServer.cpp +++ b/Core/DedicatedServer.cpp @@ -24,6 +24,7 @@ #include "../../Minecraft.World/ChunkSource.h" #include "ServerLogger.h" #include "ServerLists.h" +#include "ServerThreadPool.h" #include "../Commands/ServerCommands.h" #ifdef __linux__ @@ -55,6 +56,7 @@ bool DedicatedServer::init() ServerLog(L"Max players: %d\n", m_properties.maxPlayers); ServerLog(L"Difficulty: %d\n", m_properties.difficulty); ServerLog(L"Level size: %s\n", m_properties.levelSize.c_str()); + ServerLog(L"Chat: %s\n", m_properties.chatEnabled ? L"ENABLED" : L"DISABLED"); ServerLists_Init(m_properties.whiteList, &m_properties); @@ -66,6 +68,12 @@ bool DedicatedServer::init() extern int g_ServerMaxPlayers; g_ServerMaxPlayers = m_properties.maxPlayers; + if (g_ServerMaxPlayers < 1) + g_ServerMaxPlayers = 1; + IQNet::SetPlayerCapacity((DWORD)g_ServerMaxPlayers); + + extern int g_Win64MultiplayerPort; + g_Win64MultiplayerPort = m_properties.serverPort; extern char g_ServerBindAddress[256]; if (!m_properties.serverIp.empty()) @@ -134,6 +142,9 @@ bool DedicatedServer::init() WinsockNetLayer::Initialize(); + ServerThreadPool::Initialize(); + ServerLog(L"Thread pool initialized with %d worker threads\n", ServerThreadPool::GetThreadCount()); + app.SetGameHostOption(eGameHostOption_Difficulty, m_properties.difficulty); app.SetGameHostOption(eGameHostOption_GameType, m_properties.gamemode); app.SetGameHostOption(eGameHostOption_PvP, m_properties.pvp ? 1 : 0); @@ -142,6 +153,7 @@ bool DedicatedServer::init() app.SetGameHostOption(eGameHostOption_TNT, m_properties.tntExplodes ? 1 : 0); app.SetGameHostOption(eGameHostOption_Structures, m_properties.structures ? 1 : 0); app.SetGameHostOption(eGameHostOption_Gamertags, m_properties.showGamertags ? 1 : 0); + app.SetGameHostOption(eGameHostOption_LevelType, m_properties.levelSize == L"flat" ? 1 : 0); Minecraft *pMinecraft = new Minecraft(NULL, NULL, NULL, 1, 1, false); pMinecraft->options = new Options(pMinecraft, File(L".")); @@ -163,6 +175,7 @@ bool DedicatedServer::init() } app.InitGameSettings(); + app.SetGameHostOption(eGameHostOption_ChatDisabled, m_properties.chatEnabled ? 0 : 1); if (!m_properties.serverIp.empty()) ServerLog(L"Starting Minecraft server on %s:%d\n", m_properties.serverIp.c_str(), m_properties.serverPort); @@ -267,13 +280,19 @@ void DedicatedServer::shutdown() m_running = false; MinecraftServer::HaltServer(); + //ServerLog(L"Stopping server: shutting down thread pool\n"); + ServerThreadPool::Shutdown(); + //ServerLog(L"Stopping server: thread pool stopped\n"); + Tile::ReleaseThreadStorage(); IntCache::ReleaseThreadStorage(); AABB::ReleaseThreadStorage(); Vec3::ReleaseThreadStorage(); Level::destroyLightingCache(); + //ServerLog(L"Stopping server: terminating network manager\n"); g_NetworkManager.Terminate(); + //ServerLog(L"Stopping server: network manager terminated\n"); ServerLog(L"Shutdown complete\n"); } diff --git a/Core/DedicatedServer_Main.cpp b/Core/DedicatedServer_Main.cpp index fec36be..faa4578 100644 --- a/Core/DedicatedServer_Main.cpp +++ b/Core/DedicatedServer_Main.cpp @@ -105,6 +105,20 @@ int wmain(int argc, wchar_t *argv[]) SetConsoleOutputCP(CP_UTF8); SetConsoleCP(CP_UTF8); SetConsoleTitleW(L"LCEMP Server"); + + HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE); + if (hInput != INVALID_HANDLE_VALUE) + { + DWORD mode = 0; + if (GetConsoleMode(hInput, &mode)) + { + mode |= ENABLE_EXTENDED_FLAGS; + mode &= ~ENABLE_QUICK_EDIT_MODE; + mode &= ~ENABLE_MOUSE_INPUT; + SetConsoleMode(hInput, mode); + } + } + ServerLog_Init(); ServerLog(L"Starting Minecraft LCE server version %s (protocol %d)\n", VER_FILEVERSION_STR_W, SharedConstants::NETWORK_PROTOCOL_VERSION); diff --git a/Core/ServerProperties.cpp b/Core/ServerProperties.cpp index 22a8f76..f586bda 100644 --- a/Core/ServerProperties.cpp +++ b/Core/ServerProperties.cpp @@ -29,6 +29,7 @@ void ServerProperties::loadDefaults() motd = L"A Minecraft LCE Server"; whiteList = false; voiceChat = false; + chatEnabled = false; levelSize = L"large"; advertiseLan = true; serverIp = L""; @@ -79,7 +80,7 @@ bool ServerProperties::load(const wstring& path) difficulty = getInt(L"difficulty", 2); maxPlayers = getInt(L"max-players", 8); if (maxPlayers < 1) maxPlayers = 1; - if (maxPlayers > 32) maxPlayers = 32; + if (maxPlayers > 255) maxPlayers = 255; pvp = getBool(L"pvp", true); trustPlayers = getBool(L"trust-players", true); fireSpreads = getBool(L"fire-spreads", true); @@ -92,6 +93,7 @@ bool ServerProperties::load(const wstring& path) motd = getString(L"motd", L"A Minecraft LCE Server"); whiteList = getBool(L"white-list", false); voiceChat = getBool(L"voice-chat", false); + chatEnabled = getBool(L"enable-chat", false); levelSize = getString(L"level-size", L"large"); advertiseLan = getBool(L"advertise-lan", true); serverIp = getString(L"server-ip", L""); @@ -137,6 +139,7 @@ void ServerProperties::save(const wstring& path) fwprintf(f, L"motd=%ls\n", motd.c_str()); fwprintf(f, L"white-list=%ls\n", whiteList ? L"true" : L"false"); fwprintf(f, L"voice-chat=%ls\n", voiceChat ? L"true" : L"false"); + fwprintf(f, L"enable-chat=%ls\n", chatEnabled ? L"true" : L"false"); fwprintf(f, L"level-size=%ls\n", levelSize.c_str()); fwprintf(f, L"advertise-lan=%ls\n", advertiseLan ? L"true" : L"false"); if (!serverIp.empty()) diff --git a/Core/ServerProperties.h b/Core/ServerProperties.h index e77ff60..d2e9836 100644 --- a/Core/ServerProperties.h +++ b/Core/ServerProperties.h @@ -36,6 +36,7 @@ public: wstring motd; bool whiteList; bool voiceChat; + bool chatEnabled; wstring levelSize; bool advertiseLan; wstring serverIp; diff --git a/Core/ServerThreadPool.cpp b/Core/ServerThreadPool.cpp new file mode 100644 index 0000000..2b6475e --- /dev/null +++ b/Core/ServerThreadPool.cpp @@ -0,0 +1,375 @@ +#include "stdafx.h" + +#if defined(__linux__) || defined(_WIN32) + +#include "ServerThreadPool.h" +#include "../../Minecraft.World/AABB.h" +#include "../../Minecraft.World/Vec3.h" +#include "../../Minecraft.World/IntCache.h" +#include "../../Minecraft.World/compression.h" +#include "../../Minecraft.World/Tile.h" +#include "../../Minecraft.World/Level.h" + +#ifdef __linux__ +#include +#include +#include +#endif +#include + +#ifdef __linux__ +std::vector ServerThreadPool::s_workers; +pthread_mutex_t ServerThreadPool::s_queueLock = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t ServerThreadPool::s_queueCond = PTHREAD_COND_INITIALIZER; +pthread_cond_t ServerThreadPool::s_doneCond = PTHREAD_COND_INITIALIZER; +#else +std::vector ServerThreadPool::s_workers; +CRITICAL_SECTION ServerThreadPool::s_queueLock; +CONDITION_VARIABLE ServerThreadPool::s_queueCond; +CONDITION_VARIABLE ServerThreadPool::s_doneCond; +#endif + +std::queue ServerThreadPool::s_taskQueue; +std::atomic ServerThreadPool::s_pendingTasks(0); +volatile bool ServerThreadPool::s_running = false; +int ServerThreadPool::s_threadCount = 0; +bool ServerThreadPool::s_initialized = false; + +#ifdef __linux__ +static bool JoinWorkerWithTimeout(pthread_t worker, int timeoutMs) +{ +#if defined(__GLIBC__) + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += timeoutMs / 1000; + ts.tv_nsec += (timeoutMs % 1000) * 1000000; + if (ts.tv_nsec >= 1000000000) + { + ts.tv_sec++; + ts.tv_nsec -= 1000000000; + } + + int rc = pthread_timedjoin_np(worker, NULL, &ts); + if (rc == 0) + return true; + + pthread_detach(worker); + return false; +#else + int waitedMs = 0; + while (waitedMs < timeoutMs) + { + if (pthread_tryjoin_np(worker, NULL) == 0) + return true; + + usleep(10 * 1000); + waitedMs += 10; + } + + pthread_detach(worker); + return false; +#endif +} +#endif + +void ServerThreadPool::WorkerInit() +{ + AABB::CreateNewThreadStorage(); + Vec3::CreateNewThreadStorage(); + IntCache::CreateNewThreadStorage(); + Compression::CreateNewThreadStorage(); + Level::enableLightingCache(); + Tile::CreateNewThreadStorage(); +} + +#ifdef __linux__ +void *ServerThreadPool::WorkerProc(void *param) +#else +DWORD WINAPI ServerThreadPool::WorkerProc(LPVOID param) +#endif +{ + WorkerInit(); + + while (s_running) + { + Task task; + bool gotTask = false; + +#ifdef __linux__ + pthread_mutex_lock(&s_queueLock); + while (s_running && s_taskQueue.empty()) + pthread_cond_wait(&s_queueCond, &s_queueLock); + + if (!s_running && s_taskQueue.empty()) + { + pthread_mutex_unlock(&s_queueLock); + break; + } + + if (!s_taskQueue.empty()) + { + task = s_taskQueue.front(); + s_taskQueue.pop(); + gotTask = true; + } + pthread_mutex_unlock(&s_queueLock); +#else + EnterCriticalSection(&s_queueLock); + while (s_running && s_taskQueue.empty()) + SleepConditionVariableCS(&s_queueCond, &s_queueLock, INFINITE); + + if (!s_running && s_taskQueue.empty()) + { + LeaveCriticalSection(&s_queueLock); + break; + } + + if (!s_taskQueue.empty()) + { + task = s_taskQueue.front(); + s_taskQueue.pop(); + gotTask = true; + } + LeaveCriticalSection(&s_queueLock); +#endif + + if (gotTask && task.func != NULL) + { + try + { + task.func(task.param); + } + catch (...) + { + } + + if (s_pendingTasks.fetch_sub(1) == 1) + { +#ifdef __linux__ + pthread_mutex_lock(&s_queueLock); + pthread_cond_broadcast(&s_doneCond); + pthread_mutex_unlock(&s_queueLock); +#else + EnterCriticalSection(&s_queueLock); + WakeAllConditionVariable(&s_doneCond); + LeaveCriticalSection(&s_queueLock); +#endif + } + } + } + + AABB::ReleaseThreadStorage(); + Vec3::ReleaseThreadStorage(); + IntCache::ReleaseThreadStorage(); + Tile::ReleaseThreadStorage(); + Level::destroyLightingCache(); + +#ifdef __linux__ + return NULL; +#else + return 0; +#endif +} + +bool ServerThreadPool::Initialize(int threadCount) +{ + if (s_initialized) return true; + + if (threadCount <= 0) + { +#ifdef __linux__ + long cores = sysconf(_SC_NPROCESSORS_ONLN); +#else + SYSTEM_INFO sysInfo; + GetSystemInfo(&sysInfo); + long cores = (long)sysInfo.dwNumberOfProcessors; +#endif + if (cores < 2) cores = 2; + threadCount = (int)(cores - 1); + if (threadCount > 16) threadCount = 16; + } + +#ifdef __linux__ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&s_queueLock, &attr); + pthread_mutexattr_destroy(&attr); + pthread_cond_init(&s_queueCond, NULL); + pthread_cond_init(&s_doneCond, NULL); +#else + InitializeCriticalSection(&s_queueLock); + InitializeConditionVariable(&s_queueCond); + InitializeConditionVariable(&s_doneCond); +#endif + + s_running = true; + s_threadCount = threadCount; + + s_workers.resize(threadCount); + for (int i = 0; i < threadCount; i++) + { +#ifdef __linux__ + pthread_create(&s_workers[i], NULL, WorkerProc, NULL); +#else + s_workers[i] = CreateThread(NULL, 0, WorkerProc, NULL, 0, NULL); +#endif + } + + s_initialized = true; + return true; +} + +void ServerThreadPool::Shutdown() +{ + if (!s_initialized) return; + + s_running = false; + s_initialized = false; + +#ifdef __linux__ + pthread_mutex_lock(&s_queueLock); + while (!s_taskQueue.empty()) + s_taskQueue.pop(); + pthread_cond_broadcast(&s_queueCond); + pthread_mutex_unlock(&s_queueLock); + + bool allWorkersJoined = true; + for (int i = 0; i < (int)s_workers.size(); i++) + { + if (!JoinWorkerWithTimeout(s_workers[i], 5000)) + allWorkersJoined = false; + } +#else + EnterCriticalSection(&s_queueLock); + while (!s_taskQueue.empty()) + s_taskQueue.pop(); + WakeAllConditionVariable(&s_queueCond); + LeaveCriticalSection(&s_queueLock); + + for (int i = 0; i < (int)s_workers.size(); i++) + { + WaitForSingleObject(s_workers[i], 5000); + CloseHandle(s_workers[i]); + } +#endif + + s_workers.clear(); + s_pendingTasks.store(0); + +#ifdef __linux__ + if (allWorkersJoined) + { + pthread_mutex_destroy(&s_queueLock); + pthread_cond_destroy(&s_queueCond); + pthread_cond_destroy(&s_doneCond); + } +#else + DeleteCriticalSection(&s_queueLock); +#endif + + s_threadCount = 0; +} + +int ServerThreadPool::GetThreadCount() +{ + return s_threadCount; +} + +void ServerThreadPool::Submit(TaskFunc func, void *param) +{ + if (!s_initialized || func == NULL) return; + + Task task; + task.func = func; + task.param = param; + + s_pendingTasks.fetch_add(1); + +#ifdef __linux__ + pthread_mutex_lock(&s_queueLock); + s_taskQueue.push(task); + pthread_cond_signal(&s_queueCond); + pthread_mutex_unlock(&s_queueLock); +#else + EnterCriticalSection(&s_queueLock); + s_taskQueue.push(task); + WakeConditionVariable(&s_queueCond); + LeaveCriticalSection(&s_queueLock); +#endif +} + +void ServerThreadPool::SubmitBatch(Task *tasks, int count) +{ + if (!s_initialized || tasks == NULL || count <= 0) return; + + s_pendingTasks.fetch_add(count); + +#ifdef __linux__ + pthread_mutex_lock(&s_queueLock); + for (int i = 0; i < count; i++) + s_taskQueue.push(tasks[i]); + pthread_cond_broadcast(&s_queueCond); + pthread_mutex_unlock(&s_queueLock); +#else + EnterCriticalSection(&s_queueLock); + for (int i = 0; i < count; i++) + s_taskQueue.push(tasks[i]); + WakeAllConditionVariable(&s_queueCond); + LeaveCriticalSection(&s_queueLock); +#endif +} + +void ServerThreadPool::WaitAll() +{ + if (!s_initialized) return; + +#ifdef __linux__ + pthread_mutex_lock(&s_queueLock); + while (s_pendingTasks.load() > 0) + pthread_cond_wait(&s_doneCond, &s_queueLock); + pthread_mutex_unlock(&s_queueLock); +#else + EnterCriticalSection(&s_queueLock); + while (s_pendingTasks.load() > 0) + SleepConditionVariableCS(&s_doneCond, &s_queueLock, INFINITE); + LeaveCriticalSection(&s_queueLock); +#endif +} + +static void ParallelForTaskFunc(void *param) +{ + ParallelForData *data = (ParallelForData *)param; + if (data != NULL && data->func != NULL) + data->func(data->index, data->param); +} + +void ServerThreadPool::ParallelFor(int start, int end, void (*func)(int index, void *param), void *param) +{ + int count = end - start; + if (count <= 0 || func == NULL) return; + + if (count == 1 || !s_initialized) + { + for (int i = start; i < end; i++) + func(i, param); + return; + } + + std::vector taskData(count); + std::vector tasks(count); + + for (int i = 0; i < count; i++) + { + taskData[i].func = func; + taskData[i].param = param; + taskData[i].index = start + i; + tasks[i].func = ParallelForTaskFunc; + tasks[i].param = &taskData[i]; + } + + SubmitBatch(&tasks[0], count); + WaitAll(); +} + +#endif diff --git a/Core/ServerThreadPool.h b/Core/ServerThreadPool.h new file mode 100644 index 0000000..88eac71 --- /dev/null +++ b/Core/ServerThreadPool.h @@ -0,0 +1,73 @@ +#pragma once + +#if defined(__linux__) || defined(_WIN32) + +#ifdef __linux__ +#include +#else +#include +#endif + +#include +#include +#include +#include + +#define SERVER_THREAD_POOL_DEFAULT_SIZE 0 + +class ServerThreadPool +{ +public: + typedef void (*TaskFunc)(void *param); + + struct Task + { + TaskFunc func; + void *param; + }; + + static bool Initialize(int threadCount = SERVER_THREAD_POOL_DEFAULT_SIZE); + static void Shutdown(); + static int GetThreadCount(); + static bool IsInitialized() { return s_initialized; } + + static void Submit(TaskFunc func, void *param); + static void SubmitBatch(Task *tasks, int count); + static void WaitAll(); + + static void ParallelFor(int start, int end, void (*func)(int index, void *param), void *param); + +private: +#ifdef __linux__ + static void *WorkerProc(void *param); +#else + static DWORD WINAPI WorkerProc(LPVOID param); +#endif + static void WorkerInit(); + +#ifdef __linux__ + static std::vector s_workers; + static pthread_mutex_t s_queueLock; + static pthread_cond_t s_queueCond; + static pthread_cond_t s_doneCond; +#else + static std::vector s_workers; + static CRITICAL_SECTION s_queueLock; + static CONDITION_VARIABLE s_queueCond; + static CONDITION_VARIABLE s_doneCond; +#endif + static std::queue s_taskQueue; + static std::atomic s_pendingTasks; + static volatile bool s_running; + static int s_threadCount; + static bool s_initialized; +}; + +struct ParallelForData +{ + void (*func)(int index, void *param); + void *param; + int index; +}; + +#endif diff --git a/Linux/LinuxCompat.h b/Linux/LinuxCompat.h index ae178b3..b4c36ba 100644 --- a/Linux/LinuxCompat.h +++ b/Linux/LinuxCompat.h @@ -197,6 +197,9 @@ typedef union _LARGE_INTEGER { #define VK_F5 0x74 #define VK_LSHIFT 0xA0 #define VK_LCONTROL 0xA2 +#ifndef WHEEL_DELTA +#define WHEEL_DELTA 120 +#endif #define VK_PAD_A 0x5800 #define VK_PAD_B 0x5801 @@ -716,6 +719,21 @@ inline LONGLONG InterlockedCompareExchangeRelease64(volatile LONGLONG* destinati return __sync_val_compare_and_swap(destination, comparand, exchange); } +inline LONG InterlockedCompareExchange(volatile LONG* destination, LONG exchange, LONG comparand) +{ + return __sync_val_compare_and_swap(destination, comparand, exchange); +} + +inline LONG InterlockedCompareExchangeRelease(volatile LONG* destination, LONG exchange, LONG comparand) +{ + return __sync_val_compare_and_swap(destination, comparand, exchange); +} + +inline LONG InterlockedExchange(volatile LONG* target, LONG value) +{ + return __sync_lock_test_and_set(target, value); +} + typedef DWORD (*LPTHREAD_START_ROUTINE)(LPVOID); struct LinuxThreadData { diff --git a/Linux/PosixNetLayer.cpp b/Linux/PosixNetLayer.cpp index ceeff79..cb9c74b 100644 --- a/Linux/PosixNetLayer.cpp +++ b/Linux/PosixNetLayer.cpp @@ -15,8 +15,10 @@ #include #include #include +#include #include #include +#include SOCKET WinsockNetLayer::s_listenSocket = INVALID_SOCKET; SOCKET WinsockNetLayer::s_hostConnectionSocket = INVALID_SOCKET; @@ -25,6 +27,10 @@ bool WinsockNetLayer::s_acceptThreadActive = false; pthread_t WinsockNetLayer::s_clientRecvThread; bool WinsockNetLayer::s_clientRecvThreadActive = false; +int WinsockNetLayer::s_epollFd = -1; +pthread_t WinsockNetLayer::s_epollThreads[WIN64_NET_IO_THREADS]; +bool WinsockNetLayer::s_epollThreadsActive = false; + bool WinsockNetLayer::s_isHost = false; bool WinsockNetLayer::s_connected = false; bool WinsockNetLayer::s_active = false; @@ -32,12 +38,12 @@ bool WinsockNetLayer::s_initialized = false; uint8_t WinsockNetLayer::s_localSmallId = 0; uint8_t WinsockNetLayer::s_hostSmallId = 0; -uint8_t WinsockNetLayer::s_nextSmallId = 1; +std::atomic WinsockNetLayer::s_nextSmallId(1); pthread_mutex_t WinsockNetLayer::s_sendLock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t WinsockNetLayer::s_connectionsLock = PTHREAD_MUTEX_INITIALIZER; -Win64RemoteConnection WinsockNetLayer::s_connections[WIN64_NET_MAX_CLIENTS + 1]; +std::vector WinsockNetLayer::s_connections; SOCKET WinsockNetLayer::s_advertiseSock = INVALID_SOCKET; pthread_t WinsockNetLayer::s_advertiseThread; @@ -48,6 +54,7 @@ pthread_mutex_t WinsockNetLayer::s_advertiseLock = PTHREAD_MUTEX_INITIALIZER; int WinsockNetLayer::s_hostGamePort = WIN64_NET_DEFAULT_PORT; SOCKET WinsockNetLayer::s_discoverySock = INVALID_SOCKET; +SOCKET WinsockNetLayer::s_discoveryLegacySock = INVALID_SOCKET; pthread_t WinsockNetLayer::s_discoveryThread; bool WinsockNetLayer::s_discoveryThreadActive = false; volatile bool WinsockNetLayer::s_discovering = false; @@ -64,7 +71,14 @@ pthread_mutex_t WinsockNetLayer::s_freeSmallIdLock = PTHREAD_MUTEX_INITIALIZER; std::vector WinsockNetLayer::s_freeSmallIds; pthread_mutex_t WinsockNetLayer::s_earlyDataLock = PTHREAD_MUTEX_INITIALIZER; -std::vector WinsockNetLayer::s_earlyDataBuffers[WIN64_NET_MAX_CLIENTS + 1]; +std::vector > WinsockNetLayer::s_earlyDataBuffers; + +pthread_t WinsockNetLayer::s_asyncJoinThread; +bool WinsockNetLayer::s_asyncJoinThreadActive = false; +volatile WinsockNetLayer::JoinResult WinsockNetLayer::s_asyncJoinResult = WinsockNetLayer::JOIN_FAILED; +volatile bool WinsockNetLayer::s_asyncJoinActive = false; +char WinsockNetLayer::s_asyncJoinIP[256] = {}; +int WinsockNetLayer::s_asyncJoinPort = 0; bool g_Win64MultiplayerHost = false; bool g_Win64MultiplayerJoin = false; @@ -75,6 +89,16 @@ bool g_ServerAdvertiseLAN = true; char g_ServerBindAddress[256] = ""; int g_ServerMaxPlayers = MINECRAFT_NET_MAX_PLAYERS; +size_t WinsockNetLayer::GetConnectionSlotCount() +{ + int configured = g_ServerMaxPlayers; + if (configured < 1) + configured = 1; + if (configured > 255) + configured = 255; + return (size_t)configured; +} + static void InitMutex(pthread_mutex_t* m) { pthread_mutexattr_t attr; @@ -84,6 +108,46 @@ static void InitMutex(pthread_mutex_t* m) pthread_mutexattr_destroy(&attr); } +static bool JoinThreadWithTimeout(pthread_t thread, int timeoutMs, const char* threadName) +{ +#if defined(__GLIBC__) + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += timeoutMs / 1000; + ts.tv_nsec += (timeoutMs % 1000) * 1000000; + if (ts.tv_nsec >= 1000000000) + { + ts.tv_sec++; + ts.tv_nsec -= 1000000000; + } + + int rc = pthread_timedjoin_np(thread, NULL, &ts); + if (rc == 0) + return true; + + if (rc == ETIMEDOUT) + { + app.DebugPrintf("POSIX LAN: Timed out joining %s thread, cancelling\n", threadName); + pthread_cancel(thread); + + struct timespec tsCancel; + clock_gettime(CLOCK_REALTIME, &tsCancel); + tsCancel.tv_sec += 1; + rc = pthread_timedjoin_np(thread, NULL, &tsCancel); + if (rc == 0) + return true; + } + + app.DebugPrintf("POSIX LAN: Failed to join %s thread (rc=%d), detaching\n", threadName, rc); + pthread_detach(thread); + return false; +#else + (void)timeoutMs; + (void)threadName; + return pthread_join(thread, NULL) == 0; +#endif +} + bool WinsockNetLayer::Initialize() { if (s_initialized) return true; @@ -98,13 +162,27 @@ bool WinsockNetLayer::Initialize() InitMutex(&s_freeSmallIdLock); InitMutex(&s_earlyDataLock); - for (int i = 0; i < WIN64_NET_MAX_CLIENTS + 1; i++) + size_t slotCount = GetConnectionSlotCount(); + s_connections.clear(); + s_connections.resize(slotCount); + s_earlyDataBuffers.clear(); + s_earlyDataBuffers.resize(slotCount); + + for (size_t i = 0; i < slotCount; i++) { s_connections[i].tcpSocket = INVALID_SOCKET; - s_connections[i].smallId = 0; - s_connections[i].recvThreadActive = false; + s_connections[i].smallId = (uint8_t)i; s_connections[i].active = false; + s_connections[i].recvBuffer = NULL; + s_connections[i].recvBufferUsed = 0; + s_connections[i].recvBufferSize = 0; + s_connections[i].currentPacketSize = -1; + s_connections[i].readingHeader = true; + s_connections[i].sendBuffer = NULL; + s_connections[i].sendBufferUsed = 0; + s_connections[i].sendBufferSize = 0; InitMutex(&s_connections[i].sendLock); + InitMutex(&s_connections[i].sendBufLock); } s_initialized = true; @@ -118,6 +196,7 @@ bool WinsockNetLayer::Initialize() void WinsockNetLayer::Shutdown() { + CancelJoinGame(); StopAdvertising(); StopDiscovery(); @@ -126,43 +205,67 @@ void WinsockNetLayer::Shutdown() if (s_listenSocket != INVALID_SOCKET) { + shutdown(s_listenSocket, SHUT_RDWR); close(s_listenSocket); s_listenSocket = INVALID_SOCKET; } if (s_hostConnectionSocket != INVALID_SOCKET) { + shutdown(s_hostConnectionSocket, SHUT_RDWR); close(s_hostConnectionSocket); s_hostConnectionSocket = INVALID_SOCKET; } + if (s_epollFd >= 0) + { + close(s_epollFd); + s_epollFd = -1; + } + + if (s_epollThreadsActive) + { + for (int i = 0; i < WIN64_NET_IO_THREADS; i++) + JoinThreadWithTimeout(s_epollThreads[i], 3000, "epoll"); + s_epollThreadsActive = false; + } + pthread_mutex_lock(&s_connectionsLock); - for (int i = 0; i < WIN64_NET_MAX_CLIENTS + 1; i++) + for (size_t i = 0; i < s_connections.size(); i++) { s_connections[i].active = false; if (s_connections[i].tcpSocket != INVALID_SOCKET) { + shutdown(s_connections[i].tcpSocket, SHUT_RDWR); close(s_connections[i].tcpSocket); s_connections[i].tcpSocket = INVALID_SOCKET; } - if (s_connections[i].recvThreadActive) + if (s_connections[i].recvBuffer) { - pthread_join(s_connections[i].recvThread, NULL); - s_connections[i].recvThreadActive = false; + free(s_connections[i].recvBuffer); + s_connections[i].recvBuffer = NULL; + } + if (s_connections[i].sendBuffer) + { + free(s_connections[i].sendBuffer); + s_connections[i].sendBuffer = NULL; } pthread_mutex_destroy(&s_connections[i].sendLock); + pthread_mutex_destroy(&s_connections[i].sendBufLock); } + s_connections.clear(); + s_earlyDataBuffers.clear(); pthread_mutex_unlock(&s_connectionsLock); if (s_acceptThreadActive) { - pthread_join(s_acceptThread, NULL); + JoinThreadWithTimeout(s_acceptThread, 3000, "accept"); s_acceptThreadActive = false; } if (s_clientRecvThreadActive) { - pthread_join(s_clientRecvThread, NULL); + JoinThreadWithTimeout(s_clientRecvThread, 3000, "client-recv"); s_clientRecvThreadActive = false; } @@ -238,6 +341,19 @@ bool WinsockNetLayer::HostGame(int port) s_active = true; s_connected = true; + s_epollFd = epoll_create1(0); + if (s_epollFd < 0) + { + app.DebugPrintf("epoll_create1() failed: %s", strerror(errno)); + ::close(s_listenSocket); + s_listenSocket = INVALID_SOCKET; + return false; + } + + s_epollThreadsActive = true; + for (int i = 0; i < WIN64_NET_IO_THREADS; i++) + pthread_create(&s_epollThreads[i], NULL, EpollThreadProc, NULL); + if (pthread_create(&s_acceptThread, NULL, AcceptThreadProc, NULL) == 0) s_acceptThreadActive = true; @@ -376,6 +492,60 @@ bool WinsockNetLayer::JoinGame(const char *ip, int port) return true; } +bool WinsockNetLayer::BeginJoinGame(const char *ip, int port) +{ + if (s_asyncJoinActive) + return false; + + CancelJoinGame(); + + strncpy(s_asyncJoinIP, ip, sizeof(s_asyncJoinIP) - 1); + s_asyncJoinIP[sizeof(s_asyncJoinIP) - 1] = '\0'; + s_asyncJoinPort = port; + s_asyncJoinResult = JOIN_IN_PROGRESS; + s_asyncJoinActive = true; + + if (pthread_create(&s_asyncJoinThread, NULL, AsyncJoinThreadProc, NULL) == 0) + { + s_asyncJoinThreadActive = true; + return true; + } + + s_asyncJoinActive = false; + s_asyncJoinResult = JOIN_FAILED; + return false; +} + +WinsockNetLayer::JoinResult WinsockNetLayer::PollJoinResult() +{ + return s_asyncJoinResult; +} + +void WinsockNetLayer::CancelJoinGame() +{ + if (s_hostConnectionSocket != INVALID_SOCKET && !s_connected) + { + shutdown(s_hostConnectionSocket, SHUT_RDWR); + close(s_hostConnectionSocket); + s_hostConnectionSocket = INVALID_SOCKET; + } + + if (s_asyncJoinThreadActive) + { + s_asyncJoinActive = false; + JoinThreadWithTimeout(s_asyncJoinThread, 3000, "async-join"); + s_asyncJoinThreadActive = false; + } +} + +void* WinsockNetLayer::AsyncJoinThreadProc(void* /*param*/) +{ + bool result = JoinGame(s_asyncJoinIP, s_asyncJoinPort); + s_asyncJoinResult = result ? JOIN_SUCCESS : JOIN_FAILED; + s_asyncJoinActive = false; + return NULL; +} + bool WinsockNetLayer::SendOnSocket(SOCKET sock, const void *data, int dataSize) { if (sock == INVALID_SOCKET || dataSize <= 0 || dataSize > WIN64_NET_MAX_PACKET_SIZE) return false; @@ -412,20 +582,48 @@ bool WinsockNetLayer::SendToSmallId(uint8_t targetSmallId, const void *data, int if (s_isHost) { - pthread_mutex_lock(&s_connectionsLock); - if (targetSmallId >= WIN64_NET_MAX_CLIENTS + 1 || !s_connections[targetSmallId].active) - { - pthread_mutex_unlock(&s_connectionsLock); + if ((size_t)targetSmallId >= s_connections.size() || !s_connections[targetSmallId].active) return false; - } - SOCKET sock = s_connections[targetSmallId].tcpSocket; - pthread_mutex_t *pLock = &s_connections[targetSmallId].sendLock; - pthread_mutex_unlock(&s_connectionsLock); - pthread_mutex_lock(pLock); - bool result = SendOnSocket(sock, data, dataSize); - pthread_mutex_unlock(pLock); - return result; + uint8_t header[4]; + header[0] = (uint8_t)((dataSize >> 24) & 0xFF); + header[1] = (uint8_t)((dataSize >> 16) & 0xFF); + header[2] = (uint8_t)((dataSize >> 8) & 0xFF); + header[3] = (uint8_t)(dataSize & 0xFF); + + Win64RemoteConnection &conn = s_connections[targetSmallId]; + pthread_mutex_lock(&conn.sendBufLock); + + int totalNeeded = conn.sendBufferUsed + 4 + dataSize; + if (totalNeeded > conn.sendBufferSize) + { + int newSize = conn.sendBufferSize; + while (newSize < totalNeeded) + newSize *= 2; + if (newSize > WIN64_NET_MAX_PACKET_SIZE * 2) + newSize = WIN64_NET_MAX_PACKET_SIZE * 2; + if (totalNeeded > newSize) + { + pthread_mutex_unlock(&conn.sendBufLock); + return false; + } + uint8_t *newBuf = (uint8_t *)realloc(conn.sendBuffer, newSize); + if (!newBuf) + { + pthread_mutex_unlock(&conn.sendBufLock); + return false; + } + conn.sendBuffer = newBuf; + conn.sendBufferSize = newSize; + } + + memcpy(conn.sendBuffer + conn.sendBufferUsed, header, 4); + conn.sendBufferUsed += 4; + memcpy(conn.sendBuffer + conn.sendBufferUsed, data, dataSize); + conn.sendBufferUsed += dataSize; + + pthread_mutex_unlock(&conn.sendBufLock); + return true; } else { @@ -439,7 +637,7 @@ bool WinsockNetLayer::SendToSmallId(uint8_t targetSmallId, const void *data, int SOCKET WinsockNetLayer::GetSocketForSmallId(uint8_t smallId) { pthread_mutex_lock(&s_connectionsLock); - if (smallId < WIN64_NET_MAX_CLIENTS + 1 && s_connections[smallId].active) + if ((size_t)smallId < s_connections.size() && s_connections[smallId].active) { SOCKET sock = s_connections[smallId].tcpSocket; pthread_mutex_unlock(&s_connectionsLock); @@ -480,7 +678,7 @@ void WinsockNetLayer::HandleDataReceived(uint8_t fromSmallId, uint8_t toSmallId, if (pPlayerFrom == NULL || pPlayerTo == NULL) { - if (s_isHost && fromSmallId > 0 && fromSmallId < WIN64_NET_MAX_CLIENTS + 1) + if (s_isHost && fromSmallId > 0 && (size_t)fromSmallId < s_earlyDataBuffers.size()) { pthread_mutex_lock(&s_earlyDataLock); s_earlyDataBuffers[fromSmallId].insert( @@ -514,7 +712,7 @@ void WinsockNetLayer::HandleDataReceived(uint8_t fromSmallId, uint8_t toSmallId, void WinsockNetLayer::FlushPendingData() { pthread_mutex_lock(&s_earlyDataLock); - for (int i = 1; i < WIN64_NET_MAX_CLIENTS + 1; i++) + for (size_t i = 1; i < s_earlyDataBuffers.size(); i++) { if (s_earlyDataBuffers[i].empty()) continue; @@ -546,6 +744,11 @@ void* WinsockNetLayer::AcceptThreadProc(void* /*param*/) int noDelay = 1; setsockopt(clientSocket, IPPROTO_TCP, TCP_NODELAY, &noDelay, sizeof(noDelay)); + int sndbuf = WIN64_NET_SEND_BUFFER_SIZE; + setsockopt(clientSocket, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)); + int rcvbuf = WIN64_NET_RECV_BUFFER_SIZE; + setsockopt(clientSocket, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); + extern QNET_STATE _iQNetStubState; if (_iQNetStubState != QNET_STATE_GAME_PLAY) { @@ -561,18 +764,37 @@ void* WinsockNetLayer::AcceptThreadProc(void* /*param*/) assignedSmallId = s_freeSmallIds.back(); s_freeSmallIds.pop_back(); } - else if (s_nextSmallId < g_ServerMaxPlayers) - { - assignedSmallId = s_nextSmallId++; - } else { - pthread_mutex_unlock(&s_freeSmallIdLock); - app.DebugPrintf("POSIX LAN: Server full, rejecting connection\n"); + uint8_t expected = s_nextSmallId.load(); + if (expected < g_ServerMaxPlayers) + { + assignedSmallId = expected; + s_nextSmallId.store(expected + 1); + } + else + { + pthread_mutex_unlock(&s_freeSmallIdLock); + app.DebugPrintf("POSIX LAN: Server full, rejecting connection\n"); + close(clientSocket); + continue; + } + } + pthread_mutex_unlock(&s_freeSmallIdLock); + + if (assignedSmallId >= IQNet::GetPlayerCapacity()) + { + app.DebugPrintf("POSIX LAN: Invalid smallId %d for IQNet capacity, rejecting\n", assignedSmallId); + close(clientSocket); + continue; + } + + if ((size_t)assignedSmallId >= s_connections.size()) + { + app.DebugPrintf("POSIX LAN: Invalid smallId %d for transport capacity, rejecting\n", assignedSmallId); close(clientSocket); continue; } - pthread_mutex_unlock(&s_freeSmallIdLock); uint8_t assignBuf[1] = { assignedSmallId }; ssize_t sent = send(clientSocket, (const char *)assignBuf, 1, MSG_NOSIGNAL); @@ -583,20 +805,35 @@ void* WinsockNetLayer::AcceptThreadProc(void* /*param*/) continue; } + int flags = fcntl(clientSocket, F_GETFL, 0); + fcntl(clientSocket, F_SETFL, flags | O_NONBLOCK); + Win64RemoteConnection &conn = s_connections[assignedSmallId]; pthread_mutex_lock(&s_connectionsLock); - if (conn.recvThreadActive) - { - pthread_join(conn.recvThread, NULL); - conn.recvThreadActive = false; - } + if (conn.recvBuffer) + free(conn.recvBuffer); + if (conn.sendBuffer) + free(conn.sendBuffer); conn.tcpSocket = clientSocket; conn.smallId = assignedSmallId; conn.active = true; + conn.recvBuffer = (uint8_t *)malloc(WIN64_NET_RECV_BUFFER_SIZE); + conn.recvBufferUsed = 0; + conn.recvBufferSize = WIN64_NET_RECV_BUFFER_SIZE; + conn.currentPacketSize = -1; + conn.readingHeader = true; + conn.sendBuffer = (uint8_t *)malloc(WIN64_NET_SEND_BUFFER_SIZE); + conn.sendBufferUsed = 0; + conn.sendBufferSize = WIN64_NET_SEND_BUFFER_SIZE; pthread_mutex_unlock(&s_connectionsLock); - app.DebugPrintf("POSIX LAN: Client connected, assigned smallId=%d\n", assignedSmallId); + struct epoll_event ev; + ev.events = EPOLLIN | EPOLLET; + ev.data.u32 = assignedSmallId; + epoll_ctl(s_epollFd, EPOLL_CTL_ADD, clientSocket, &ev); + + app.DebugPrintf("POSIX LAN: Client connected, assigned smallId=%d (epoll)\n", assignedSmallId); IQNetPlayer *qnetPlayer = &IQNet::m_player[assignedSmallId]; @@ -606,88 +843,186 @@ void* WinsockNetLayer::AcceptThreadProc(void* /*param*/) pthread_mutex_lock(&s_pendingJoinLock); s_pendingJoinSmallIds.push_back(assignedSmallId); pthread_mutex_unlock(&s_pendingJoinLock); - - DWORD *threadParam = new DWORD; - *threadParam = assignedSmallId; - - pthread_mutex_lock(&s_connectionsLock); - if (pthread_create(&s_connections[assignedSmallId].recvThread, NULL, RecvThreadProc, threadParam) == 0) - s_connections[assignedSmallId].recvThreadActive = true; - else - delete threadParam; - pthread_mutex_unlock(&s_connectionsLock); } return NULL; } -void* WinsockNetLayer::RecvThreadProc(void* param) +bool WinsockNetLayer::ProcessRecvData(Win64RemoteConnection &conn) { - uint8_t clientSmallId = (uint8_t)*(DWORD *)param; - delete (DWORD *)param; - - pthread_mutex_lock(&s_connectionsLock); - if (clientSmallId >= WIN64_NET_MAX_CLIENTS + 1 || !s_connections[clientSmallId].active) + while (conn.recvBufferUsed > 0) { - pthread_mutex_unlock(&s_connectionsLock); - return NULL; - } - SOCKET sock = s_connections[clientSmallId].tcpSocket; - pthread_mutex_unlock(&s_connectionsLock); + if (conn.readingHeader) + { + if (conn.recvBufferUsed < 4) + return true; - std::vector recvBuf; - recvBuf.resize(WIN64_NET_RECV_BUFFER_SIZE); + int packetSize = + ((uint32_t)conn.recvBuffer[0] << 24) | + ((uint32_t)conn.recvBuffer[1] << 16) | + ((uint32_t)conn.recvBuffer[2] << 8) | + ((uint32_t)conn.recvBuffer[3]); + + if (packetSize <= 0 || (unsigned int)packetSize > WIN64_NET_MAX_PACKET_SIZE) + return false; + + conn.currentPacketSize = packetSize; + conn.readingHeader = false; + + memmove(conn.recvBuffer, conn.recvBuffer + 4, conn.recvBufferUsed - 4); + conn.recvBufferUsed -= 4; + } + else + { + if (conn.recvBufferUsed < conn.currentPacketSize) + return true; + + HandleDataReceived(conn.smallId, s_hostSmallId, conn.recvBuffer, conn.currentPacketSize); + + int remaining = conn.recvBufferUsed - conn.currentPacketSize; + if (remaining > 0) + memmove(conn.recvBuffer, conn.recvBuffer + conn.currentPacketSize, remaining); + conn.recvBufferUsed = remaining; + conn.currentPacketSize = -1; + conn.readingHeader = true; + } + } + return true; +} + +void* WinsockNetLayer::EpollThreadProc(void* /*param*/) +{ + struct epoll_event events[WIN64_NET_EPOLL_MAX_EVENTS]; while (s_active) { - uint8_t header[4]; - if (!RecvExact(sock, header, 4)) + int nfds = epoll_wait(s_epollFd, events, WIN64_NET_EPOLL_MAX_EVENTS, 50); + if (nfds < 0) { - app.DebugPrintf("POSIX LAN: Client smallId=%d disconnected (header)\n", clientSmallId); + if (errno == EINTR) continue; break; } - int packetSize = - ((uint32_t)header[0] << 24) | - ((uint32_t)header[1] << 16) | - ((uint32_t)header[2] << 8) | - ((uint32_t)header[3]); - - if (packetSize <= 0 || (unsigned int)packetSize > WIN64_NET_MAX_PACKET_SIZE) + for (int i = 0; i < nfds; i++) { - app.DebugPrintf("POSIX LAN: Invalid packet size %d from client smallId=%d (max=%d)\n", - packetSize, clientSmallId, (int)WIN64_NET_MAX_PACKET_SIZE); - break; - } + uint8_t smallId = (uint8_t)events[i].data.u32; - if ((int)recvBuf.size() < packetSize) - { - recvBuf.resize(packetSize); - app.DebugPrintf("POSIX LAN: Resized host recv buffer to %d bytes for client smallId=%d\n", packetSize, clientSmallId); - } + if ((size_t)smallId >= s_connections.size()) + continue; - if (!RecvExact(sock, &recvBuf[0], packetSize)) - { - app.DebugPrintf("POSIX LAN: Client smallId=%d disconnected (body)\n", clientSmallId); - break; - } + Win64RemoteConnection &conn = s_connections[smallId]; + if (!conn.active) + continue; - HandleDataReceived(clientSmallId, s_hostSmallId, &recvBuf[0], packetSize); + if (events[i].events & (EPOLLERR | EPOLLHUP)) + { + conn.active = false; + epoll_ctl(s_epollFd, EPOLL_CTL_DEL, conn.tcpSocket, NULL); + close(conn.tcpSocket); + conn.tcpSocket = INVALID_SOCKET; + + pthread_mutex_lock(&s_disconnectLock); + s_disconnectedSmallIds.push_back(smallId); + pthread_mutex_unlock(&s_disconnectLock); + continue; + } + + if (events[i].events & EPOLLIN) + { + bool disconnected = false; + while (true) + { + int space = conn.recvBufferSize - conn.recvBufferUsed; + if (space < 4096) + { + int newSize = conn.recvBufferSize * 2; + if (newSize > WIN64_NET_MAX_PACKET_SIZE + 4) + newSize = WIN64_NET_MAX_PACKET_SIZE + 4; + uint8_t *newBuf = (uint8_t *)realloc(conn.recvBuffer, newSize); + if (!newBuf) + { + disconnected = true; + break; + } + conn.recvBuffer = newBuf; + conn.recvBufferSize = newSize; + space = newSize - conn.recvBufferUsed; + } + + ssize_t r = recv(conn.tcpSocket, (char *)conn.recvBuffer + conn.recvBufferUsed, space, 0); + if (r > 0) + { + conn.recvBufferUsed += (int)r; + if (!ProcessRecvData(conn)) + { + disconnected = true; + break; + } + } + else if (r == 0) + { + disconnected = true; + break; + } + else + { + if (errno == EAGAIN || errno == EWOULDBLOCK) + break; + disconnected = true; + break; + } + } + + if (disconnected) + { + conn.active = false; + epoll_ctl(s_epollFd, EPOLL_CTL_DEL, conn.tcpSocket, NULL); + close(conn.tcpSocket); + conn.tcpSocket = INVALID_SOCKET; + + pthread_mutex_lock(&s_disconnectLock); + s_disconnectedSmallIds.push_back(smallId); + pthread_mutex_unlock(&s_disconnectLock); + } + } + } } + return NULL; +} +void WinsockNetLayer::FlushSendBuffers() +{ pthread_mutex_lock(&s_connectionsLock); - s_connections[clientSmallId].active = false; - if (s_connections[clientSmallId].tcpSocket != INVALID_SOCKET) + for (size_t i = 1; i < s_connections.size(); i++) { - close(s_connections[clientSmallId].tcpSocket); - s_connections[clientSmallId].tcpSocket = INVALID_SOCKET; + Win64RemoteConnection &conn = s_connections[i]; + if (!conn.active || conn.tcpSocket == INVALID_SOCKET) + continue; + + pthread_mutex_lock(&conn.sendBufLock); + if (conn.sendBufferUsed > 0) + { + int totalSent = 0; + while (totalSent < conn.sendBufferUsed) + { + ssize_t sent = send(conn.tcpSocket, (const char *)conn.sendBuffer + totalSent, + conn.sendBufferUsed - totalSent, MSG_NOSIGNAL); + if (sent <= 0) + break; + totalSent += (int)sent; + } + if (totalSent < conn.sendBufferUsed && totalSent > 0) + { + memmove(conn.sendBuffer, conn.sendBuffer + totalSent, conn.sendBufferUsed - totalSent); + conn.sendBufferUsed -= totalSent; + } + else + { + conn.sendBufferUsed = 0; + } + } + pthread_mutex_unlock(&conn.sendBufLock); } pthread_mutex_unlock(&s_connectionsLock); - - pthread_mutex_lock(&s_disconnectLock); - s_disconnectedSmallIds.push_back(clientSmallId); - pthread_mutex_unlock(&s_disconnectLock); - - return NULL; } bool WinsockNetLayer::PopDisconnectedSmallId(uint8_t *outSmallId) @@ -727,15 +1062,17 @@ bool WinsockNetLayer::PopPendingJoinSmallId(uint8_t *outSmallId) bool WinsockNetLayer::IsSmallIdConnected(uint8_t smallId) { - if (smallId >= WIN64_NET_MAX_CLIENTS + 1) return false; + if ((size_t)smallId >= s_connections.size()) return false; return s_connections[smallId].active; } void WinsockNetLayer::CloseConnectionBySmallId(uint8_t smallId) { pthread_mutex_lock(&s_connectionsLock); - if (smallId < WIN64_NET_MAX_CLIENTS + 1 && s_connections[smallId].active && s_connections[smallId].tcpSocket != INVALID_SOCKET) + if ((size_t)smallId < s_connections.size() && s_connections[smallId].active && s_connections[smallId].tcpSocket != INVALID_SOCKET) { + epoll_ctl(s_epollFd, EPOLL_CTL_DEL, s_connections[smallId].tcpSocket, NULL); + shutdown(s_connections[smallId].tcpSocket, SHUT_RDWR); close(s_connections[smallId].tcpSocket); s_connections[smallId].tcpSocket = INVALID_SOCKET; app.DebugPrintf("POSIX LAN: Force-closed TCP connection for smallId=%d\n", smallId); @@ -743,7 +1080,7 @@ void WinsockNetLayer::CloseConnectionBySmallId(uint8_t smallId) pthread_mutex_unlock(&s_connectionsLock); pthread_mutex_lock(&s_earlyDataLock); - if (smallId < WIN64_NET_MAX_CLIENTS + 1) + if ((size_t)smallId < s_earlyDataBuffers.size()) s_earlyDataBuffers[smallId].clear(); pthread_mutex_unlock(&s_earlyDataLock); } @@ -827,7 +1164,7 @@ bool WinsockNetLayer::StartAdvertising(int gamePort, const wchar_t *hostName, un if (pthread_create(&s_advertiseThread, NULL, AdvertiseThreadProc, NULL) == 0) s_advertiseThreadActive = true; - app.DebugPrintf("POSIX LAN: Started advertising on UDP port %d\n", WIN64_LAN_DISCOVERY_PORT); + app.DebugPrintf("POSIX LAN: Started advertising on UDP port %d\n", gamePort); return true; } @@ -837,13 +1174,14 @@ void WinsockNetLayer::StopAdvertising() if (s_advertiseSock != INVALID_SOCKET) { + shutdown(s_advertiseSock, SHUT_RDWR); close(s_advertiseSock); s_advertiseSock = INVALID_SOCKET; } if (s_advertiseThreadActive) { - pthread_join(s_advertiseThread, NULL); + JoinThreadWithTimeout(s_advertiseThread, 2000, "advertise"); s_advertiseThreadActive = false; } } @@ -872,26 +1210,41 @@ void WinsockNetLayer::UpdateAdvertiseJoinable(bool joinable) pthread_mutex_unlock(&s_advertiseLock); } +void WinsockNetLayer::UpdateAdvertiseGameHostSettings(unsigned int settings) +{ + pthread_mutex_lock(&s_advertiseLock); + s_advertiseData.gameHostSettings = settings; + pthread_mutex_unlock(&s_advertiseLock); +} + void* WinsockNetLayer::AdvertiseThreadProc(void* /*param*/) { struct sockaddr_in broadcastAddr; memset(&broadcastAddr, 0, sizeof(broadcastAddr)); broadcastAddr.sin_family = AF_INET; - broadcastAddr.sin_port = htons(WIN64_LAN_DISCOVERY_PORT); broadcastAddr.sin_addr.s_addr = INADDR_BROADCAST; while (s_advertising) { pthread_mutex_lock(&s_advertiseLock); Win64LANBroadcast data = s_advertiseData; + int gamePort = s_hostGamePort; pthread_mutex_unlock(&s_advertiseLock); + broadcastAddr.sin_port = htons((uint16_t)gamePort); ssize_t sent = sendto(s_advertiseSock, (const char *)&data, sizeof(data), 0, (struct sockaddr *)&broadcastAddr, sizeof(broadcastAddr)); if (sent < 0 && s_advertising) app.DebugPrintf("POSIX LAN: Broadcast sendto failed: %s\n", strerror(errno)); + if (gamePort != WIN64_LAN_DISCOVERY_PORT) + { + broadcastAddr.sin_port = htons(WIN64_LAN_DISCOVERY_PORT); + sendto(s_advertiseSock, (const char *)&data, sizeof(data), 0, + (struct sockaddr *)&broadcastAddr, sizeof(broadcastAddr)); + } + sleep(1); } @@ -903,40 +1256,64 @@ bool WinsockNetLayer::StartDiscovery() if (s_discovering) return true; if (!s_initialized) return false; + int reuseAddr = 1; + struct sockaddr_in bindAddr; + memset(&bindAddr, 0, sizeof(bindAddr)); + bindAddr.sin_family = AF_INET; + bindAddr.sin_addr.s_addr = INADDR_ANY; + s_discoverySock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (s_discoverySock == INVALID_SOCKET) { app.DebugPrintf("POSIX LAN: Failed to create discovery socket: %s\n", strerror(errno)); - return false; } - - int reuseAddr = 1; - setsockopt(s_discoverySock, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr)); - - struct sockaddr_in bindAddr; - memset(&bindAddr, 0, sizeof(bindAddr)); - bindAddr.sin_family = AF_INET; - bindAddr.sin_port = htons(WIN64_LAN_DISCOVERY_PORT); - bindAddr.sin_addr.s_addr = INADDR_ANY; - - if (bind(s_discoverySock, (struct sockaddr *)&bindAddr, sizeof(bindAddr)) < 0) + else { - app.DebugPrintf("POSIX LAN: Discovery bind failed: %s\n", strerror(errno)); - close(s_discoverySock); - s_discoverySock = INVALID_SOCKET; - return false; + setsockopt(s_discoverySock, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr)); + bindAddr.sin_port = htons(WIN64_NET_DEFAULT_PORT); + if (bind(s_discoverySock, (struct sockaddr *)&bindAddr, sizeof(bindAddr)) < 0) + { + app.DebugPrintf("POSIX LAN: Discovery bind to port %d failed: %s\n", WIN64_NET_DEFAULT_PORT, strerror(errno)); + close(s_discoverySock); + s_discoverySock = INVALID_SOCKET; + } + else + { + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 500000; + setsockopt(s_discoverySock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + } } - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 500000; - setsockopt(s_discoverySock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + s_discoveryLegacySock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s_discoveryLegacySock != INVALID_SOCKET) + { + setsockopt(s_discoveryLegacySock, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr)); + bindAddr.sin_port = htons(WIN64_LAN_DISCOVERY_PORT); + if (bind(s_discoveryLegacySock, (struct sockaddr *)&bindAddr, sizeof(bindAddr)) < 0) + { + app.DebugPrintf("POSIX LAN: Legacy discovery bind to port %d failed: %s\n", WIN64_LAN_DISCOVERY_PORT, strerror(errno)); + close(s_discoveryLegacySock); + s_discoveryLegacySock = INVALID_SOCKET; + } + else + { + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 500000; + setsockopt(s_discoveryLegacySock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + } + } + + if (s_discoverySock == INVALID_SOCKET && s_discoveryLegacySock == INVALID_SOCKET) + return false; s_discovering = true; if (pthread_create(&s_discoveryThread, NULL, DiscoveryThreadProc, NULL) == 0) s_discoveryThreadActive = true; - app.DebugPrintf("POSIX LAN: Listening for LAN games on UDP port %d\n", WIN64_LAN_DISCOVERY_PORT); + app.DebugPrintf("POSIX LAN: Listening for LAN games on UDP ports %d and %d\n", WIN64_NET_DEFAULT_PORT, WIN64_LAN_DISCOVERY_PORT); return true; } @@ -946,13 +1323,21 @@ void WinsockNetLayer::StopDiscovery() if (s_discoverySock != INVALID_SOCKET) { + shutdown(s_discoverySock, SHUT_RDWR); close(s_discoverySock); s_discoverySock = INVALID_SOCKET; } + if (s_discoveryLegacySock != INVALID_SOCKET) + { + shutdown(s_discoveryLegacySock, SHUT_RDWR); + close(s_discoveryLegacySock); + s_discoveryLegacySock = INVALID_SOCKET; + } + if (s_discoveryThreadActive) { - pthread_join(s_discoveryThread, NULL); + JoinThreadWithTimeout(s_discoveryThread, 2000, "discovery"); s_discoveryThreadActive = false; } @@ -977,98 +1362,129 @@ void* WinsockNetLayer::DiscoveryThreadProc(void* /*param*/) while (s_discovering) { - struct sockaddr_in senderAddr; - socklen_t senderLen = sizeof(senderAddr); - - ssize_t recvLen = recvfrom(s_discoverySock, recvBuf, sizeof(recvBuf), 0, - (struct sockaddr *)&senderAddr, &senderLen); - - if (recvLen < 0) - continue; - - if ((size_t)recvLen < sizeof(Win64LANBroadcast)) - continue; - - Win64LANBroadcast *broadcast = (Win64LANBroadcast *)recvBuf; - if (broadcast->magic != WIN64_LAN_BROADCAST_MAGIC) - continue; - - broadcast->hostName[31] = 0; - - for (int pn = 0; pn < WIN64_LAN_BROADCAST_PLAYERS; pn++) - broadcast->playerNames[pn][31] = '\0'; - - char senderIP[64]; - inet_ntop(AF_INET, &senderAddr.sin_addr, senderIP, sizeof(senderIP)); - - DWORD now = GetTickCount(); - - pthread_mutex_lock(&s_discoveryLock); - - bool found = false; - for (size_t i = 0; i < s_discoveredSessions.size(); i++) + fd_set readSet; + FD_ZERO(&readSet); + int nfds = 0; + if (s_discoverySock != INVALID_SOCKET) { - if (strcmp(s_discoveredSessions[i].hostIP, senderIP) == 0 && - s_discoveredSessions[i].hostPort == (int)broadcast->gamePort) - { - s_discoveredSessions[i].netVersion = broadcast->netVersion; - for (int c = 0; c < 32; c++) - s_discoveredSessions[i].hostName[c] = (wchar_t)broadcast->hostName[c]; - s_discoveredSessions[i].playerCount = broadcast->playerCount; - s_discoveredSessions[i].maxPlayers = broadcast->maxPlayers; - s_discoveredSessions[i].gameHostSettings = broadcast->gameHostSettings; - s_discoveredSessions[i].texturePackParentId = broadcast->texturePackParentId; - s_discoveredSessions[i].subTexturePackId = broadcast->subTexturePackId; - s_discoveredSessions[i].isJoinable = (broadcast->isJoinable != 0); - s_discoveredSessions[i].isDedicatedServer = (broadcast->isDedicatedServer != 0); - s_discoveredSessions[i].lastSeenTick = now; - memcpy(s_discoveredSessions[i].playerNames, broadcast->playerNames, sizeof(broadcast->playerNames)); - found = true; - break; - } + FD_SET(s_discoverySock, &readSet); + if (s_discoverySock > nfds) nfds = s_discoverySock; + } + if (s_discoveryLegacySock != INVALID_SOCKET) + { + FD_SET(s_discoveryLegacySock, &readSet); + if (s_discoveryLegacySock > nfds) nfds = s_discoveryLegacySock; } - if (!found) + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 500000; + int selResult = select(nfds + 1, &readSet, NULL, NULL, &tv); + if (selResult <= 0) + continue; + + int readySocks[2]; + int readyCount = 0; + if (s_discoverySock != INVALID_SOCKET && FD_ISSET(s_discoverySock, &readSet)) + readySocks[readyCount++] = s_discoverySock; + if (s_discoveryLegacySock != INVALID_SOCKET && FD_ISSET(s_discoveryLegacySock, &readSet)) + readySocks[readyCount++] = s_discoveryLegacySock; + + for (int si = 0; si < readyCount; si++) { - if (s_discoveredSessions.size() >= MAX_DISCOVERED_SESSIONS) - { - pthread_mutex_unlock(&s_discoveryLock); + struct sockaddr_in senderAddr; + socklen_t senderLen = sizeof(senderAddr); + + ssize_t recvLen = recvfrom(readySocks[si], recvBuf, sizeof(recvBuf), 0, + (struct sockaddr *)&senderAddr, &senderLen); + + if (recvLen < 0) continue; - } - Win64LANSession session; - memset(&session, 0, sizeof(session)); - strncpy(session.hostIP, senderIP, sizeof(session.hostIP) - 1); - session.hostPort = (int)broadcast->gamePort; - session.netVersion = broadcast->netVersion; - for (int c = 0; c < 32; c++) - session.hostName[c] = (wchar_t)broadcast->hostName[c]; - session.playerCount = broadcast->playerCount; - session.maxPlayers = broadcast->maxPlayers; - session.gameHostSettings = broadcast->gameHostSettings; - session.texturePackParentId = broadcast->texturePackParentId; - session.subTexturePackId = broadcast->subTexturePackId; - session.isJoinable = (broadcast->isJoinable != 0); - session.isDedicatedServer = (broadcast->isDedicatedServer != 0); - session.lastSeenTick = now; - memcpy(session.playerNames, broadcast->playerNames, sizeof(broadcast->playerNames)); - s_discoveredSessions.push_back(session); + if ((size_t)recvLen < sizeof(Win64LANBroadcast)) + continue; - app.DebugPrintf("POSIX LAN: Discovered game \"%ls\" at %s:%d\n", - session.hostName, session.hostIP, session.hostPort); - } + Win64LANBroadcast *broadcast = (Win64LANBroadcast *)recvBuf; + if (broadcast->magic != WIN64_LAN_BROADCAST_MAGIC) + continue; - for (size_t i = s_discoveredSessions.size(); i > 0; i--) - { - if (now - s_discoveredSessions[i - 1].lastSeenTick > 5000) + broadcast->hostName[31] = 0; + + for (int pn = 0; pn < WIN64_LAN_BROADCAST_PLAYERS; pn++) + broadcast->playerNames[pn][31] = '\0'; + + char senderIP[64]; + inet_ntop(AF_INET, &senderAddr.sin_addr, senderIP, sizeof(senderIP)); + + DWORD now = GetTickCount(); + + pthread_mutex_lock(&s_discoveryLock); + + bool found = false; + for (size_t i = 0; i < s_discoveredSessions.size(); i++) { - app.DebugPrintf("POSIX LAN: Session \"%ls\" at %s timed out\n", - s_discoveredSessions[i - 1].hostName, s_discoveredSessions[i - 1].hostIP); - s_discoveredSessions.erase(s_discoveredSessions.begin() + (i - 1)); + if (strcmp(s_discoveredSessions[i].hostIP, senderIP) == 0 && + s_discoveredSessions[i].hostPort == (int)broadcast->gamePort) + { + s_discoveredSessions[i].netVersion = broadcast->netVersion; + for (int c = 0; c < 32; c++) + s_discoveredSessions[i].hostName[c] = (wchar_t)broadcast->hostName[c]; + s_discoveredSessions[i].playerCount = broadcast->playerCount; + s_discoveredSessions[i].maxPlayers = broadcast->maxPlayers; + s_discoveredSessions[i].gameHostSettings = broadcast->gameHostSettings; + s_discoveredSessions[i].texturePackParentId = broadcast->texturePackParentId; + s_discoveredSessions[i].subTexturePackId = broadcast->subTexturePackId; + s_discoveredSessions[i].isJoinable = (broadcast->isJoinable != 0); + s_discoveredSessions[i].isDedicatedServer = (broadcast->isDedicatedServer != 0); + s_discoveredSessions[i].lastSeenTick = now; + memcpy(s_discoveredSessions[i].playerNames, broadcast->playerNames, sizeof(broadcast->playerNames)); + found = true; + break; + } } - } - pthread_mutex_unlock(&s_discoveryLock); + if (!found) + { + if (s_discoveredSessions.size() >= MAX_DISCOVERED_SESSIONS) + { + pthread_mutex_unlock(&s_discoveryLock); + continue; + } + + Win64LANSession session; + memset(&session, 0, sizeof(session)); + strncpy(session.hostIP, senderIP, sizeof(session.hostIP) - 1); + session.hostPort = (int)broadcast->gamePort; + session.netVersion = broadcast->netVersion; + for (int c = 0; c < 32; c++) + session.hostName[c] = (wchar_t)broadcast->hostName[c]; + session.playerCount = broadcast->playerCount; + session.maxPlayers = broadcast->maxPlayers; + session.gameHostSettings = broadcast->gameHostSettings; + session.texturePackParentId = broadcast->texturePackParentId; + session.subTexturePackId = broadcast->subTexturePackId; + session.isJoinable = (broadcast->isJoinable != 0); + session.isDedicatedServer = (broadcast->isDedicatedServer != 0); + session.lastSeenTick = now; + memcpy(session.playerNames, broadcast->playerNames, sizeof(broadcast->playerNames)); + s_discoveredSessions.push_back(session); + + app.DebugPrintf("POSIX LAN: Discovered game \"%ls\" at %s:%d\n", + session.hostName, session.hostIP, session.hostPort); + } + + for (size_t i = s_discoveredSessions.size(); i > 0; i--) + { + if (now - s_discoveredSessions[i - 1].lastSeenTick > 5000) + { + app.DebugPrintf("POSIX LAN: Session \"%ls\" at %s timed out\n", + s_discoveredSessions[i - 1].hostName, s_discoveredSessions[i - 1].hostIP); + s_discoveredSessions.erase(s_discoveredSessions.begin() + (i - 1)); + } + } + + pthread_mutex_unlock(&s_discoveryLock); + } } return NULL; diff --git a/Linux/PosixNetLayer.h b/Linux/PosixNetLayer.h index d66e247..0073d42 100644 --- a/Linux/PosixNetLayer.h +++ b/Linux/PosixNetLayer.h @@ -12,9 +12,11 @@ #include #include #include +#include #include #include #include +#include #include "../../Minecraft.Client/Common/Network/NetworkPlayerInterface.h" @@ -25,12 +27,14 @@ typedef int SOCKET; inline int closesocket(SOCKET s) { return close(s); } #define WIN64_NET_DEFAULT_PORT 25565 -#define WIN64_NET_MAX_CLIENTS 31 -#define WIN64_NET_RECV_BUFFER_SIZE 65536 +#define WIN64_NET_RECV_BUFFER_SIZE 131072 #define WIN64_NET_MAX_PACKET_SIZE (3 * 1024 * 1024) #define WIN64_LAN_DISCOVERY_PORT 25566 #define WIN64_LAN_BROADCAST_MAGIC 0x4D434C4E #define WIN64_LAN_BROADCAST_PLAYERS 8 +#define WIN64_NET_IO_THREADS 2 +#define WIN64_NET_EPOLL_MAX_EVENTS 64 +#define WIN64_NET_SEND_BUFFER_SIZE 262144 class Socket; @@ -73,10 +77,19 @@ struct Win64RemoteConnection { SOCKET tcpSocket; uint8_t smallId; - pthread_t recvThread; - bool recvThreadActive; volatile bool active; pthread_mutex_t sendLock; + + uint8_t *recvBuffer; + int recvBufferUsed; + int recvBufferSize; + int currentPacketSize; + bool readingHeader; + + uint8_t *sendBuffer; + int sendBufferUsed; + int sendBufferSize; + pthread_mutex_t sendBufLock; }; class WinsockNetLayer @@ -88,6 +101,11 @@ public: static bool HostGame(int port); static bool JoinGame(const char *ip, int port); + enum JoinResult { JOIN_IN_PROGRESS, JOIN_SUCCESS, JOIN_FAILED }; + static bool BeginJoinGame(const char *ip, int port); + static JoinResult PollJoinResult(); + static void CancelJoinGame(); + static bool SendToSmallId(uint8_t targetSmallId, const void *data, int dataSize); static bool SendOnSocket(SOCKET sock, const void *data, int dataSize); @@ -117,6 +135,7 @@ public: static void UpdateAdvertisePlayerCount(uint8_t count); static void UpdateAdvertiseJoinable(bool joinable); static void UpdateAdvertisePlayerNames(uint8_t count, const char playerNames[][32]); + static void UpdateAdvertiseGameHostSettings(unsigned int settings); static bool StartDiscovery(); static void StopDiscovery(); @@ -124,12 +143,17 @@ public: static int GetHostPort() { return s_hostGamePort; } + static void FlushSendBuffers(); + private: + static size_t GetConnectionSlotCount(); static void* AcceptThreadProc(void* param); - static void* RecvThreadProc(void* param); + static void* EpollThreadProc(void* param); static void* ClientRecvThreadProc(void* param); static void* AdvertiseThreadProc(void* param); static void* DiscoveryThreadProc(void* param); + static void* AsyncJoinThreadProc(void* param); + static bool ProcessRecvData(Win64RemoteConnection &conn); static SOCKET s_listenSocket; static SOCKET s_hostConnectionSocket; @@ -138,6 +162,10 @@ private: static pthread_t s_clientRecvThread; static bool s_clientRecvThreadActive; + static int s_epollFd; + static pthread_t s_epollThreads[WIN64_NET_IO_THREADS]; + static bool s_epollThreadsActive; + static bool s_isHost; static bool s_connected; static bool s_active; @@ -145,12 +173,12 @@ private: static uint8_t s_localSmallId; static uint8_t s_hostSmallId; - static uint8_t s_nextSmallId; + static std::atomic s_nextSmallId; static pthread_mutex_t s_sendLock; static pthread_mutex_t s_connectionsLock; - static Win64RemoteConnection s_connections[WIN64_NET_MAX_CLIENTS + 1]; + static std::vector s_connections; static SOCKET s_advertiseSock; static pthread_t s_advertiseThread; @@ -161,6 +189,7 @@ private: static int s_hostGamePort; static SOCKET s_discoverySock; + static SOCKET s_discoveryLegacySock; static pthread_t s_discoveryThread; static bool s_discoveryThreadActive; static volatile bool s_discovering; @@ -177,7 +206,14 @@ private: static std::vector s_freeSmallIds; static pthread_mutex_t s_earlyDataLock; - static std::vector s_earlyDataBuffers[WIN64_NET_MAX_CLIENTS + 1]; + static std::vector > s_earlyDataBuffers; + + static pthread_t s_asyncJoinThread; + static bool s_asyncJoinThreadActive; + static volatile JoinResult s_asyncJoinResult; + static volatile bool s_asyncJoinActive; + static char s_asyncJoinIP[256]; + static int s_asyncJoinPort; }; extern bool g_Win64MultiplayerHost; diff --git a/Minecraft.Server.vcxproj b/Minecraft.Server.vcxproj index 714bc7a..a3404d8 100644 --- a/Minecraft.Server.vcxproj +++ b/Minecraft.Server.vcxproj @@ -24,6 +24,7 @@ Win32Proj MinecraftServer Minecraft.Server + 10.0 @@ -124,8 +125,8 @@ Use stdafx.h Level3 - None - Full + ProgramDatabase + MinSpace Sync false $(IntDir)$(ProjectName).pch @@ -136,14 +137,19 @@ true Default false - Speed + Size + true + true - false + true + $(OutDir)$(ProjectName).pdb Console ws2_32.lib;..\Minecraft.World\x64_Release\Minecraft.World.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Storage.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Profile_r.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Render_PC.lib;%(AdditionalDependencies) NotSet false + true + true @@ -212,6 +218,7 @@ true + diff --git a/Minecraft.Server.vcxproj.filters b/Minecraft.Server.vcxproj.filters index 56a9406..581a6bc 100644 --- a/Minecraft.Server.vcxproj.filters +++ b/Minecraft.Server.vcxproj.filters @@ -226,6 +226,9 @@ Source Files\Core + + Source Files\Core + Source Files\Core diff --git a/Stubs/ServerStubs2.cpp b/Stubs/ServerStubs2.cpp index c62752b..44c79ce 100644 --- a/Stubs/ServerStubs2.cpp +++ b/Stubs/ServerStubs2.cpp @@ -2,6 +2,7 @@ #include "stdafx.h" #include "../../Minecraft.World/Dimension.h" +#include "../../Minecraft.World/ChunkSource.h" #include "../../Minecraft.Client/Windows64/4JLibs/inc/4J_Input.h" #include "../../Minecraft.Client/Windows64/4JLibs/inc/4J_Storage.h" #include "../../Minecraft.Client/TexturePackRepository.h"