summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--MCServer/delete_windows_service.cmd4
-rw-r--r--MCServer/install_windows_service.cmd7
-rw-r--r--src/BlockID.cpp29
-rw-r--r--src/Root.cpp12
-rw-r--r--src/Root.h4
-rw-r--r--src/main.cpp224
6 files changed, 245 insertions, 35 deletions
diff --git a/MCServer/delete_windows_service.cmd b/MCServer/delete_windows_service.cmd
new file mode 100644
index 000000000..ab6238e2f
--- /dev/null
+++ b/MCServer/delete_windows_service.cmd
@@ -0,0 +1,4 @@
+@echo off
+set SERVICENAME="MCServer"
+
+sc delete %SERVICENAME% \ No newline at end of file
diff --git a/MCServer/install_windows_service.cmd b/MCServer/install_windows_service.cmd
new file mode 100644
index 000000000..ba8a8c128
--- /dev/null
+++ b/MCServer/install_windows_service.cmd
@@ -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" \ No newline at end of file
diff --git a/src/BlockID.cpp b/src/BlockID.cpp
index 06f4232d3..7f0db3cfc 100644
--- a/src/BlockID.cpp
+++ b/src/BlockID.cpp
@@ -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);
}
diff --git a/src/Root.cpp b/src/Root.cpp
index 27d87c717..87bc29627 100644
--- a/src/Root.cpp
+++ b/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()
{
diff --git a/src/Root.h b/src/Root.h
index fdaf444bd..d552466dc 100644
--- a/src/Root.h
+++ b/src/Root.h
@@ -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);
diff --git a/src/main.cpp b/src/main.cpp
index 428e89e93..da8eb75d4 100644
--- a/src/main.cpp
+++ b/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)
+
+ #if defined(_WIN32)
+ // Attempt to run as a service
+ if (cRoot::m_RunAsService)
{
- LOGERROR("Standard exception: %s", e.what());
+ SERVICE_TABLE_ENTRY ServiceTable[] =
+ {
+ { SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)serviceMain },
+ { NULL, NULL }
+ };
+
+ if (StartServiceCtrlDispatcher(ServiceTable) == FALSE)
+ {
+ LOGERROR("Attempted, but failed, service startup.");
+ return GetLastError();
+ }
}
- catch (...)
+ else
+ #endif
{
- LOGERROR("Unknown exception!");
+ // Not running as a service, do normal startup
+ universalMain();
}
- #endif
-
#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;
}