This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Commando/slavemaster.cpp

1266 lines
54 KiB
C++
Raw Permalink Normal View History

/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : Combat *
* *
* $Archive:: /Commando/Code/Commando/slavemaster.cpp $*
* *
* Author:: Steve Tall *
* *
* $Modtime:: 2/15/02 12:44p $*
* *
* $Revision:: 18 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "always.h"
#include <windows.h>
#include "slavemaster.h"
#include "wwdebug.h"
#include "registry.h"
#include "_globals.h"
#include "autostart.h"
#include "ini.h"
#include "rawfile.h"
#include "inisup.h"
#include "natter.h"
#include "gamesideservercontrol.h"
#include "win.h"
#include "gamedata.h"
#include "serversettings.h"
#include "bandwidth.h"
#include "consolemode.h"
#include "specialbuilds.h"
#include "useroptions.h"
#include <string.h>
#include <stdio.h>
#define KEY_NUM_SLAVES "Count"
#define KEY_SLAVE_NAME "Name"
#define KEY_SLAVE_SERIAL "Serial"
#define KEY_SLAVE_ENABLE "Enable"
#define KEY_SLAVE_PORT "Port"
#define KEY_SLAVE_RUNNING_ID "RunningID"
#define KEY_SLAVE_SETTINGS "Settings"
#define KEY_SLAVE_BANDWIDTH "Bandwidth"
#define KEY_SLAVE_PASSWORD "Password"
const char *RegistryFileName = "slave.ini";
SlaveMasterClass SlaveMaster;
extern char DefaultRegistryModifier[1024];
/***********************************************************************************************
* SlaveServerClass::SlaveServerClass -- SlaveServerClass constuctor *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 11/21/2001 3:51PM ST : Created *
*=============================================================================================*/
SlaveServerClass::SlaveServerClass(void)
{
Enable = false;
NickName[0] = 0;
Serial[0] = 0;
Port = 0;
Bandwidth = 0;
Password[0] = 0;
}
/***********************************************************************************************
* SlaveServerClass::~SlaveServerClass -- SlaveServerClass desturctor *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 11/21/2001 3:51PM ST : Created *
*=============================================================================================*/
SlaveServerClass::~SlaveServerClass(void)
{
}
/***********************************************************************************************
* SlaveServerClass::Set -- Set info about this slave *
* *
* *
* *
* INPUT: Is slave enabled? *
* Nickname to use with this slave *
* Serial number to use with this slave *
* Port to use with this slave *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 11/21/2001 3:50PM ST : Created *
*=============================================================================================*/
void SlaveServerClass::Set(bool enable, char *nick, char *serial, unsigned short port, char *settings_file, int bandwidth, char *password)
{
Enable = enable;
Port = port;
Bandwidth = bandwidth;
if (nick) {
strncpy(NickName, nick, sizeof(NickName));
}
if (serial) {
strncpy(Serial, serial, sizeof(Serial));
}
if (password) {
strncpy(Password, password, sizeof(Password));
}
if (settings_file) {
strncpy(SettingsFileName, settings_file, sizeof(SettingsFileName));
}
}
/***********************************************************************************************
* SlaveServerClass::Get -- Get info about this slave *
* *
* *
* *
* INPUT: Is slave enabled? *
* Nickname to use with this slave *
* Serial number to use with this slave *
* Port to use with this slave *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 11/21/2001 3:49PM ST : Created *
*=============================================================================================*/
void SlaveServerClass::Get(bool &enable, char *nick, char *serial, unsigned short &port, char *settings_file, int &bandwidth, char *password)
{
enable = Enable;
port = Port;
bandwidth = Bandwidth;
if (nick) {
strcpy(nick, NickName);
}
if (serial) {
strcpy(serial, Serial);
}
if (password) {
strcpy(password, Password);
}
if (settings_file) {
strcpy(settings_file, SettingsFileName);
}
}
/***********************************************************************************************
* SlaveMasterClass::SlaveMasterClass -- SlaveMasterClass constructor *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 11/21/2001 3:49PM ST : Created *
*=============================================================================================*/
SlaveMasterClass::SlaveMasterClass(void)
{
NumSlaveServers = 0;
SlaveMode = false;
}
/***********************************************************************************************
* SlaveMasterClass::~SlaveMasterClass -- SlaveMasterClass destructor *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 11/21/2001 3:49PM ST : Created *
*=============================================================================================*/
SlaveMasterClass::~SlaveMasterClass(void)
{
/*
** Make sure all slaves are gone before we quit.
*/
Wait_For_Slave_Shutdown();
}
/***********************************************************************************************
* SlaveMasterClass::Wait_For_Slave_Shutdown -- Wait for slaves to exit *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 1/7/2002 2:58PM ST : Created *
*=============================================================================================*/
void SlaveMasterClass::Wait_For_Slave_Shutdown(void)
{
if (!SlaveMode) {
unsigned long time = TIMEGETTIME();
int num_running = 0;
int last_num_running = 0;
bool forced = false;
/*
** Don't wait longer than 35 secs. It takes 15 secs for the slave to do it's intermission.
*/
while (TIMEGETTIME() - time < 40000) {
num_running = 0;
for (int i=0 ; i<NumSlaveServers ; i++) {
if (SlaveServers[i].IsRunning) {
if (SlaveServers[i].ProcessInfo.hProcess) {
unsigned long code;
int res = GetExitCodeProcess(SlaveServers[i].ProcessInfo.hProcess, &code);
if (res && code == STILL_ACTIVE) {
num_running++;
}
} else {
if (SlaveServers[i].ProcessInfo.dwProcessId) {
unsigned long ver = GetProcessVersion(SlaveServers[i].ProcessInfo.dwProcessId);
if (ver && ver == GetProcessVersion(GetCurrentProcessId())) {
num_running++;
}
}
}
}
}
if (num_running && num_running != last_num_running) {
ConsoleBox.Print("Waiting for %d slave(s) to shut down\n", num_running);
WWDEBUG_SAY(("Waiting for %d slave(s) to shut down\n", num_running));
last_num_running = num_running;
}
if (num_running == 0) {
break;
}
/*
** Force a shutdown if they are not cooperating.
*/
if (!forced && TIMEGETTIME() - time > 27000) {
forced = true;
for (int i=0 ; i<NumSlaveServers ; i++) {
if (SlaveServers[i].ProcessInfo.dwProcessId) {
unsigned long ver = GetProcessVersion(SlaveServers[i].ProcessInfo.dwProcessId);
if (ver && ver == GetProcessVersion(GetCurrentProcessId())) {
WWDEBUG_SAY(("Terminating process %d due to timeout\n", SlaveServers[i].ProcessInfo.dwProcessId));
/*
** Get a handle to the process.
*/
HANDLE proc_handle = OpenProcess(PROCESS_TERMINATE, false, SlaveServers[i].ProcessInfo.dwProcessId);
if (proc_handle != INVALID_HANDLE_VALUE) {
TerminateProcess(proc_handle, 0);
CloseHandle(proc_handle);
} else {
WWDEBUG_SAY(("Failed to get process handle for termination - error code %d\n", GetLastError()));
}
num_running++;
}
}
}
}
}
}
}
/***********************************************************************************************
* SlaveMasterClass::Get_Slave -- Get slave server entry by index *
* *
* *
* *
* INPUT: Index *
* *
* OUTPUT: Ptr to slave server *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 11/21/2001 3:48PM ST : Created *
*=============================================================================================*/
SlaveServerClass *SlaveMasterClass::Get_Slave(int index)
{
WWASSERT(index < NumSlaveServers);
WWASSERT(index >= 0);
if (index >= 0 && index <NumSlaveServers) {
return(&SlaveServers[index]);
}
return(NULL);
}
/***********************************************************************************************
* SlaveMasterClass::Save -- Save slave server info to registry *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 11/21/2001 3:48PM ST : Created *
*=============================================================================================*/
void SlaveMasterClass::Save(void)
{
RegistryClass reg(APPLICATION_SUB_KEY_NAME_NET_SLAVE);
if (reg.Is_Valid()) {
reg.Set_Int(KEY_NUM_SLAVES, NumSlaveServers);
}
for (int i=0 ; i<NumSlaveServers ; i++) {
char entry[256];
sprintf(entry, "%s%d", KEY_SLAVE_NAME, i);
reg.Set_String(entry, SlaveServers[i].NickName);
sprintf(entry, "%s%d", KEY_SLAVE_PASSWORD, i);
reg.Set_String(entry, SlaveServers[i].Password);
sprintf(entry, "%s%d", KEY_SLAVE_SETTINGS, i);
reg.Set_String(entry, SlaveServers[i].SettingsFileName);
sprintf(entry, "%s%d", KEY_SLAVE_ENABLE, i);
reg.Set_Bool(entry, SlaveServers[i].Enable);
sprintf(entry, "%s%d", KEY_SLAVE_PORT, i);
reg.Set_Int(entry, SlaveServers[i].Port);
sprintf(entry, "%s%d", KEY_SLAVE_BANDWIDTH, i);
reg.Set_Int(entry, SlaveServers[i].Bandwidth);
sprintf(entry, "%s%d", KEY_SLAVE_SERIAL, i);
StringClass serial(SlaveServers[i].Serial, true);
StringClass encrypted_serial = serial;
if (serial.Get_Length()) {
ServerSettingsClass::Encrypt_Serial(serial, encrypted_serial);
}
reg.Set_String(entry, encrypted_serial.Peek_Buffer());
}
}
/***********************************************************************************************
* SlaveMasterClass::Load -- Fetch slave server info from registry *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 11/21/2001 3:47PM ST : Created *
*=============================================================================================*/
void SlaveMasterClass::Load(void)
{
RegistryClass reg(APPLICATION_SUB_KEY_NAME_NET_SLAVE);
if (reg.Is_Valid()) {
NumSlaveServers = reg.Get_Int(KEY_NUM_SLAVES, 0);
}
char entry[256];
for (int i=0 ; i<NumSlaveServers ; i++) {
sprintf(entry, "%s%d", KEY_SLAVE_NAME, i);
reg.Get_String(entry, SlaveServers[i].NickName, sizeof(SlaveServers[i].NickName), "");
sprintf(entry, "%s%d", KEY_SLAVE_PASSWORD, i);
reg.Get_String(entry, SlaveServers[i].Password, sizeof(SlaveServers[i].Password), "");
sprintf(entry, "%s%d", KEY_SLAVE_ENABLE, i);
SlaveServers[i].Enable = reg.Get_Bool(entry, false);
sprintf(entry, "%s%d", KEY_SLAVE_PORT, i);
SlaveServers[i].Port = reg.Get_Int(entry, false);
sprintf(entry, "%s%d", KEY_SLAVE_BANDWIDTH, i);
SlaveServers[i].Bandwidth = reg.Get_Int(entry, false);
sprintf(entry, "%s%d", KEY_SLAVE_SETTINGS, i);
reg.Get_String(entry, SlaveServers[i].SettingsFileName, sizeof(SlaveServers[i].SettingsFileName), "");
sprintf(entry, "%s%d", KEY_SLAVE_SERIAL, i);
reg.Get_String(entry, SlaveServers[i].Serial, sizeof(SlaveServers[i].Serial), "");
if (strlen(SlaveServers[i].Serial)) {
StringClass serial(SlaveServers[i].Serial, true);
StringClass decrypted_serial = serial;
if (serial.Get_Length()) {
ServerSettingsClass::Decrypt_Serial(serial, decrypted_serial);
}
strcpy(SlaveServers[i].Serial, decrypted_serial.Peek_Buffer());
}
char filename[MAX_PATH];
sprintf(filename, "data\\%s", SlaveServers[i].SettingsFileName);
RawFileClass file(filename);
if (!file.Is_Available()) {
strcpy(SlaveServers[i].SettingsFileName, "svrcfg_cnc.ini");
}
}
}
/***********************************************************************************************
* SlaveMasterClass::Reset -- Clear out slave server list *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 11/21/2001 3:47PM ST : Created *
*=============================================================================================*/
void SlaveMasterClass::Reset(void)
{
for (int i=0 ; i<NumSlaveServers ; i++) {
SlaveServers[i].Set(false, "", "", 0, "", 0, "");
}
NumSlaveServers = 0;
}
/***********************************************************************************************
* SlaveMasterClass::Add_Slave -- Add slave to list *
* *
* *
* *
* INPUT: Is slave enabled? *
* Nickname for this slave to use *
* Serial number for this slave to use *
* Port number for this slave to use *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 11/21/2001 3:46PM ST : Created *
*=============================================================================================*/
void SlaveMasterClass::Add_Slave(bool enable, char *nick, char *serial, unsigned short port, char *settings_file, int bandwidth, char *password)
{
WWASSERT(NumSlaveServers < MAX_SLAVES);
SlaveServers[NumSlaveServers++].Set(enable, nick, serial, port, settings_file, bandwidth, password);
}
/***********************************************************************************************
* SlaveMasterClass::Aquire_Slave -- Find a running slave by it's process ID *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 12/16/2001 11:34PM ST : Created *
*=============================================================================================*/
bool SlaveMasterClass::Aquire_Slave(int index)
{
int proc_id = 0;
/*
** Try the slaves record of his process ID. If it's not there, it can't have run yet.
*/
char slave_name[64];
sprintf(slave_name, "\\slave_%d", index);
strcpy(DefaultRegistryModifier, slave_name+1);
RegistryClass slave_reg(APPLICATION_SUB_KEY_NAME);
DefaultRegistryModifier[0] = 0;
if (slave_reg.Is_Valid()) {
proc_id = slave_reg.Get_Int("ProcessId", proc_id);
}
/*
** Try our record of the slaves process ID.
*/
if (proc_id == 0) {
RegistryClass reg(APPLICATION_SUB_KEY_NAME_NET_SLAVE);
if (reg.Is_Valid()) {
char entry[128];
sprintf(entry, "%s%d", KEY_SLAVE_RUNNING_ID, index);
proc_id = reg.Get_Int(entry, 0);
}
}
/*
** Search for the slave's console window if we are in console mode. This is a better test than just hoping the Process ID is
** correct.
*/
if (ConsoleBox.Is_Exclusive()) {
HWND slave_window = ConsoleBox.Get_Slave_Window_By_Title(SlaveServers[index].NickName, SlaveServers[index].SettingsFileName);
if (slave_window != NULL) {
/*
** Note the process ID for later.
*/
SlaveServers[index].ProcessInfo.hProcess = NULL; // Don't know handle.
GetWindowThreadProcessId(slave_window, &SlaveServers[index].ProcessInfo.dwProcessId);
WWDEBUG_SAY(("Slave found by HWND with process ID %d\n", SlaveServers[index].ProcessInfo.dwProcessId));
RegistryClass reg(APPLICATION_SUB_KEY_NAME_NET_SLAVE);
if (reg.Is_Valid()) {
char entry[128];
sprintf(entry, "%s%d", KEY_SLAVE_RUNNING_ID, index);
reg.Set_Int(entry, SlaveServers[index].ProcessInfo.dwProcessId);
}
return(true);
}
}
/*
** See if the process is already running.
*/
if (proc_id) {
unsigned long ver = GetProcessVersion(proc_id);
if (ver && ver == GetProcessVersion(GetCurrentProcessId())) {
/*
** It looks like one of our slaves. See if we already know about it.
*/
if (SlaveServers[index].IsRunning && SlaveServers[index].ProcessInfo.dwProcessId == (unsigned) proc_id) {
return(true);
}
/*
** Note the process ID for later.
*/
SlaveServers[index].ProcessInfo.hProcess = NULL; // Don't know handle.
SlaveServers[index].ProcessInfo.dwProcessId = proc_id;
WWDEBUG_SAY(("Slave found with process ID %d\n", SlaveServers[index].ProcessInfo.dwProcessId));
return(true);
}
}
return(false);
}
/***********************************************************************************************
* SlaveMasterClass::Startup_Slaves -- Create extra slave server processes *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 11/21/2001 3:46PM ST : Created *
*=============================================================================================*/
void SlaveMasterClass::Startup_Slaves(void)
{
if (!SlaveMode) {
/*
** Make sure we are a dedicated server.
*/
if (!The_Game() || The_Game()->IsDedicated.Is_True()) {
/*
** Slaves only available in windowed or console mode.
*/
if (WW3D::Is_Windowed() || ConsoleBox.Is_Exclusive()) {
/*
** Slaves only available in internet mode.
*/
GameModeClass *game_mode = GameModeManager::Find("WOL");
if (game_mode && game_mode->Is_Active()) {
Wait_For_Slave_Shutdown();
Load();
Delete_Registry_Copies();
Create_Registry_Copies();
/*
** Spawn the servers.
*/
char command_line[300];
for (int i=0 ; i<NumSlaveServers ; i++) {
if (SlaveServers[i].Enable) {
bool slave_running = false;
/*
** Get an access point into the slaves registry base.
*/
char slave_name[64];
sprintf(slave_name, "\\slave_%d", i);
strcpy(DefaultRegistryModifier, slave_name+1);
RegistryClass slave_reg(APPLICATION_SUB_KEY_NAME);
DefaultRegistryModifier[0] = 0;
/*
** If we are autostarting then take inventory of which slaves are running already.
** An autostart after a crash should still see the slaves running. An autostart after a reboot will see no slaves.
*/
if (AutoRestart.Is_Active()) {
slave_running = Aquire_Slave(i);
}
/*
** Figure out the name of the .exe to run.
*/
char path_to_exe[256];
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
char path[_MAX_PATH];
GetModuleFileName(ProgramInstance, path_to_exe, sizeof(path_to_exe));
_splitpath(path_to_exe, drive, dir, NULL, NULL);
#ifdef FREEDEDICATEDSERVER
_makepath(path, drive, dir, "renegadeserver", "exe");
#else //FREEDEDICATEDSERVER
_makepath(path, drive, dir, "renegade", "exe");
#endif //FREEDEDICATEDSERVER
sprintf(command_line, "%s /MULTI /SLAVE /REGMOD=slave_%d", path, i);
if (ConsoleBox.Is_Exclusive()) {
strcat(command_line, " /NODX");
}
STARTUPINFO startup_info;
memset(&startup_info, 0, sizeof(startup_info));
startup_info.cb = sizeof(startup_info);
int result = 1;
if (!slave_running) {
if (slave_reg.Is_Valid()) {
slave_reg.Set_Int("ProcessId", 0);
}
result = CreateProcess(path, command_line, NULL, NULL, false, 0, NULL, NULL, &startup_info, &SlaveServers[i].ProcessInfo);
}
if (result) {
SlaveServers[i].IsRunning = true;
/*
** The process ID we have here is actually the ID of the slaves launcher. We need the ID of the actual
** game process. Wait a few seconds until the slave sets his ID into his registry location.
*/
if (!slave_running) {
unsigned long time = TIMEGETTIME();
while (TIMEGETTIME() - time < 10000) {
if (!slave_reg.Is_Valid()) {
break;
}
/*
** Break out once we read the slaves process ID from the registry indicating that the slave
** has already parsed its command line.
*/
int process_id = slave_reg.Get_Int("ProcessId", 0);
if (process_id != 0) {
SlaveServers[i].ProcessInfo.dwProcessId = process_id;
break;
}
Sleep(250);
}
}
/*
** Set a registry flag to say this server is active. We need to know this if the master server (us)
** crashes and restarts.
*/
RegistryClass reg(APPLICATION_SUB_KEY_NAME_NET_SLAVE);
if (reg.Is_Valid()) {
char entry[128];
sprintf(entry, "%s%d", KEY_SLAVE_RUNNING_ID, i);
reg.Set_Int(entry, SlaveServers[i].ProcessInfo.dwProcessId);
}
} else {
WWDEBUG_SAY(("Failed to start slave process - error code %d\n", GetLastError()));
SlaveServers[i].IsRunning = false;
}
}
}
}
}
}
}
GameSideServerControlClass::Set_Welcome_Message();
}
/***********************************************************************************************
* SlaveMasterClass::Shutdown_Slaves -- Send quit message to all slaves *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 11/21/2001 3:45PM ST : Created *
*=============================================================================================*/
void SlaveMasterClass::Shutdown_Slaves(void)
{
if (!SlaveMode) {
char password[64] = DEFAULT_SERVER_CONTROL_PASSWORD;
RegistryClass reg(APPLICATION_SUB_KEY_NAME_NET_SERVER_CONTROL);
reg.Get_String(SERVER_CONTROL_PASSWORD_KEY, password, sizeof(password), password);
for (int i=0 ; i<NumSlaveServers ; i++) {
if (SlaveServers[i].IsRunning) {
/*
** In case the slave was restarted - we won't know it's new process ID.
*/
Aquire_Slave(i);
/*
** Set the slaves auto-restart flag to false or it will just start right up again.
*/
char slave_name[64];
sprintf(slave_name, "\\slave_%d", i);
strcpy(DefaultRegistryModifier, slave_name+1);
RegistryClass slave_reg(APPLICATION_SUB_KEY_NAME_WOLSETTINGS);
DefaultRegistryModifier[0] = 0;
slave_reg.Set_Int(AutoRestartClass::REG_VALUE_AUTO_RESTART_FLAG, 0);
/*
** Send the password to the slave to authenticate the connection.
*/
GameSideServerControlClass::Send_Message(password, ntohl(INADDR_LOOPBACK), SlaveServers[i].ControlPort);
Sleep(10);
GameSideServerControlClass::Send_Message("quit", ntohl(INADDR_LOOPBACK), SlaveServers[i].ControlPort);
/*
** Remember that we shut this guy down.
*/
RegistryClass reg(APPLICATION_SUB_KEY_NAME_NET_SLAVE);
if (reg.Is_Valid()) {
char entry[128];
sprintf(entry, "%s%d", KEY_SLAVE_RUNNING_ID, i);
reg.Set_Int(entry, 0);
}
}
}
}
}
/***********************************************************************************************
* SlaveMasterClass::Shutdown_Slaves -- Send quit message to all slaves *
* *
* *
* *
* INPUT: Slave login name *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 11/21/2001 3:45PM ST : Created *
*=============================================================================================*/
bool SlaveMasterClass::Shutdown_Slave(char *slave_login)
{
if (!SlaveMode && slave_login) {
char password[64] = DEFAULT_SERVER_CONTROL_PASSWORD;
RegistryClass reg(APPLICATION_SUB_KEY_NAME_NET_SERVER_CONTROL);
reg.Get_String(SERVER_CONTROL_PASSWORD_KEY, password, sizeof(password), password);
for (int i=0 ; i<NumSlaveServers ; i++) {
if (SlaveServers[i].IsRunning && stricmp(slave_login, SlaveServers[i].NickName) == 0) {
/*
** In case the slave was restarted - we won't know it's new process ID.
*/
Aquire_Slave(i);
/*
** Set the slaves auto-restart flag to false or it will just start right up again.
*/
char slave_name[64];
sprintf(slave_name, "\\slave_%d", i);
strcpy(DefaultRegistryModifier, slave_name+1);
RegistryClass slave_reg(APPLICATION_SUB_KEY_NAME_WOLSETTINGS);
DefaultRegistryModifier[0] = 0;
slave_reg.Set_Int(AutoRestartClass::REG_VALUE_AUTO_RESTART_FLAG, 0);
/*
** Send the password to the slave to authenticate the connection.
*/
GameSideServerControlClass::Send_Message(password, ntohl(INADDR_LOOPBACK), SlaveServers[i].ControlPort);
Sleep(10);
GameSideServerControlClass::Send_Message("quit", ntohl(INADDR_LOOPBACK), SlaveServers[i].ControlPort);
/*
** Remember that we shut this guy down.
*/
RegistryClass reg(APPLICATION_SUB_KEY_NAME_NET_SLAVE);
if (reg.Is_Valid()) {
char entry[128];
sprintf(entry, "%s%d", KEY_SLAVE_RUNNING_ID, i);
reg.Set_Int(entry, 0);
}
SlaveServers[i].IsRunning = false;
return(true);
}
}
}
return(false);
}
/***********************************************************************************************
* SlaveMasterClass::Get_Slave_Info -- Get text slave info *
* *
* *
* *
* INPUT: Ptr to text buffer *
* buffer size *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 11/21/2001 3:45PM ST : Created *
*=============================================================================================*/
char *SlaveMasterClass::Get_Slave_Info(char *buffer, int buflen)
{
bool any = false;
if (buffer) {
assert(buflen >= 500);
*buffer = 0;
for (int i=0 ; i<NumSlaveServers ; i++) {
if (SlaveServers[i].IsRunning) {
any = true;
char temp[64];
sprintf(temp, " Slave %d on port %d\n", i+1, SlaveServers[i].ControlPort);
if (strlen(temp) + strlen(buffer) < (unsigned)buflen) {
strcat(buffer, temp);
}
}
}
if (!any) {
strcpy(buffer, "No slave servers active\n");
}
}
return(buffer);
}
/***********************************************************************************************
* SlaveMasterClass::Create_Registry_Copies -- Create 'shadow' registry copies for slaves *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 11/21/2001 3:44PM ST : Created *
*=============================================================================================*/
void SlaveMasterClass::Create_Registry_Copies(void)
{
WWASSERT(!SlaveMode);
/*
** Make sure the Process ID isn't set in our base registry. It's shouldn't be unless I ran with the /slave command during dev.
*/
RegistryClass reg(APPLICATION_SUB_KEY_NAME);
if (reg.Is_Valid()) {
reg.Delete_Value("ProcessId");
}
RegistryClass::Save_Registry(RegistryFileName, APPLICATION_SUB_KEY_NAME);
char new_path[1024];
char slave_name[64];
for (int i=0 ; i<NumSlaveServers ; i++) {
if (SlaveServers[i].Enable) {
strcpy(new_path, APPLICATION_SUB_KEY_NAME);
sprintf(slave_name, "\\slave_%d", i);
strcat(new_path, slave_name);
RegistryClass::Load_Registry(RegistryFileName, APPLICATION_SUB_KEY_NAME, new_path);
/*
** Store the slave settings into the registry.
*/
/*
** Port numbers.
*/
{
strcpy(DefaultRegistryModifier, slave_name+1);
RegistryClass reg(APPLICATION_SUB_KEY_NAME_NET_FIREWALL);
DefaultRegistryModifier[0] = 0;
RegistryClass my_reg(APPLICATION_SUB_KEY_NAME_NET_FIREWALL);
if (SlaveServers[i].Port != 0) {
reg.Set_Int("ForcePort", SlaveServers[i].Port);
} else {
reg.Set_Int("ForcePort", 0);
int port = my_reg.Get_Int("PortBase", PORT_BASE_MIN);
port = port + ((i+1) * 256);
if (port >= PORT_BASE_MAX-1) {
port -= (PORT_BASE_MAX - PORT_BASE_MIN);
}
reg.Set_Int("PortBase", port);
port = my_reg.Get_Int("PortPool", PORT_BASE_MIN);
port = port + ((i+1) * 1024);
if (port >= PORT_POOL_MAX-1) {
port -= (PORT_POOL_MAX - PORT_POOL_MIN);
}
reg.Set_Int("PortPool", port);
}
}
/*
** Server control info.
*/
{
strcpy(DefaultRegistryModifier, slave_name+1);
RegistryClass reg(APPLICATION_SUB_KEY_NAME_NET_SERVER_CONTROL);
DefaultRegistryModifier[0] = 0;
RegistryClass my_reg(APPLICATION_SUB_KEY_NAME_NET_SERVER_CONTROL);
/*
** The password will be the same for all slaves but they each need a port to listen on.
*/
int my_sc_port = my_reg.Get_Int(SERVER_CONTROL_PORT_KEY, DEFAULT_SERVER_CONTROL_PORT);
int slave_port = my_sc_port;
if (my_sc_port == 0) {
/*
** If server control isn't enabled for me then we need to make up some port.
*/
slave_port = DEFAULT_SERVER_CONTROL_PORT;
}
slave_port += i;
slave_port++;
SlaveServers[i].ControlPort = slave_port;
reg.Set_Int(SERVER_CONTROL_PORT_KEY, slave_port);
/*
** Inherit this from the master now.
*/
//if (my_sc_port == 0) {
// reg.Set_Int(SERVER_CONTROL_LOOPBACK_KEY, 1);
//} else {
// reg.Set_Int(SERVER_CONTROL_LOOPBACK_KEY, 0);
//}
}
/*
** Login name.
*/
{
strcpy(DefaultRegistryModifier, slave_name+1);
RegistryClass reg(APPLICATION_SUB_KEY_NAME_WOLSETTINGS);
DefaultRegistryModifier[0] = 0;
reg.Set_String("AutoLogin", SlaveServers[i].NickName);
reg.Set_String("LastLogin", SlaveServers[i].NickName);
}
/*
** Password name.
*/
{
strcpy(DefaultRegistryModifier, slave_name+1);
RegistryClass reg(APPLICATION_SUB_KEY_NAME_WOLSETTINGS);
DefaultRegistryModifier[0] = 0;
reg.Set_String("AutoPassword", SlaveServers[i].Password);
}
/*
** Serial number.
*/
{
strcpy(DefaultRegistryModifier, slave_name+1);
RegistryClass reg(APPLICATION_SUB_KEY_NAME);
DefaultRegistryModifier[0] = 0;
StringClass serial(SlaveServers[i].Serial, true);
StringClass encrypted_serial = serial;
if (serial.Get_Length()) {
ServerSettingsClass::Encrypt_Serial(serial, encrypted_serial);
}
reg.Set_String(KEY_SLAVE_SERIAL, encrypted_serial.Peek_Buffer());
}
/*
** Make it autostart.
*/
{
strcpy(DefaultRegistryModifier, slave_name+1);
RegistryClass reg(APPLICATION_SUB_KEY_NAME_WOLSETTINGS);
DefaultRegistryModifier[0] = 0;
if (reg.Is_Valid()) {
reg.Set_Int(AutoRestartClass::REG_VALUE_AUTO_RESTART_FLAG, 1);
int game_type = 0;
GameModeClass *game_mode = GameModeManager::Find("WOL");
if (game_mode && game_mode->Is_Active()) {
game_type = 1;
}
reg.Set_Int(AutoRestartClass::REG_VALUE_AUTO_RESTART_TYPE, game_type);
}
}
/*
** Tell it which multiplayer settings to use.
*/
{
strcpy(DefaultRegistryModifier, slave_name+1);
RegistryClass reg(APPLICATION_SUB_KEY_NAME_OPTIONS);
DefaultRegistryModifier[0] = 0;
reg.Set_String("MultiplayerSettings", SlaveServers[i].SettingsFileName);
}
/*
** Set the SKU number to be the FDS SKU. Do this whether the Master is a FDS or not.
*/
{
strcpy(DefaultRegistryModifier, slave_name+1);
RegistryClass reg(APPLICATION_SUB_KEY_NAME);
DefaultRegistryModifier[0] = 0;
reg.Set_Int("SKU", RENEGADE_FDS_SKU);
}
/*
** Set the bandwidth information.
** A value of 0 means auto. A value of 0xffffffff means not specified (i.e. use master settings).
*/
{
int bw = SlaveServers[i].Bandwidth;
if (bw != -1) {
strcpy(DefaultRegistryModifier, slave_name+1);
RegistryClass reg_netopt(APPLICATION_SUB_KEY_NAME_NETOPTIONS);
RegistryClass reg_bw(APPLICATION_SUB_KEY_NAME_BANDTEST);
DefaultRegistryModifier[0] = 0;
RegistryClass my_reg_netopt(APPLICATION_SUB_KEY_NAME_NETOPTIONS);
RegistryClass my_reg_bw(APPLICATION_SUB_KEY_NAME_BANDTEST);
//reg_netopt.Set_Int("BandwidthType", BANDWIDTH_AUTO);
cUserOptions::Set_Bandwidth_Type(BANDWIDTH_AUTO);
int slave_bw = bw;
/*
** If bandwidth is set to auto then divide it by the number of servers on this box.
*/
if (slave_bw == 0) {
slave_bw = my_reg_bw.Get_Int("Up", 0);
int num = Get_Num_Enabled_Slaves();
if (num) {
slave_bw = slave_bw / (num+1);
}
}
reg_bw.Set_Int("Up", slave_bw);
reg_bw.Set_Int("Down", slave_bw);
}
}
#if (0)
/*
** Give the window a different position so we are not completely overlapping.
*/
{
strcpy(DefaultRegistryModifier, slave_name+1);
RegistryClass reg(APPLICATION_SUB_KEY_NAME_OPTIONS);
DefaultRegistryModifier[0] = 0;
reg.Set_Int("WindowX", (i * 32) + 32);
reg.Set_Int("WindowY", (i * 32) + 32);
}
#endif //(0)
}
}
}
/***********************************************************************************************
* SlaveMasterClass::Delete_Registry_Copies -- Delete old slave registry copies *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 11/21/2001 3:44PM ST : Created *
*=============================================================================================*/
void SlaveMasterClass::Delete_Registry_Copies(void)
{
HKEY base_key;
long result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, APPLICATION_SUB_KEY_NAME, 0, KEY_ALL_ACCESS, &base_key);
WWASSERT(result == ERROR_SUCCESS);
if (result == ERROR_SUCCESS) {
int index = 0;
char new_path[1024];
char slave_name[64];
while (index < MAX_SLAVES) {
strcpy(new_path, APPLICATION_SUB_KEY_NAME);
sprintf(slave_name, "\\slave_%d", index);
strcat(new_path, slave_name);
RegistryClass::Delete_Registry_Tree(new_path);
index++;
}
}
}
/***********************************************************************************************
* SlaveMasterClass::Get_Num_Enabled_Slaves -- How many slaves are enabled? *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Number of enabled slaves *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 1/5/2002 11:47PM ST : Created *
*=============================================================================================*/
int SlaveMasterClass::Get_Num_Enabled_Slaves(void)
{
int enabled = 0;
for (int i=0 ; i<NumSlaveServers ; i++) {
if (SlaveServers[i].Enable) {
enabled++;
}
}
return(enabled);
}