/* ** 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 . */ /*********************************************************************************************** *** 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 #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 #include #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 27000) { forced = true; for (int i=0 ; i= 0); if (index >= 0 && index 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= 500); *buffer = 0; for (int i=0 ; i= 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