Compare commits

...

7 Commits

Author SHA1 Message Date
STRWarrior e6819c4c65 Fixed the nether and end getting snow if they were generated in the overworld. 2015-04-03 16:32:31 +02:00
Mattes D afdd751fe3 Merge pull request #1799 from SnappingTurtles/BlockButton
Buttons can be placed on the top and on the bottom face of a block
2015-04-03 15:41:39 +02:00
Mattes D 11f5ee27ab Merge pull request #1845 from birkett/master
Enable MCServer to run as a service on Windows.
2015-04-03 10:35:41 +02:00
Anthony Birkett 51891b766c Working as a Windows service. Starts and stops correctly.
Added "/service" switch, to prompt the binary to attempt starting as a service.
Added service* methods, to control service startup.
Split up main() into universalMain(), which contains the startup code for both service and normal start.
Added cRoot::m_RunningAsService bool,
Added cRoot::SetStopping(bool) to allow a stop request to be sent by the service controller.
Added cBlockIDMap::init() to avoid loading items.ini before the working directory has been set.
2015-04-01 00:03:37 +01:00
Mattes D d3aba9ed3f QtBiomeVisualiser: Fixed compilation and INI loading. 2015-03-28 20:47:24 +01:00
Alexandre Guertin 97daf810ff Removed 0x6, 0x7 from BlockButton. 2015-03-25 16:12:02 -04:00
Alexandre Guertin adf5edc913 Buttons can be placed on the top and on the bottom face of a block 2015-03-09 18:13:55 -04:00
11 changed files with 273 additions and 45 deletions

View File

@ -0,0 +1,4 @@
@echo off
set SERVICENAME="MCServer"
sc delete %SERVICENAME%

View File

@ -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"

View File

@ -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);

View File

@ -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

View File

@ -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 \

View File

@ -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:
{

View File

@ -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);
}

View File

@ -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!");

View File

@ -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()
{

View File

@ -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);

View File

@ -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;
}