mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/LCEMP-Server.git
synced 2026-05-22 13:26:07 +00:00
581 lines
18 KiB
C++
581 lines
18 KiB
C++
#include "stdafx.h"
|
|
#include "DedicatedServer.h"
|
|
|
|
#include "../../Minecraft.Client/MinecraftServer.h"
|
|
#include "../../Minecraft.Client/Minecraft.h"
|
|
#include "../../Minecraft.Client/ProgressRenderer.h"
|
|
#include "../../Minecraft.Client/GameRenderer.h"
|
|
#include "../../Minecraft.Client/Options.h"
|
|
#include "../../Minecraft.Client/User.h"
|
|
#include "../../Minecraft.Client/StatsCounter.h"
|
|
#include "../../Minecraft.Client/Windows64/Network/WinsockNetLayer.h"
|
|
#include "../../Minecraft.Client/Common/Network/PlatformNetworkManagerStub.h"
|
|
#include "../../Minecraft.World/Minecraft.World.h"
|
|
#include "../../Minecraft.World/System.h"
|
|
#include "../../Minecraft.World/AABB.h"
|
|
#include "../../Minecraft.World/Vec3.h"
|
|
#include "../../Minecraft.World/IntCache.h"
|
|
#include "../../Minecraft.World/compression.h"
|
|
#include "../../Minecraft.World/OldChunkStorage.h"
|
|
#include "../../Minecraft.World/Level.h"
|
|
#include "../../Minecraft.World/Tile.h"
|
|
#include "../../Minecraft.World/Entity.h"
|
|
#include "../../Minecraft.World/SharedConstants.h"
|
|
#include "../../Minecraft.World/ChunkSource.h"
|
|
#include "ServerLogger.h"
|
|
#include "ServerLists.h"
|
|
#include "ServerThreadPool.h"
|
|
#include "../Commands/ServerCommands.h"
|
|
|
|
#ifdef __linux__
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
#else
|
|
#include <conio.h>
|
|
#endif
|
|
|
|
DedicatedServer::DedicatedServer()
|
|
: m_running(false)
|
|
{
|
|
}
|
|
|
|
DedicatedServer::~DedicatedServer()
|
|
{
|
|
}
|
|
|
|
bool DedicatedServer::init()
|
|
{
|
|
if (!m_properties.load(L"server.properties"))
|
|
{
|
|
ServerLog(L"server.properties not found, generating defaults\n");
|
|
m_properties.save(L"server.properties");
|
|
}
|
|
|
|
ServerLog(L"Loading properties\n");
|
|
ServerLog(L"Default game type: %s\n", m_properties.gamemode == 0 ? L"SURVIVAL" : L"CREATIVE");
|
|
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);
|
|
|
|
extern bool g_voiceChatEnabled;
|
|
g_voiceChatEnabled = m_properties.voiceChat;
|
|
|
|
extern bool g_ServerAdvertiseLAN;
|
|
g_ServerAdvertiseLAN = m_properties.advertiseLan;
|
|
|
|
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())
|
|
{
|
|
wcstombs(g_ServerBindAddress, m_properties.serverIp.c_str(), sizeof(g_ServerBindAddress) - 1);
|
|
g_ServerBindAddress[sizeof(g_ServerBindAddress) - 1] = '\0';
|
|
}
|
|
else
|
|
{
|
|
g_ServerBindAddress[0] = '\0';
|
|
}
|
|
if (g_voiceChatEnabled && m_properties.maxPlayers > 8)
|
|
{
|
|
ServerLog(L"Voice chat has been disabled: max-players (%d) exceeds the maximum supported for voice chat (8)\n", m_properties.maxPlayers);
|
|
g_voiceChatEnabled = false;
|
|
}
|
|
else if (g_voiceChatEnabled)
|
|
{
|
|
ServerLog(L"Voice chat is enabled\n");
|
|
}
|
|
|
|
AABB::CreateNewThreadStorage();
|
|
Vec3::CreateNewThreadStorage();
|
|
IntCache::CreateNewThreadStorage();
|
|
Compression::CreateNewThreadStorage();
|
|
OldChunkStorage::CreateNewThreadStorage();
|
|
Level::enableLightingCache();
|
|
Tile::CreateNewThreadStorage();
|
|
|
|
MinecraftWorld_RunStaticCtors();
|
|
|
|
{
|
|
wchar_t exePath[MAX_PATH];
|
|
if (GetModuleFileNameW(NULL, exePath, MAX_PATH))
|
|
{
|
|
wstring exeDir(exePath);
|
|
size_t lastSlash = exeDir.find_last_of(L"\\/");
|
|
if (lastSlash != wstring::npos)
|
|
exeDir = exeDir.substr(0, lastSlash);
|
|
SetCurrentDirectoryW(exeDir.c_str());
|
|
#ifdef __linux__
|
|
wstring gameHDDPath = exeDir + L"/Linux/GameHDD";
|
|
CreateDirectoryW((exeDir + L"/Linux").c_str(), NULL);
|
|
#else
|
|
wstring gameHDDPath = exeDir + L"\\Windows64\\GameHDD";
|
|
CreateDirectoryW((exeDir + L"\\Windows64").c_str(), NULL);
|
|
#endif
|
|
CreateDirectoryW(gameHDDPath.c_str(), NULL);
|
|
}
|
|
}
|
|
|
|
StorageManager.Init(1, app.GetString(IDS_DEFAULT_SAVENAME), "savegame.dat", FIFTY_ONE_MB, NULL, NULL, "MinecraftDedicatedServer");
|
|
StorageManager.SetMaxSaves(99);
|
|
StorageManager.StoreTMSPathName();
|
|
|
|
g_NetworkManager.Initialise();
|
|
|
|
for (int i = 0; i < g_ServerMaxPlayers; i++)
|
|
{
|
|
IQNet::m_player[i].m_smallId = (BYTE)i;
|
|
IQNet::m_player[i].m_isRemote = false;
|
|
IQNet::m_player[i].m_isHostPlayer = (i == 0);
|
|
swprintf_s(IQNet::m_player[i].m_gamertag, 32, L"Server%d", i);
|
|
}
|
|
wcscpy_s(IQNet::m_player[0].m_gamertag, 32, m_properties.motd.c_str());
|
|
|
|
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);
|
|
app.SetGameHostOption(eGameHostOption_TrustPlayers, m_properties.trustPlayers ? 1 : 0);
|
|
app.SetGameHostOption(eGameHostOption_FireSpreads, m_properties.fireSpreads ? 1 : 0);
|
|
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"."));
|
|
pMinecraft->progressRenderer = new ProgressRenderer(pMinecraft);
|
|
pMinecraft->gameRenderer = new GameRenderer(pMinecraft);
|
|
for (int i = 0; i < 4; i++)
|
|
pMinecraft->stats[i] = new StatsCounter();
|
|
|
|
{
|
|
static DWORD dwProfileSettingsA[5] = {0, 0, 0, 0, 0};
|
|
ProfileManager.Initialise(TITLEID_MINECRAFT,
|
|
app.m_dwOfferID,
|
|
PROFILE_VERSION_10,
|
|
5,
|
|
4,
|
|
dwProfileSettingsA,
|
|
app.GAME_DEFINED_PROFILE_DATA_BYTES * XUSER_MAX_COUNT,
|
|
&app.uiGameDefinedDataChangedBitmask);
|
|
}
|
|
|
|
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);
|
|
else
|
|
ServerLog(L"Starting Minecraft server on *:%d\n", m_properties.serverPort);
|
|
m_running = true;
|
|
return true;
|
|
}
|
|
|
|
int DedicatedServer::run()
|
|
{
|
|
if (!m_running)
|
|
{
|
|
ServerWarn(L"Server not initialized!\n");
|
|
return 1;
|
|
}
|
|
|
|
ServerLog(L"Preparing level \"%s\"\n", m_properties.levelName.c_str());
|
|
|
|
StorageManager.GetSavesInfo(0, NULL, NULL, NULL);
|
|
PSAVE_DETAILS pSaveDetails = StorageManager.ReturnSavesInfo();
|
|
if (pSaveDetails && pSaveDetails->iSaveC > 0)
|
|
{
|
|
int bestIdx = 0;
|
|
time_t bestTime = pSaveDetails->SaveInfoA[0].metaData.modifiedTime;
|
|
for (int i = 1; i < pSaveDetails->iSaveC; i++)
|
|
{
|
|
if (pSaveDetails->SaveInfoA[i].metaData.modifiedTime > bestTime)
|
|
{
|
|
bestTime = pSaveDetails->SaveInfoA[i].metaData.modifiedTime;
|
|
bestIdx = i;
|
|
}
|
|
}
|
|
ServerLog(L"Loading existing world from save \"%S\"\n", pSaveDetails->SaveInfoA[bestIdx].UTF8SaveFilename);
|
|
StorageManager.LoadSaveData(&pSaveDetails->SaveInfoA[bestIdx], NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
ServerLog(L"No existing save found, creating new world\n");
|
|
wstring title = m_properties.levelName;
|
|
StorageManager.SetSaveTitle(title.c_str());
|
|
}
|
|
|
|
g_NetworkManager.HostGame(1, true, false, (unsigned char)m_properties.maxPlayers, 0);
|
|
|
|
extern CPlatformNetworkManagerStub *g_pPlatformNetworkManager;
|
|
g_pPlatformNetworkManager->NotifyPlayerJoined(&IQNet::m_player[0]);
|
|
|
|
g_NetworkManager.ServerReadyCreate(true);
|
|
g_NetworkManager.ServerStoppedCreate(true);
|
|
|
|
NetworkGameInitData initData;
|
|
initData.seed = m_properties.levelSeed;
|
|
initData.saveData = NULL;
|
|
initData.settings = 0;
|
|
initData.levelGen = NULL;
|
|
initData.texturePackId = 0;
|
|
initData.findSeed = (m_properties.levelSeed == 0);
|
|
|
|
if (m_properties.levelSize == L"classic")
|
|
{
|
|
initData.xzSize = 1 * 54;
|
|
initData.hellScale = 3;
|
|
}
|
|
else if (m_properties.levelSize == L"small")
|
|
{
|
|
initData.xzSize = 1 * 64;
|
|
initData.hellScale = 3;
|
|
}
|
|
else if (m_properties.levelSize == L"medium")
|
|
{
|
|
initData.xzSize = 3 * 64;
|
|
initData.hellScale = 6;
|
|
}
|
|
else
|
|
{
|
|
initData.xzSize = LEVEL_MAX_WIDTH;
|
|
initData.hellScale = HELL_LEVEL_MAX_SCALE;
|
|
}
|
|
|
|
AABB::CreateNewThreadStorage();
|
|
Vec3::CreateNewThreadStorage();
|
|
IntCache::CreateNewThreadStorage();
|
|
Compression::UseDefaultThreadStorage();
|
|
Entity::useSmallIds();
|
|
Level::enableLightingCache();
|
|
Tile::CreateNewThreadStorage();
|
|
|
|
ServerLog(L"Preparing start region for level 0\n");
|
|
MinecraftServer::main(m_properties.levelSeed, &initData);
|
|
|
|
g_NetworkManager.ServerReadyDestroy();
|
|
g_NetworkManager.ServerStoppedDestroy();
|
|
|
|
ServerLog(L"Server stopped\n");
|
|
return 0;
|
|
}
|
|
|
|
void DedicatedServer::shutdown()
|
|
{
|
|
ServerLog(L"Stopping server\n");
|
|
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");
|
|
}
|
|
|
|
void DedicatedServer::info(const wstring& string)
|
|
{
|
|
ServerLog(L"%s\n", string.c_str());
|
|
}
|
|
|
|
void DedicatedServer::warn(const wstring& string)
|
|
{
|
|
ServerWarn(L"%s\n", string.c_str());
|
|
}
|
|
|
|
wstring DedicatedServer::getConsoleName()
|
|
{
|
|
return L"Server";
|
|
}
|
|
|
|
int DedicatedServer::consoleInputThread(void *param)
|
|
{
|
|
DedicatedServer *server = (DedicatedServer *)param;
|
|
|
|
#ifdef __linux__
|
|
struct termios origTermios;
|
|
tcgetattr(STDIN_FILENO, &origTermios);
|
|
{
|
|
struct termios raw = origTermios;
|
|
raw.c_lflag &= ~(ICANON | ECHO);
|
|
raw.c_cc[VMIN] = 1;
|
|
raw.c_cc[VTIME] = 0;
|
|
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
|
|
}
|
|
#endif
|
|
|
|
wstring buffer;
|
|
vector<wstring> history;
|
|
int historyPos = -1;
|
|
wstring savedBuffer;
|
|
ServerLog_RegisterInput(&buffer);
|
|
|
|
auto clearLine = [&]() {
|
|
if (buffer.empty()) return;
|
|
#ifdef __linux__
|
|
for (size_t i = 0; i < buffer.size(); i++)
|
|
printf("\b \b");
|
|
fflush(stdout);
|
|
#else
|
|
for (size_t i = 0; i < buffer.size(); i++)
|
|
wprintf(L"\b \b");
|
|
#endif
|
|
};
|
|
|
|
auto redrawLine = [&]() {
|
|
#ifdef __linux__
|
|
printf("%ls", buffer.c_str());
|
|
fflush(stdout);
|
|
#else
|
|
wprintf(L"%s", buffer.c_str());
|
|
#endif
|
|
};
|
|
|
|
while (server->m_running)
|
|
{
|
|
#ifdef __linux__
|
|
char c;
|
|
if (read(STDIN_FILENO, &c, 1) <= 0) break;
|
|
wchar_t wc = (wchar_t)(unsigned char)c;
|
|
#else
|
|
wchar_t wc = _getwch();
|
|
#endif
|
|
|
|
if (wc == L'\t')
|
|
{
|
|
ServerLog_LockOutput();
|
|
MinecraftServer *mcServer = MinecraftServer::getInstance();
|
|
vector<wstring> completions = GetServerCommandCompletions(buffer, mcServer);
|
|
|
|
if (completions.size() == 1)
|
|
{
|
|
size_t lastSpace = buffer.find_last_of(L' ');
|
|
wstring currentWord = (lastSpace == wstring::npos) ? buffer : buffer.substr(lastSpace + 1);
|
|
wstring toAppend = completions[0].substr(currentWord.size()) + L" ";
|
|
buffer += toAppend;
|
|
#ifdef __linux__
|
|
printf("%ls", toAppend.c_str());
|
|
fflush(stdout);
|
|
#else
|
|
wprintf(L"%s", toAppend.c_str());
|
|
#endif
|
|
}
|
|
else if (completions.size() > 1)
|
|
{
|
|
wstring common = completions[0];
|
|
for (size_t i = 1; i < completions.size(); i++)
|
|
{
|
|
size_t j = 0;
|
|
while (j < common.size() && j < completions[i].size() &&
|
|
towlower(common[j]) == towlower(completions[i][j]))
|
|
j++;
|
|
common = common.substr(0, j);
|
|
}
|
|
|
|
size_t lastSpace = buffer.find_last_of(L' ');
|
|
wstring currentWord = (lastSpace == wstring::npos) ? buffer : buffer.substr(lastSpace + 1);
|
|
|
|
if (common.size() > currentWord.size())
|
|
{
|
|
wstring toAppend = common.substr(currentWord.size());
|
|
buffer += toAppend;
|
|
#ifdef __linux__
|
|
printf("%ls", toAppend.c_str());
|
|
fflush(stdout);
|
|
#else
|
|
wprintf(L"%s", toAppend.c_str());
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef __linux__
|
|
printf("\n");
|
|
for (size_t i = 0; i < completions.size(); i++)
|
|
printf("%ls ", completions[i].c_str());
|
|
printf("\n%ls", buffer.c_str());
|
|
fflush(stdout);
|
|
#else
|
|
wprintf(L"\n");
|
|
for (size_t i = 0; i < completions.size(); i++)
|
|
wprintf(L"%s ", completions[i].c_str());
|
|
wprintf(L"\n%s", buffer.c_str());
|
|
#endif
|
|
}
|
|
}
|
|
ServerLog_UnlockOutput();
|
|
}
|
|
else if (wc == L'\r' || wc == L'\n')
|
|
{
|
|
ServerLog_LockOutput();
|
|
#ifdef __linux__
|
|
printf("\n");
|
|
fflush(stdout);
|
|
#else
|
|
wprintf(L"\n");
|
|
#endif
|
|
wstring cmd = buffer;
|
|
buffer.clear();
|
|
ServerLog_UnlockOutput();
|
|
if (!cmd.empty())
|
|
{
|
|
if (history.empty() || history.back() != cmd)
|
|
history.push_back(cmd);
|
|
historyPos = -1;
|
|
|
|
if (cmd == L"stop" || cmd == L"quit" || cmd == L"exit")
|
|
{
|
|
ServerLog(L"Stopping the server\n");
|
|
MinecraftServer::HaltServer();
|
|
server->m_running = false;
|
|
break;
|
|
}
|
|
MinecraftServer *mcServer = MinecraftServer::getInstance();
|
|
if (mcServer)
|
|
mcServer->handleConsoleInput(cmd, server);
|
|
}
|
|
}
|
|
else if (wc == 127 || wc == 8)
|
|
{
|
|
if (!buffer.empty())
|
|
{
|
|
ServerLog_LockOutput();
|
|
buffer.pop_back();
|
|
#ifdef __linux__
|
|
printf("\b \b");
|
|
fflush(stdout);
|
|
#else
|
|
wprintf(L"\b \b");
|
|
#endif
|
|
ServerLog_UnlockOutput();
|
|
}
|
|
}
|
|
#ifdef __linux__
|
|
else if (c == 27)
|
|
{
|
|
char seq[2];
|
|
if (read(STDIN_FILENO, &seq[0], 1) != 1) continue;
|
|
if (seq[0] != '[') continue;
|
|
if (read(STDIN_FILENO, &seq[1], 1) != 1) continue;
|
|
|
|
if (seq[1] == 'A')
|
|
{
|
|
if (history.empty()) continue;
|
|
if (historyPos == -1)
|
|
{
|
|
savedBuffer = buffer;
|
|
historyPos = (int)history.size() - 1;
|
|
}
|
|
else if (historyPos > 0)
|
|
{
|
|
historyPos--;
|
|
}
|
|
ServerLog_LockOutput();
|
|
clearLine();
|
|
buffer = history[historyPos];
|
|
redrawLine();
|
|
ServerLog_UnlockOutput();
|
|
}
|
|
else if (seq[1] == 'B')
|
|
{
|
|
if (historyPos == -1) continue;
|
|
ServerLog_LockOutput();
|
|
clearLine();
|
|
if (historyPos < (int)history.size() - 1)
|
|
{
|
|
historyPos++;
|
|
buffer = history[historyPos];
|
|
}
|
|
else
|
|
{
|
|
historyPos = -1;
|
|
buffer = savedBuffer;
|
|
}
|
|
redrawLine();
|
|
ServerLog_UnlockOutput();
|
|
}
|
|
}
|
|
#else
|
|
else if (wc == 0 || wc == 0xE0)
|
|
{
|
|
wchar_t ext = _getwch();
|
|
if (ext == 72)
|
|
{
|
|
if (history.empty()) continue;
|
|
if (historyPos == -1)
|
|
{
|
|
savedBuffer = buffer;
|
|
historyPos = (int)history.size() - 1;
|
|
}
|
|
else if (historyPos > 0)
|
|
{
|
|
historyPos--;
|
|
}
|
|
ServerLog_LockOutput();
|
|
clearLine();
|
|
buffer = history[historyPos];
|
|
redrawLine();
|
|
ServerLog_UnlockOutput();
|
|
}
|
|
else if (ext == 80)
|
|
{
|
|
if (historyPos == -1) continue;
|
|
ServerLog_LockOutput();
|
|
clearLine();
|
|
if (historyPos < (int)history.size() - 1)
|
|
{
|
|
historyPos++;
|
|
buffer = history[historyPos];
|
|
}
|
|
else
|
|
{
|
|
historyPos = -1;
|
|
buffer = savedBuffer;
|
|
}
|
|
redrawLine();
|
|
ServerLog_UnlockOutput();
|
|
}
|
|
}
|
|
#endif
|
|
else if (wc >= 32)
|
|
{
|
|
ServerLog_LockOutput();
|
|
buffer += wc;
|
|
#ifdef __linux__
|
|
printf("%lc", wc);
|
|
fflush(stdout);
|
|
#else
|
|
wprintf(L"%c", wc);
|
|
#endif
|
|
ServerLog_UnlockOutput();
|
|
}
|
|
}
|
|
|
|
ServerLog_UnregisterInput();
|
|
#ifdef __linux__
|
|
tcsetattr(STDIN_FILENO, TCSAFLUSH, &origTermios);
|
|
#endif
|
|
return 0;
|
|
} |