Compare commits
7 Commits
53f5cc209f
...
e6819c4c65
Author | SHA1 | Date |
---|---|---|
STRWarrior | e6819c4c65 | |
Mattes D | afdd751fe3 | |
Mattes D | 11f5ee27ab | |
Anthony Birkett | 51891b766c | |
Mattes D | d3aba9ed3f | |
Alexandre Guertin | 97daf810ff | |
Alexandre Guertin | adf5edc913 |
|
@ -0,0 +1,4 @@
|
|||
@echo off
|
||||
set SERVICENAME="MCServer"
|
||||
|
||||
sc delete %SERVICENAME%
|
|
@ -0,0 +1,7 @@
|
|||
rem Alter this if you need to install multiple instances.
|
||||
@echo off
|
||||
set SERVICENAME="MCServer"
|
||||
|
||||
set CURRENTDIR=%CD%
|
||||
sc create %SERVICENAME% binPath= "%CURRENTDIR%\MCServer.exe /service" start= auto DisplayName= %SERVICENAME%
|
||||
sc description %SERVICENAME% "Minecraft server instance"
|
|
@ -120,15 +120,23 @@ void GeneratorSetup::editChanged(const QString & a_NewValue)
|
|||
|
||||
void GeneratorSetup::updateFromIni()
|
||||
{
|
||||
m_eSeed->setText(QString::number(m_IniFile->GetValueI("Seed", "Seed", 0)));
|
||||
// Set the seed editbox:
|
||||
int seed = m_IniFile->GetValueI("Seed", "Seed", 0);
|
||||
m_eSeed->setText(QString::number(seed));
|
||||
int keyID = m_IniFile->FindKey("Generator");
|
||||
if (keyID <= -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int numItems = m_IniFile->GetNumValues(keyID);
|
||||
|
||||
// Set the Generator combobox:
|
||||
AString generatorName = m_IniFile->GetValue("Generator", "BiomeGen");
|
||||
size_t generatorNameLen = generatorName.length();
|
||||
int index = m_cbGenerator->findText(QString::fromStdString(generatorName));
|
||||
m_cbGenerator->setCurrentIndex(index);
|
||||
|
||||
// Create the controls for all the generator settings in the INI file:
|
||||
int numItems = m_IniFile->GetNumValues(keyID);
|
||||
for (int i = 0; i < numItems; i++)
|
||||
{
|
||||
AString itemName = m_IniFile->GetValueName(keyID, i);
|
||||
|
|
|
@ -176,8 +176,7 @@ template class SizeChecker<UInt16, 2>;
|
|||
// OS-dependent stuff:
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#define _WIN32_WINNT 0x501 // We want to target WinXP and higher
|
||||
#define _WIN32_WINNT _WIN32_WINNT_WS03 // We want to target Windows XP with Service Pack 2 & Windows Server 2003 with Service Pack 1 and higher
|
||||
|
||||
#include <Windows.h>
|
||||
#include <winsock2.h>
|
||||
|
@ -248,11 +247,9 @@ template class SizeChecker<UInt16, 2>;
|
|||
#ifndef TEST_GLOBALS
|
||||
// Common headers (part 1, without macros):
|
||||
#include "src/StringUtils.h"
|
||||
#include "src/OSSupport/Sleep.h"
|
||||
#include "src/OSSupport/CriticalSection.h"
|
||||
#include "src/OSSupport/Semaphore.h"
|
||||
#include "src/OSSupport/Event.h"
|
||||
#include "src/OSSupport/Thread.h"
|
||||
#include "src/OSSupport/File.h"
|
||||
#include "src/Logger.h"
|
||||
#else
|
||||
|
|
|
@ -22,6 +22,7 @@ SOURCES += \
|
|||
../../src/LoggerListeners.cpp \
|
||||
../../src/Logger.cpp \
|
||||
../../src/IniFile.cpp \
|
||||
../../src/OSSupport/Event.cpp \
|
||||
../../src/OSSupport/File.cpp \
|
||||
../../src/OSSupport/CriticalSection.cpp \
|
||||
../../src/OSSupport/IsThread.cpp \
|
||||
|
@ -69,6 +70,7 @@ HEADERS += \
|
|||
../../src/LoggerListeners.h \
|
||||
../../src/Logger.h \
|
||||
../../src/IniFile.h \
|
||||
../../src/OSSupport/Event.h \
|
||||
../../src/OSSupport/File.h \
|
||||
../../src/OSSupport/CriticalSection.h \
|
||||
../../src/OSSupport/IsThread.h \
|
||||
|
|
|
@ -338,6 +338,13 @@ int GetSnowStartHeight(EMCSBiome a_Biome)
|
|||
// These biomes don't actualy have any downfall.
|
||||
return 1000;
|
||||
}
|
||||
|
||||
case biNether:
|
||||
case biEnd:
|
||||
{
|
||||
// These shouldn't get any snow at all.
|
||||
return 9999;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
|
|
|
@ -26,8 +26,18 @@ class cBlockIDMap
|
|||
typedef std::map<AString, std::pair<short, short>, Comparator> ItemMap;
|
||||
|
||||
public:
|
||||
static bool m_bHasRunInit;
|
||||
|
||||
cBlockIDMap(void)
|
||||
{
|
||||
// Dont load items.ini on construct, this will search the wrong path when running as a service.
|
||||
}
|
||||
|
||||
|
||||
void init()
|
||||
{
|
||||
m_bHasRunInit = true;
|
||||
|
||||
cIniFile Ini;
|
||||
if (!Ini.ReadFile("items.ini"))
|
||||
{
|
||||
|
@ -174,7 +184,7 @@ protected:
|
|||
|
||||
|
||||
|
||||
|
||||
bool cBlockIDMap::m_bHasRunInit = false;
|
||||
static cBlockIDMap gsBlockIDMap;
|
||||
|
||||
|
||||
|
@ -209,6 +219,10 @@ int BlockStringToType(const AString & a_BlockTypeString)
|
|||
return res;
|
||||
}
|
||||
|
||||
if (!gsBlockIDMap.m_bHasRunInit)
|
||||
{
|
||||
gsBlockIDMap.init();
|
||||
}
|
||||
return gsBlockIDMap.Resolve(TrimString(a_BlockTypeString));
|
||||
}
|
||||
|
||||
|
@ -222,6 +236,11 @@ bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item)
|
|||
{
|
||||
ItemName = ItemName.substr(10);
|
||||
}
|
||||
|
||||
if (!gsBlockIDMap.m_bHasRunInit)
|
||||
{
|
||||
gsBlockIDMap.init();
|
||||
}
|
||||
return gsBlockIDMap.ResolveItem(ItemName, a_Item);
|
||||
}
|
||||
|
||||
|
@ -231,6 +250,10 @@ bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item)
|
|||
|
||||
AString ItemToString(const cItem & a_Item)
|
||||
{
|
||||
if (!gsBlockIDMap.m_bHasRunInit)
|
||||
{
|
||||
gsBlockIDMap.init();
|
||||
}
|
||||
return gsBlockIDMap.Desolve(a_Item.m_ItemType, a_Item.m_ItemDamage);
|
||||
}
|
||||
|
||||
|
@ -240,6 +263,10 @@ AString ItemToString(const cItem & a_Item)
|
|||
|
||||
AString ItemTypeToString(short a_ItemType)
|
||||
{
|
||||
if (!gsBlockIDMap.m_bHasRunInit)
|
||||
{
|
||||
gsBlockIDMap.init();
|
||||
}
|
||||
return gsBlockIDMap.Desolve(a_ItemType, -1);
|
||||
}
|
||||
|
||||
|
|
|
@ -61,14 +61,16 @@ public:
|
|||
{
|
||||
switch (a_BlockFace)
|
||||
{
|
||||
case BLOCK_FACE_YP: return 0x5;
|
||||
case BLOCK_FACE_ZM: return 0x4;
|
||||
case BLOCK_FACE_ZP: return 0x3;
|
||||
case BLOCK_FACE_XM: return 0x2;
|
||||
case BLOCK_FACE_XP: return 0x1;
|
||||
case BLOCK_FACE_YM: return 0x0;
|
||||
default:
|
||||
{
|
||||
ASSERT(!"Unhandled block face!");
|
||||
return 0x0; // No idea, give a special meta (button in centre of block)
|
||||
return 0x0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,10 +79,12 @@ public:
|
|||
{
|
||||
switch (a_Meta & 0x7)
|
||||
{
|
||||
case 0x0: return BLOCK_FACE_YM;
|
||||
case 0x1: return BLOCK_FACE_XP;
|
||||
case 0x2: return BLOCK_FACE_XM;
|
||||
case 0x3: return BLOCK_FACE_ZP;
|
||||
case 0x4: return BLOCK_FACE_ZM;
|
||||
case 0x5: return BLOCK_FACE_YP;
|
||||
default:
|
||||
{
|
||||
ASSERT(!"Unhandled block meta!");
|
||||
|
|
12
src/Root.cpp
12
src/Root.cpp
|
@ -84,7 +84,10 @@ void cRoot::InputThread(cRoot & a_Params)
|
|||
if (m_TerminateEventRaised || !std::cin.good())
|
||||
{
|
||||
// We have come here because the std::cin has received an EOF / a terminate signal has been sent, and the server is still running; stop the server:
|
||||
a_Params.m_bStop = true;
|
||||
if (m_RunAsService) // HACK: Dont kill if running as a service
|
||||
{
|
||||
a_Params.m_bStop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,6 +271,13 @@ void cRoot::Start(void)
|
|||
|
||||
|
||||
|
||||
void cRoot::SetStopping(bool a_Stopping)
|
||||
{
|
||||
m_bStop = a_Stopping;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void cRoot::LoadGlobalSettings()
|
||||
{
|
||||
|
|
|
@ -46,6 +46,7 @@ public:
|
|||
// tolua_end
|
||||
|
||||
static bool m_TerminateEventRaised;
|
||||
static bool m_RunAsService;
|
||||
|
||||
|
||||
cRoot(void);
|
||||
|
@ -53,6 +54,9 @@ public:
|
|||
|
||||
void Start(void);
|
||||
|
||||
// Added so the service handler can request a stop
|
||||
void SetStopping(bool a_Stopping);
|
||||
|
||||
// tolua_begin
|
||||
cServer * GetServer(void) { return m_Server; }
|
||||
cWorld * GetDefaultWorld(void);
|
||||
|
|
230
src/main.cpp
230
src/main.cpp
|
@ -15,6 +15,8 @@
|
|||
|
||||
|
||||
|
||||
/** Make the Root instance global, so it can be terminated from the worker threads */
|
||||
cRoot Root;
|
||||
|
||||
|
||||
/** If something has told the server to stop; checked periodically in cRoot */
|
||||
|
@ -29,8 +31,15 @@ bool g_ShouldLogCommIn;
|
|||
/** If set to true, the protocols will log each player's outgoing (S->C) communication to a per-connection logfile */
|
||||
bool g_ShouldLogCommOut;
|
||||
|
||||
/** If set to true, binary will attempt to run as a service on Windows */
|
||||
bool cRoot::m_RunAsService = false;
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
|
||||
HANDLE g_ServiceThread = INVALID_HANDLE_VALUE;
|
||||
#define SERVICE_NAME "MCServerService"
|
||||
#endif
|
||||
|
||||
|
||||
/// If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window
|
||||
|
@ -179,6 +188,165 @@ BOOL CtrlHandler(DWORD fdwCtrlType)
|
|||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// universalMain - Main startup logic for both standard running and as a service
|
||||
|
||||
void universalMain()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE))
|
||||
{
|
||||
LOGERROR("Could not install the Windows CTRL handler!");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Initialize logging subsystem:
|
||||
cLogger::InitiateMultithreading();
|
||||
|
||||
// Initialize LibEvent:
|
||||
cNetworkSingleton::Get();
|
||||
|
||||
#if !defined(ANDROID_NDK)
|
||||
try
|
||||
#endif
|
||||
{
|
||||
Root.Start();
|
||||
}
|
||||
#if !defined(ANDROID_NDK)
|
||||
catch (std::exception & e)
|
||||
{
|
||||
LOGERROR("Standard exception: %s", e.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOGERROR("Unknown exception!");
|
||||
}
|
||||
#endif
|
||||
|
||||
g_ServerTerminated = true;
|
||||
|
||||
// Shutdown all of LibEvent:
|
||||
cNetworkSingleton::Get().Terminate();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// serviceWorkerThread: Keep the service alive
|
||||
|
||||
DWORD WINAPI serviceWorkerThread(LPVOID lpParam)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(lpParam);
|
||||
|
||||
// Do the normal startup
|
||||
universalMain();
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// serviceSetState: Set the internal status of the service
|
||||
|
||||
void serviceSetState(DWORD acceptedControls, DWORD newState, DWORD exitCode)
|
||||
{
|
||||
SERVICE_STATUS serviceStatus;
|
||||
ZeroMemory(&serviceStatus, sizeof(SERVICE_STATUS));
|
||||
serviceStatus.dwCheckPoint = 0;
|
||||
serviceStatus.dwControlsAccepted = acceptedControls;
|
||||
serviceStatus.dwCurrentState = newState;
|
||||
serviceStatus.dwServiceSpecificExitCode = 0;
|
||||
serviceStatus.dwServiceType = SERVICE_WIN32;
|
||||
serviceStatus.dwWaitHint = 0;
|
||||
serviceStatus.dwWin32ExitCode = exitCode;
|
||||
|
||||
if (SetServiceStatus(g_StatusHandle, &serviceStatus) == FALSE)
|
||||
{
|
||||
LOGERROR("SetServiceStatus() failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// serviceCtrlHandler: Handle stop events from the Service Control Manager
|
||||
|
||||
void WINAPI serviceCtrlHandler(DWORD CtrlCode)
|
||||
{
|
||||
switch (CtrlCode)
|
||||
{
|
||||
case SERVICE_CONTROL_STOP:
|
||||
{
|
||||
Root.SetStopping(true);
|
||||
serviceSetState(0, SERVICE_STOP_PENDING, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// serviceMain: Startup logic for running as a service
|
||||
|
||||
void WINAPI serviceMain(DWORD argc, TCHAR *argv[])
|
||||
{
|
||||
#if defined(_DEBUG) && defined(DEBUG_SERVICE_STARTUP)
|
||||
Sleep(10000);
|
||||
#endif
|
||||
|
||||
char applicationFilename[MAX_PATH];
|
||||
char applicationDirectory[MAX_PATH];
|
||||
|
||||
GetModuleFileName(NULL, applicationFilename, MAX_PATH); // This binaries fill path.
|
||||
|
||||
// GetModuleFileName() returns the path and filename. Strip off the filename.
|
||||
strncpy(applicationDirectory, applicationFilename, (strrchr(applicationFilename, '\\') - applicationFilename));
|
||||
applicationDirectory[strlen(applicationDirectory)] = '\0'; // Make sure new path is null terminated
|
||||
|
||||
// Services are run by the SCM, and inherit its working directory - usually System32.
|
||||
// Set the working directory to the same location as the binary.
|
||||
SetCurrentDirectory(applicationDirectory);
|
||||
|
||||
g_StatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, serviceCtrlHandler);
|
||||
|
||||
if (g_StatusHandle == NULL)
|
||||
{
|
||||
OutputDebugString("RegisterServiceCtrlHandler() failed\n");
|
||||
serviceSetState(0, SERVICE_STOPPED, GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
serviceSetState(SERVICE_ACCEPT_STOP, SERVICE_RUNNING, 0);
|
||||
|
||||
g_ServiceThread = CreateThread(NULL, 0, serviceWorkerThread, NULL, 0, NULL);
|
||||
if (g_ServiceThread == NULL)
|
||||
{
|
||||
OutputDebugString("CreateThread() failed\n");
|
||||
serviceSetState(0, SERVICE_STOPPED, GetLastError());
|
||||
return;
|
||||
}
|
||||
WaitForSingleObject(g_ServiceThread, INFINITE); // Wait here for a stop signal.
|
||||
|
||||
CloseHandle(g_ServiceThread);
|
||||
|
||||
serviceSetState(0, SERVICE_STOPPED, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// main:
|
||||
|
@ -219,13 +387,6 @@ int main( int argc, char **argv)
|
|||
#endif // _WIN32 && !_WIN64
|
||||
// End of dump-file magic
|
||||
|
||||
#ifdef _WIN32
|
||||
if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE))
|
||||
{
|
||||
LOGERROR("Could not install the Windows CTRL handler!");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_DEBUG) && defined(_MSC_VER)
|
||||
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
||||
|
||||
|
@ -280,42 +441,39 @@ int main( int argc, char **argv)
|
|||
{
|
||||
setvbuf(stdout, nullptr, _IONBF, 0);
|
||||
}
|
||||
else if (NoCaseCompare(Arg, "/service") == 0)
|
||||
{
|
||||
cRoot::m_RunAsService = true;
|
||||
}
|
||||
} // for i - argv[]
|
||||
|
||||
// Initialize logging subsystem:
|
||||
cLogger::InitiateMultithreading();
|
||||
|
||||
// Initialize LibEvent:
|
||||
cNetworkSingleton::Get();
|
||||
|
||||
#if !defined(ANDROID_NDK)
|
||||
try
|
||||
#endif
|
||||
{
|
||||
cRoot Root;
|
||||
Root.Start();
|
||||
}
|
||||
#if !defined(ANDROID_NDK)
|
||||
catch (std::exception & e)
|
||||
{
|
||||
LOGERROR("Standard exception: %s", e.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOGERROR("Unknown exception!");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Attempt to run as a service
|
||||
if (cRoot::m_RunAsService)
|
||||
{
|
||||
SERVICE_TABLE_ENTRY ServiceTable[] =
|
||||
{
|
||||
{ SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)serviceMain },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
if (StartServiceCtrlDispatcher(ServiceTable) == FALSE)
|
||||
{
|
||||
LOGERROR("Attempted, but failed, service startup.");
|
||||
return GetLastError();
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// Not running as a service, do normal startup
|
||||
universalMain();
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
|
||||
DeinitLeakFinder();
|
||||
#endif
|
||||
|
||||
g_ServerTerminated = true;
|
||||
|
||||
// Shutdown all of LibEvent:
|
||||
cNetworkSingleton::Get().Terminate();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue