LCEMP v1.1.0 Dedicated Server

This commit is contained in:
NOTPIES
2026-04-20 18:47:13 -04:00
parent f3fc8574c8
commit 797530e883
13 changed files with 1204 additions and 222 deletions
+19
View File
@@ -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");
}
+14
View File
@@ -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);
+4 -1
View File
@@ -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())
+1
View File
@@ -36,6 +36,7 @@ public:
wstring motd;
bool whiteList;
bool voiceChat;
bool chatEnabled;
wstring levelSize;
bool advertiseLan;
wstring serverIp;
+375
View File
@@ -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 <unistd.h>
#include <errno.h>
#include <time.h>
#endif
#include <cstring>
#ifdef __linux__
std::vector<pthread_t> 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<HANDLE> ServerThreadPool::s_workers;
CRITICAL_SECTION ServerThreadPool::s_queueLock;
CONDITION_VARIABLE ServerThreadPool::s_queueCond;
CONDITION_VARIABLE ServerThreadPool::s_doneCond;
#endif
std::queue<ServerThreadPool::Task> ServerThreadPool::s_taskQueue;
std::atomic<int> 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<ParallelForData> taskData(count);
std::vector<Task> 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
+73
View File
@@ -0,0 +1,73 @@
#pragma once
#if defined(__linux__) || defined(_WIN32)
#ifdef __linux__
#include <pthread.h>
#else
#include <windows.h>
#endif
#include <vector>
#include <queue>
#include <functional>
#include <atomic>
#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<pthread_t> s_workers;
static pthread_mutex_t s_queueLock;
static pthread_cond_t s_queueCond;
static pthread_cond_t s_doneCond;
#else
static std::vector<HANDLE> s_workers;
static CRITICAL_SECTION s_queueLock;
static CONDITION_VARIABLE s_queueCond;
static CONDITION_VARIABLE s_doneCond;
#endif
static std::queue<Task> s_taskQueue;
static std::atomic<int> 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