diff options
-rw-r--r-- | MCServer/delete_windows_service.cmd | 4 | ||||
-rw-r--r-- | MCServer/install_windows_service.cmd | 7 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/GeneratorSetup.cpp | 12 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/Globals.h | 5 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/QtBiomeVisualiser.pro | 2 | ||||
-rw-r--r-- | src/BiomeDef.cpp | 7 | ||||
-rw-r--r-- | src/BlockID.cpp | 29 | ||||
-rw-r--r-- | src/Blocks/BlockButton.h | 6 | ||||
-rw-r--r-- | src/Items/ItemSlab.h | 4 | ||||
-rw-r--r-- | src/Root.cpp | 12 | ||||
-rw-r--r-- | src/Root.h | 4 | ||||
-rw-r--r-- | src/main.cpp | 224 |
12 files changed, 272 insertions, 44 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/Tools/QtBiomeVisualiser/GeneratorSetup.cpp b/Tools/QtBiomeVisualiser/GeneratorSetup.cpp index 8f97e1f37..54b7ef36e 100644 --- a/Tools/QtBiomeVisualiser/GeneratorSetup.cpp +++ b/Tools/QtBiomeVisualiser/GeneratorSetup.cpp @@ -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); diff --git a/Tools/QtBiomeVisualiser/Globals.h b/Tools/QtBiomeVisualiser/Globals.h index e2e9a9970..cc90f9383 100644 --- a/Tools/QtBiomeVisualiser/Globals.h +++ b/Tools/QtBiomeVisualiser/Globals.h @@ -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 diff --git a/Tools/QtBiomeVisualiser/QtBiomeVisualiser.pro b/Tools/QtBiomeVisualiser/QtBiomeVisualiser.pro index cccee1305..daf2d4020 100644 --- a/Tools/QtBiomeVisualiser/QtBiomeVisualiser.pro +++ b/Tools/QtBiomeVisualiser/QtBiomeVisualiser.pro @@ -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 \ diff --git a/src/BiomeDef.cpp b/src/BiomeDef.cpp index 3e34ebdbe..a2a06f10c 100644 --- a/src/BiomeDef.cpp +++ b/src/BiomeDef.cpp @@ -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: { 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/Blocks/BlockButton.h b/src/Blocks/BlockButton.h index 8e4f04740..d24c7d952 100644 --- a/src/Blocks/BlockButton.h +++ b/src/Blocks/BlockButton.h @@ -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!"); diff --git a/src/Items/ItemSlab.h b/src/Items/ItemSlab.h index 1b68b9d0c..b0b5ce005 100644 --- a/src/Items/ItemSlab.h +++ b/src/Items/ItemSlab.h @@ -44,8 +44,8 @@ public: NIBBLETYPE ClickedBlockMeta; a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlockType, ClickedBlockMeta); if ( - (ClickedBlockType == m_ItemType) && // Placing the same slab material - (ClickedBlockMeta == a_EquippedItem.m_ItemDamage) // Placing the same slab sub-kind (and existing slab is single) + (ClickedBlockType == m_ItemType) && // Placing the same slab material + ((ClickedBlockMeta & 0x07) == a_EquippedItem.m_ItemDamage) // Placing the same slab sub-kind (and existing slab is single) ) { // If clicking the top side of a bottom-half slab, combine into a doubleslab: 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; } |