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

/***********************************************************************************************
 ***                            Confidential - Westwood Studios                              ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : Commando                                                     *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/Commando/cnetwork.cpp                         $*
 *                                                                                             *
 *                      $Author:: Byon_g                                                     $*
 *                                                                                             *
 *                     $Modtime:: 3/29/02 5:02p                                               $*
 *                                                                                             *
 *                    $Revision:: 152                                                        $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include "cnetwork.h"

#include <shellapi.h>
#include <stdio.h>

#include "specialbuilds.h"

#include "langmode.h"
#include "wolgmode.h"
#include "playermanager.h"
#include "textdisplay.h"
#include "gameobjmanager.h"
#include "multihud.h"
#include "WWAudio.H"
#include "useroptions.h"
#include "devoptions.h"
#include "translatedb.h"
#include "string_ids.h"
#include "msgstatlistgroup.h"
#include "wwprofile.h"
#include "gametype.h"
#include "crc.h"
#include "render2d.h"
#include "wwmemlog.h"
#include "svrgoodbyeevent.h"
#include "gameoptionsevent.h"
#include "evictionevent.h"
#include "changeteamevent.h"
#include "clientcontrol.h"
#include "serverfps.h"
#include "sbbomanager.h"
#include "clientgoodbyeevent.h"
//#include "helptext.h"
#include	"natter.h"
#include	"vistable.h"
#include "gameinitmgr.h"
#include "dlgmessagebox.h"
#include "apppacketstats.h"
#include "clientfps.h"
#include "gamechanlist.h"
#include "packetmgr.h"
#include "clientpingmanager.h"
#include "bandwidthgraph.h"
#include "buildnum.h"
#include "messagewindow.h"
#include "wwmemlog.h"
#include "consolemode.h"
#include "slavemaster.h"
#include "gamedataupdateevent.h"
#include "gamespyadmin.h"
#include "demosupport.h"
#include "serversettings.h"
#include "dlgmpconnectionrefused.h"

#include "Resource.h"
#include <WWUI\DialogMgr.h>
#include "ffactory.h"
#include "realcrc.h"

extern bool g_is_loading;

//
// Networking cNetwork statics
//
char												cNetwork::MessageToSend[];
char												cNetwork::Command[];
int												cNetwork::ExeKey								= 0;
int												cNetwork::ExeCRC								= 0;
int												cNetwork::StringsCRC							= 0;
char												cNetwork::ClientString[];
char												cNetwork::ClientEnumerationString[];
CombatNetworkReceiverInstanceClass *	cNetwork::NetworkReceiver					= NULL;
GameCombatNetworkHandlerClass				cNetwork::NetHandler;
int												cNetwork::Fps									= 0;
int												cNetwork::ThinkCount							= 0;
cMsgStatList *									cNetwork::PClientStatList					= NULL;
cMsgStatListGroup *							cNetwork::PServerStatListGroup			= NULL;
cConnection *									cNetwork::PClientConnection				= NULL;
cConnection *									cNetwork::PServerConnection				= NULL;
CombatNetworkReceiver *						cNetwork::Receiver							= NULL;
float												cNetwork::GraphingY;
float												cNetwork::BandwidthBarLength				= 0;
//int												cNetwork::BandwidthScaler					= 28800;
int												cNetwork::BandwidthScaler					= 50000;

bool												cNetwork::HaveDoneTeamChangeDialog		= false;

bool												cNetwork::HaveDoneMotdDialog				= false;
VisTableClass *								cNetwork::VisTable							= NULL;
bool												cNetwork::LastServerConnectionStateBad = false;
bool												cNetwork::SensibleUpdates					= true;

//-----------------------------------------------------------------------------
void cNetwork::Init_Client(unsigned short my_port)
{
	WWMEMLOG(MEM_NETWORK);

#ifndef FREEDEDICATEDSERVER

   WWDEBUG_SAY(("cNetwork::Init_Client\n"));

	if (PClientConnection != NULL) {
		Cleanup_Client();
	}

	PClientStatList = new cMsgStatList;
	WWASSERT(PClientStatList != NULL);
	//PClientStatList->Init(MESSAGE_COUNT);
	PClientStatList->Init(1);
	for (int i = 0; i < PClientStatList->Get_Num_Stats(); i++) {
		//PClientStatList->Set_Name(i, Message_Type_Translation(i));

		/*
		char message_trans[200];
		strcpy(message_trans, Message_Type_Translation(i));
		PClientStatList->Set_Name(i, &message_trans[8]);
		*/
		PClientStatList->Set_Name(i, "message");
	}

   WWASSERT(PClientConnection == NULL);
   PClientConnection = new cConnection;
   WWASSERT(PClientConnection != NULL);

	CombatManager::Set_I_Am_Client(true);

	PClientConnection->Install_Accept_Handler(Accept_Handler);
	PClientConnection->Install_Refusal_Handler(Refusal_Handler);
	PClientConnection->Install_Client_Broken_Connection_Handler(Client_Broken_Connection_Handler);
	PClientConnection->Install_Client_Packet_Handler(Client_Packet_Handler);

	if (cGameSpyAdmin::Is_Gamespy_Game()) {
		WWASSERT(PTheGameData != NULL);
		The_Game()->Set_Password(cGameSpyAdmin::Get_Password_Attempt());
	}

	ULONG bbo = 0;
	//if (IS_SOLOPLAY || GameModeManager::Find("LAN")->Is_Active()) {
	if (IS_SOLOPLAY || 
		 (GameModeManager::Find("LAN")->Is_Active() && !cGameSpyAdmin::Is_Gamespy_Game())) {

		bbo = cBandwidth::Get_Bandwidth_Bps_From_Type(BANDWIDTH_LANT1);

		WWASSERT(bbo > 0);
		cBandwidthGraph::Set_Scale(200000);

		HaveDoneTeamChangeDialog = false;
	} else {
		//WWASSERT(GameModeManager::Find("WOL")->Is_Active());
		bbo = cBandwidth::Get_Bandwidth_Bps_From_Type((BANDWIDTH_TYPE_ENUM)cUserOptions::Get_Bandwidth_Type());
		//bbo = cUserOptions::BandwidthBps.Get();
		WWASSERT(bbo > 0);

		int bw_scale = (bbo * 2) / 10;
		bw_scale = (bw_scale / 1000) * 1000;
		cBandwidthGraph::Set_Scale(bw_scale);

		if (GameModeManager::Find("WOL")->Is_Active()) {
			HaveDoneTeamChangeDialog = true;
		}
	}

	PClientConnection->Set_Bandwidth_Budget_Out(bbo);

   BOOL is_flow_control_enabled = !IS_SOLOPLAY;
   PClientConnection->Enable_Flow_Control(is_flow_control_enabled);

	WWASSERT(PTheGameData != NULL);
   PClientConnection->Init_As_Client(
		The_Game()->Get_Ip_Address(), The_Game()->Get_Port(), my_port);

	//
	// This packet resurfaces on the server in Application_Acceptance_Handler
	//

	cPacket packet;
   packet.Add_Wide_Terminated_String(cNetInterface::Get_Nickname());
   packet.Add_Wide_Terminated_String(The_Game()->Get_Password(), true);
	packet.Add(ExeKey);
	packet.Add(bbo); // note, this field is consumed by wwnet

   PClientConnection->Connect_Cs(packet);
	packet.Flush();

	/*
	if (I_Am_Only_Client()) {
		ServerFps.Init();
	}
	*/

	HaveDoneMotdDialog = false;

	if (I_Am_Only_Client())
	{
		cAppPacketStats::Reset();
	}
	LastServerConnectionStateBad = false;

	cClientPingManager::Init();

#endif // !FREEDEDICATEDSERVER

}

//-----------------------------------------------------------------------------
void cNetwork::Cleanup_Client(void)
{
	WWMEMLOG(MEM_NETWORK);
#ifndef FREEDEDICATEDSERVER

   WWDEBUG_SAY(("cNetwork::Cleanup_Client\n"));

   if (I_Am_Client()) {

      if (PClientConnection->Is_Established()) { // i.e we did have a connection

			cClientGoodbyeEvent * p_event = new cClientGoodbyeEvent;
			p_event->Init();
			Flush();
      }

      delete PClientConnection;
      PClientConnection = NULL;
   }

	CombatManager::Set_I_Am_Client(false);

	delete PClientStatList;
	PClientStatList = NULL;

	delete PClientControl;
	PClientControl = NULL;

	delete PClientFps;
	PClientFps = NULL;

#endif // !FREEDEDICATEDSERVER
}

//-----------------------------------------------------------------------------
void cNetwork::Accept_Handler(void)
{
	WWMEMLOG(MEM_NETWORK);
#ifndef FREEDEDICATEDSERVER

   WWDEBUG_SAY(("cNetwork::Accept_Handler\n"));

   WWASSERT(I_Am_Client());

	CombatManager::Set_My_Id(Get_My_Id());

	NetworkObjectMgrClass::Init_New_Client_ID(Get_My_Id());

	if (I_Am_Only_Client()) {

		//
		// Create C->S mirrored client control object
		//
		WWASSERT(PClientControl == NULL);
		PClientControl = new CClientControl;
		PClientControl->Init();

		//
		// Create C->S mirrored client fps object
		//
		WWASSERT(PClientFps == NULL);
		PClientFps = new CClientFps;
		PClientFps->Init();
	}


   if (!I_Am_Server()) {
      if (GameModeManager::Find("LAN")->Is_Active()) {
         PLC->Accept_Actions();
      } else {
			 GameModeClass* gameMode = GameModeManager::Find("WOL");

			 if (gameMode && gameMode->Is_Active()) {
				 WolGameModeClass* wolGame = static_cast<WolGameModeClass*>(gameMode);
				 WWASSERT(wolGame);
				 wolGame->Accept_Actions();
			 }
      }
   }

#endif // !FREEDEDICATEDSERVER
}

//-----------------------------------------------------------------------------
void cNetwork::Refusal_Handler(REFUSAL_CODE refusal_code)
{
#ifndef FREEDEDICATEDSERVER

   WWDEBUG_SAY(("cNetwork::Refusal_Handler\n"));

	// Close connecting dialog as neccessary.
	DialogBaseClass* dialog = DialogMgrClass::Find_Dialog(IDD_MULTIPLAY_CONNECTING);

	if (dialog != NULL) {
		// Sending 1 as the parameter tells the dialog that it is being closed
		// as a result of a refusal from the server.
 		dialog->On_Command(IDCANCEL, 0, 1);
	}

	 // Verify refusal code is within valid range
	 WWASSERT(REFUSAL_CLIENT_ACCEPTED <= refusal_code && REFUSAL_BY_APPLICATION >= refusal_code);

   WWASSERT(I_Am_Client());

   if (GameModeManager::Find("LAN")->Is_Active()) {
      PLC->Refusal_Actions();
   } else {
		 GameModeClass* gameMode = GameModeManager::Find("WOL");

		 if (gameMode && gameMode->Is_Active()) {
			 WolGameModeClass* wolGame = static_cast<WolGameModeClass*>(gameMode);
			 WWASSERT(wolGame);
			 wolGame->Refusal_Actions();
		 }
   }

   //
   // The server refused our connection request! At this stage this
   // is fatal. Later on we would have to re-init the connection etc.
   //
	static const unsigned long _refusalStrings[] = {
		IDS_MP_CONNECTION_REFUSED_GAME_FULL,      // REFUSAL_GAME_FULL
		IDS_MP_PASSWORD_WRONG, // REFUSAL_BAD_PASSWORD
		IDS_MENU_VERSION_MISMATCH, // REFUSAL_VERSION_MISMATCH
		IDS_MP_CONNECTION_REFUSED_BY_APPLICATION, // REFUSAL_PLAYER_EXISTS
		IDS_MP_CONNECTION_REFUSED_BY_APPLICATION  // REFUSAL_BY_APPLICATION
		};

	const unsigned long refusalMsg = _refusalStrings[refusal_code - 1];

	if (cGameSpyAdmin::Is_Gamespy_Game()) {
		if (refusal_code == REFUSAL_VERSION_MISMATCH) {
			WideStringClass tval;
			tval.Format(L"%s...%s", TRANSLATE(IDS_MP_CONNECTION_REFUSED_BY_APPLICATION), 
				TRANSLATE(IDS_MENU_VERSION_MISMATCH));
			DlgMPConnectionRefused::DoDialog(tval, false);
		} else {
			DlgMPConnectionRefused::DoDialog(TRANSLATE(refusalMsg), false);
		}
	} else {
		DlgMsgBox::DoDialog(TRANSLATE (IDS_MENU_SERVER_MESSAGE_TITLE), TRANSLATE(refusalMsg));
	}
	
	
	//
	// N.B. We cannot destroy the connection from inside this callback.
	//

#endif // !FREEDEDICATEDSERVER
}

//-----------------------------------------------------------------------------
int cNetwork::Get_Data_Files_CRC(void)
{
#define	UNINITIALLIZED_CRC	0x4592abf1
	static int crc = UNINITIALLIZED_CRC;
	if ( crc == UNINITIALLIZED_CRC ) {
		char * filelist[] = {
		"jgo`fqv+aag",					//"objects.ddb",           
		"dwhjw+lkl",					//"armor.ini",             
		"gjk`v+lkl",					//"bones.ini",             
		"vpwcdf``cc`fqv+lkl",		//"surfaceeffects.ini",    
		"fdh`wdv+lkl",					//"cameras.ini",           
		"fZkjaZhbZi5+r6a",			//"c_nod_mg_l0.w3d",       
		"fZkjaZwnZi5+r6a",			//"c_nod_rk_l0.w3d",       
		"fZkjaZciZi5+r6a",			//"c_nod_fl_l0.w3d",       
		"fZkjaZ`kZi5+r6a",			//"c_nod_en_l0.w3d",       
		"fZkjaZhbjZi5+r6a",			//"c_nod_mgo_l0.w3d",      
		"fZkjaZwnjZi5+r6a",			//"c_nod_rko_l0.w3d",      
		"fZkjaZfm`hqZi5+r6a",		//"c_nod_chemt_l0.w3d",    
		"fZkjaZvklu`wZi5+r6a",		//"c_nod_sniper_l0.w3d",   
		"fZkjaZwvjiaZi5+r6a",		//"c_nod_rsold_l0.w3d",    
		"fZkjaZvqiqmZi5+r6a",		//"c_nod_stlth_l0.w3d",    
		"fZkjaZvdnpZi5+r6a",			//"c_nod_saku_l0.w3d",     
		"fZkjaZvdnp7Zi5+r6a",		//"c_nod_saku2_l0.w3d",    
		"fZkjaZwdsZi5+r6a",			//"c_nod_rav_l0.w3d",      
		"fZkjaZhwdsZi5+r6a",			//"c_nod_mrav_l0.w3d",     
		"fZkjaZhaZi5+r6a",			//"c_nod_mdz_l0.w3d",      
		"fZkjaZha7Zi5+r6a",			//"c_nod_mdz2_l0.w3d",     
		"fZkjaZqfZi5+r6a",			//"c_nod_tc_l0.w3d",       
		"fZkjaZhpqdkqZi5+r6a",		//"c_nod_mutant_l0.w3d",   
		"fZkjaZhviaZi5+r6a",			//"c_nod_msld_l0.w3d",     
		"fZkjaZvvjiaZi5+r6a",		//"c_nod_ssold_l0.w3d",    
		"fZkjaZu`qhZi5+r6a",			//"c_nod_petm_l0.w3d",     
		"fZkjaZndk`Zi5+r6a",			//"c_nod_kane_l0.w3d",     
		"fZbalZhbZi5+r6a",			//"c_gdi_mg_l0.w3d",       
		"fZbalZwnZi5+r6a",			//"c_gdi_rk_l0.w3d",       
		"fZbalZbwZi5+r6a",			//"c_gdi_gr_l0.w3d",       
		"fZbalZ`kZi5+r6a",			//"c_gdi_en_l0.w3d",       
		"fZbalZhbjZi5+r6a",			//"c_gdi_mgo_l0.w3d",      
		"fZbalZwnjZi5+r6a",			//"c_gdi_rko_l0.w3d",      
		"fZbalZv|aZi5+r6a",			//"c_gdi_syd_l0.w3d",      
		"fZbalZa`daZi5+r6a",			//"c_gdi_dead_l0.w3d",     
		"fZbalZbpkZi5+r6a",			//"c_gdi_gun_l0.w3d",      
		"fZbalZuqfmZi5+r6a",			//"c_gdi_ptch_l0.w3d",     
		"fZmdsjfZi5+r6a",				//"c_havoc_l0.w3d",        
		"fZmdsjfkZi5+r6a",			//"c_havocn_l0.w3d",       
		"fZmdsjfrZi5+r6a",			//"c_havocw_l0.w3d",       
		"fZmdsjfaZi5+r6a",			//"c_havocd_l0.w3d",       
		"fZbalZv|aZi5+r6a",			//"c_gdi_syd_l0.w3d",      
		"fZbalZv|a7Zi5+r6a",			//"c_gdi_syd2_l0.w3d",     
		"fZbalZhjglZi5+r6a",			//"c_gdi_mobi_l0.w3d",     
		"fZbalZmjqrZi5+r6a",			//"c_gdi_hotw_l0.w3d",     
		"fZbalZiqZi5+r6a",			//"c_gdi_lt_l0.w3d",       
		"fZkjaZu`qwZi5+r6a",			//"c_nod_petr_l0.w3d",     
		"fZijbdkZi5+r6a",				//"c_logan_l0.w3d",        
		"fZbalZijfn`Zi5+r6a",		//"c_gdi_locke_l0.w3d",    
		"sZkjaZgpbb|+r6a",			//"v_nod_buggy.w3d",       
		"sZkjaZdufZh+r6a",			//"v_nod_apc_m.w3d",       
		"sZkjaZdwqiw|+r6a",			//"v_nod_artlry.w3d",      
		"sZkjaZcidh`+r6a",			//"v_nod_flame.w3d",       
		"sZkjaZiqdkn+r6a",			//"v_nod_ltank.w3d",       
		"sZkjaZvqiqm+r6a",			//"v_nod_stlth.w3d",       
		"sZkjaZqwkvuqZh+r6a",		//"v_nod_trnspt_m.w3d",    
		"sZkjaZdudfm`Zh+r6a",		//"v_nod_apache_m.w3d",    
		"sZfmdh`i`jk+r6a",			//"v_chameleon.w3d",       
		"sZbalZmphs``+r6a",			//"v_gdi_humvee.w3d",      
		"sZbalZdufZh+r6a",			//"v_gdi_apc_m.w3d",       
		"sZbalZhwiv+r6a",				//"v_gdi_mrls.w3d",        
		"sZbalZh`aqkn+r6a",			//"v_gdi_medtnk.w3d",      
		"sZbalZhdhhqm+r6a",			//"v_gdi_mammth.w3d",      
		"sZulfnpu54+r6a",				//"v_pickup01.w3d",        
		"sZv`adk54+r6a",				//"v_sedan01.w3d",         
		"sZbalZjwfdZh+r6a",			//"v_gdi_orca_m.w3d",      
		"sZbalZqwkvuqZh+r6a",		//"v_gdi_trnspt_m.w3d",    
		};
#define	NUM_CRC_FILES	(sizeof(	filelist ) / sizeof( filelist[0] ) )
		crc = 0;
		for ( int i = 0; i < NUM_CRC_FILES; i++ ) {
			StringClass name = filelist[i];
			// Obfuscate name
			char * n = &name[0];
			while ( *n ) *n++ ^= 0x5;
//			Debug_Say(( "		\"%s\",\n", name ));
			FileClass * file = _TheFileFactory->Get_File( name );
			if ( file && file->Is_Available() ) {
				int size = file->Size();
				file->Open();
				while ( size > 0 ) {
					unsigned char buffer[ 4096 ];
					int amount = min( (int)size, (int)sizeof(buffer) );
					amount = file->Read( buffer, amount );
					crc = CRC_Memory( buffer, amount, crc );
					size -= amount;
				}
				file->Close();
			} else {
//				Debug_Say(( "******%s not found\n", name ));
			}
		}
	}
	return crc;
}
//-----------------------------------------------------------------------------

void cNetwork::Compute_Exe_Key(void)
{
   WWDEBUG_SAY(("cNetwork::Compute_Exe_Key\n"));

	char exe_filename[500];
   int succeeded = 0;
	succeeded = ::GetModuleFileName(NULL, exe_filename, sizeof(exe_filename));
	WWASSERT(succeeded);

	StringClass key_string;
	StringClass string;
	StringClass build_string;

	//
	// 11/07/01
	// We now match only on build number.
	//
	string.Format("RENEGADE %u", BuildInfoClass::Get_Build_Number());

	WWDEBUG_SAY(("File id string: %s\n", string));
	key_string += string;
	key_string += " ";
	ExeCRC = CRCEngine()(string, strlen(string));

	//
	// TSS 09/07/01
	// N.B. Do not require scripts to be in sync any more, since they do not run on the client.
	// Furthermore we would have to match the correct scripts target (d/p/r) and
	// also handle the registry flag that forces scriptsd.dll.
	//

	//
	// TSS102401
	// We cannot match on exact strings.tdb because foreign language versions
	// are different files. Instead, match on filename and internal version number.
	//
	//cMiscUtil::Get_File_Id_String("Data\\strings.tdb", string);
	string.Format("strings.tdb %u", TranslateDBClass::Get_Version_Number());
	WWDEBUG_SAY(("File id string: %s\n", string));
	key_string += string;
	key_string += " ";
	StringsCRC = CRCEngine()(string, strlen(string));

	//
	// TSS102401 - we can't match always.dbs either.
	// always.dbs will not match among foreign language version for numerous reasons.
	//

	//
	// Use the crc of the keystring as the key
	//
	ExeKey = CRCEngine()(key_string, strlen(key_string));

	//
	// Include data file crc
	//
	int data_file_crc = Get_Data_Files_CRC();
	ExeKey ^= data_file_crc;
}

//-----------------------------------------------------------------------------
void cNetwork::Onetime_Init(void)
{
	WWMEMLOG(MEM_NETWORK);
   WWDEBUG_SAY(("cNetwork::Onetime_Init\n"));

	Compute_Exe_Key();

   NetworkReceiver = new CombatNetworkReceiverInstanceClass;
	Set_Receiver(NetworkReceiver);

	CombatManager::Set_Combat_Network_Handler(&NetHandler);

   //Clear_Help_Text();
}

//-----------------------------------------------------------------------------
void cNetwork::Onetime_Shutdown(void)
{
   WWDEBUG_SAY(("cNetwork::Onetime_Shutdown\n"));

   Set_Receiver(NULL);
	delete NetworkReceiver;

#if 0
	UINT comp_bytes	= cConnection::Get_Total_Compressed_Bytes_Sent();
	UINT uncomp_bytes = cConnection::Get_Total_Uncompressed_Bytes_Sent();
	Debug_Say(("\n"));
	Debug_Say(("TotalCompressedBytesSent   = %d\n", comp_bytes));
	Debug_Say(("Without compression        = %d\n", uncomp_bytes));
	if (uncomp_bytes > 0) {
		Debug_Say(("Compressed/Uncompressed    = %-5.2f\n\n",
			comp_bytes / (float) uncomp_bytes));
	}
#endif // 0

#pragma message("(TSS) This packet ref count assert very occasionally fails.")
   //WWASSERT(cPacket::Get_Ref_Count() == 0);

	REF_PTR_RELEASE(VisTable);

	cGameChannelList::Remove_All();//TSS092201 added

	//
	// These are only printed once. So only run one game on a test run.
	//
	cAppPacketStats::Dump_Diagnostics();
}

//-----------------------------------------------------------------------------
void cNetwork::Init_Server(void)
{
	WWMEMLOG(MEM_NETWORK);
   WWDEBUG_SAY(("cNetwork::Init_Server\n"));

#ifndef BETACLIENT

	NetworkObjectClass::Set_Is_Server(true);

	PServerStatListGroup = new cMsgStatListGroup;
	WWASSERT(PServerStatListGroup != NULL);
	//PServerStatListGroup->Init(The_Game()->Get_Max_Players(), MESSAGE_COUNT);
	WWASSERT(PTheGameData != NULL);
	PServerStatListGroup->Init(The_Game()->Get_Max_Players(), 1);

	/*
	for (int message_type = 0; message_type < MESSAGE_COUNT; message_type++) {

		char message_trans[200];
		strcpy(message_trans, Message_Type_Translation(message_type));
		PServerStatListGroup->Set_Name(message_type, &message_trans[8]);
	}
	*/

   WWASSERT(PServerConnection == NULL);
   PServerConnection = new cConnection;
   WWASSERT(PServerConnection != NULL);

   CombatManager::Set_I_Am_Server(true);

	PServerConnection->Install_Server_Broken_Connection_Handler(Server_Broken_Connection_Handler);
	PServerConnection->Install_Eviction_Handler(Eviction_Handler);
	PServerConnection->Install_Conn_Handler(Connection_Handler);
	PServerConnection->Install_Application_Acceptance_Handler(Application_Acceptance_Handler);
	PServerConnection->Install_Server_Packet_Handler(Server_Packet_Handler);


	//if (IS_SOLOPLAY || GameModeManager::Find("LAN")->Is_Active()) {
	if (IS_SOLOPLAY || 
		 (GameModeManager::Find("LAN")->Is_Active() && !cGameSpyAdmin::Is_Gamespy_Game())) {

		ULONG bbo = cBandwidth::Get_Bandwidth_Bps_From_Type(BANDWIDTH_LANT1);
		WWASSERT(bbo > 0);
		PServerConnection->Set_Bandwidth_Budget_Out(bbo);
		cBandwidthGraph::Set_Scale(200000);
	} else {
		//WWASSERT(GameModeManager::Find("WOL")->Is_Active());
		WWASSERT(cUserOptions::BandwidthBps.Get() > 0);
		unsigned long bw = cBandwidth::Get_Bandwidth_Bps_From_Type((BANDWIDTH_TYPE_ENUM)cUserOptions::Get_Bandwidth_Type());

		/*
		** Only use a portion of the bandwidth based on how many slave servers there are.
		*/
		if (The_Game()->IsDedicated.Get() && !SlaveMaster.Am_I_Slave()) {
			if (cUserOptions::Get_Bandwidth_Type() == BANDWIDTH_AUTO) {
				if (ServerSettingsClass::Get_Master_Bandwidth() == 0 || ServerSettingsClass::Get_Master_Bandwidth() == 0xffffffff) {
					int num_slaves = SlaveMaster.Get_Num_Enabled_Slaves();
					if (num_slaves) {
						bw = bw / (num_slaves+1);
					}
				}
			}
		}

		PServerConnection->Set_Bandwidth_Budget_Out(bw);
		//PServerConnection->Set_Bandwidth_Budget_Out(cUserOptions::BandwidthBps.Get());
		int bw_scale = (cUserOptions::BandwidthBps.Get() * 2) / 10;
		bw_scale = (bw_scale / 1000) * 1000;
		cBandwidthGraph::Set_Scale(bw_scale);
	}

   double max_acceptable_packetloss_pc = 10;
   PServerConnection->Set_Max_Acceptable_Packetloss_Pc(max_acceptable_packetloss_pc);

   BOOL is_flow_control_enabled = !IS_SOLOPLAY;
   PServerConnection->Enable_Flow_Control(is_flow_control_enabled);

	WWASSERT(PTheGameData != NULL);
	PServerConnection->Init_As_Server(
		The_Game()->Get_Port(),
		The_Game()->Get_Max_Players(),
		The_Game()->IsDedicated.Get(),
		ntohl(The_Game()->Get_Ip_Address()));

   //
   // Create teams
   //
	//if (The_Game()->Is_Team_Game()) {
	for (int team_num = 0; team_num < MAX_TEAMS; team_num++) {
		cTeam * p_team = new cTeam;
		p_team->Init(team_num);
	}
	//}

	cSbboManager::Reset();

	cAppPacketStats::Reset();

#endif // not BETACLIENT
}

//-----------------------------------------------------------------------------
void cNetwork::Cleanup_Server(void)
{
   WWDEBUG_SAY(("cNetwork::Cleanup_Server\n"));

   if (I_Am_Server()) {

      delete PServerConnection;
      PServerConnection = NULL;
   }

   CombatManager::Set_I_Am_Server(false);

	delete PServerStatListGroup;
	PServerStatListGroup = NULL;

	NetworkObjectClass::Set_Is_Server(false);
}

//-----------------------------------------------------------------------------
enum {
	CHUNKID_PLAYERMANAGER = 9876543,
};

//-----------------------------------------------------------------------------
bool cNetwork::Save(ChunkSaveClass & csave)
{
	csave.Begin_Chunk(CHUNKID_PLAYERMANAGER);
	cPlayerManager::Save(csave);
	csave.End_Chunk();

	return true;
}

//-----------------------------------------------------------------------------
bool cNetwork::Load(ChunkLoadClass &cload)
{
	while (cload.Open_Chunk()) {
		switch(cload.Cur_Chunk_ID()) {

			case CHUNKID_PLAYERMANAGER:
				//cPlayerManager::Remove_All();
				cPlayerManager::Load(cload);
				break;

			default:
				Debug_Say(( "Unrecognized cNetwork chunkID\n" ));
				break;

		}
		cload.Close_Chunk();
	}

	return true;
}

//-----------------------------------------------------------------------------
void cNetwork::Update_Fps(void)
{
	static DWORD last_time_ms = 0;
	static int frame_count = 0;
   DWORD time_now_ms = TIMEGETTIME();

	// Handle timer resetting.
	if (time_now_ms < last_time_ms) {
		last_time_ms = time_now_ms;
	}
	frame_count++;
	DWORD time_interval = time_now_ms - last_time_ms;

	if (time_interval > 1000) {

		Fps = (int) (frame_count * 1000 / (float) time_interval + 0.5f);
		last_time_ms = time_now_ms;
		frame_count = 0;

// Needed for release mode too. ST - 1/23/2002 11:02PM
//#ifdef WWDEBUG
		if (I_Am_Server()) {
			WWASSERT(cServerFps::Get_Instance() != NULL);
			cServerFps::Get_Instance()->Set_Fps(Fps);
		}
//#endif // WWDEBUG

		if (I_Am_Client() && PClientFps != NULL) {
			PClientFps->Set_Fps(Fps);
		}
	}
}




void cNetwork::Connection_Status_Change_Feedback(void)
{
	static unsigned long _last_print = TIMEGETTIME();
	static bool _last_print_bad = false;
	static unsigned long _print_good_soon = 0;

	unsigned long time = TIMEGETTIME();
	const WCHAR *string = NULL;
	if (LastServerConnectionStateBad) {
		if (_last_print_bad && time - _last_print < 4000) {
			return;
		}
		string = TRANSLATE (IDS_MENU_CONNECTION_INTERRUPTED);
		_last_print_bad = true;
	} else {
		/*
		** Wait 2 secs after connection restore before printing message.
		*/
		if (_last_print_bad && _print_good_soon == 0) {
			_print_good_soon = time;
			return;
		}
		if (!_last_print_bad) {
			return;
		}
		if (time - _print_good_soon < 2000) {
			return;
		}
		string = TRANSLATE (IDS_MENU_CONNECTION_RESTORED);
		_last_print_bad = false;
	}
	_last_print = time;
	WideStringClass widestring(string, true);
	if (CombatManager::Get_Message_Window() != NULL) {
		//
		//	Display the message...
		//
		CombatManager::Get_Message_Window ()->Add_Message (widestring);
	}

	//
	// Write it to the logfile
	//
	StringClass temp_string;
	widestring.Convert_To(temp_string);
	WWDEBUG_SAY(("\n***%s\n", temp_string.Peek_Buffer()));
}

//-----------------------------------------------------------------------------
void cNetwork::Update(void)
{
	WWPROFILE( "cNetwork::Update" );
	WWMEMLOG(MEM_GAMEDATA);

	bool flush_packets = false;

	ThinkCount++;

	//
	// Coding bugs may result in this function being called recursively.
	// Detect and assert!
	//
	static int recursion_level = 0;
	recursion_level++;
	WWASSERT(recursion_level == 1);

	Update_Fps();

#ifdef WWDEBUG
	//
	// Watch out for unexpected slow frames. They may interrupt networking.
	//
	static DWORD last_time_ms = TIMEGETTIME();
	DWORD time_now_ms = TIMEGETTIME();
	if (time_now_ms - last_time_ms > 2000) {
		Debug_Say(("\n***cNetwork::Update: warning, think # %d was slow (%u ms)\n\n",
			ThinkCount,
			time_now_ms - last_time_ms));
	}
	last_time_ms = time_now_ms;
#endif // WWDEBUG

	if (I_Am_Server()) {
		if (I_Am_Client()) {
			WWPROFILE( "Client Send" );
			PClientConnection->Service_Send();

			if (PClientConnection->Is_Bad_Connection() != LastServerConnectionStateBad) {
				LastServerConnectionStateBad = PClientConnection->Is_Bad_Connection();
				//Connection_Status_Change_Feedback();
			}
			Connection_Status_Change_Feedback();
		}

//		{	WWPROFILE("GameSpy_QnR");
//			GameSpyQnR.Think();
//		}

		WWPROFILE( "Server Read" );
		PServerConnection->Service_Read();

		if (!g_is_loading) {
			WWPROFILE( "Shared CS Think" );
			Shared_Client_And_Server_Think();
		}

		if (I_Am_Client() && !g_is_loading) {
			WWPROFILE( "Client_Think" );
			if (Client_Think()) {
				flush_packets = true;
			}
		}

		if (!g_is_loading) {
			WWPROFILE( "Server_Think" );
			if (Server_Think()) {
				flush_packets = true;
			} else {
				// Server only sends packets in response to server think, not client think.
				flush_packets = false;
			}
		}

		{
		WWPROFILE( "Server Send" );
		PServerConnection->Service_Send();
		}

		if (I_Am_Client()) {
			WWPROFILE( "Client Read" );
			PClientConnection->Service_Read();
		}

	} else if (I_Am_Client()) {

		{
		WWPROFILE( "Client Read" );
		PClientConnection->Service_Read();
		}

		if (PClientConnection->Is_Bad_Connection() != LastServerConnectionStateBad) {
			LastServerConnectionStateBad = PClientConnection->Is_Bad_Connection();
			Connection_Status_Change_Feedback();
		}

		if (!g_is_loading) {
			WWPROFILE( "Shared CS Think" );
			Shared_Client_And_Server_Think();
		}

		if (!g_is_loading) {
			WWPROFILE( "Client_Think" );
			if (Client_Think()) {
				flush_packets = true;
			}
		}

		{
		WWPROFILE( "Client Send" );
		if (PClientConnection != NULL) {
			PClientConnection->Service_Send();
		}
		}
	}

	DEMO_SECURITY_CHECK;

	NetworkObjectMgrClass::Delete_Pending();

	if (flush_packets) {
		PacketManager.Flush(true);
	}

	recursion_level--;
	WWASSERT(recursion_level == 0);
}

//-----------------------------------------------------------------------------
void cNetwork::Client_Send_Packet(cPacket & packet, int mode)
{
#ifndef FREEDEDICATEDSERVER

   WWASSERT(I_Am_Client());

	if (cNetwork::PClientConnection->Is_Established()) {

		PClientConnection->Send_Packet_To_Individual(packet, 0, mode);

		/*
		BYTE message_type = packet.Peek_Message_Type();
		PClientStatList->Increment_Num_Msg_Sent(message_type);
		PClientStatList->Increment_Num_Byte_Sent(message_type, packet.Get_Compressed_Size_Bytes());
		*/

	} else {
		WWDEBUG_SAY(("cNetwork::Client_Send_Packet: warning: Connection not yet established.\n"));
		//TSS2001 XXX DIE;
	}

#endif // !FREEDEDICATEDSERVER
}

//-----------------------------------------------------------------------------
void cNetwork::Server_Send_Packet(cPacket & packet, int mode, int recipient)
{
#ifndef BETACLIENT

   WWASSERT(I_Am_Server());
   WWASSERT(PServerConnection->Is_Established());

	if (recipient == ALL) {
		//
		// We cannot just send to all rhosts because that includes anyone
		// browsing the server settings, whereas here we wish to send to the
		// ingame players.
		//
		//PServerConnection->Send_Packet_To_All(*p_packet, mode);

      SLNode<cPlayer> * objnode;
      for (objnode = cPlayerManager::Get_Player_Object_List()->Head();
         objnode; objnode = objnode->Next()) {

		   cPlayer * p_player = objnode->Data();
         WWASSERT(p_player != NULL);

			//if (p_player->Is_Human()) {
			if (p_player->Get_Is_Active().Is_True() &&
				 p_player->Is_Human() &&
				 p_player->Get_Is_In_Game().Is_True()) {

				int client_id = p_player->Get_Id();

				PServerConnection->Send_Packet_To_Individual(
					packet, client_id, mode);

				/*
				BYTE message_type = packet.Peek_Message_Type();
				PServerStatListGroup->Increment_Num_Msg_Sent(client_id - 1, message_type);
				PServerStatListGroup->Increment_Num_Byte_Sent(client_id - 1, message_type, packet.Get_Compressed_Size_Bytes());
				*/
			}
		}

	} else {
		PServerConnection->Send_Packet_To_Individual(packet, recipient, mode);

		/*
		BYTE message_type = packet.Peek_Message_Type();
		PServerStatListGroup->Increment_Num_Msg_Sent(recipient - 1, message_type);
		PServerStatListGroup->Increment_Num_Byte_Sent(recipient - 1, message_type, packet.Get_Compressed_Size_Bytes());
		*/
	}

#endif // not BETACLIENT
}

//-----------------------------------------------------------------------------
void cNetwork::Server_Send_Packet_To_All_Connected(cPacket & packet, int mode)
{
#ifndef BETACLIENT

	//
	// Traverse the rhost list here in the application level, so that
	// we can record message stats.
	//

	WWASSERT(I_Am_Server());
   WWASSERT(PServerConnection->Is_Established());

	//BYTE message_type = packet.Peek_Message_Type();

   for (int rhost_id = PServerConnection->Get_Min_RHost(); rhost_id <= PServerConnection->Get_Max_RHost(); rhost_id++) {
		if (Get_Server_Rhost(rhost_id) != NULL) {
         PServerConnection->Send_Packet_To_Individual(packet, rhost_id, mode);

			/*
			PServerStatListGroup->Increment_Num_Msg_Sent(rhost_id - 1, message_type);
			PServerStatListGroup->Increment_Num_Byte_Sent(rhost_id - 1, message_type, packet.Get_Compressed_Size_Bytes());
			*/
		}
	}

#endif // not BETACLIENT
}

//-----------------------------------------------------------------------------
LPCSTR cNetwork::Get_Client_Enumeration_String(void)
{
   WWASSERT(I_Am_Server());
   WWASSERT(PServerConnection->Is_Established());

   char temp_str[10];
   strcpy(ClientEnumerationString, "");
   for (int rhost_id = PServerConnection->Get_Min_RHost(); rhost_id <= PServerConnection->Get_Max_RHost(); rhost_id++) {
		if (Get_Server_Rhost(rhost_id) != NULL) {
         strcat(ClientEnumerationString, itoa(rhost_id, temp_str, 10));
         strcat(ClientEnumerationString, " ");
      }

      if (strlen(ClientEnumerationString) > 20) {
         strcpy(ClientEnumerationString, "many");
         break;
      }
	}

   return ClientEnumerationString;
}

//-----------------------------------------------------------------------------
cRemoteHost * cNetwork::Get_Server_Rhost(int client_id)
{
   WWASSERT(I_Am_Server());
   return PServerConnection->Get_Remote_Host(client_id);
}

//-----------------------------------------------------------------------------
cRemoteHost * cNetwork::Get_Client_Rhost(void)
{
   WWASSERT(I_Am_Client());
   return PClientConnection->Get_Remote_Host(SERVER_RHOST_ID);
}

//-----------------------------------------------------------------------------
float cNetwork::Get_Server_Rhost_Threshold_Priority(int client_id)
{
   WWASSERT(I_Am_Server());
   WWASSERT(Get_Server_Rhost(client_id) != NULL);
   return Get_Server_Rhost(client_id)->Get_Threshold_Priority();
}

//-----------------------------------------------------------------------------
float cNetwork::Get_Client_Rhost_Threshold_Priority(void)
{
   WWASSERT(I_Am_Client());
   WWASSERT(Get_Client_Rhost() != NULL);
   return Get_Client_Rhost()->Get_Threshold_Priority();
}

//-----------------------------------------------------------------------------
int cNetwork::Get_My_Id(void)
{
   WWASSERT(I_Am_Client());
   return PClientConnection->Get_Local_Id();
}

//-----------------------------------------------------------------------------
LPCSTR cNetwork::Get_Client_String(int recipient)
{
   //WWASSERT(I_Am_Server());
   if (recipient > 0) {
		WWASSERT(PServerConnection->Is_Established());

		if (recipient == ALL) {
			sprintf(ClientString, "all %d clients (%s)",
				PServerConnection->Get_Num_RHosts(),
				Get_Client_Enumeration_String());
		} else {
			sprintf(ClientString, "client %d", recipient);
		}
	} else {
		sprintf(ClientString, "server");
	}

   //
   // Note: This WWASSERT would catch the overwrite AFTER it has happened...
   //
   WWASSERT(strlen(ClientString) < sizeof(ClientString));

   return(ClientString);
}

//-----------------------------------------------------------------------------
void cNetwork::Server_Broken_Connection_Handler(int broken_rhost_id)
{
	WWASSERT(broken_rhost_id >= 0);

   //
   // The net lib calls this when a reliable packet fails after
   // many attempts. The connection data has already been destroyed.
   //
   WWDEBUG_SAY(("\n***Connection to client %d broken   \n\n", broken_rhost_id));

	WideStringClass widestring;
	widestring.Format(
		L"%s %d\n",
		TRANSLATION(IDS_MP_CONNECTION_TO_CLIENT_BROKEN),
		broken_rhost_id);
   WWASSERT(CombatManager::Get_Message_Window () != NULL);
	//Get_Text_Display()->Print_System(widestring);

	//
	//	Display the message...
	//
	CombatManager::Get_Message_Window ()->Add_Message (widestring);
	Vector3 color(1.0f, 1.0f, 0.0f);
	ConsoleBox.Add_Message(&widestring, &color);
	WWAudioClass::Get_Instance()->Create_Instant_Sound("Broken_Connection", Matrix3D(1));

   cNetwork::Cleanup_After_Client(broken_rhost_id);
}

//-----------------------------------------------------------------------------
void cNetwork::Client_Broken_Connection_Handler(void)
{
   //
   // The net lib calls this when a reliable packet fails after
   // many attempts. The connection data has already been destroyed.
   //

   WWASSERT(Get_Text_Display() != NULL);
   WWASSERT(PClientConnection != NULL);

   /**/
	if (PClientConnection->Have_Id()) {
		//cHelpText::Set(TRANSLATION(IDS_MP_CONNECTION_TO_SERVER_BROKEN));
		DlgMsgBox::DoDialog(L"", TRANSLATION(IDS_MP_CONNECTION_TO_SERVER_BROKEN));
	} else {
		//cHelpText::Set(TRANSLATION(IDS_MP_UNABLE_CONNECT_TO_SERVER));
	}


	//TSS090401
	//
	// The client needs to quit back to the game list
	//
	extern bool g_client_quit;
	g_client_quit = true;

	//WWAudioClass::Get_Instance()->Create_Instant_Sound("Broken_Connection", Matrix3D(1));
	/**/

	/*
	WWDEBUG_SAY(("Connection to server broken.\n"));

	if (PClientConnection->Have_Id()) {
		DlgMPConnectionRefused::DoDialog(TRANSLATION(IDS_MP_CONNECTION_TO_SERVER_BROKEN), true);
	} else {
		extern bool g_client_quit;
		g_client_quit = true;
	}
	*/
}

//-----------------------------------------------------------------------------
void cNetwork::Process_Eviction_Sc(cPacket & packet)
{
   WWDEBUG_SAY(("cNetwork::Process_Eviction_Sc\n"));

	WWAudioClass::Get_Instance()->Create_Instant_Sound("Evicted_By_Server", Matrix3D(1));

   WWASSERT(I_Am_Client());


	UINT min_bps;
	packet.Get(min_bps);//naughty... type conversion
   float max_packetloss;
	packet.Get(max_packetloss);

   //UINT sustainable_bps  = (UINT) packet.Get();

	WWDEBUG_SAY(("\n* You were evicted from the server for inadequate bandwidth performance.\n"
         "* This server requires packetloss of less than %5.2f %%, and a minimum\n"
         "* sustainable bandwidth of %d bps.\n", max_packetloss, min_bps));

   //
   // At this stage, just signal to close down. This is
   // a convenience during development
   //
   DIE;
}

//-----------------------------------------------------------------------------
void cNetwork::Eviction_Handler(int evicted_rhost_id)
{
	WWMEMLOG(MEM_NETWORK);
   WWDEBUG_SAY(("cNetwork::Eviction_Handler\n"));

	WWASSERT(evicted_rhost_id >= 0);

   WWASSERT(I_Am_Server());
   //Send_Eviction_Sc(evicted_rhost_id);

	cEvictionEvent * p_event = new cEvictionEvent;
	p_event->Init(evicted_rhost_id, EVICTION_POOR_BANDWIDTH);

	cNetwork::Flush();

	//WWAudioClass::Get_Instance()->Create_Instant_Sound("Broken_Connection", Matrix3D(1));
}

//-----------------------------------------------------------------------------
bool cNetwork::I_Am_God(void)
{
	bool i_am_god = false;

	if (I_Am_Client()) {
		cPlayer * p_player = cPlayerManager::Find_Player(Get_My_Id());
		if (p_player != NULL && p_player->Invulnerable.Is_True()) {
			i_am_god = true;
		}
	}

	return i_am_god;
}

//-----------------------------------------------------------------------------
cPlayer * cNetwork::Get_My_Player_Object(void)
{
   cPlayer * p_me = NULL;

	if (I_Am_Client()) {
		p_me = cPlayerManager::Find_Player(Get_My_Id());
	}

   return p_me;
}

//-----------------------------------------------------------------------------
int cNetwork::Get_My_Team_Number(void)
{
   cPlayer * p_me = Get_My_Player_Object();
   WWASSERT(p_me != NULL);
   return p_me->Get_Player_Type();
}

//-----------------------------------------------------------------------------
Vector3 cNetwork::Get_My_Color(void)
{
   cPlayer * p_me = cPlayerManager::Find_Player(Get_My_Id());
   WWASSERT(p_me != NULL);
   return p_me->Get_Color();
}

//-----------------------------------------------------------------------------
int cNetwork::Show_Welcome_Message(WideStringClass & name)
{
   int show = false;

   if (!IS_MISSION && (name == cNetInterface::Get_Nickname())) {
      show = true;
   }

   return show;
}

//---------------------------------------------------------------------------
float cNetwork::Get_Distance_Priority(Vector3 & pos1, Vector3 & pos2)
{
   Vector3 gap = pos2 - pos1;
   float d = gap.Length();

	WWASSERT(PTheGameData != NULL);
	float max_distance = The_Game()->Get_Maximum_World_Distance();
   WWASSERT(max_distance > 0);

	float range1 = max_distance / 25.0f;
   float range2 = max_distance / 5.0f;
   float range3 = max_distance + 1;

   float priority;

   if (d < range1) {
      priority = (float) ((range1 - d) / range1 * 0.499 + 0.50);
   } else if (d < range2) {
      priority = (float) ((range2 - d) / (range2 - range1) * 0.40 + 0.10);
   } else if (d < range3) {
      priority = (float) ((range3 - d) / (range3 - range2) * 0.10 + 0.00);
   } else {
		//WWASSERT(0);
		static bool already_warned = false;
		if (!already_warned) {
			Debug_Say(("Someone went outside the world box! (possibly numerous times)\n"));
			already_warned = true;
		}
      priority = 0;
   }

   WWASSERT(priority > -WWMATH_EPSILON && priority < 1 + WWMATH_EPSILON);

   return(priority);
}

//-----------------------------------------------------------------------------
void cNetwork::Shell_Command(LPCSTR command)
{
	WWASSERT(command != NULL);

	HINSTANCE hinst = ShellExecute(NULL, NULL, command, NULL, "", SW_SHOW);
	if ((int) hinst <= 32) {
      WWDEBUG_SAY(("Error: ShellExecute failed.\n"));
	}
}

//-----------------------------------------------------------------------------
REFUSAL_CODE cNetwork::Application_Acceptance_Handler(cPacket & packet)
{
#ifndef BETACLIENT

   WWDEBUG_SAY(("cNetwork::Application_Acceptance_Handler\n"));

	WWASSERT(I_Am_Server());

	//
	// Get player name
	// This is not supposed to be empty, but if for whatever reason it it, we should
	// just refuse, rather than crash.
	//
	WideStringClass player_name(0, true);
	//packet.Get_Wide_Terminated_String(player_name.Get_Buffer(256), 256);
	packet.Get_Wide_Terminated_String(player_name.Get_Buffer(256), 256, true);
	if (player_name.Get_Length() == 0) {
      return REFUSAL_VERSION_MISMATCH;
	}

	// Get the clients password
	WideStringClass password(0, true);
	packet.Get_Wide_Terminated_String(password.Get_Buffer(256), 256, true);

	// Get clients exe version
	int client_exe_key = packet.Get(client_exe_key);

	// Make sure the clients password matches the games password.
	WWASSERT(PTheGameData != NULL);
	if (The_Game()->IsPassworded.Is_True() && password.Compare(The_Game()->Get_Password()) != 0) {
		return REFUSAL_BAD_PASSWORD;
	}

	// Make sure the exe versions match
	if (client_exe_key != cNetwork::Get_Exe_Key()) {
      return REFUSAL_VERSION_MISMATCH;
   }

	// Make sure we haven't exceeded our max player limit
	if (cPlayerManager::Count() >= The_Game()->Get_Max_Players()) {
      return REFUSAL_GAME_FULL;
	}

	//
	// TSS100501
	//
	// Make sure the player is not already in the game
   //GAMESPY
	//if (cPlayerManager::Find_Player(player_name)) {
	if (!cGameSpyAdmin::Is_Gamespy_Game() && 
	    cPlayerManager::Find_Player(player_name)) {
		return REFUSAL_PLAYER_EXISTS;
	}

#endif // not BETACLIENT

	return REFUSAL_CLIENT_ACCEPTED;
}

//-----------------------------------------------------------------------------
void cNetwork::Connection_Handler(int new_rhost_id)
{
	WWMEMLOG(MEM_NETWORK);
#ifndef BETACLIENT

   WWDEBUG_SAY(("cNetwork::Connection_Handler\n"));

	WWASSERT(new_rhost_id >= 0);
   WWASSERT(I_Am_Server());
   WWASSERT(Receiver != NULL);

   /*
	//
   // Tell the new guy about all the teams. He needs this early in case he
   // needs to choose a team
   //
	SLNode<cTeam> * team_node;
   cTeam * p_team;
   for (team_node = cTeamManager::Get_Team_Object_List()->Head(); team_node; team_node = team_node->Next()) {
		p_team = team_node->Data();
      WWASSERT(p_team != NULL);
		Send_Server_New_Team(p_team, new_rhost_id);
   }
	*/

	//TSS2001 - normal update mechanism won't catch this in time.
	//
   // Tell the new guy about all the teams. He needs this early in case he
   // needs to choose a team
   //
	if (!cNetwork::I_Am_Client() || new_rhost_id != cNetwork::Get_My_Id()) {
		for (
			SLNode<cTeam> * team_node = cTeamManager::Get_Team_Object_List()->Head();
			team_node;
			team_node = team_node->Next()) {

			cTeam * p_team = team_node->Data();
			WWASSERT(p_team != NULL);

			Send_Object_Update(p_team, new_rhost_id);
		}
	}

   //
   // Next, send info about any remaining game options. This is only those
   // options that he doesn't pick up from the channel listing.
   //
   //Send_Server_Game_Options(new_rhost_id);
	cGameOptionsEvent * p_event = new cGameOptionsEvent;
	p_event->Init(new_rhost_id);
	Send_Object_Update(p_event, new_rhost_id);

#endif // not BETACLIENT
}


//-----------------------------------------------------------------------------
void cNetwork::Set_Desired_Frame_Sleep_Ms(int b)
{
   WWASSERT(b >= 0);

#ifdef WWDEBUG
	cDevOptions::DesiredFrameSleepMs.Set(b);
#endif //WWDEBUG
}

//-----------------------------------------------------------------------------
void cNetwork::Set_Simulated_Packet_Loss_Pc(int b)
{
   WWASSERT(b >= 0);

   if (PClientConnection != NULL) {
      PClientConnection->Set_Packet_Loss(b);
   }
   if (PServerConnection != NULL) {
      PServerConnection->Set_Packet_Loss(b);
   }

   //SimulatedPacketLossPc = b;
#ifdef WWDEBUG
	cDevOptions::SimulatedPacketLossPc.Set(b);
#endif //WWDEBUG
}

//-----------------------------------------------------------------------------
void cNetwork::Set_Simulated_Packet_Duplication_Pc(int b)
{
   WWASSERT(b >= 0);

   if (PClientConnection != NULL) {
      PClientConnection->Set_Packet_Duplication(b);
   }
   if (PServerConnection != NULL) {
      PServerConnection->Set_Packet_Duplication(b);
   }

   //SimulatedPacketDuplicationPc = b;
#ifdef WWDEBUG
	cDevOptions::SimulatedPacketDuplicationPc.Set(b);
#endif
}

//-----------------------------------------------------------------------------
void cNetwork::Set_Simulated_Latency_Range_Ms(int lower, int upper)
{
   WWASSERT(lower >= 0);
   WWASSERT(upper >= 0);
   WWASSERT(lower <= upper);

   if (PClientConnection != NULL) {
      PClientConnection->Set_Packet_Latency_Range(lower, upper);
   }
   if (PServerConnection != NULL) {
      PServerConnection->Set_Packet_Latency_Range(lower, upper);
   }

   //SimulatedLatencyRangeMsLower = lower;
   //SimulatedLatencyRangeMsUpper = upper;

#ifdef WWDEBUG
	cDevOptions::SimulatedLatencyRangeMsLower.Set(lower);
	cDevOptions::SimulatedLatencyRangeMsUpper.Set(upper);
#endif
}

//-----------------------------------------------------------------------------
void cNetwork::Set_Spam_Count(int spam_count)
{
   WWASSERT(spam_count >= 0);

	//SpamCount = spam_count;
#ifdef WWDEBUG
	cDevOptions::SpamCount.Set(spam_count);
#endif
}

//-----------------------------------------------------------------------------
void cNetwork::Get_Simulated_Latency_Range_Ms(int & lower, int & upper)
{
#ifdef WWDEBUG
	lower = cDevOptions::SimulatedLatencyRangeMsLower.Get();
	upper = cDevOptions::SimulatedLatencyRangeMsUpper.Get();
#endif
}

//-----------------------------------------------------------------------------
void
cNetwork::Flush(void)
{
   WWDEBUG_SAY(("cNetwork::Flush\n"));

	const bool is_urgent = true;

	//if (GameModeManager::Find ("Combat")->Is_Active() && Receiver != NULL) {
	if (Receiver != NULL) {

		if (I_Am_Server()) {
			Receiver->Server_Update_Dynamic_Objects(is_urgent);
		}

		if (I_Am_Client()) {
			Receiver->Client_Update_Dynamic_Objects(is_urgent);
		}
	}

	if (I_Am_Server()) {
		PServerConnection->Service_Send(is_urgent);
	}

	if (I_Am_Client()) {
		PClientConnection->Service_Send(is_urgent);
	}

	PacketManager.Flush(true);
}


void cNetwork::SwitchTeam(int newTeam)
{
	cPlayer* player = cPlayerManager::Find_Player(Get_My_Id());

	if (player) {
		int team = player->Get_Player_Type();

		if (newTeam != team) {
			cChangeTeamEvent* event = new cChangeTeamEvent;
			event->Init();
		}
	}
}


//-----------------------------------------------------------------------------
void cNetwork::Enable_Waiting_Players(void)
{
	SLNode<cPlayer> * objnode;
   for (objnode = cPlayerManager::Get_Player_Object_List()->Head() ; objnode != NULL ; objnode = objnode->Next()) {

		cPlayer * p_player = objnode->Data();
      WWASSERT(p_player != NULL);

		if (p_player->Is_Human() && p_player->Get_Is_Waiting_For_Intermission().Is_True()) {

			p_player->Set_Is_In_Game(true);
			p_player->Set_Is_Waiting_For_Intermission(false);
			if (cNetwork::PServerConnection) {
				cNetwork::PServerConnection->Set_Rhost_Is_In_Game(p_player->Get_Id(), true);
			}
			cGameDataUpdateEvent * p_event = new cGameDataUpdateEvent();
			p_event->Init(p_player->Get_Id());
		}
   }
}