/*
** 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