/********************************************************************* * Purpose: to initialize the timer * Return: TRUE(success) or FALSE(failed) *********************************************************************/ int D2GSTimerInitialize(void) { DWORD dwThreadId; /* create stop event */ hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!hStopEvent) { D2GSEventLog("D2GSTimerInitialize", "Failed in creating event object. Code: %lu", GetLastError()); return FALSE; } /* create the working thread */ ghTimerThread = CreateThread(NULL, 0, D2GSTimerProcessor, NULL, 0, &dwThreadId); if (!ghTimerThread) { D2GSEventLog("D2GSTimerInitialize", "Can't CreateThread D2GSTimerProcessor. Code: %lu", GetLastError()); CleanupRoutineForTimer(); return FALSE; } /* add to the cleanup routine list */ if (CleanupRoutineInsert(CleanupRoutineForTimer, "D2GS Timer")) { return TRUE; } else { /* do some cleanup before quiting */ CleanupRoutineForTimer(); return FALSE; } } /* End of D2GSTimerInitialize() */
/********************************************************************* * Purpose: to initialize the global variables * Return: TRUE or FALSE *********************************************************************/ int D2GSVarsInitialize(void) { DWORD val; ZeroMemory(&d2gsconf, sizeof(d2gsconf)); bGERunning = FALSE; /* calculate file checksum */ val = VersionCheck(); if (!val) { D2GSEventLog("D2GSVarsInitialize", "Failed calculating file checksum"); return FALSE; } d2gsconf.checksum = val; /* initialize char list table */ if (charlist_init(DEFAULT_HASHTBL_LEN)!=0) { D2GSEventLog("D2GSVarsInitialize", "Failed initialize charlist table"); return FALSE; } /* initialize the CriticalSection Objects */ InitializeCriticalSection(&csGameList); if (CleanupRoutineInsert(CleanupRoutineForVars, "Global Variables")) { return TRUE; } else { /* do some cleanup before quiting */ CleanupRoutineForVars(); return FALSE; } } /* End of D2GSVarsInitialize() */
/********************************************************************* * Purpose: timer processor * Return: return value of the thread *********************************************************************/ DWORD WINAPI D2GSTimerProcessor(LPVOID lpParameter) { DWORD dwWait; int WaitSeq = 0; int WaitSeq2 = 0; srand(time(NULL)); while(TRUE) { dwWait = WaitForSingleObject(hStopEvent, TIMER_TICK_IN_MS); if (dwWait==WAIT_FAILED) { D2GSEventLog("D2GSTimerProcessor", "WaitForSingleObject failed. Code: %lu", GetLastError()); continue; } else if (dwWait==WAIT_OBJECT_0) { /* stop event be set, quit */ D2GSEventLog("D2GSTimerProcessor", "Terminate timer thread"); return TRUE; } else if (dwWait==WAIT_TIMEOUT) { /*char gamename[16]; int i; DWORD x;*/ /* a tick passed, call the routine to do something */ WaitSeq ++; D2GSPendingCharTimerRoutine(); D2GSGetDataRequestTimerRoutine(); D2GSCalculateNetStatistic(); D2GSSendMOTD(); //just for test /*if (D2GSGetCurrentGameNumber()<(int)(d2gsconf.gsmaxgames)) { for (i = 0; i < 10; i ++) gamename[i] = 97 + rand() % 25; gamename[10] = 0; D2GSNewEmptyGame(gamename, "", "", 0x302004, 0x11, 0x22, 0x33, &x); }*/ // if (WaitSeq > 10) { D2GSCheckGameLife(); WaitSeq = 0; } S2STick(); } else { continue; } } return 0; } /* End of D2GSTimerProcessor() */
extern void DebugEventCallback(char const * module, int count, ...) { SYSTEMTIME st; va_list ap; int i,value; char * name; char function[MAX_LINE_LEN]; if (!(d2gsconf.debugeventcallback)) return; GetLocalTime(&st); D2GSEventLog("DebugEventCallback","Event Called From Module \"%s\"",module); fprintf(hexstrm,"%d: Checking Module \"%s\" (%d)\tTime:%d.%d.%d.%d\n",\ gdwTotalCount++,module,count,st.wHour,st.wMinute,\ st.wSecond,st.wMilliseconds); sprintf (function,"%s(",module); va_start(ap,count); for (i=0; i<count; i++) { name=va_arg(ap, char *); value=va_arg(ap, int); strcat(function,name); if (i != count-1) strcat(function,", "); DebugDumpParam(name,value); } va_end(ap); strcat(function,")"); fprintf(hexstrm,"Function: %s\n",function); fprintf(hexstrm,"\n\n"); fflush(hexstrm); return; }
/********************************************************************* * Purpose: D2 GE main thread * Return: return value of the thread *********************************************************************/ DWORD WINAPI D2GEThread(LPVOID lpParameter) { DWORD dwThreadId; DWORD dwRetval; HANDLE hObjects[2]; DWORD dwExitCode; HANDLE hEvent; hEvent = (HANDLE)lpParameter; if (!hEvent) return FALSE; bGERunning = FALSE; gD2GSInfo.bStop = FALSE; hObjects[0] = CreateEvent(NULL, FALSE, FALSE, NULL); gD2GSInfo.hEventInited = hObjects[0]; if (!hObjects[0]) { D2GSEventLog("D2GEThread", "Error in CreateEvent. Code: %lu", GetLastError()); SetEvent(hEvent); return FALSE; } hObjects[1] = CreateThread(NULL, 0, D2GSStart, &gD2GSInfo, 0, &dwThreadId); if (!hObjects[1]) { D2GSEventLog("D2GEThread", "Error Creating Server Thread. Code: %lu", GetLastError()); CloseHandle(hObjects[0]); SetEvent(hEvent); return FALSE; } else { D2GSEventLog("D2GEThread", "Server Thread %lu Created", dwThreadId); } dwRetval = WaitForMultipleObjects(NELEMS(hObjects), hObjects, FALSE, D2GE_INIT_TIMEOUT); if (dwRetval==WAIT_FAILED) { D2GSEventLog("D2GEThread", "Wait Server Thread Failed. Code: %lu", GetLastError()); SetEvent(hEvent); } else if (dwRetval==WAIT_TIMEOUT) { D2GSEventLog("D2GEThread", "Game Server Thread Timedout"); SetEvent(hEvent); } else if (dwRetval==WAIT_OBJECT_0 + 1) { GetExitCodeThread(hObjects[1], &dwExitCode); D2GSEventLog("D2GEThread", "Game Server Thread Exit with %d", dwExitCode); SetEvent(hEvent); } else if (dwRetval==WAIT_OBJECT_0) { D2GSEventLog("D2GEThread", "Game Server Thread Start Successfully"); SetEvent(hEvent); bGERunning = TRUE; } else { D2GSEventLog("D2GEThread", "Wait Server Thread Returned %d", dwRetval); SetEvent(hEvent); } WaitForSingleObject(hObjects[1], INFINITE); CloseHandle(hObjects[0]); CloseHandle(hObjects[1]); bGERunning = FALSE; return TRUE; } /* End of D2GEThread */
int StartupTrunk() { D2GSEventLog(__FUNCTION__, "Starting up Trunk..."); trunkPipe = CreateFileA("\\\\.\\pipe\\d2gs_trunk_pipe_13", GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); if (!trunkPipe) return -1; CreateThread(NULL, 0, TrunkWorker, NULL, NULL, NULL); return 1; }
/********************************************************************* * Purpose: to startup the D2 Game Engine * Return: TRUE(success) or FALSE(failed) *********************************************************************/ int D2GEStartup(void) { HANDLE hEvent; DWORD dwThreadId; DWORD dwWait; /* init GE thread */ if (!D2GEThreadInit()) { D2GSEventLog("D2GEStartup", "Failed to Initialize Server"); return FALSE; } /* create event for notification of GE startup */ hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!hEvent) return FALSE; /* startup the server thread */ ghServerThread = CreateThread(NULL, 0, D2GEThread, (LPVOID)hEvent, 0, &dwThreadId); if (!ghServerThread) { D2GSEventLog("D2GEStartup", "Can't CreateThread D2GEThread. Code: %lu", GetLastError()); CloseHandle(hEvent); return FALSE; } dwWait = WaitForSingleObject(hEvent, D2GE_INIT_TIMEOUT); if (dwWait!=WAIT_OBJECT_0) { CloseHandle(hEvent); return FALSE; } CloseHandle(hEvent); if (CleanupRoutineInsert(D2GECleanup, "Diablo II Game Engine")) { return TRUE; } else { /* do some cleanup before quiting */ D2GECleanup(); return FALSE; } } /* End of D2GEStartup() */
/********************************************************************* * Purpose: called by Game Engine when error occur * Return: TRUE(success) or FALSE(failed) *********************************************************************/ static DWORD __stdcall D2GSErrorHandle(void) { D2GSEventLog("D2GSErrorHandle", "Error occur, exiting...\n\n"); #ifdef DEBUG_ON_CONSOLE printf("Press Any Key to Continue"); _getch(); #endif CloseServerMutex(); ExitProcess(0); return 0; } /* End of D2GSErrorHandle() */
DWORD WINAPI TrunkWorker(LPVOID lpParam) { char charname[17]; int bytesRead; while (1) { if (ReadFile(trunkPipe, charname, 17, (LPDWORD)&bytesRead, NULL)) { short port = 0; #ifdef _STRESS_TEST DWORD dwGameId; int ge; printf("Creating %s\n", charname); ge = D2GSNewEmptyGame(charname, "", "", 0x300004, 0x11, 0x22, 0x33, &dwGameId); D2GSGameListInsert((UCHAR*)charname, (UCHAR*)"", (UCHAR*)"", (UCHAR*)charname, (UCHAR*)charname, (UCHAR*)"127.0.0.1", 1, 1, 0, 0, dwGameId, ge); port = GetGEById(ge)->trunk_port; #else D2CHARINFO* player = D2GSFindPendingCharByCharName((UCHAR*)charname); if (player != NULL) { port = GetGEById(player->ge)->trunk_port; } #endif WriteFile(trunkPipe, &port, 2, (LPDWORD)&bytesRead, NULL); } else { int error = GetLastError(); HANDLE StopEvent; if (error == ERROR_BROKEN_PIPE || error == ERROR_INVALID_HANDLE) { D2GSEventLog(__FUNCTION__, "Broken trunk detected, self destruction activated."); StopEvent = CreateEventA(0, 1, 0, D2GS_STOP_EVENT_NAME); if (StopEvent) { SetEvent(StopEvent); CloseHandle(StopEvent); } return 0; } } } }
/********************************************************************* * Purpose: to initialize the D2 Game Engine thread * Return: TRUE(success) or FALSE(failed) *********************************************************************/ int D2GEThreadInit(void) { if (!D2GSGetInterface()) { D2GSEventLog("D2GSThread", "Failed to Get Server Interface"); return FALSE; } gD2GSInfo.szVersion = D2GS_VERSION_STRING; gD2GSInfo.dwLibVersion = D2GS_LIBRARY_VERSION; gD2GSInfo.bIsNT = d2gsconf.enablentmode; gD2GSInfo.bEnablePatch = d2gsconf.enablegepatch; gD2GSInfo.fpEventLog = D2GEEventLog; gD2GSInfo.fpErrorHandle = D2GSErrorHandle; gD2GSInfo.fpCallback = EventCallbackTableInit(); gD2GSInfo.bPreCache = d2gsconf.enableprecachemode; gD2GSInfo.dwIdleSleep = d2gsconf.idlesleep; gD2GSInfo.dwBusySleep = d2gsconf.busysleep; gD2GSInfo.dwMaxGame = d2gsconf.gemaxgames; gD2GSInfo.dwProcessAffinityMask = d2gsconf.multicpumask; return TRUE; } /* D2GEThreadInit() */
/********************************************************************* * Purpose: to read configurations to the D2GSCONFIGS structure * Return: TRUE(success) or FALSE(failed) *********************************************************************/ int D2GSReadConfig(void) { HKEY hKey; BOOL result; u_long ipaddr; DWORD dwval; char strbuf[256]; result = FALSE; if (!RegkeyOpen(HKEY_LOCAL_MACHINE, REGKEY_ROOT, &hKey, KEY_READ)) { D2GSEventLog("D2GSReadConfig", "Can't open registry key '\\\\HKEY_LOCAL_MACHINE\\%s'", REGKEY_ROOT); //return result; } /* D2CSIP */ if (!RegkeyReadString(hKey, REGKEY_D2CSIP, strbuf, sizeof(strbuf))) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s'", REGKEY_D2CSIP); goto tocloseregkey; } ipaddr = inet_addr(strbuf); if (ipaddr==INADDR_NONE) { D2GSEventLog("D2GSReadConfig", "Invalid D2CSIP '%s'", strbuf); goto tocloseregkey; } d2gsconf.d2csip = ipaddr; /* D2DBSIP */ if (!RegkeyReadString(hKey, REGKEY_D2DBSIP, strbuf, sizeof(strbuf))) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s'", REGKEY_D2DBSIP); goto tocloseregkey; } ipaddr = inet_addr(strbuf); if (ipaddr==INADDR_NONE) { D2GSEventLog("D2GSReadConfig", "Invalid D2DBSIP '%s'", strbuf); goto tocloseregkey; } d2gsconf.d2dbsip = ipaddr; /* D2CSPORT */ if (!RegkeyReadDWORD(hKey, REGKEY_D2CSPORT, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_D2CSPORT, DEFAULT_D2CS_PORT); d2gsconf.d2csport = DEFAULT_D2CS_PORT; } else d2gsconf.d2csport = htons((u_short)dwval); /* D2DBSPORT */ if (!RegkeyReadDWORD(hKey, REGKEY_D2DBSPORT, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_D2DBSPORT, DEFAULT_D2DBS_PORT); d2gsconf.d2csport = DEFAULT_D2DBS_PORT; } else d2gsconf.d2dbsport = htons((short)dwval); /* MAXGAMES */ if (!RegkeyReadDWORD(hKey, REGKEY_MAXGAMES, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_MAXGAMES, DEFAULT_MAX_GAMES); d2gsconf.gemaxgames = DEFAULT_MAX_GAMES; } else d2gsconf.gemaxgames = dwval; d2gsconf.gsmaxgames = 0; /* ENABLENTMODE */ if (!RegkeyReadDWORD(hKey, REGKEY_ENABLENTMODE, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_ENABLENTMODE, DEFAULT_NT_MODE); d2gsconf.enablentmode = DEFAULT_NT_MODE; } else d2gsconf.enablentmode = (BOOL)dwval; /* ENABLEGEPATCH */ if (!RegkeyReadDWORD(hKey, REGKEY_ENABLEGEPATCH, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_ENABLEGEPATCH, DEFAULT_GE_PATCH); d2gsconf.enablegepatch = DEFAULT_GE_PATCH; } else d2gsconf.enablegepatch = (BOOL)dwval; /* ENABLEPRECACHEMODE */ if (!RegkeyReadDWORD(hKey, REGKEY_ENABLEPRECACHEMODE, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_ENABLEPRECACHEMODE, DEFAULT_PRECACHE_MODE); d2gsconf.enableprecachemode = DEFAULT_PRECACHE_MODE; } else d2gsconf.enableprecachemode = (BOOL)dwval; /* ENABLEGELOG */ if (!RegkeyReadDWORD(hKey, REGKEY_ENABLEGELOG, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_ENABLEGELOG, DEFAULT_GE_LOG); d2gsconf.enablegelog = DEFAULT_GE_LOG; } else d2gsconf.enablegelog = (BOOL)dwval; /* ENABLEGSLOG */ if (!RegkeyReadDWORD(hKey, REGKEY_ENABLEGSLOG, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_ENABLEGELOG, DEFAULT_GS_LOG); d2gsconf.enablegslog = DEFAULT_GS_LOG; } else d2gsconf.enablegslog = (BOOL)dwval; /* ENABLEGEMSG */ if (!RegkeyReadDWORD(hKey, REGKEY_ENABLEGEMSG, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_ENABLEGEMSG, DEFAULT_GE_MSG); d2gsconf.enablegemsg = DEFAULT_GE_MSG; } else d2gsconf.enablegemsg = (BOOL)dwval; /* DEBUGNETPACKET */ if (!RegkeyReadDWORD(hKey, REGKEY_DEBUGNETPACKET, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_DEBUGNETPACKET, DEFAULT_DEBUGNETPACKET); d2gsconf.debugnetpacket = DEFAULT_DEBUGNETPACKET; } else d2gsconf.debugnetpacket = (BOOL)dwval; /* DEBUGEVENTCALLBACK */ if (!RegkeyReadDWORD(hKey, REGKEY_DEBUGEVENTCALLBACK, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_DEBUGEVENTCALLBACK, DEFAULT_DEBUGEVENTCALLBACK); d2gsconf.debugeventcallback = DEFAULT_DEBUGEVENTCALLBACK; } else d2gsconf.debugeventcallback = (BOOL)dwval; /* IDLESLEEP */ if (!RegkeyReadDWORD(hKey, REGKEY_IDLESLEEP, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_IDLESLEEP, DEFAULT_IDLE_SLEEP); d2gsconf.idlesleep = DEFAULT_IDLE_SLEEP; } else d2gsconf.idlesleep = dwval; /* BUSYSLEEP */ if (!RegkeyReadDWORD(hKey, REGKEY_BUSYSLEEP, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_BUSYSLEEP, DEFAULT_BUSY_SLEEP); d2gsconf.busysleep = DEFAULT_BUSY_SLEEP; } else d2gsconf.busysleep = dwval; /* CHARPENDINGTIMEOUT */ if (!RegkeyReadDWORD(hKey, REGKEY_CHARPENDINGTIMEOUT, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_CHARPENDINGTIMEOUT, DEFAULT_CHARPENDINGTIMEOUT); d2gsconf.charpendingtimeout = DEFAULT_CHARPENDINGTIMEOUT; } else d2gsconf.charpendingtimeout = dwval; /* INTERVALRECONNECTD2CS */ if (!RegkeyReadDWORD(hKey, REGKEY_INTERVALRECONNECTD2CS, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_INTERVALRECONNECTD2CS, DEFAULT_INTERVALRECONNECTD2CS); d2gsconf.intervalreconnectd2cs = DEFAULT_INTERVALRECONNECTD2CS; } else d2gsconf.intervalreconnectd2cs = dwval; /* ADMINPWD */ ZeroMemory(d2gsconf.adminpwd, sizeof(d2gsconf.adminpwd)); if (!RegkeyReadString(hKey, REGKEY_ADMINPWD, strbuf, sizeof(strbuf))) { strcpy(d2gsconf.adminpwd, "d2gsdmin"); } else strncpy(d2gsconf.adminpwd, strbuf, sizeof(d2gsconf.adminpwd)-1); /* D2CSSECRECT */ ZeroMemory(d2gsconf.d2cssecrect, sizeof(d2gsconf.d2cssecrect)); if (!RegkeyReadString(hKey, REGKEY_D2CSSECRECT, strbuf, sizeof(strbuf))) { strcpy(d2gsconf.d2cssecrect, ""); } else strncpy(d2gsconf.d2cssecrect, strbuf, sizeof(d2gsconf.d2cssecrect)-1); /* ADMINPORT */ if (!RegkeyReadDWORD(hKey, REGKEY_ADMINPORT, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_ADMINPORT, DEFAULT_ADMIN_PORT); d2gsconf.adminport = DEFAULT_D2DBS_PORT; } else d2gsconf.adminport = htons((short)dwval); /* ADMINTIMEOUT */ if (!RegkeyReadDWORD(hKey, REGKEY_ADMINTIMEOUT, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_ADMINTIMEOUT, DEFAULT_ADMIN_TIMEOUT); d2gsconf.admintimeout = DEFAULT_ADMIN_TIMEOUT; } else d2gsconf.admintimeout = dwval; /* MAXGAMELIFE */ if (!RegkeyReadDWORD(hKey, REGKEY_MAXGAMELIFE, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_MAXGAMELIFE, DEFAULT_MAXGAMELIFE); d2gsconf.maxgamelife = DEFAULT_MAXGAMELIFE; } else d2gsconf.maxgamelife = dwval; /* GSSHUTDOWNINTERVAL */ if (!RegkeyReadDWORD(hKey, REGKEY_GSSHUTDOWNINTERVAL, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_GSSHUTDOWNINTERVAL, DEFAULT_GS_SHUTDOWN_INTERVAL); d2gsconf.gsshutdowninterval = DEFAULT_GS_SHUTDOWN_INTERVAL; } else d2gsconf.gsshutdowninterval = dwval; /* MULTICPUMASK */ if (!RegkeyReadDWORD(hKey, REGKEY_MULTICPUMASK, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_MULTICPUMASK, DEFAULT_MULTICPUMASK); d2gsconf.multicpumask = DEFAULT_MULTICPUMASK; } else d2gsconf.multicpumask = dwval; /* LISTENPORT */ if (!RegkeyReadDWORD(hKey, REGKEY_LISTENPORT, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_LISTENPORT, DEFAULT_LISTENPORT); d2gsconf.listenport = DEFAULT_LISTENPORT; } else d2gsconf.listenport = dwval; /* LISTENADDR */ ZeroMemory(d2gsconf.listenaddr, sizeof(d2gsconf.listenaddr)); if (!RegkeyReadString(hKey, REGKEY_LISTENADDR, strbuf, sizeof(strbuf))) { strcpy(d2gsconf.listenaddr, "0.0.0.0"); } else strncpy(d2gsconf.listenaddr, strbuf, sizeof(d2gsconf.listenaddr)-1); /* GETHREAD */ if (!RegkeyReadDWORD(hKey, REGKEY_GETHREAD, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_GETHREAD, DEFAULT_GETHREAD); d2gsconf.gethread = DEFAULT_GETHREAD; } else d2gsconf.gethread = dwval; /* SYNPROT */ if (!RegkeyReadDWORD(hKey, REGKEY_SYNPROT, &dwval)) { D2GSEventLog("D2GSReadConfig", "Can't read key '%s', set to default %d", REGKEY_SYNPROT, DEFAULT_SYNPROT); d2gsconf.synprot = DEFAULT_SYNPROT; } else d2gsconf.synprot = dwval; /* MOTD */ ZeroMemory(d2gsconf.motd, sizeof(d2gsconf.motd)); if (RegkeyReadString(hKey, REGKEY_MOTD, strbuf, sizeof(strbuf))) strncpy(d2gsconf.motd, strbuf, sizeof(d2gsconf.motd)-1); strcpy(d2gsconf.motd, strbuf); string_color(d2gsconf.motd); d2gsconf.eventmotd[0] = 0; d2gsconf.roomMotd[0] = 0; result = TRUE; tocloseregkey: RegkeyClose(hKey); return result; } /* End of D2GSReadConfig() */