Initial commit of Command & Conquer Renegade source code.

This commit is contained in:
LFeenanEA
2025-02-27 16:39:46 +00:00
parent 74ab8fa5e0
commit 58ed459113
4918 changed files with 1366710 additions and 0 deletions

396
Code/wwnet/BWBalance.cpp Normal file
View File

@@ -0,0 +1,396 @@
/*
** 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 : Command & Conquer *
* *
* $Archive:: /Commando/Code/wwnet/BWBalance.cpp $*
* *
* $Author:: Steve_t $*
* *
* $Modtime:: 10/13/02 9:40p $*
* *
* $Revision:: 12 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include <always.h>
#include "bwbalance.h"
#include "connect.h"
#include "packetmgr.h"
#include "systimer.h"
BandwidthBalancerClass BandwidthBalancer;
/*
** 8000 bits per second is about as low as we want to go.
*/
#define MIN_ACCEPTABLE_BANDWIDTH 8000
/***********************************************************************************************
* BandwidthBalancerClass::BandwidthBalancerClass -- Class constructor *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 10/21/2001 8:47PM ST : Created *
*=============================================================================================*/
BandwidthBalancerClass::BandwidthBalancerClass(void)
{
NumClientStructs = 0;
ClientInfo = NULL;
Allocate_Client_Structs(8);
IsEnabled = true;
}
/***********************************************************************************************
* BandwidthBalancerClass::~BandwidthBalancerClass -- Class destructor *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 10/21/2001 8:47PM ST : Created *
*=============================================================================================*/
BandwidthBalancerClass::~BandwidthBalancerClass(void)
{
if (ClientInfo) {
delete [] ClientInfo;
}
ClientInfo = NULL;
}
/***********************************************************************************************
* BandwidthBalancerClass::Allocate_Client_Structs -- Make room to store per client info *
* *
* *
* *
* INPUT: Likely number of clients *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 10/21/2001 8:47PM ST : Created *
*=============================================================================================*/
void BandwidthBalancerClass::Allocate_Client_Structs(int num_structs)
{
if (num_structs > NumClientStructs) {
num_structs += 15;
num_structs &= 0xfffffff0;
if (ClientInfo) {
delete [] ClientInfo;
}
ClientInfo = new ClientInfoStruct[num_structs];
NumClientStructs = num_structs;
}
}
/***********************************************************************************************
* BandwidthBalancerClass::Adjust -- Adjust bandwidth to each client *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 10/21/2001 8:48PM ST : Created *
*=============================================================================================*/
void BandwidthBalancerClass::Adjust(cConnection *connection, bool is_dedicated)
{
static unsigned long _last_adjustment = 0; //TIMEGETTIME();
/*
**
** Get average average priority.
**
** Do a pass and assign bandwidth to clients based on priority. Boost bandwidth to players currently loading. Clip bandwidth to
** clients max bps (and +- 50%) and keep a total of spare bandwidth.
**
** Repeat until no bandwidth left.
**
*/
if (connection != NULL) {
bool need_update = false;
for (int i=connection->Get_Min_RHost() + (is_dedicated ? 0 : 1) ; i <= connection->Get_Max_RHost() ; i++) {
cRemoteHost *client = connection->Get_Remote_Host(i);
if (client && client->Get_Target_Bps() == 0) {
need_update = true;
break;
}
}
if (need_update || TIMEGETTIME() - _last_adjustment > 100) {
_last_adjustment = TIMEGETTIME();
Adjust_Connection_Budget(connection);
int num_remote_hosts = connection->Get_Num_RHosts();
if (!is_dedicated) {
num_remote_hosts--;
}
if (num_remote_hosts > NumClientStructs) {
Allocate_Client_Structs(num_remote_hosts);
}
WWASSERT(NumClientStructs >= num_remote_hosts);
NumClients = 0;
/*
** Copy all the info we need into a single place.
*/
float average_priority = 0;
for (int i=connection->Get_Min_RHost() + (is_dedicated ? 0 : 1) ; i <= connection->Get_Max_RHost() ; i++) {
cRemoteHost *client = connection->Get_Remote_Host(i);
if (client != NULL) {
ClientInfo[NumClients].AveragePriority = client->Get_Average_Priority();
ClientInfo[NumClients].MaxBpsDown = client->Get_Maximum_Bps();
ClientInfo[NumClients].AllocatedBBO = client->Get_Target_Bps();
ClientInfo[NumClients].ID = client->Get_Id();
ClientInfo[NumClients].IsLoading = client->Get_Is_Loading();
ClientInfo[NumClients].IsDone = false;
/*
** Track the overally average priority.
*/
average_priority += ClientInfo[NumClients].AveragePriority;
NumClients++;
}
}
WWASSERT(NumClients == num_remote_hosts);
if (NumClients) {
/*
** Get the average object priority among all players.
*/
average_priority = average_priority / NumClients;
int bw_adjust = 100;
unsigned long total_bbo_allocated = Allocate_Bandwidth(average_priority, bw_adjust, connection->Get_Bandwidth_Budget_Out());
/*
** Ok, we have a rough bandwidth allocation. If we allocated too much or too little then we will need to do
** some fine tuning.
*/
unsigned long total_bbo = connection->Get_Bandwidth_Budget_Out();
int diff = total_bbo - total_bbo_allocated;
int percent_diff = (100 * abs(diff)) / total_bbo;
diff = total_bbo - total_bbo_allocated;
bw_adjust = 100 + ((100 * diff) / (((int)total_bbo * 9) / 10));
if (bw_adjust) {
/*
** If we allocated more or less than we have then keep adjusting until it comes out at about 95%.
*/
int tries = 5;
while ((diff < 0 || percent_diff > 10) && tries >= 0) {
//WWDEBUG_SAY(("Allocated too much or too little bandwidth. total_bbo = %d, total_bbo_allocated = %d, bw_adjust = %d\n", total_bbo, total_bbo_allocated, bw_adjust));
total_bbo_allocated = Allocate_Bandwidth(average_priority, bw_adjust, connection->Get_Bandwidth_Budget_Out());
diff = total_bbo - total_bbo_allocated;
percent_diff = (100 * abs(diff)) / total_bbo;
bw_adjust = bw_adjust * (100 + ((100 * diff) / (((int)total_bbo * 9) / 10))) / 100;
if (bw_adjust <= 0) {
break;
}
tries--;
}
/*
** If we get to this point, there should still be bandwidth left.
*/
if (diff < 0 && percent_diff > 3) {
WWDEBUG_SAY(("***** WARNING - BandwidthBalancer: Insufficient bandwidth for the number of clients in the game *********\n"));
WWDEBUG_SAY(("Allocated too much bandwidth. total_bbo = %d, total_bbo_allocated = %d, bw_adjust = %d\n", total_bbo, total_bbo_allocated, bw_adjust));
}
/*
** Oh well, let's assume that's right. Set the bandwidth budgets back into the rhosts.
*/
int index = 0;
for (int i=connection->Get_Min_RHost() + (is_dedicated ? 0 : 1) ; i <= connection->Get_Max_RHost() ; i++) {
cRemoteHost *client = connection->Get_Remote_Host(i);
if (client != NULL) {
client->Set_Target_Bps(ClientInfo[index++].AllocatedBBO);
}
}
}
}
}
}
}
/***********************************************************************************************
* BandwidthBalancerClass::Allocate_Bandwidth -- Split bandwidth between clients *
* *
* *
* *
* INPUT: Average object priority for all clients *
* *
* *
* OUTPUT: Percentage to apply to final bandwidth numbers *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 10/21/2001 8:49PM ST : Created *
*=============================================================================================*/
unsigned long BandwidthBalancerClass::Allocate_Bandwidth(float average_priority, int bw_adjust, unsigned long total_server_bbo)
{
WWASSERT(bw_adjust != 0);
WWASSERT(average_priority >= 0.0f);
WWASSERT(average_priority <= 1.0f);
/*
** Get our total bandwidth budget out. This has to be split between all clients.
*/
unsigned long total_bbo = total_server_bbo;
unsigned long bbo_per_client = total_bbo / NumClients;
unsigned long total_bbo_allocated = 0;
/*
** Loop through and assign bandwidth based on average priority and other metrics.
*/
for (int i=0 ; i<NumClients ; i++) {
ClientInfoStruct *client = &ClientInfo[i];
float pri = client->AveragePriority;
/*
** Work out the bandwidth, initially based on average priority.
*/
int client_bbo_adjust = (pri - average_priority) * bbo_per_client;
if (client_bbo_adjust > 0) {
client_bbo_adjust = min(client_bbo_adjust, (int)bbo_per_client / 2);
} else {
client_bbo_adjust = max(client_bbo_adjust, -((int)bbo_per_client / 2));
}
int new_client_bbo = bbo_per_client + client_bbo_adjust;
WWASSERT(new_client_bbo > 0);
/*
** Boost the bandwidth if the client is loading. He doesn't need it now but there will be a huge demand for
** bandwidth as soon as the load is done so it's good to free some up now.
*/
if (client->IsLoading) {
new_client_bbo = new_client_bbo << 1;
}
/*
** Apply the overall percentage adjustment.
*/
double big_client_bbo = (double) new_client_bbo;
double big_bw_adjust = (double) bw_adjust;
big_client_bbo = (big_client_bbo * big_bw_adjust) / 100.0;
big_client_bbo = min(big_client_bbo, (double) 0x7fffffff);
//new_client_bbo = (new_client_bbo * bw_adjust) / 100;
new_client_bbo = (int) big_client_bbo;
/*
** OK, we have the bandwidth adjusted according to priority.
** Clamp it to the min allowed and the max the client can take.
*/
new_client_bbo = min(new_client_bbo, (int)client->MaxBpsDown);
new_client_bbo = max(new_client_bbo, MIN_ACCEPTABLE_BANDWIDTH);
client->AllocatedBBO = (unsigned long) new_client_bbo;
total_bbo_allocated += (unsigned long) new_client_bbo;
}
return(total_bbo_allocated);
}
/***********************************************************************************************
* BandwidthBalancerClass::Adjust_Connection_Budget -- Adjust BBO of server connection *
* *
* *
* *
* INPUT: Ptr to server connection *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 10/24/2001 1:56PM ST : Created *
*=============================================================================================*/
void BandwidthBalancerClass::Adjust_Connection_Budget(cConnection *connection)
{
static unsigned long _last_time = 0;
unsigned long time = TIMEGETTIME();
/*
** Check every n seconds and see if we had any send errors that would be caused by sending too much data.
*/
if (PacketManager.Get_Error_State() == PacketManagerClass::STATE_WS_BUFFERS_FULL) {
if (time - _last_time > 10000) {
_last_time = time;
ULONG bbo = connection->Get_Bandwidth_Budget_Out();
ULONG new_bbo = (bbo * 9) / 10;
connection->Set_Bandwidth_Budget_Out(new_bbo);
WWDEBUG_SAY(("*** WARNING BandwidthBalancerClass - Adjusting Server connection BBO from %d to %d due to send overflow ***\n", bbo, new_bbo));
}
}
}

105
Code/wwnet/BWBalance.h Normal file
View File

@@ -0,0 +1,105 @@
/*
** 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 : Command & Conquer *
* *
* $Archive:: /Commando/Code/wwnet/BWBalance.h $*
* *
* $Author:: Steve_t $*
* *
* $Modtime:: 10/24/01 1:58p $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#pragma once
#ifndef _BWBALANCE_H
class cConnection;
/*
** This class balances bandwidth between clients.
**
**
*/
class BandwidthBalancerClass
{
public:
/*
** Constructor, desturctor.
*/
BandwidthBalancerClass(void);
~BandwidthBalancerClass(void);
/*
** Public function.
*/
void Adjust(cConnection *connection, bool is_dedicated);
bool IsEnabled;
private:
unsigned long Allocate_Bandwidth(float average_priority, int bw_adjust, unsigned long total_server_bbo);
void Allocate_Client_Structs(int num_structs);
void Adjust_Connection_Budget(cConnection *connection);
/*
** Struct to keep info about each client.
*/
struct ClientInfoStruct {
float AveragePriority;
unsigned long MaxBpsDown;
unsigned long AllocatedBBO;
bool IsLoading;
unsigned long ID;
bool IsDone;
};
/*
** Pointer to array of client info structs.
*/
ClientInfoStruct *ClientInfo;
/*
** Number of client info structs.
*/
int NumClientStructs;
/*
** Number of clients to balance between.
*/
int NumClients;
};
extern BandwidthBalancerClass BandwidthBalancer;
#endif //_BWBALANCE_H

2672
Code/wwnet/connect.cpp Normal file

File diff suppressed because it is too large Load Diff

278
Code/wwnet/connect.h Normal file
View File

@@ -0,0 +1,278 @@
/*
** 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/>.
*/
//
// Filename: connect.h
// Project: wwnet
// Author: Tom Spencer-Smith
// Date: June 1998
// Description:
//
//-----------------------------------------------------------------------------
#if defined(_MSV_VER)
#pragma once
#endif
#ifndef CONNECT_H
#define CONNECT_H
#include "rhost.h"
#include "netstats.h"
#include "bittype.h"
#include "netutil.h"
#include "slist.h"
#include "wwpacket.h"
#include "packettype.h"
//
// A server can have this many clients (a client has only 1 rhost: the server)
// -1 is used as ID_UNKNOWN
// This is used as an array bound and is not insulated,
// but using a namespace is ugly...
//
//const int MAX_RHOSTS = 256;
//
// These defines are useful at the app level
//
const int SERVER_RHOST_ID = 0;
//
// This is the default dummy ID used by a client until he receives a real id
// from the server
//
const int ID_UNKNOWN = -1;
typedef enum {
REFUSAL_CLIENT_ACCEPTED = 0,
REFUSAL_GAME_FULL,
REFUSAL_BAD_PASSWORD,
REFUSAL_VERSION_MISMATCH,
REFUSAL_PLAYER_EXISTS,
REFUSAL_BY_APPLICATION
} REFUSAL_CODE;
//
// Send flags. Normal send is individual, single.
// Strictly speaking SEND_UNRELIABLE could be deduced by absense
// of SEND_RELIABLE, but it's inclusion makes a clearer API.
//
const BYTE SEND_RELIABLE = 0x01; // you must specify this or SEND_UNRELIABLE
const BYTE SEND_UNRELIABLE = 0x02; // you must specify this or SEND_RELIABLE
const BYTE SEND_MULTI = 0x04; // For SEND_UNRELIABLE only. Default is single send.
class cMsgStatList;
typedef void (*Accept_Handler)(void);
typedef void (*Refusal_Handler)(REFUSAL_CODE refusal_code);
typedef void (*Server_Broken_Connection_Handler)(int broken_rhost_id);
typedef void (*Client_Broken_Connection_Handler)(void);
typedef void (*Eviction_Handler)(int evicted_rhost_id);
typedef void (*Conn_Handler)(int new_rhost_id);
typedef REFUSAL_CODE (*Application_Acceptance_Handler)(cPacket & packet);
typedef void (*Server_Packet_Handler)(cPacket & packet, int rhost_id);
typedef void (*Client_Packet_Handler)(cPacket & packet);
extern char * Addr_As_String(sockaddr_in *addr);
//-----------------------------------------------------------------------------
//
// This class represents the link between C & S.
// The 1-many S-C relationship is expressed in the cRemoteHost component.
//
class cConnection
{
public:
cConnection();
~cConnection();
void Init_As_Client(ULONG server_ip, USHORT server_port, unsigned short my_port = 0);
void Init_As_Server(USHORT server_port, int max_players,
bool is_dedicated_server, ULONG addr = 0);
void Connect_Cs(cPacket & app_data);
void Send_Packet_To_Individual(cPacket & packet, int addressee, BYTE send_flags);
bool Have_Id() const {return LocalId != ID_UNKNOWN;}
bool Is_Established() const;
void Service_Read();
void Service_Send(bool is_urgent = false);
ULONG Get_Bandwidth_Budget_Out() const {return BandwidthBudgetOut;}
void Set_Bandwidth_Budget_Out(ULONG bw_budget);
void Destroy_Connection(int rhost_id);
void Init_Stats();
double Get_Threshold_Priority(int rhost_id);
void Set_Packet_Loss(double percent_lost);
void Set_Packet_Duplication(double percent_duplicated);
void Set_Packet_Latency_Range(int minimum_latency_ms, int maximum_latency_ms);
void Set_Max_Acceptable_Packetloss_Pc(double max_packetloss_pc);
void Enable_Flow_Control(BOOL is_enabled) {IsFlowControlEnabled = is_enabled;} // This should be called at startup.
SList<cPacket> * Get_Packet_List() {return &PacketList;}
void Clear_Resend_Counts();
int Get_Min_RHost() {return MinRHost;}
int Get_Max_RHost() {return MaxRHost;}
int Get_Num_RHosts() {return NumRHosts;}
cRemoteHost * Get_Remote_Host(int rhost);
bool Is_Destroy() {return IsDestroy;}
int Get_Local_Id() const {return LocalId;}
double Get_Max_Acceptable_Packetloss_Pc() const {return MaxAcceptablePacketlossPc;}
cNetStats & Get_Combined_Stats() {return CombinedStats;}
cNetStats & Get_Averaged_Stats() {return AveragedStats;}
static BOOL Is_Flow_Control_Enabled() {return IsFlowControlEnabled;}
static UINT Get_Total_Compressed_Bytes_Sent(void)
{return TotalCompressedBytesSent;}
static UINT Get_Total_Uncompressed_Bytes_Sent(void)
{return TotalUncompressedBytesSent;}
cMsgStatList * Get_Stat_List(void) {return PStatList;}
bool Is_Bad_Connection(void) {return(IsBadConnection);};
void Set_Rhost_Is_In_Game(int id, bool state);
void Set_Rhost_Expect_Packet_Flood(int id, bool state);
void Allow_Extra_Timeout_For_Loading(void);
void Allow_Packet_Processing(bool set) {CanProcess = set;}
void Install_Accept_Handler(Accept_Handler handler);
void Install_Refusal_Handler(Refusal_Handler handler);
void Install_Server_Broken_Connection_Handler(Server_Broken_Connection_Handler handler);
void Install_Client_Broken_Connection_Handler(Client_Broken_Connection_Handler handler);
void Install_Eviction_Handler(Eviction_Handler handler);
void Install_Conn_Handler(Conn_Handler handler);
void Install_Application_Acceptance_Handler(Application_Acceptance_Handler handler);
void Install_Server_Packet_Handler(Server_Packet_Handler handler);
void Install_Client_Packet_Handler(Client_Packet_Handler handler);
// Need to use this. ST - 8/10/2001 11:52AM
void Send_Packet_To_Address(cPacket & packet, LPSOCKADDR_IN p_address);
#ifdef WWDEBUG
// Debug support for latency testing.
static void Set_Latency(int low, int high);
static void Get_Latency(int &low, int &high, int &current);
#endif //WWDEBUG
private:
cConnection(const cConnection& rhs); // Disallow copy (compile/link time)
cConnection& operator=(const cConnection& rhs); // Disallow assignment (compile/link time)
void Init_As_Client(LPSOCKADDR_IN p_server_address, unsigned short my_port = 0);
bool Demultiplex_R_Or_U_Packet(cPacket * p_packet, int rhost_id);
void Send_Accept_Sc(int new_rhost_id);
bool Bind(USHORT port, ULONG addr = 0);
bool Receive_Packet();
int Low_Level_Send_Wrapper(cPacket & packet, LPSOCKADDR_IN p_address);
int Send_Wrapper(cPacket & packet, LPSOCKADDR_IN p_address);
int Send_Wrapper(cPacket & packet, int addressee);
int Low_Level_Receive_Wrapper(cPacket & packet);
int Receive_Wrapper(cPacket & packet);
void Set_R_And_U_Packet_Id(cPacket & packet, int addressee, BYTE send_type);
void R_And_U_Send(cPacket & packet, int addressee);
void Send_Ack(LPSOCKADDR_IN p_address, int reliable_packet_id);
void Send_Refusal_Sc(LPSOCKADDR_IN p_address, REFUSAL_CODE refusal_code);
void Process_Connection_Request(cPacket & packet);
void Send_Keepalives();
static LPCSTR Type_Translation(int type);
bool Sender_Id_Tests(cPacket & packet);
USHORT Calculate_Packet_Bits(USHORT app_bytes);
int Single_Player_sendto(cPacket & packet);
int Single_Player_recvfrom(char * data);
int Address_To_Rhostid(const SOCKADDR_IN* p_address);
bool Is_Time_To_Resend_Packet_To_Remote_Host(const cPacket *packet, cRemoteHost *rhost);
bool Is_Packet_Too_Old(const cPacket *packet, cRemoteHost *rhost);
int LocalId; // Each client has a unique id
USHORT LocalPort; // port we are bound to.
double MaxAcceptablePacketlossPc;
cNetStats CombinedStats;
cNetStats AveragedStats;
unsigned long ThisFrameTimeMs; // to avoid excess timeGetTime clls
bool IsServer; // for C/S specific-code
bool IsDedicatedServer; // for C/S specific-code
static BOOL IsFlowControlEnabled;
bool InitDone; // Used to ensure certain API's are used at the right time.
SOCKET Sock;
USHORT SimulatedPacketLossPerRANDMAX;
USHORT SimulatedPacketDuplicationPerRANDMAX;
int MinimumLatencyMs;
int MaximumLatencyMs;
int RefusalPacketSendId; // server
int HighestRefusalPacketRcvId; // client
ULONG BandwidthBudgetOut;
SList<cPacket> PacketList;
int ServiceCount;
bool IsBadConnection;
cRemoteHost ** PRHost;
int MinRHost;
int MaxRHost;
int NumRHosts;
bool IsDestroy;
static UINT TotalCompressedBytesSent;
static UINT TotalUncompressedBytesSent;
cMsgStatList * PStatList;
unsigned long ExtraTimeoutTime;
unsigned long ExtraTimeoutTimeStarted;
bool CanProcess;
Accept_Handler AcceptHandler;
Refusal_Handler RefusalHandler;
Server_Broken_Connection_Handler ServerBrokenConnectionHandler;
Client_Broken_Connection_Handler ClientBrokenConnectionHandler;
Eviction_Handler EvictionHandler;
Conn_Handler ConnHandler;
Application_Acceptance_Handler ApplicationAcceptanceHandler;
Server_Packet_Handler ServerPacketHandler;
Client_Packet_Handler ClientPacketHandler;
#ifdef WWDEBUG
// Testing support for high latency connections.
// Dynamic vector is ineffecient here but it doesn't matter since this is a debug testing only kinda thing.
static int LatencyAddLow;
static int LatencyAddHigh;
static int CurrentLatencyAdd;
static unsigned long LastLatencyChange;
DynamicVectorClass<cPacket*> LaggedPackets;
DynamicVectorClass<unsigned long> LaggedPacketTimes;
DynamicVectorClass<int> LaggedPacketRetCodes;
#endif //WWDEBUG
};
#endif // CONNECT_H
//void Init_As_Client(LPCSTR server_ip, USHORT server_port);
//void Send_Packet_To_All(cPacket & packet, BYTE send_flags); // send to all rhosts
//cRemoteHost * PRHost[MAX_RHOSTS];
//virtual void Server_Broken_Connection_Handler(int rhost_id);
//virtual void Client_Broken_Connection_Handler();
//virtual void Accept_Handler();
//virtual void Refusal_Handler(int refusal_code);
//virtual void Connection_Handler(int new_rhost_id);
//virtual bool Application_Acceptance_Handler(cPacket & packet);
//virtual void Eviction_Handler(int evicted_rhost_id);
//virtual void Server_Packet_Handler(cPacket & packet, int rhost_id);
//virtual void Client_Packet_Handler(cPacket & packet);

49
Code/wwnet/fromaddress.h Normal file
View File

@@ -0,0 +1,49 @@
/*
** 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/>.
*/
//
// Filename: fromaddress.h
// Project: wwnet
// Author: Tom Spencer-Smith
// Date: June 1998
// Description:
//
//-----------------------------------------------------------------------------
#if defined(_MSV_VER)
#pragma once
#endif
#ifndef FROMADDRESS_H
#define FROMADDRESS_H
#include "win.h"
#include <winsock.h>
//
// This trivial class exists solely to speed compile times.
//
class cFromAddress
{
public:
cFromAddress& operator=(const cFromAddress& rhs) {FromAddress = rhs.FromAddress; return * this;}
SOCKADDR_IN FromAddress;
};
#endif // FROMADDRESS_H

37
Code/wwnet/lan.cpp Normal file
View File

@@ -0,0 +1,37 @@
/*
** 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/>.
*/
//
// Filename: lan.cpp
// Project: wwnet
// Author: Tom Spencer-Smith
// Date: Nov 1998
// Description: LAN routines
//
#include "netutil.h" // I WANNA BE FIRST!
#include <stdio.h>
#include "mmsys.h" // for timegettime
#include "miscutil.h"
#include "wwpacket.h"
#include "wwdebug.h"
#include "fromaddress.h"
//-------------------------------------------------------------------------------

120
Code/wwnet/msgstat.cpp Normal file
View File

@@ -0,0 +1,120 @@
/*
** 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/>.
*/
//
// Filename: msgstat.cpp
// Project:
// Author: Tom Spencer-Smith
// Date:
// Description:
//
//------------------------------------------------------------------------------------
#include "msgstat.h" // I WANNA BE FIRST!
#include <string.h>
#include "mathutil.h"
#include "wwdebug.h"
//
// Class statics
//
//------------------------------------------------------------------------------------
cMsgStat::cMsgStat(void) :
NumMsgSent(0),
NumByteSent(0),
NumMsgRecd(0),
NumByteRecd(0)
{
::strcpy(Name, "UNNAMED");
}
//---------------- --------------------------------------------------------------------
cMsgStat::~cMsgStat(void)
{
}
//---------------- --------------------------------------------------------------------
void cMsgStat::Increment_Num_Msg_Sent(int increment)
{
WWASSERT(increment > 0);
NumMsgSent += increment;
}
//---------------- --------------------------------------------------------------------
void cMsgStat::Increment_Num_Byte_Sent(int increment)
{
WWASSERT(increment > 0);
NumByteSent += increment;
}
//---------------- --------------------------------------------------------------------
void cMsgStat::Increment_Num_Msg_Recd(int increment)
{
WWASSERT(increment > 0);
NumMsgRecd += increment;
}
//---------------- --------------------------------------------------------------------
void cMsgStat::Increment_Num_Byte_Recd(int increment)
{
WWASSERT(increment > 0);
NumByteRecd += increment;
}
//---------------- --------------------------------------------------------------------
DWORD cMsgStat::Compute_Avg_Num_Byte_Sent(void) const
{
DWORD avg = 0;
if (NumMsgSent > 0) {
avg = (DWORD) cMathUtil::Round(NumByteSent / (float) NumMsgSent);
}
return avg;
}
//---------------- --------------------------------------------------------------------
DWORD cMsgStat::Compute_Avg_Num_Byte_Recd(void) const
{
DWORD avg = 0;
if (NumMsgRecd > 0) {
avg = (DWORD) cMathUtil::Round(NumByteRecd / (float) NumMsgRecd);
}
return avg;
}
//---------------- --------------------------------------------------------------------
void cMsgStat::Set_Name(LPCSTR name)
{
WWASSERT(name != NULL);
WWASSERT(::strlen(name) < sizeof(Name));
::strcpy(Name, name);
}

81
Code/wwnet/msgstat.h Normal file
View File

@@ -0,0 +1,81 @@
/*
** 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/>.
*/
//
// Filename: msgstat.h
// Project:
// Author: Tom Spencer-Smith
// Date:
// Description: Send and receive stats for a single type of message.
//
//-----------------------------------------------------------------------------
#if defined(_MSV_VER)
#pragma once
#endif
#ifndef MSGSTAT_H
#define MSGSTAT_H
#pragma warning(disable:4514)
#include "bittype.h"
#ifndef NULL
#define NULL 0L
#endif
//-----------------------------------------------------------------------------
class cMsgStat
{
public:
cMsgStat(void);
~cMsgStat(void);
void Increment_Num_Msg_Sent( int increment = 1);
void Increment_Num_Byte_Sent( int increment);
void Increment_Num_Msg_Recd( int increment = 1);
void Increment_Num_Byte_Recd( int increment);
DWORD Get_Num_Msg_Sent(void) const {return NumMsgSent;}
DWORD Get_Num_Byte_Sent(void) const {return NumByteSent;}
DWORD Get_Num_Msg_Recd(void) const {return NumMsgRecd;}
DWORD Get_Num_Byte_Recd(void) const {return NumByteRecd;}
DWORD Compute_Avg_Num_Byte_Sent(void) const;
DWORD Compute_Avg_Num_Byte_Recd(void) const;
void Set_Name(LPCSTR name);
LPCSTR Get_Name(void) const {return Name;}
private:
cMsgStat(const cMsgStat& source); // disallow
cMsgStat& operator=(const cMsgStat& source); // disallow
DWORD NumMsgSent;
DWORD NumByteSent;
DWORD NumMsgRecd;
DWORD NumByteRecd;
char Name[30];
};
//-----------------------------------------------------------------------------
#endif // MSGSTAT_H

192
Code/wwnet/msgstatlist.cpp Normal file
View File

@@ -0,0 +1,192 @@
/*
** 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/>.
*/
//
// Filename: msgstatlist.cpp
// Project:
// Author: Tom Spencer-Smith
// Date:
// Description:
//
//------------------------------------------------------------------------------------
#include "msgstatlist.h" // I WANNA BE FIRST!
#include "mathutil.h"
#include "wwdebug.h"
//
// Class statics
//
//------------------------------------------------------------------------------------
cMsgStatList::cMsgStatList(void) :
NumStats(0)
{
}
//---------------- --------------------------------------------------------------------
cMsgStatList::~cMsgStatList(void)
{
if (PStat != NULL) {
delete [] PStat;
PStat = NULL;
}
}
//-----------------------------------------------------------------------------
void cMsgStatList::Init(int num_stats)
{
WWASSERT(num_stats > 0);
NumStats = num_stats;
PStat = new cMsgStat[NumStats + 1];
WWASSERT(PStat != NULL);
}
//-----------------------------------------------------------------------------
void cMsgStatList::Increment_Num_Msg_Sent(int message_type, int increment)
{
WWASSERT(message_type >= 0 && message_type < NumStats);
WWASSERT(increment > 0);
PStat[message_type].Increment_Num_Msg_Sent(increment);
PStat[NumStats].Increment_Num_Msg_Sent(increment);
}
//-----------------------------------------------------------------------------
void cMsgStatList::Increment_Num_Byte_Sent(int message_type, int increment)
{
WWASSERT(message_type >= 0 && message_type < NumStats);
WWASSERT(increment > 0);
PStat[message_type].Increment_Num_Byte_Sent(increment);
PStat[NumStats].Increment_Num_Byte_Sent(increment);
}
//-----------------------------------------------------------------------------
void cMsgStatList::Increment_Num_Msg_Recd(int message_type, int increment)
{
WWASSERT(message_type >= 0 && message_type < NumStats);
WWASSERT(increment > 0);
PStat[message_type].Increment_Num_Msg_Recd(increment);
PStat[NumStats].Increment_Num_Msg_Recd(increment);
}
//-----------------------------------------------------------------------------
void cMsgStatList::Increment_Num_Byte_Recd(int message_type, int increment)
{
WWASSERT(message_type >= 0 && message_type < NumStats);
WWASSERT(increment > 0);
PStat[message_type].Increment_Num_Byte_Recd(increment);
PStat[NumStats].Increment_Num_Byte_Recd(increment);
}
//-----------------------------------------------------------------------------
DWORD cMsgStatList::Get_Num_Msg_Sent(int message_type) const
{
if (message_type == ALL_MESSAGES) {
message_type = NumStats;
}
WWASSERT(message_type >= 0 && message_type <= NumStats);
return PStat[message_type].Get_Num_Msg_Sent();
}
//-----------------------------------------------------------------------------
DWORD cMsgStatList::Get_Num_Byte_Sent(int message_type) const
{
if (message_type == ALL_MESSAGES) {
message_type = NumStats;
}
WWASSERT(message_type >= 0 && message_type <= NumStats);
return PStat[message_type].Get_Num_Byte_Sent();
}
//-----------------------------------------------------------------------------
DWORD cMsgStatList::Get_Num_Msg_Recd(int message_type) const
{
if (message_type == ALL_MESSAGES) {
message_type = NumStats;
}
WWASSERT(message_type >= 0 && message_type <= NumStats);
return PStat[message_type].Get_Num_Msg_Recd();
}
//-----------------------------------------------------------------------------
DWORD cMsgStatList::Get_Num_Byte_Recd(int message_type) const
{
if (message_type == ALL_MESSAGES) {
message_type = NumStats;
}
WWASSERT(message_type >= 0 && message_type <= NumStats);
return PStat[message_type].Get_Num_Byte_Recd();
}
//-----------------------------------------------------------------------------
DWORD cMsgStatList::Compute_Avg_Num_Byte_Sent(int message_type) const
{
if (message_type == ALL_MESSAGES) {
message_type = NumStats;
}
WWASSERT(message_type >= 0 && message_type <= NumStats);
return PStat[message_type].Compute_Avg_Num_Byte_Sent();
}
//-----------------------------------------------------------------------------
DWORD cMsgStatList::Compute_Avg_Num_Byte_Recd(int message_type) const
{
if (message_type == ALL_MESSAGES) {
message_type = NumStats;
}
WWASSERT(message_type >= 0 && message_type <= NumStats);
return PStat[message_type].Compute_Avg_Num_Byte_Recd();
}
//-----------------------------------------------------------------------------
cMsgStat & cMsgStatList::Get_Stat(int message_type)
{
if (message_type == ALL_MESSAGES) {
message_type = NumStats;
}
WWASSERT(message_type >= 0 && message_type <= NumStats);
return PStat[message_type];
}
//-----------------------------------------------------------------------------
void cMsgStatList::Set_Name(int message_type, LPCSTR name)
{
WWASSERT(message_type >= 0 && message_type < NumStats);
PStat[message_type].Set_Name(name);
}
//-----------------------------------------------------------------------------
LPCSTR cMsgStatList::Get_Name(int message_type) const
{
WWASSERT(message_type >= 0 && message_type <= NumStats);
return PStat[message_type].Get_Name();
}

80
Code/wwnet/msgstatlist.h Normal file
View File

@@ -0,0 +1,80 @@
/*
** 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/>.
*/
//
// Filename: msgstatlist.h
// Project:
// Author: Tom Spencer-Smith
// Date:
// Description: Collection of message stats for all message types
//
//-----------------------------------------------------------------------------
#if defined(_MSV_VER)
#pragma once
#endif
#ifndef MSGSTATLIST_H
#define MSGSTATLIST_H
#include "msgstat.h"
#ifndef NULL
#define NULL 0L
#endif
//-----------------------------------------------------------------------------
class cMsgStatList
{
public:
cMsgStatList(void);
~cMsgStatList(void);
enum {ALL_MESSAGES = -1};
void Init(int num_stats);
void Increment_Num_Msg_Sent( int message_type, int increment = 1);
void Increment_Num_Byte_Sent( int message_type, int increment);
void Increment_Num_Msg_Recd( int message_type, int increment = 1);
void Increment_Num_Byte_Recd( int message_type, int increment);
DWORD Get_Num_Msg_Sent( int message_type) const;
DWORD Get_Num_Byte_Sent( int message_type) const;
DWORD Get_Num_Msg_Recd( int message_type) const;
DWORD Get_Num_Byte_Recd( int message_type) const;
DWORD Compute_Avg_Num_Byte_Sent( int message_type) const;
DWORD Compute_Avg_Num_Byte_Recd( int message_type) const;
cMsgStat & Get_Stat(int message_type);
int Get_Num_Stats(void) const {return NumStats;}
void Set_Name(int message_type, LPCSTR name);
LPCSTR Get_Name(int message_type) const;
private:
cMsgStatList(const cMsgStatList& source); // disallow
cMsgStatList& operator=(const cMsgStatList& source); // disallow
cMsgStat * PStat;
int NumStats;
};
//-----------------------------------------------------------------------------
#endif // MSGSTATLIST_H

View File

@@ -0,0 +1,202 @@
/*
** 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/>.
*/
//
// Filename: msgstatlistgroup.cpp
// Project:
// Author: Tom Spencer-Smith
// Date:
// Description:
//
//------------------------------------------------------------------------------------
#include "msgstatlistgroup.h" // I WANNA BE FIRST!
#include "mathutil.h"
#include "wwdebug.h"
//
// Class statics
//
//------------------------------------------------------------------------------------
cMsgStatListGroup::cMsgStatListGroup(void) :
NumLists(0)
{
}
//------------------------------------------------------------------------------------
cMsgStatListGroup::~cMsgStatListGroup(void)
{
if (PStatList != NULL) {
delete [] PStatList;
PStatList = NULL;
}
}
//-----------------------------------------------------------------------------
void cMsgStatListGroup::Init(int num_lists, int num_stats)
{
WWASSERT(num_lists > 0);
NumLists = num_lists;
PStatList = new cMsgStatList[NumLists + 1];
WWASSERT(PStatList != NULL);
for (int i = 0; i < NumLists + 1; i++) {
PStatList[i].Init(num_stats);
}
}
//-----------------------------------------------------------------------------
void cMsgStatListGroup::Increment_Num_Msg_Sent(int list_num, int message_type, int increment)
{
WWASSERT(list_num >= 0 && list_num < NumLists);
WWASSERT(increment > 0);
PStatList[list_num].Increment_Num_Msg_Sent(message_type, increment);
PStatList[NumLists].Increment_Num_Msg_Sent(message_type, increment);
}
//-----------------------------------------------------------------------------
void cMsgStatListGroup::Increment_Num_Byte_Sent(int list_num, int message_type, int increment)
{
WWASSERT(list_num >= 0 && list_num < NumLists);
WWASSERT(increment > 0);
PStatList[list_num].Increment_Num_Byte_Sent(message_type, increment);
PStatList[NumLists].Increment_Num_Byte_Sent(message_type, increment);
}
//-----------------------------------------------------------------------------
void cMsgStatListGroup::Increment_Num_Msg_Recd(int list_num, int message_type, int increment)
{
WWASSERT(list_num >= 0 && list_num < NumLists);
WWASSERT(increment > 0);
PStatList[list_num].Increment_Num_Msg_Recd(message_type, increment);
PStatList[NumLists].Increment_Num_Msg_Recd(message_type, increment);
}
//-----------------------------------------------------------------------------
void cMsgStatListGroup::Increment_Num_Byte_Recd(int list_num, int message_type, int increment)
{
WWASSERT(list_num >= 0 && list_num < NumLists);
WWASSERT(increment > 0);
PStatList[list_num].Increment_Num_Byte_Recd(message_type, increment);
PStatList[NumLists].Increment_Num_Byte_Recd(message_type, increment);
}
//-----------------------------------------------------------------------------
DWORD cMsgStatListGroup::Get_Num_Msg_Sent(int list_num, int message_type) const
{
if (list_num == ALL_LISTS) {
list_num = NumLists;
}
WWASSERT(list_num >= 0 && list_num <= NumLists);
WWASSERT(message_type >= 0 && message_type <= ALL_MESSAGES);
return PStatList[list_num].Get_Num_Msg_Sent(message_type);
}
//-----------------------------------------------------------------------------
DWORD cMsgStatListGroup::Get_Num_Byte_Sent(int list_num, int message_type) const
{
if (list_num == ALL_LISTS) {
list_num = NumLists;
}
WWASSERT(list_num >= 0 && list_num <= NumLists);
WWASSERT(message_type >= 0 && message_type <= ALL_MESSAGES);
return PStatList[list_num].Get_Num_Byte_Sent(message_type);
}
//-----------------------------------------------------------------------------
DWORD cMsgStatListGroup::Get_Num_Msg_Recd(int list_num, int message_type) const
{
if (list_num == ALL_LISTS) {
list_num = NumLists;
}
WWASSERT(list_num >= 0 && list_num <= NumLists);
WWASSERT(message_type >= 0 && message_type <= ALL_MESSAGES);
return PStatList[list_num].Get_Num_Msg_Recd(message_type);
}
//-----------------------------------------------------------------------------
DWORD cMsgStatListGroup::Get_Num_Byte_Recd(int list_num, int message_type) const
{
if (list_num == ALL_LISTS) {
list_num = NumLists;
}
WWASSERT(list_num >= 0 && list_num <= NumLists);
WWASSERT(message_type >= 0 && message_type <= ALL_MESSAGES);
return PStatList[list_num].Get_Num_Byte_Recd(message_type);
}
//-----------------------------------------------------------------------------
DWORD cMsgStatListGroup::Compute_Avg_Num_Byte_Sent(int list_num, int message_type) const
{
if (list_num == ALL_LISTS) {
list_num = NumLists;
}
WWASSERT(list_num >= 0 && list_num <= NumLists);
WWASSERT(message_type >= 0 && message_type <= ALL_MESSAGES);
return PStatList[list_num].Compute_Avg_Num_Byte_Sent(message_type);
}
//-----------------------------------------------------------------------------
DWORD cMsgStatListGroup::Compute_Avg_Num_Byte_Recd(int list_num, int message_type) const
{
if (list_num == ALL_LISTS) {
list_num = NumLists;
}
WWASSERT(list_num >= 0 && list_num <= NumLists);
WWASSERT(message_type >= 0 && message_type <= ALL_MESSAGES);
return PStatList[list_num].Compute_Avg_Num_Byte_Recd(message_type);
}
//-----------------------------------------------------------------------------
cMsgStatList * cMsgStatListGroup::Get_Stat_List(int list_num)
{
if (list_num == ALL_LISTS) {
list_num = NumLists;
}
WWASSERT(list_num >= 0 && list_num <= NumLists);
return &PStatList[list_num];
}
//-----------------------------------------------------------------------------
void cMsgStatListGroup::Set_Name(int message_type, LPCSTR name)
{
for (int i = 0; i <= NumLists; i++) {
PStatList[i].Set_Name(message_type, name);
}
}

View File

@@ -0,0 +1,81 @@
/*
** 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/>.
*/
//
// Filename: msgstatlistgroup.h
// Project:
// Author: Tom Spencer-Smith
// Date:
// Description: Collection of msgstatlists. The server will need one
// of these to sum send and receive data to multiple clients.
//
//-----------------------------------------------------------------------------
#if defined(_MSV_VER)
#pragma once
#endif
#ifndef MSGSTATLISTGROUP_H
#define MSGSTATLISTGROUP_H
#include "msgstatlist.h"
#ifndef NULL
#define NULL 0L
#endif
//-----------------------------------------------------------------------------
class cMsgStatListGroup
{
public:
cMsgStatListGroup(void);
~cMsgStatListGroup(void);
void Init(int num_lists, int num_stats);
enum {ALL_LISTS = -1};
enum {ALL_MESSAGES = -1};
void Increment_Num_Msg_Sent( int list_num, int message_type, int increment = 1);
void Increment_Num_Byte_Sent( int list_num, int message_type, int increment);
void Increment_Num_Msg_Recd( int list_num, int message_type, int increment = 1);
void Increment_Num_Byte_Recd( int list_num, int message_type, int increment);
DWORD Get_Num_Msg_Sent( int list_num, int message_type) const;
DWORD Get_Num_Byte_Sent( int list_num, int message_type) const;
DWORD Get_Num_Msg_Recd( int list_num, int message_type) const;
DWORD Get_Num_Byte_Recd( int list_num, int message_type) const;
DWORD Compute_Avg_Num_Byte_Sent( int list_num, int message_type) const;
DWORD Compute_Avg_Num_Byte_Recd( int list_num, int message_type) const;
cMsgStatList * Get_Stat_List(int list_num);
void Set_Name(int message_type, LPCSTR name);
//LPCSTR Get_Name(int message_type) const;
private:
cMsgStatListGroup(const cMsgStatListGroup& source); // disallow
cMsgStatListGroup& operator=(const cMsgStatListGroup& source); // disallow
cMsgStatList * PStatList;
int NumLists;
};
//-----------------------------------------------------------------------------
#endif // MSGSTATLISTGROUP_H

176
Code/wwnet/netstats.cpp Normal file
View File

@@ -0,0 +1,176 @@
/*
** 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/>.
*/
//
// Filename: netstats.cpp
// Project: wwnet
// Author: Tom Spencer-Smith
// Date: Oct 1998
// Description:
//
//------------------------------------------------------------------------------------
#include "netstats.h" // I WANNA BE FIRST!
#include "win.h"
#include "systimer.h"
#include "miscutil.h"
#include "netutil.h"
#include "wwdebug.h"
//
// class defines
//
//------------------------------------------------------------------------------------
cNetStats::cNetStats() :
LastUnreliablePacketId(-1),
FreezePacketId(-1)
{
Init_Net_Stats();
}
//------------------------------------------------------------------------------------
void cNetStats::Init_Net_Stats()
{
StartTime = TIMEGETTIME();
SampleStartTime = StartTime;
for (int statistic = 0; statistic < STAT_COUNT; statistic++) {
StatTotal[statistic] = 0;
StatAverage[statistic] = 0;
StatSnapshot[statistic] = 0;
StatMacroSnapshot[statistic] = 0;
StatSample[statistic] = 0;
StatMacroSample[statistic] = 0;
}
RemotePacketloss = 0;
UnreliableCount = 0;
FreezePacketId = LastUnreliablePacketId;
RemoteServiceCount = 0;
}
//------------------------------------------------------------------------------------
double cNetStats::Get_Pc_Packetloss_Received() const
{
double packetloss_pc = 0;
int packet_count = LastUnreliablePacketId - FreezePacketId;
WWASSERT(packet_count >= 0);
if (packet_count > 0) {
//
// Must add 1 to PrevLastUnreliable because packet id starts at 0
//
packetloss_pc = 100 * (1 - UnreliableCount / (double) packet_count);
}
return packetloss_pc;
}
//------------------------------------------------------------------------------------
void cNetStats::Set_Pc_Packetloss_Sent(double packetloss_pc)
{
/*TSS102901
WWASSERT(packetloss_pc > -MISCUTIL_EPSILON && packetloss_pc < 100 + MISCUTIL_EPSILON);
RemotePacketloss = packetloss_pc;
*/
}
//------------------------------------------------------------------------------------
void cNetStats::Set_Remote_Service_Count(int remote_service_count)
{
//WWASSERT(remote_service_count >= 0);
RemoteServiceCount = remote_service_count;
}
//------------------------------------------------------------------------------------
bool cNetStats::Update_If_Sample_Done(int this_frame_time, bool force_update)
{
bool is_updated = false;
static int update_count = 0;
if (force_update || this_frame_time - SampleStartTime > cNetUtil::NETSTATS_SAMPLE_TIME_MS) {
update_count++;
double total_time = (this_frame_time - StartTime) / 1000.0;
if (total_time < MISCUTIL_EPSILON) {
total_time = MISCUTIL_EPSILON;
}
for (int statistic = 0; statistic < STAT_COUNT; statistic++) {
StatTotal[statistic] += StatSample[statistic];
StatAverage[statistic] = (UINT) (StatTotal[statistic] / total_time);
StatSnapshot[statistic] = StatSample[statistic];
StatMacroSample[statistic] += StatSample[statistic];
StatSample[statistic] = 0;
if (update_count % 4 == 0) {
StatMacroSnapshot[statistic] = StatMacroSample[statistic];
StatMacroSample[statistic] = 0;
}
}
SampleStartTime = this_frame_time;
is_updated = true;
}
return is_updated;
}
//const USHORT cNetStats::SAMPLE_TIME = 500;
// crash here when exit server thread before client
//WWASSERT(packetloss_pc >= 0 && packetloss_pc <= 100);
/*
//
// These 2 stats are computed from others
//
if (StatSample[STAT_AppByteSent] > 0) {
StatSample[STAT_AppDataSentPc] = (UINT) (100 * StatSample[STAT_AppByteSent] /
(double) (StatSample[STAT_AppByteSent] + StatSample[STAT_HdrByteSent]));
}
if (StatSample[STAT_AppByteRcv] > 0) {
StatSample[STAT_AppDataRcvPc] = (UINT) (100 * StatSample[STAT_AppByteRcv] /
(double) (StatSample[STAT_AppByteRcv] + StatSample[STAT_HdrByteRcv]));
}
*/

180
Code/wwnet/netstats.h Normal file
View File

@@ -0,0 +1,180 @@
/*
** 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/>.
*/
//
// Filename: netstats.h
// Project: wwnet
// Author: Tom Spencer-Smith
// Date: Oct 1998
// Description:
//
//-----------------------------------------------------------------------------
#if defined(_MSV_VER)
#pragma once
#endif
#ifndef NETSTATS_H
#define NETSTATS_H
#include "bittype.h"
#include "wwdebug.h"
//
// Statistics.
// Abbrev:
// R = Reliable
// U = Unreliable
// Pkt = packet
// Rcv = Received
// App = Application
// Hdr = Header
// Byte = bytes, including header
//
// Syntax is U/R then Pkt/Byte/AppByte then Sent/Rcv
//
typedef enum Statistic
{
STAT_PktSent,
STAT_PktRcv,
STAT_MsgSent, // Need to count separately since messages can be combined
STAT_MsgRcv, // into packets TSS - bug: this not accurate because of combining
STAT_AppByteSent,
STAT_AppByteRcv,
//STAT_HdrByteSent,
//STAT_HdrByteRcv,
STAT_BitsSent,
STAT_BitsRcv,
STAT_UPktSent,
STAT_RPktSent,
STAT_UByteSent,
STAT_RByteSent,
STAT_UPktRcv,
STAT_RPktRcv,
STAT_UByteRcv,
STAT_RByteRcv,
/*
STAT_AppDataSentPc, // App / (App + Hdr)
STAT_AppDataRcvPc, // App / (App + Hdr)
*/
STAT_AckCountSent,
STAT_AckCountRcv,
STAT_DiscardCount,
STAT_ResendCount,
STAT_SendFailureCount,
STAT_PktRcvRemote,
STAT_UPktShouldRcv,
STAT_UPktRcv2, // used in packetloss calculations. Slightly less than STAT_UPktRcv
STAT_ServiceCount,
STAT_COUNT // must include, used as array bound
};
//-----------------------------------------------------------------------------
class cNetStats
{
public:
cNetStats();
~cNetStats() {};
void Init_Net_Stats();
bool Update_If_Sample_Done(int this_frame_time, bool force_update = false);
double Get_Pc_Packetloss_Received() const;
void Set_Pc_Packetloss_Sent(double packetloss_pc);
double Get_Pc_Packetloss_Sent() const {return RemotePacketloss;}
int Get_Remote_Service_Count() {return RemoteServiceCount;}
void Set_Remote_Service_Count(int remote_service_count);
int Get_Sample_Start_Time() const {return SampleStartTime;}
int Get_Last_Unreliable_Packet_Id() const {return LastUnreliablePacketId;}
void Set_Last_Unreliable_Packet_Id(int id) {LastUnreliablePacketId = id;}
int Get_Freeze_Packet_Id() const {return FreezePacketId;}
void Increment_Unreliable_Count() {UnreliableCount++;}
UINT Get_Stat_Sample(int stat) const {WWASSERT(stat >= 0 && stat < STAT_COUNT); return StatSample[stat];}
UINT Get_Stat_Macro_Sample(int stat) const {WWASSERT(stat >= 0 && stat < STAT_COUNT); return StatMacroSample[stat];}
UINT Get_Stat_Snapshot(int stat) const {WWASSERT(stat >= 0 && stat < STAT_COUNT); return StatSnapshot[stat];}
UINT Get_Stat_Macro_Snapshot(int stat) const {WWASSERT(stat >= 0 && stat < STAT_COUNT); return StatMacroSnapshot[stat];}
UINT Get_Stat_Total(int stat) const {WWASSERT(stat >= 0 && stat < STAT_COUNT); return StatTotal[stat];}
UINT Get_Stat_Average(int stat) const {WWASSERT(stat >= 0 && stat < STAT_COUNT); return StatAverage[stat];}
void Increment_Stat_Sample(int stat, UINT increment) {WWASSERT(stat >= 0 && stat < STAT_COUNT); StatSample[stat] += increment;}
void Increment_Stat_Macro_Sample(int stat, UINT increment) {WWASSERT(stat >= 0 && stat < STAT_COUNT); StatMacroSample[stat] += increment;}
void Increment_Stat_Snapshot(int stat, UINT increment) {WWASSERT(stat >= 0 && stat < STAT_COUNT); StatSnapshot[stat] += increment;}
void Increment_Stat_Macro_Snapshot(int stat, UINT increment){WWASSERT(stat >= 0 && stat < STAT_COUNT); StatMacroSnapshot[stat] += increment;}
void Increment_Stat_Total(int stat, UINT increment) {WWASSERT(stat >= 0 && stat < STAT_COUNT); StatTotal[stat] += increment;}
void Increment_Stat_Average(int stat, UINT increment) {WWASSERT(stat >= 0 && stat < STAT_COUNT); StatAverage[stat] += increment;}
//
// TSS - shift these down, and use above access operators
//
UINT StatSample[STAT_COUNT];
UINT StatMacroSample[STAT_COUNT];
UINT StatSnapshot[STAT_COUNT];
UINT StatMacroSnapshot[STAT_COUNT];
UINT StatTotal[STAT_COUNT];
UINT StatAverage[STAT_COUNT];
private:
cNetStats(const cNetStats& source); // Disallow copy (compile/link time)
cNetStats& operator=(const cNetStats& rhs); // Disallow assignment (compile/link time)
//int PrevLastUnreliable;
unsigned long SampleStartTime;
int LastUnreliablePacketId;
int FreezePacketId;
int UnreliableCount;
unsigned long StartTime;
double RemotePacketloss;
int RemoteServiceCount;
};
//-----------------------------------------------------------------------------
#endif // NETSTATS_H
//static const USHORT SAMPLE_TIME; // stats gathering period in ms

678
Code/wwnet/netutil.cpp Normal file
View File

@@ -0,0 +1,678 @@
/*
** 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/>.
*/
//
// Filename: netutil.cpp
// Project: wwnet
// Author: Tom Spencer-Smith
// Date: June 1998
// Description:
//
//-----------------------------------------------------------------------------
#include "netutil.h" // I WANNA BE FIRST!
#include <stdio.h>
#include <string.h>
#include "miscutil.h"
#include "mathutil.h"
#include "singlepl.h"
#include "wwpacket.h"
#include "wwdebug.h"
#include "ffactory.h"
#include "ini.h"
#include "systimer.h"
#include "fromaddress.h"
//
// class statics
//
//const WORD cNetUtil::WS_VERSION_REQD = MAKEWORD(1, 1); // Winsock 1.1
//USHORT cNetUtil::HeaderBytes;
//USHORT cNetUtil::MaxPacketAppDataSize = MAX_LAN_PACKET_APP_DATA_SIZE;
UINT cNetUtil::DefaultResendTimeoutMs = 200; // used for singleplayer
bool cNetUtil::IsInternet = false;
static const int INVALID_VALUE = -999;
const USHORT cNetUtil::NETSTATS_SAMPLE_TIME_MS = 2000;
const USHORT cNetUtil::KEEPALIVE_TIMEOUT_MS = 2000;
const USHORT cNetUtil::MAX_RESENDS = 50;
const USHORT cNetUtil::MULTI_SENDS = 10;
const USHORT cNetUtil::RESEND_TIMEOUT_LAN_MS = 300;
const USHORT cNetUtil::RESEND_TIMEOUT_INTERNET_MS = 500;
const ULONG cNetUtil::CLIENT_CONNECTION_LOSS_TIMEOUT = 15000; // Milliseconds til client gives up on server
const ULONG cNetUtil::SERVER_CONNECTION_LOSS_TIMEOUT = 15000; // Milliseconds til server gives up on client
const ULONG cNetUtil::SERVER_CONNECTION_LOSS_TIMEOUT_LOADING_ALLOWANCE = 45000; // Milliseconds extra allowed til server gives up on loading client.
//int cNetUtil::DefaultMultiSends = INVALID_VALUE;
//int cNetUtil::DefaultMaxResends = INVALID_VALUE;
//int cNetUtil::DefaultKeepaliveTimeoutMs = INVALID_VALUE;
//int cNetUtil::DesiredSendBufferSizeBytes = INVALID_VALUE;
//int cNetUtil::DesiredReceiveBufferSizeBytes = INVALID_VALUE;
//int cNetUtil::DefaultServerPort = INVALID_VALUE;
//int cNetUtil::MaxReceiveTimeMs = INVALID_VALUE;
//float cNetUtil::PriorityToleranceDownwards = INVALID_VALUE;
//float cNetUtil::PriorityToleranceUpwards = INVALID_VALUE;
//float cNetUtil::MaxTPCorrectionDownwards = INVALID_VALUE;
//float cNetUtil::MaxTPCorrectionUpwards = INVALID_VALUE;
//float cNetUtil::PriorityNoiseFactor = INVALID_VALUE;
//float cNetUtil::InitialThresholdPriority = INVALID_VALUE;
//float cNetUtil::PriorityGrowthPerSecond = INVALID_VALUE;
char cNetUtil::WorkingAddressBuffer[] = "";
//-----------------------------------------------------------------------------
//
// Macro
//
#define ADD_CASE(exp) case exp: ::sprintf(error_msg, #exp); break;
void cNetUtil::Wsa_Error(LPCSTR sFile, unsigned uLine)
{
WWDEBUG_SAY(("* %s:%d: WSA function returned error code: %s\n", sFile, uLine, Winsock_Error_Text(::WSAGetLastError())));
DIE;
}
//-----------------------------------------------------------------------------
//
// Just get the text for the specified error code.
//
const char * cNetUtil::Winsock_Error_Text(int error_code)
{
static char error_msg[500];
switch (error_code) {
//
// Windows Sockets definitions of regular Microsoft C error constants
//
ADD_CASE(WSAEINTR)
ADD_CASE(WSAEBADF)
ADD_CASE(WSAEACCES)
ADD_CASE(WSAEFAULT)
ADD_CASE(WSAEINVAL)
ADD_CASE(WSAEMFILE)
//
// Windows Sockets definitions of regular Berkeley error constants
//
ADD_CASE(WSAEWOULDBLOCK)
ADD_CASE(WSAEINPROGRESS)
ADD_CASE(WSAEALREADY)
ADD_CASE(WSAENOTSOCK)
ADD_CASE(WSAEDESTADDRREQ)
ADD_CASE(WSAEMSGSIZE)
ADD_CASE(WSAEPROTOTYPE)
ADD_CASE(WSAENOPROTOOPT)
ADD_CASE(WSAEPROTONOSUPPORT)
ADD_CASE(WSAESOCKTNOSUPPORT)
ADD_CASE(WSAEOPNOTSUPP)
ADD_CASE(WSAEPFNOSUPPORT)
ADD_CASE(WSAEAFNOSUPPORT)
ADD_CASE(WSAEADDRINUSE)
ADD_CASE(WSAEADDRNOTAVAIL)
ADD_CASE(WSAENETDOWN)
ADD_CASE(WSAENETUNREACH)
ADD_CASE(WSAENETRESET)
ADD_CASE(WSAECONNABORTED)
ADD_CASE(WSAECONNRESET)
ADD_CASE(WSAENOBUFS)
ADD_CASE(WSAEISCONN)
ADD_CASE(WSAENOTCONN)
ADD_CASE(WSAESHUTDOWN)
ADD_CASE(WSAETOOMANYREFS)
ADD_CASE(WSAETIMEDOUT)
ADD_CASE(WSAECONNREFUSED)
ADD_CASE(WSAELOOP)
ADD_CASE(WSAENAMETOOLONG)
ADD_CASE(WSAEHOSTDOWN)
ADD_CASE(WSAEHOSTUNREACH)
ADD_CASE(WSAENOTEMPTY)
ADD_CASE(WSAEPROCLIM)
ADD_CASE(WSAEUSERS)
ADD_CASE(WSAEDQUOT)
ADD_CASE(WSAESTALE)
ADD_CASE(WSAEREMOTE)
//
// Extended Windows Sockets error constant definitions
///
ADD_CASE(WSASYSNOTREADY)
ADD_CASE(WSAVERNOTSUPPORTED)
ADD_CASE(WSANOTINITIALISED)
ADD_CASE(WSAEDISCON)
default:
::sprintf(error_msg, "Unknown Winsock Error (%d)", error_code);
break;
}
return(error_msg);
}
/*
int g_c_wouldblock = 0;
int g_c_nobufs = 0;
*/
//-----------------------------------------------------------------------------
bool cNetUtil::Send_Resource_Failure(LPCSTR sFile, unsigned uLine, int ret_code)
{
bool return_code = false;
if (ret_code == SOCKET_ERROR) {
int wsa_error = ::WSAGetLastError();
if (wsa_error == WSAEWOULDBLOCK || wsa_error == WSAENOBUFS) {
/*
if (wsa_error == WSAEWOULDBLOCK) {
g_c_wouldblock++;
} else {
g_c_nobufs++;
}
*/
return_code = true;
} else {
Wsa_Error(sFile, uLine);
}
} else {
return_code = false;
}
return return_code;
}
//-----------------------------------------------------------------------------
bool cNetUtil::Would_Block(LPCSTR sFile, unsigned uLine, int ret_code)
{
bool retcode = false;
if (ret_code == SOCKET_ERROR) {
if (::WSAGetLastError() == WSAEWOULDBLOCK) {
retcode = true;
} else {
Wsa_Error(sFile, uLine);
retcode = false;
}
} else {
retcode = false;
}
return retcode;
}
//-----------------------------------------------------------------------------
//
// Returns up to max_addresses adapter addresses for the local host
//
int cNetUtil::Get_Local_Tcpip_Addresses(SOCKADDR_IN ip_address[], USHORT max_addresses)
{
WWDEBUG_SAY(("cNetUtil::Get_Local_Tcpip_Addresses:\n"));
//
// Get the local hostname
//
char local_host_name[200];
WSA_CHECK(::gethostname(local_host_name, sizeof(local_host_name)));
WWDEBUG_SAY((" Host name is %s\n", local_host_name));
//
// Resolve hostname for local adapter addresses. This does
// a DNS lookup (name resolution)
//
LPHOSTENT p_hostent = ::gethostbyname(local_host_name);
int num_adapters = 0;
if (p_hostent == NULL) {
num_adapters = 0;
} else {
while (num_adapters < max_addresses && p_hostent->h_addr_list[num_adapters] != NULL) {
ZeroMemory(&ip_address[num_adapters], sizeof(SOCKADDR_IN));
ip_address[num_adapters].sin_family = AF_INET;
ip_address[num_adapters].sin_addr.s_addr =
*((u_long *) (p_hostent->h_addr_list[num_adapters]));
WWDEBUG_SAY((" Address: %s\n", Address_To_String(ip_address[num_adapters].sin_addr.s_addr)));
num_adapters++;
}
}
return num_adapters;
}
//-----------------------------------------------------------------------------
bool cNetUtil::Is_Same_Address(LPSOCKADDR_IN p_address1, const SOCKADDR_IN* p_address2)
{
//
// C disallows comparison of structs...
//
WWASSERT(!cSinglePlayerData::Is_Single_Player());
WWASSERT(p_address1 != NULL);
WWASSERT(p_address2 != NULL);
return
p_address1->sin_addr.s_addr == p_address2->sin_addr.s_addr &&
p_address1->sin_port == p_address2->sin_port;
}
//-------------------------------------------------------------------------------
void cNetUtil::Address_To_String(LPSOCKADDR_IN p_address, char * str, UINT len,
USHORT & port)
{
WWASSERT(p_address != NULL);
WWASSERT(str != NULL);
char temp_str[1000];
::strcpy(temp_str, ::inet_ntoa(p_address->sin_addr));
port = ::ntohs(p_address->sin_port);
WWASSERT(::strlen(temp_str) <= len);
::strcpy(str, temp_str);
}
//-------------------------------------------------------------------------------
LPCSTR cNetUtil::Address_To_String(ULONG ip)
{
IN_ADDR in_addr;
in_addr.s_addr = ip;
char * p = ::inet_ntoa(in_addr);
if (p == NULL) {
::sprintf(WorkingAddressBuffer, "Invalid ip (%u)", ip);
} else {
::strcpy(WorkingAddressBuffer, p);
}
return WorkingAddressBuffer;
}
//-------------------------------------------------------------------------------
void cNetUtil::String_To_Address(LPSOCKADDR_IN p_address, LPCSTR str, USHORT port)
{
WWASSERT(p_address != NULL);
ZeroMemory(p_address, sizeof(SOCKADDR_IN));
p_address->sin_family = AF_INET;
p_address->sin_addr.s_addr = ::inet_addr(str);
p_address->sin_port = ::htons(port);
WWASSERT(p_address->sin_addr.s_addr != INADDR_NONE);
}
//-------------------------------------------------------------------------------
bool cNetUtil::Is_Tcpip_Present(void)
{
//
// N.B. I tested EnumProtocols and found it unreliable.
//
bool retcode = true;
SOCKET test_socket = ::socket(AF_INET, SOCK_DGRAM, 0);
if (test_socket == INVALID_SOCKET) {
if (::WSAGetLastError() == WSAEAFNOSUPPORT) {
retcode = false;
} else {
WSA_ERROR;
}
} else {
WSA_CHECK(::closesocket(test_socket));
}
return retcode;
}
//-------------------------------------------------------------------------------
void cNetUtil::Wsa_Init()
{
//
// winsock 1.1
//
WSADATA wsa_data;
if (::WSAStartup(MAKEWORD(1, 1), &wsa_data) != 0) {
DIE;
}
}
//-------------------------------------------------------------------------------
bool cNetUtil::Protocol_Init(bool is_internet)
{
IsInternet = is_internet;
bool retcode = false;
if (Is_Tcpip_Present()) {
retcode = true;
if (IsInternet) {
DefaultResendTimeoutMs = RESEND_TIMEOUT_INTERNET_MS;
} else {
DefaultResendTimeoutMs = RESEND_TIMEOUT_LAN_MS;
}
} else {
retcode = false;
}
return retcode;
}
/*
//-------------------------------------------------------------------------------
float cNetUtil::Compute_Priority_Noise()
{
//
// Add some noise to increase the spread. This noise must be bigger
// than the maximum threshold adjustment, so PriorityNoiseFactor should
// be at least 1 if you are adding noise.
// Do not jitter a priority of zero!
//
// Or maybe we can just specify a max noise now?
//
float noise_width = PriorityNoiseFactor * MaxTPCorrectionDownwards;
return cMathUtil::Get_Hat_Pdf_Double(-noise_width / 2.0f, +noise_width / 2.0f);
}
*/
//-------------------------------------------------------------------------------
void cNetUtil::Set_Socket_Buffer_Sizes(SOCKET sock, int new_size)
{
WWDEBUG_SAY(("cNetUtil::Set_Socket_Buffer_Sizes:\n"));
int buffersize = 0;
int len = 0;
buffersize = 0;
len = sizeof(int);
WSA_CHECK(::getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&buffersize, &len));
//WWDEBUG_SAY((" SO_SNDBUF = %d\n", buffersize));
buffersize = 0;
len = sizeof(int);
WSA_CHECK(::getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&buffersize, &len));
//WWDEBUG_SAY((" SO_RCVBUF = %d\n", buffersize));
buffersize = new_size;
len = sizeof(int);
WSA_CHECK(setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&buffersize, len));
//WWDEBUG_SAY((" Attempting to set SO_SNDBUF = %d\n", buffersize));
buffersize = new_size;
len = sizeof(int);
WSA_CHECK(setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&buffersize, len));
//WWDEBUG_SAY((" Attempting to set SO_RCVBUF = %d\n", buffersize));
buffersize = 0;
len = sizeof(int);
WSA_CHECK(::getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&buffersize, &len));
//WWDEBUG_SAY((" SO_SNDBUF = %d\n", buffersize));
buffersize = 0;
len = sizeof(int);
WSA_CHECK(::getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&buffersize, &len));
//WWDEBUG_SAY((" SO_RCVBUF = %d\n", buffersize));
}
/*
//-------------------------------------------------------------------------------
void cNetUtil::Onetime_Init()
{
FileClass * p_ini_file = _TheFileFactory->Get_File("netparams.ini");
if (p_ini_file != NULL) {
INIClass netparams_ini(*p_ini_file);
WWASSERT(netparams_ini.Section_Count() == 1);
const LPCSTR SECTION_NAME = "Settings";
//NETSTATS_SAMPLE_TIME_MS = netparams_ini.Get_Int(SECTION_NAME, "NETSTATS_SAMPLE_TIME_MS", INVALID_VALUE);
//WWASSERT(NETSTATS_SAMPLE_TIME_MS > 0);
//RESEND_TIMEOUT_LAN_MS = netparams_ini.Get_Int(SECTION_NAME, "RESEND_TIMEOUT_LAN_MS", INVALID_VALUE);
//WWASSERT(RESEND_TIMEOUT_LAN_MS > 0);
//RESEND_TIMEOUT_INTERNET_MS = netparams_ini.Get_Int(SECTION_NAME, "RESEND_TIMEOUT_INTERNET_MS", INVALID_VALUE);
//WWASSERT(RESEND_TIMEOUT_INTERNET_MS > 0);
//DefaultMultiSends = netparams_ini.Get_Int(SECTION_NAME, "DefaultMultiSends", INVALID_VALUE);
//WWASSERT(DefaultMultiSends > 0);
//DefaultMaxResends = netparams_ini.Get_Int(SECTION_NAME, "DefaultMaxResends", INVALID_VALUE);
//WWASSERT(DefaultMaxResends > 0);
//DefaultKeepaliveTimeoutMs = netparams_ini.Get_Int(SECTION_NAME, "DefaultKeepaliveTimeoutMs", INVALID_VALUE);
//WWASSERT(DefaultKeepaliveTimeoutMs > 0);
//DesiredSendBufferSizeBytes = netparams_ini.Get_Int(SECTION_NAME, "DesiredSendBufferSizeBytes", INVALID_VALUE);
//WWASSERT(DesiredSendBufferSizeBytes > 0);
//DESIRED_RECEIVE_BUFFER_SIZE_BYTES = netparams_ini.Get_Int(SECTION_NAME, "DesiredReceiveBufferSizeBytes", INVALID_VALUE);
//WWASSERT(DesiredReceiveBufferSizeBytes > 0);
//DefaultServerPort = netparams_ini.Get_Int(SECTION_NAME, "DefaultServerPort", INVALID_VALUE);
//WWASSERT(DefaultServerPort >= MIN_SERVER_PORT && DefaultServerPort <= MAX_SERVER_PORT);
//MaxReceiveTimeMs = netparams_ini.Get_Int(SECTION_NAME, "MaxReceiveTimeMs", INVALID_VALUE);
//WWASSERT(MaxReceiveTimeMs > 0);
//PriorityToleranceDownwards = netparams_ini.Get_Float(SECTION_NAME, "PriorityToleranceDownwards", INVALID_VALUE);
//WWASSERT(PriorityToleranceDownwards > -1 - MISCUTIL_EPSILON && PriorityToleranceDownwards < 1 + MISCUTIL_EPSILON);
//PriorityToleranceUpwards = netparams_ini.Get_Float(SECTION_NAME, "PriorityToleranceUpwards", INVALID_VALUE);
//WWASSERT(PriorityToleranceUpwards > -1 - MISCUTIL_EPSILON && PriorityToleranceUpwards < 1 + MISCUTIL_EPSILON);
//MaxTPCorrectionDownwards = netparams_ini.Get_Float(SECTION_NAME, "MaxTPCorrectionDownwards", INVALID_VALUE);
//WWASSERT(MaxTPCorrectionDownwards > -1 - MISCUTIL_EPSILON && MaxTPCorrectionDownwards < 1 + MISCUTIL_EPSILON);
//MaxTPCorrectionUpwards = netparams_ini.Get_Float(SECTION_NAME, "MaxTPCorrectionUpwards", INVALID_VALUE);
//WWASSERT(MaxTPCorrectionUpwards > -1 - MISCUTIL_EPSILON && MaxTPCorrectionUpwards < 1 + MISCUTIL_EPSILON);
//PriorityNoiseFactor = netparams_ini.Get_Float(SECTION_NAME, "PriorityNoiseFactor", INVALID_VALUE);
//WWASSERT(PriorityNoiseFactor >= -MISCUTIL_EPSILON);
//InitialThresholdPriority = netparams_ini.Get_Float(SECTION_NAME, "InitialThresholdPriority", INVALID_VALUE);
//WWASSERT(InitialThresholdPriority >= -MISCUTIL_EPSILON);
//PriorityNoiseFactor = netparams_ini.Get_Float(SECTION_NAME, "PriorityNoiseFactor", INVALID_VALUE);
//WWASSERT(PriorityNoiseFactor >= -MISCUTIL_EPSILON);
//PriorityGrowthPerSecond = netparams_ini.Get_Float(SECTION_NAME, "PriorityGrowthPerSecond", INVALID_VALUE);
//WWASSERT(PriorityGrowthPerSecond >= -MISCUTIL_EPSILON);
_TheFileFactory->Return_File(p_ini_file);
}
}
*/
void cNetUtil::Create_Unbound_Socket(SOCKET & sock)
{
sock = ::socket(AF_INET, SOCK_DGRAM, 0);
if (sock == INVALID_SOCKET) {
WSA_ERROR;
}
//
// Enable broadcasts
//
int optval = TRUE;
WSA_CHECK(setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *) &optval, sizeof(optval)));
//
// Make socket non-blocking
//
u_long arg = 1L;
WSA_CHECK(ioctlsocket(sock, FIONBIO, (u_long *) &arg));
}
//-------------------------------------------------------------------------------
bool cNetUtil::Create_Bound_Socket(SOCKET & sock, USHORT port, SOCKADDR_IN & local_address)
{
//
// TSS - is all this necessary or is above function OK?
//
Create_Unbound_Socket(sock);
Create_Local_Address(&local_address, port);
int result = ::bind(sock, (LPSOCKADDR) &local_address, sizeof(SOCKADDR_IN));
if (result == 0) {
return true;
} else {
WWASSERT(result == SOCKET_ERROR);
//if (::WSAGetLastError() != WSAEADDRINUSE) {
WSA_ERROR;
//}
return false;
}
}
//-------------------------------------------------------------------------------
void cNetUtil::Close_Socket(SOCKET & sock)
{
::closesocket(sock);
}
//-----------------------------------------------------------------------------
void cNetUtil::Broadcast(SOCKET & sock, USHORT port, cPacket & packet)
{
SOCKADDR_IN broadcast_address;
Create_Broadcast_Address(&broadcast_address, port);
int bytes_sent;
//WSA_CHECK(bytes_sent = sendto(sock, packet.Data, packet.SendLength,
// 0, &broadcast_address, sizeof(SOCKADDR_IN)));
bytes_sent = sendto(sock, packet.Get_Data(), packet.Get_Compressed_Size_Bytes(),
0, (LPSOCKADDR) &broadcast_address, sizeof(SOCKADDR_IN));
#pragma message("(TSS) WSAENOBUFS")
//WWDEBUG_SAY(("Sent broadcast, length = %d bytes\n", bytes_sent));
}
//-------------------------------------------------------------------------------
void cNetUtil::Create_Broadcast_Address(LPSOCKADDR_IN p_broadcast_address,
USHORT port)
{
WWASSERT(p_broadcast_address != NULL);
ZeroMemory(p_broadcast_address, sizeof(SOCKADDR_IN));
p_broadcast_address->sin_family = AF_INET;
p_broadcast_address->sin_addr.s_addr = INADDR_BROADCAST; // ::inet_addr("255.255.255.255");
p_broadcast_address->sin_port = ::htons(port);
}
//-------------------------------------------------------------------------------
void cNetUtil::Create_Local_Address(LPSOCKADDR_IN p_local_address, USHORT port)
{
WWASSERT(p_local_address != NULL);
ZeroMemory(p_local_address, sizeof(SOCKADDR_IN));
p_local_address->sin_family = AF_INET;
p_local_address->sin_addr.s_addr = INADDR_ANY;
p_local_address->sin_port = ::htons(port);
}
//-------------------------------------------------------------------------------
bool cNetUtil::Get_Local_Address(LPSOCKADDR_IN p_local_address)
{
WWASSERT(p_local_address != NULL);
/*
const USHORT MAX_ADDRESSES = 1;
int num_addresses = Get_Local_Tcpip_Addresses(p_local_address, MAX_ADDRESSES);
return (num_addresses == 1);
*/
const USHORT MAX_ADDRESSES = 10;
SOCKADDR_IN local_address[MAX_ADDRESSES];
int num_addresses = Get_Local_Tcpip_Addresses(local_address, MAX_ADDRESSES);
if (num_addresses > 0) {
::memcpy(p_local_address, &local_address[0], sizeof(SOCKADDR_IN));
}
return (num_addresses > 0);
}
//-----------------------------------------------------------------------------
void cNetUtil::Lan_Servicing(SOCKET & sock, LanPacketHandlerCallback p_callback)
{
int retcode;
unsigned long start_time = TIMEGETTIME();
do {
cPacket packet;
int address_len = sizeof(SOCKADDR_IN);
//
// If we appear to crash INSIDE recvfrom then this tends to indicate
// that net neighbourhood broke.
//
retcode = recvfrom(sock, packet.Get_Data(), packet.Get_Max_Size(),
0, (LPSOCKADDR) &packet.Get_From_Address_Wrapper()->FromAddress, &address_len);
if (retcode == SOCKET_ERROR) {
if (::WSAGetLastError() != WSAEWOULDBLOCK) {
WSA_ERROR;
}
} else {
/*
//
// diagnostic
//
ULONG ip = packet.Get_From_Address_Wrapper()->FromAddress.sin_addr.s_addr;
WWDEBUG_SAY(("cNetUtil::Lan_Servicing: %s\n", cNetUtil::Address_To_String(ip)));
*/
//packet.Set_Received_Length(retcode);
packet.Set_Bit_Length(retcode * 8);
(*p_callback)(packet);
}
} while (retcode != SOCKET_ERROR); // this will indicate no more data
unsigned long time_spent = TIMEGETTIME() - start_time;
if (time_spent > 100) {
WWDEBUG_SAY(("*** cNetUtil::Lan_Servicing: Too much time (%d ms)) spent receiving lan packets.\n",
time_spent));
}
}

149
Code/wwnet/netutil.h Normal file
View File

@@ -0,0 +1,149 @@
/*
** 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/>.
*/
//
// Filename: netutil.h
// Project: wwnet
// Author: Tom Spencer-Smith
// Date: June 1998
// Description:
//
//-----------------------------------------------------------------------------
#if defined(_MSV_VER)
#pragma once
#endif
#ifndef NETUTIL_H
#define NETUTIL_H
#include "win.h"
#include <winsock.h>
#include "bittype.h"
class cPacket;
#define WOULD_BLOCK(exp) cNetUtil::Would_Block(__FILE__, __LINE__, exp)
#define SEND_RESOURCE_FAILURE(exp) cNetUtil::Send_Resource_Failure(__FILE__, __LINE__, exp)
#define WSA_ERROR {cNetUtil::Wsa_Error(__FILE__, __LINE__);}
#define WSA_CHECK(exp) {if ((exp) == SOCKET_ERROR) {cNetUtil::Wsa_Error(__FILE__, __LINE__);}}
typedef void (*LanPacketHandlerCallback)(cPacket & packet);
enum {
MIN_SERVER_PORT = 1024, //1025,
MAX_SERVER_PORT = 65535 //5000
};
class cNetUtil
{
public:
static void Wsa_Init();
static bool Protocol_Init(bool is_internet);
static bool Get_Local_Address(LPSOCKADDR_IN p_local_address);
static void Wsa_Error(LPCSTR sFile, unsigned uLine);
static bool Is_Same_Address(LPSOCKADDR_IN p_address1, const SOCKADDR_IN* p_address2);
static bool Would_Block(LPCSTR sFile, unsigned uLine, int ret_code);
static bool Send_Resource_Failure(LPCSTR sFile, unsigned uLine, int ret_code);
static void Address_To_String(LPSOCKADDR_IN p_address, char * str, UINT len,
USHORT & port);
static LPCSTR Address_To_String(ULONG ip_address);
static void String_To_Address(LPSOCKADDR_IN p_address, LPCSTR str, USHORT port);
static void Create_Unbound_Socket(SOCKET & sock);
static bool Create_Bound_Socket(SOCKET & sock, USHORT port, SOCKADDR_IN & local_address);
static void Close_Socket(SOCKET & sock);
static void Create_Broadcast_Address(LPSOCKADDR_IN p_broadcast_address, USHORT port);
static void Create_Local_Address(LPSOCKADDR_IN p_local_address, USHORT port);
static void Broadcast(SOCKET & sock, USHORT port, cPacket & packet);
static bool Is_Tcpip_Present();
static void Lan_Servicing(SOCKET & sock, LanPacketHandlerCallback p_callback);
static bool Is_Internet() {return IsInternet;}
static void Set_Socket_Buffer_Sizes(SOCKET sock, int new_size = 10000);
static UINT Get_Default_Resend_Timeout_Ms() {return DefaultResendTimeoutMs;}
static const USHORT NETSTATS_SAMPLE_TIME_MS;
static const USHORT KEEPALIVE_TIMEOUT_MS;
static const USHORT MAX_RESENDS;
static const USHORT MULTI_SENDS;
static const USHORT RESEND_TIMEOUT_LAN_MS;
static const USHORT RESEND_TIMEOUT_INTERNET_MS;
static const ULONG CLIENT_CONNECTION_LOSS_TIMEOUT;
static const ULONG SERVER_CONNECTION_LOSS_TIMEOUT;
static const ULONG SERVER_CONNECTION_LOSS_TIMEOUT_LOADING_ALLOWANCE;
static const char *Winsock_Error_Text(int error_code);
private:
static int Get_Local_Tcpip_Addresses(SOCKADDR_IN ip_address[], USHORT max_addresses);
static bool IsInternet;
static UINT DefaultResendTimeoutMs;
static char WorkingAddressBuffer[300];
};
#endif // NETUTIL_H
//static USHORT Get_Header_Bytes() {return HeaderBytes;}
//static USHORT Get_Max_Packet_App_Data_Size() {return MaxPacketAppDataSize;}
//static USHORT HeaderBytes;
//static USHORT MaxPacketAppDataSize;
//static const WORD WS_VERSION_REQD;
//static int Get_Net_Stats_Sample_Time_Ms() {return NETSTATS_SAMPLE_TIME_MS;}
//static int Get_Default_Multi_Sends() {return DefaultMultiSends;}
//static int Get_Default_Max_Resends() {return DefaultMaxResends;}
//static int Get_Default_Keepalive_Timeout_Ms() {return DefaultKeepaliveTimeoutMs;}
//static int Get_Desired_Send_Buffer_Size_Bytes() {return DesiredSendBufferSizeBytes;}
//static int Get_Desired_Receive_Buffer_Size_Bytes() {return DesiredReceiveBufferSizeBytes;}
//static int Get_Default_Server_Port() {return DefaultServerPort;}
//static int Get_Max_Receive_Time_Ms() {return MaxReceiveTimeMs;}
//static float Get_Priority_Tolerance_Downwards() {return PriorityToleranceDownwards;}
//static float Get_Priority_Tolerance_Upwards() {return PriorityToleranceUpwards;}
//static float Get_Max_TP_Correction_Downwards() {return MaxTPCorrectionDownwards;}
//static float Get_Max_TP_Correction_Upwards() {return MaxTPCorrectionUpwards;}
//static float Get_Priority_Noise_Factor() {return PriorityNoiseFactor;}
//static float Get_Initial_Threshold_Priority() {return InitialThresholdPriority;}
//static float Get_Priority_Growth_Per_Second() {return PriorityGrowthPerSecond;}
//static float Compute_Priority_Noise();
//static int DefaultMultiSends; // Number of sends in a SEND_MULTI
//static int DefaultMaxResends; // If you exceed this then the connection is regarded as broken
//static int DefaultKeepaliveTimeoutMs;
//static int DesiredSendBufferSizeBytes;
//static int DesiredReceiveBufferSizeBytes;
//static int DefaultServerPort;
//static int MaxReceiveTimeMs;
//static float PriorityToleranceDownwards;
//static float PriorityToleranceUpwards;
//static float MaxTPCorrectionDownwards;
//static float MaxTPCorrectionUpwards;
//static float PriorityNoiseFactor;
//static float InitialThresholdPriority;
//static float PriorityGrowthPerSecond;
//static int Get_Default_Resend_Timeout_Lan_Ms() {return RESEND_TIMEOUT_LAN_MS;}
//static int Get_Default_Resend_Timeout_Internet_Ms() {return RESEND_TIMEOUT_INTERNET_MS;}
//static void Onetime_Init();

View File

@@ -0,0 +1,661 @@
/*
** 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/wwnet/networkobject.cpp $*
* *
* $Author:: Tom_s $*
* *
* $Modtime:: 1/07/02 5:16p $*
* *
* $Revision:: 28 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "networkobject.h"
#include "networkobjectmgr.h"
#include "wwmath.h"
#include "vector3.h"
#include "wwprofile.h"
#include "systimer.h"
#define CLIENT_SIDE_UPDATE_FREQUENCY_SAMPLE_PERIOD (1000 * 10)
////////////////////////////////////////////////////////////////
// Static member initializtaion
////////////////////////////////////////////////////////////////
bool NetworkObjectClass::IsServer = false;
////////////////////////////////////////////////////////////////
//
// NetworkObjectClass
//
////////////////////////////////////////////////////////////////
NetworkObjectClass::NetworkObjectClass (void) :
ImportStateCount (0),
LastClientsideUpdateTime (0),
NetworkID (0),
IsDeletePending (false),
CachedPriority (0),
UnreliableOverride (false),
AppPacketType (0),
FrequentExportPacketSize(0),
ClientsideUpdateFrequencySampleStartTime(TIMEGETTIME()),
ClientsideUpdateFrequencySampleCount(0),
ClientsideUpdateRate(0),
#ifdef WWDEBUG
CreatedByPacketID(0),
#endif //WWDEBUG
LastObjectIdIDamaged(-1),
LastObjectIdIGotDamagedBy(-1)
{
if (IsServer)
{
//
// Assign the object a unique ID. This will happen on the client too during object
// imports, but will be corrected immediately with an explicit Set_Network_ID call.
//
int new_id = NetworkObjectMgrClass::Get_New_Dynamic_ID();
//WWDEBUG_SAY(("New network id = %d\n", new_id));//TSS2001
Set_Network_ID(new_id);
}
//
// By default, objects have the modifiction dirty bit set.
// Static objects therefore don't need to remember to set this in their constructor.
// Game objects will set BIT_CREATION.
//
Clear_Object_Dirty_Bits ();
memset(CachedPriority_2, 0, sizeof(CachedPriority_2));
return ;
}
////////////////////////////////////////////////////////////////
//
// ~NetworkObjectClass
//
////////////////////////////////////////////////////////////////
NetworkObjectClass::~NetworkObjectClass (void)
{
//
// Unregister this object from network updates
//
NetworkObjectMgrClass::Unregister_Object (this);
return ;
}
extern bool SensibleUpdates;
////////////////////////////////////////////////////////////////
//
// Set_Network_ID
//
////////////////////////////////////////////////////////////////
void
NetworkObjectClass::Set_Network_ID (int id)
{
WWASSERT(id > 0);
//
// Remove the object from the manager, change it's ID,
// and re-insert it.
//
NetworkObjectMgrClass::Unregister_Object (this);
NetworkID = id;
NetworkObjectMgrClass::Register_Object (this);
return ;
}
////////////////////////////////////////////////////////////////
//
// Get_Object_Dirty_Bits
//
////////////////////////////////////////////////////////////////
BYTE
NetworkObjectClass::Get_Object_Dirty_Bits (int client_id)
{
return ClientStatus[client_id];
}
////////////////////////////////////////////////////////////////
//
// Set_Object_Dirty_Bits
//
////////////////////////////////////////////////////////////////
void
NetworkObjectClass::Set_Object_Dirty_Bits (int client_id, BYTE bits)
{
ClientStatus[client_id] = bits;
}
////////////////////////////////////////////////////////////////
//
// Get_Object_Dirty_Bit
//
////////////////////////////////////////////////////////////////
bool
NetworkObjectClass::Get_Object_Dirty_Bit (int client_id, DIRTY_BIT dirty_bit)
{
return ((ClientStatus[client_id] & dirty_bit) == dirty_bit);
}
////////////////////////////////////////////////////////////////
//
// Clear_Object_Dirty_Bits
//
////////////////////////////////////////////////////////////////
void
NetworkObjectClass::Clear_Object_Dirty_Bits (void)
{
//
// Reset the status for each client
//
for (int index = 0; index < MAX_CLIENT_COUNT; index ++) {
ClientStatus[index] = 0;
UpdateInfo[index].LastUpdateTime = 0;
UpdateInfo[index].UpdateRate = 50;
UpdateInfo[index].ClientHintCount = 0;
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Set_Object_Dirty_Bit
//
////////////////////////////////////////////////////////////////
void
NetworkObjectClass::Set_Object_Dirty_Bit (int client_id, DIRTY_BIT dirty_bit, bool onoff)
{
if (onoff) {
ClientStatus[client_id] |= dirty_bit;
} else {
ClientStatus[client_id] &= (~dirty_bit);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Set_Object_Dirty_Bit
//
////////////////////////////////////////////////////////////////
void
NetworkObjectClass::Set_Object_Dirty_Bit (DIRTY_BIT dirty_bit, bool onoff)
{
if (!IsServer)
{
return;
}
//
// Change the status for each client
// N.B. Client 0 is actually the server.
//
for (int index = 1; index < MAX_CLIENT_COUNT; index ++) {//TSS2001
if (onoff) {
ClientStatus[index] |= dirty_bit;
} else {
ClientStatus[index] &= (~dirty_bit);
}
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Is_Client_Dirty
//
////////////////////////////////////////////////////////////////
bool
NetworkObjectClass::Is_Client_Dirty (int client_id)
{
return ClientStatus[client_id] != 0;
}
////////////////////////////////////////////////////////////////
//
// Set_Delete_Pending
//
////////////////////////////////////////////////////////////////
void
NetworkObjectClass::Set_Delete_Pending (void)
{
IsDeletePending = true;
NetworkObjectMgrClass::Register_Object_For_Deletion (this);
return;
}
////////////////////////////////////////////////////////////////
//
// Reset_Client_Hint_Count
//
////////////////////////////////////////////////////////////////
void
NetworkObjectClass::Reset_Client_Hint_Count(int client_id)
{
WWASSERT(client_id >= 0 && client_id < MAX_CLIENT_COUNT);
UpdateInfo[client_id].ClientHintCount = 0;
}
////////////////////////////////////////////////////////////////
//
// Increment_Client_Hint_Count
//
////////////////////////////////////////////////////////////////
void
NetworkObjectClass::Increment_Client_Hint_Count(int client_id)
{
WWASSERT(client_id >= 0 && client_id < MAX_CLIENT_COUNT);
if (UpdateInfo[client_id].ClientHintCount < 255) {
UpdateInfo[client_id].ClientHintCount++;
}
}
////////////////////////////////////////////////////////////////
//
// Hint_To_All_Clients
//
////////////////////////////////////////////////////////////////
void
NetworkObjectClass::Hint_To_All_Clients(void)
{
//
// Hint that an update should be sent to all clients
//
for (int index = 0; index < MAX_CLIENT_COUNT; index ++) {
Increment_Client_Hint_Count(index);
}
}
////////////////////////////////////////////////////////////////
//
// Get_Client_Hint_Count
//
////////////////////////////////////////////////////////////////
BYTE
NetworkObjectClass::Get_Client_Hint_Count(int client_id)
{
WWASSERT(client_id >= 0 && client_id < MAX_CLIENT_COUNT);
return UpdateInfo[client_id].ClientHintCount;
}
////////////////////////////////////////////////////////////////
//
// Belongs_To_Client
//
////////////////////////////////////////////////////////////////
bool
NetworkObjectClass::Belongs_To_Client (int client_id)
{
WWASSERT(client_id > 0);
int id_min = NETID_CLIENT_OBJECT_MIN + (client_id - 1) * 100000;
int id_max = id_min + 100000 - 1;
return (NetworkID >= id_min) && (NetworkID <= id_max);
}
////////////////////////////////////////////////////////////////
//
// Get_Last_Update_Time - Returns time that the client last received an update for this object.
//
// In: ID of client
// Out: Last update time.
//
// 10/16/2001 2:45PM ST
////////////////////////////////////////////////////////////////
unsigned long
NetworkObjectClass::Get_Last_Update_Time(int client_id)
{
// Is this assert right? ST - 10/16/2001 2:44PM
WWASSERT(client_id > 0 && client_id <= MAX_CLIENT_COUNT);
return(UpdateInfo[client_id].LastUpdateTime);
}
////////////////////////////////////////////////////////////////
//
// Get_Update_Rate - Returns delay in ms between updates for this object to the given client
//
// In: ID of client
// Out: Update delay in ms
//
// 10/16/2001 2:45PM ST
////////////////////////////////////////////////////////////////
unsigned short
NetworkObjectClass::Get_Update_Rate(int client_id)
{
// Is this assert right? ST - 10/16/2001 2:44PM
WWASSERT(client_id > 0 && client_id <= MAX_CLIENT_COUNT);
return(UpdateInfo[client_id].UpdateRate);
}
////////////////////////////////////////////////////////////////
//
// Set_Last_Update_Time - Sets time that the client last received an update for this object.
//
// In: ID of client, time this object was sent to the client.
// Out: Nothing
//
// 10/16/2001 2:45PM ST
////////////////////////////////////////////////////////////////
void
NetworkObjectClass::Set_Last_Update_Time(int client_id, unsigned long time)
{
// Is this assert right? ST - 10/16/2001 2:44PM
WWASSERT(client_id > 0 && client_id <= MAX_CLIENT_COUNT);
UpdateInfo[client_id].LastUpdateTime = time;
}
////////////////////////////////////////////////////////////////
//
// Set_Update_Rate - Sets the ms delay between updates for this client
//
// In: ID of client, delay in ms between updates
// Out: Nothing
//
// 10/16/2001 2:45PM ST
////////////////////////////////////////////////////////////////
void
NetworkObjectClass::Set_Update_Rate(int client_id, unsigned short rate)
{
// Is this assert right? ST - 10/16/2001 2:44PM
WWASSERT(client_id > 0 && client_id <= MAX_CLIENT_COUNT);
UpdateInfo[client_id].UpdateRate = rate;
}
/*
////////////////////////////////////////////////////////////////
//
// Clear_Object_Dirty_Bits
//
////////////////////////////////////////////////////////////////
void
NetworkObjectClass::Clear_Object_Dirty_Bits (int client_id)
{
ClientStatus[client_id] = 0;
return ;
}
*/
//float NetworkObjectClass::RandomFloat = 0.0F;
//TSS2001d float perturbation = RandomFloat * 2 * distance - distance;
/*
////////////////////////////////////////////////////////////////
//
// Set_Random_Float
//
////////////////////////////////////////////////////////////////
void
NetworkObjectClass::Set_Random_Float (float random_float)
{
WWASSERT(random_float >= 0 && random_float <= 1);
RandomFloat = random_float;
}
*/
//Set_Object_Dirty_Bit (BIT_RARE, true);
//float NetworkObjectClass::MaxDistance = 300.0F;
////////////////////////////////////////////////////////////////
//
// Compute_Object_Priority
//
////////////////////////////////////////////////////////////////
/*
float
NetworkObjectClass::Compute_Object_Priority (int client_id, const Vector3 &client_pos)
{
//
// Priority depends on physical distance. Objects with no physical location will
// have a priority of 1.
//
CachedPriority = 0;
if (Is_Client_Dirty(client_id)) {
float distance = Get_Object_Distance (client_pos);
if (distance < MaxDistance)
{
//
// This object is visible to this client.
// Add a random perturbation in the range [-distance, distance].
//
float rand_float = ::rand() / (float) RAND_MAX;
float perturbation = rand_float * 2 * distance - distance;
distance += perturbation;
}
//
// Priority simply decreases linearly with distance and is zero at MaxDistance.
//
CachedPriority = 1 - distance / MaxDistance;
CachedPriority = WWMath::Clamp (CachedPriority, 0.0F, 1.0F);
}
return CachedPriority;
}
*/
/*
float
NetworkObjectClass::Compute_Object_Priority (int client_id, const Vector3 &client_pos)
{
//
// Compute the priority of this object to the given client at his given position.
// Priority depends on physical distance. Objects with no physical location will
// have a priority of 1.
//
CachedPriority = 0;
if (Is_Client_Dirty(client_id)) {
float distance = Get_Object_Distance (client_pos);
//
// Priority simply decreases linearly with distance and is zero at MaxDistance.
//
CachedPriority = 1 - distance / MaxDistance;
CachedPriority = WWMath::Clamp (CachedPriority, 0.0F, 1.0F);
}
return CachedPriority;
}
*/
////////////////////////////////////////////////////////////////
//
// Set_Cached_Priority
//
////////////////////////////////////////////////////////////////
void
NetworkObjectClass::Set_Cached_Priority (float priority)
{
WWASSERT(priority >= 0 && priority <= 1);
CachedPriority = priority;
}
/*
////////////////////////////////////////////////////////////////
//
// Get_Object_Distance
//
////////////////////////////////////////////////////////////////
float
NetworkObjectClass::Get_Object_Distance (const Vector3 &client_pos)
{
//
// Objects without a physical location will return a distance of zero.
//
float distance = 0;
//
// Get the object's world position (if it has one)
//
Vector3 position;
if (Get_World_Position (position)) {
//
// Simple distance calculation based on the distance
// between points.
//
distance = (position - client_pos).Length ();
}
return distance;
}
*/
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Reset_Last_Clientside_Update_Time -- Reset the time this client object last got an update for the server
//
// In: Nothing.
// Out: Nothing
//
// 10/19/2001 12:19PM ST
////////////////////////////////////////////////////////////////////////////////////////////////////////////
void NetworkObjectClass::Reset_Last_Clientside_Update_Time(void)
{
LastClientsideUpdateTime = 0;
ClientsideUpdateFrequencySampleStartTime = TIMEGETTIME();
ClientsideUpdateFrequencySampleCount = 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Set_Last_Clientside_Update_Time -- Set the time this client object last got an update for the server
//
// In: Nothing.
// Out: Nothing
//
// 10/19/2001 12:19PM ST
////////////////////////////////////////////////////////////////////////////////////////////////////////////
void NetworkObjectClass::Set_Last_Clientside_Update_Time (ULONG time)
{
LastClientsideUpdateTime = time;
ClientsideUpdateFrequencySampleCount++;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Get_Clientside_Update_Frequency -- Get the updates rate from the server in ms
//
// In: Nothing.
// Out: Average time between updates from the server in ms
//
// 10/19/2001 12:19PM ST
////////////////////////////////////////////////////////////////////////////////////////////////////////////
int NetworkObjectClass::Get_Clientside_Update_Frequency(void)
{
unsigned long time = TIMEGETTIME();
if (time - ClientsideUpdateFrequencySampleStartTime > CLIENT_SIDE_UPDATE_FREQUENCY_SAMPLE_PERIOD) {
// Say 10 seconds if we don't know.
int rate = 10000;
if (ClientsideUpdateFrequencySampleCount) {
rate = (time - ClientsideUpdateFrequencySampleStartTime) / ClientsideUpdateFrequencySampleCount;
ClientsideUpdateFrequencySampleStartTime = time;
ClientsideUpdateFrequencySampleCount = 0;
}
ClientsideUpdateRate = rate;
}
return(ClientsideUpdateRate);
}
#ifdef WWDEBUG
void NetworkObjectClass::Set_Created_By_Packet_ID (int id)
{
CreatedByPacketID = id;
}
#endif //WWDEBUG

352
Code/wwnet/networkobject.h Normal file
View File

@@ -0,0 +1,352 @@
/*
** 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/wwnet/networkobject.h $*
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 1/15/02 2:03p $*
* *
* $Revision:: 34 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef __NETWORKOBJECT_H
#define __NETWORKOBJECT_H
#include "wwpacket.h"
enum PACKET_TIER_ENUM
{
PACKET_TIER_CREATION,
PACKET_TIER_RARE,
PACKET_TIER_OCCASIONAL,
PACKET_TIER_FREQUENT,
PACKET_TIER_COUNT
};
////////////////////////////////////////////////////////////////
// Forward delcarations
////////////////////////////////////////////////////////////////
class BitStreamClass;
////////////////////////////////////////////////////////////////
//
// NetworkObjectClass
//
// Base class that all objects which need to transmit state
// updates across the network are derived from.
//
////////////////////////////////////////////////////////////////
class NetworkObjectClass
{
public:
////////////////////////////////////////////////////////////////
// Public constants
////////////////////////////////////////////////////////////////
typedef enum
{
BIT_FREQUENT = 0x01,
BIT_OCCASIONAL = 0x02 | BIT_FREQUENT,
BIT_RARE = 0x04 | BIT_OCCASIONAL,
BIT_CREATION = 0x08 | BIT_RARE,
} DIRTY_BIT;
enum
{
MAX_CLIENT_COUNT = 128
};
////////////////////////////////////////////////////////////////
// Public constructors/destructors
////////////////////////////////////////////////////////////////
NetworkObjectClass (void);
virtual ~NetworkObjectClass (void);
////////////////////////////////////////////////////////////////
// Public methods
////////////////////////////////////////////////////////////////
//
// ID support
//
int Get_Network_ID (void) const { return NetworkID; }
void Set_Network_ID (int id);
#ifdef WWDEBUG
int Get_Created_By_Packet_ID (void) const { return CreatedByPacketID; }
void Set_Created_By_Packet_ID (int id);
#endif //WWDEBUG
//
// Class ID support
//
virtual uint32 Get_Network_Class_ID (void) const { return 0; }
//
// Server-to-client data importing/exporting
//
virtual void Import_Creation (BitStreamClass &packet) {}
virtual void Import_Rare (BitStreamClass &packet) {}
virtual void Import_Occasional (BitStreamClass &packet) {}
virtual void Import_Frequent (BitStreamClass &packet) {}
virtual void Export_Creation (BitStreamClass &packet) {}
virtual void Export_Rare (BitStreamClass &packet) {}
virtual void Export_Occasional (BitStreamClass &packet) {}
virtual void Export_Frequent (BitStreamClass &packet) {}
//
// Timestep support
//
virtual void Network_Think (void) {}
//
// Delete support.
// Override Delete in the subclass if you have a destructor there.
//
bool Is_Delete_Pending (void) { return IsDeletePending; }
virtual void Set_Delete_Pending (void);
virtual void Delete (void) = 0;
//
// Record application packet type
//
void Set_App_Packet_Type (BYTE type) { AppPacketType = type; }
BYTE Get_App_Packet_Type (void) { return AppPacketType; }
//
// Dirty bit support
//
virtual void Set_Object_Dirty_Bit (DIRTY_BIT dirty_bit, bool onoff);
virtual void Set_Object_Dirty_Bit (int client_id, DIRTY_BIT dirty_bit, bool onoff);
virtual void Clear_Object_Dirty_Bits (void);
virtual bool Get_Object_Dirty_Bit (int client_id, DIRTY_BIT dirty_bit);
virtual BYTE Get_Object_Dirty_Bits (int client_id);
virtual void Set_Object_Dirty_Bits (int client_id, BYTE bits);
virtual bool Is_Client_Dirty (int client_id);
inline bool Get_Object_Dirty_Bit_2 (int client_id, DIRTY_BIT dirty_bit);
inline BYTE Get_Object_Dirty_Bits_2 (int client_id);
//
// Filtering support
//
virtual int Get_Vis_ID (void) { return -1; }
virtual bool Get_World_Position (Vector3 &pos) const { return false; }
virtual float Get_Filter_Distance(void) const { return 10000.0f; }
//
// Client-side update tracking
//
void Reset_Client_Hint_Count(int client_id);
void Increment_Client_Hint_Count(int client_id);
void Hint_To_All_Clients(void);
BYTE Get_Client_Hint_Count(int client_id);
inline BYTE Get_Client_Hint_Count_2(int client_id);
void Reset_Import_State_Count (void) { ImportStateCount = 0; }
void Increment_Import_State_Count (void) { ImportStateCount ++; }
int Get_Import_State_Count (void) { return ImportStateCount; }
void Reset_Last_Clientside_Update_Time (void);
void Set_Last_Clientside_Update_Time (ULONG time);
ULONG Get_Last_Clientside_Update_Time (void) { return LastClientsideUpdateTime; }
int Get_Clientside_Update_Frequency(void);
//
// Ownership
//
bool Belongs_To_Client(int client_id);
//
// Per client update functions.
//
unsigned char Get_Frequent_Update_Export_Size(void) {return(FrequentExportPacketSize);}
void Set_Frequent_Update_Export_Size(unsigned char size) {FrequentExportPacketSize = size;}
unsigned long Get_Last_Update_Time(int client_id);
unsigned short Get_Update_Rate(int client_id);
void Set_Last_Update_Time(int client_id, unsigned long time);
void Set_Update_Rate(int client_id, unsigned short rate);
//
// Diagnostics
//
virtual bool Is_Tagged(void) { return false; }
virtual void Get_Description(StringClass & description) {}
void Set_Unreliable_Override(bool flag) {UnreliableOverride = flag;}
bool Get_Unreliable_Override(void) {return UnreliableOverride;}
//
// Static methods
//
static void Set_Is_Server(bool flag) { IsServer = flag; }
void Set_Cached_Priority (float priority);
virtual float Get_Cached_Priority (void) const { return CachedPriority; }
inline void Set_Cached_Priority_2 (int client_id, float priority);
inline float Get_Cached_Priority_2 (int client_id) const;
void Set_Last_Object_Id_I_Damaged(int id) {LastObjectIdIDamaged = id;}
int Get_Last_Object_Id_I_Damaged(void) const {return LastObjectIdIDamaged;}
void Set_Last_Object_Id_I_Got_Damaged_By(int id) {LastObjectIdIGotDamagedBy = id;}
int Get_Last_Object_Id_I_Got_Damaged_By(void) const {return LastObjectIdIGotDamagedBy;}
private:
////////////////////////////////////////////////////////////////
// Private constants
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
// Private member data
////////////////////////////////////////////////////////////////
int NetworkID;
#ifdef WWDEBUG
int CreatedByPacketID;
#endif //WWDEBUG
//
// Per client update information. Bandwidth will be allocated per object, per client.
//
struct PerClientUpdateInfoStruct {
unsigned long LastUpdateTime;
unsigned short UpdateRate;
BYTE ClientHintCount;
} UpdateInfo [MAX_CLIENT_COUNT];
BYTE ClientStatus[MAX_CLIENT_COUNT];
int ImportStateCount;
ULONG LastClientsideUpdateTime;
ULONG ClientsideUpdateFrequencySampleStartTime;
int ClientsideUpdateFrequencySampleCount;
int ClientsideUpdateRate;
bool IsDeletePending;
BYTE AppPacketType;
int LastObjectIdIDamaged;
int LastObjectIdIGotDamagedBy;
//
// The size of this objects FREQUENT tier export. Used as a starting point for bandwidth calculation.
// It better not be exporting more than 255 bytes!
//
unsigned char FrequentExportPacketSize;
float CachedPriority;
float CachedPriority_2[MAX_CLIENT_COUNT];
bool UnreliableOverride;
static bool IsServer;
};
#endif // __NETWORKOBJECT_H
////////////////////////////////////////////////////////////////
//
// Set_Cached_Priority
//
////////////////////////////////////////////////////////////////
inline void NetworkObjectClass::Set_Cached_Priority_2(int client_id, float priority)
{
CachedPriority_2[client_id] = priority;
}
////////////////////////////////////////////////////////////////
//
// Set_Cached_Priority
//
////////////////////////////////////////////////////////////////
inline float NetworkObjectClass::Get_Cached_Priority_2(int client_id) const
{
return(CachedPriority_2[client_id]);
}
////////////////////////////////////////////////////////////////
//
// Get_Object_Dirty_Bit
//
////////////////////////////////////////////////////////////////
inline bool NetworkObjectClass::Get_Object_Dirty_Bit_2 (int client_id, DIRTY_BIT dirty_bit)
{
return ((ClientStatus[client_id] & dirty_bit) == dirty_bit);
}
////////////////////////////////////////////////////////////////
//
// Get_Object_Dirty_Bits
//
////////////////////////////////////////////////////////////////
inline BYTE NetworkObjectClass::Get_Object_Dirty_Bits_2 (int client_id)
{
return ClientStatus[client_id];
}
////////////////////////////////////////////////////////////////
//
// Get_Client_Hint_Count
//
////////////////////////////////////////////////////////////////
inline BYTE NetworkObjectClass::Get_Client_Hint_Count_2(int client_id)
{
return UpdateInfo[client_id].ClientHintCount;
}
//virtual void Clear_Object_Dirty_Bits (int client_id);
//static void Set_Random_Float (float random_float);
//static float RandomFloat;
//float CachedPriority;
//static float MaxDistance;
//virtual float Compute_Object_Priority (int client_id, const Vector3 &client_pos);
//void Set_Cached_Priority (float priority);
//virtual float Get_Object_Distance (const Vector3 &client_pos);
//virtual float Get_Cached_Priority (void) const { return CachedPriority; }
//static void Set_Max_Distance (float distance) { MaxDistance = distance; }
//static float MaxDistance;

View File

@@ -0,0 +1,64 @@
/*
** 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 : WWSaveLoad *
* *
* $Archive:: /Commando/Code/wwnet/networkobjectfactory.cpp $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 5/18/01 2:34p $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "networkobjectfactory.h"
#include "networkobjectfactorymgr.h"
/////////////////////////////////////////////////////////
//
// NetworkObjectFactoryClass
//
/////////////////////////////////////////////////////////
NetworkObjectFactoryClass::NetworkObjectFactoryClass (void) :
NextFactory (0),
PrevFactory (0)
{
NetworkObjectFactoryMgrClass::Register_Factory (this);
return ;
}
/////////////////////////////////////////////////////////
//
// ~NetworkObjectFactoryClass
//
/////////////////////////////////////////////////////////
NetworkObjectFactoryClass::~NetworkObjectFactoryClass (void)
{
NetworkObjectFactoryMgrClass::Unregister_Factory (this);
return ;
}

View File

@@ -0,0 +1,138 @@
/*
** 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 : WWSaveLoad *
* *
* $Archive:: /Commando/Code/wwnet/networkobjectfactory.h $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 5/18/01 3:49p $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef __NETWORK_OBJECT_FACTORY_H
#define __NETWORK_OBJECT_FACTORY_H
#include "always.h"
#include "bittype.h"
#include "wwpacket.h"
//////////////////////////////////////////////////////////////////////////////////
// Forward declarations
//////////////////////////////////////////////////////////////////////////////////
class NetworkObjectClass;
//////////////////////////////////////////////////////////////////////////////////
//
// NetworkObjectFactoryClass
//
// These factories act as virtual constructors for object network objects. They
// are responsible for creating new network objects for a particular class of objects.
//
//////////////////////////////////////////////////////////////////////////////////
class NetworkObjectFactoryClass
{
public:
//////////////////////////////////////////////////////////////
// Public constructors/destructors
//////////////////////////////////////////////////////////////
NetworkObjectFactoryClass (void);
virtual ~NetworkObjectFactoryClass (void);
//////////////////////////////////////////////////////////////
// Public methods
//////////////////////////////////////////////////////////////
virtual NetworkObjectClass * Create (cPacket &packet) const = 0;
virtual void Prep_Packet (NetworkObjectClass *object, cPacket &packet) const {};
virtual uint32 Get_Class_ID (void) const = 0;
protected:
//////////////////////////////////////////////////////////////
// Protected member data
//////////////////////////////////////////////////////////////
NetworkObjectFactoryClass * NextFactory;
NetworkObjectFactoryClass * PrevFactory;
//////////////////////////////////////////////////////////////
// Friends
//////////////////////////////////////////////////////////////
friend class NetworkObjectFactoryMgrClass;
};
//////////////////////////////////////////////////////////////////////////////////
//
// SimpleNetworkObjectFactoryClass
//
// Template class to automate the creation of simple network object factories.
//
//////////////////////////////////////////////////////////////////////////////////
template<class T, int class_id>
class SimpleNetworkObjectFactoryClass : public NetworkObjectFactoryClass
{
public:
//////////////////////////////////////////////////////////////
// Public constructors/destructors
//////////////////////////////////////////////////////////////
SimpleNetworkObjectFactoryClass (void) {}
//////////////////////////////////////////////////////////////
// Public methods
//////////////////////////////////////////////////////////////
virtual NetworkObjectClass * Create (cPacket &packet) const;
virtual uint32 Get_Class_ID (void) const;
};
template<class T, int class_id>
inline NetworkObjectClass *
SimpleNetworkObjectFactoryClass<T, class_id>::Create (cPacket & /*packet*/) const
{
return new T;
}
template<class T, int class_id>
inline uint32
SimpleNetworkObjectFactoryClass<T, class_id>::Get_Class_ID (void) const
{
return class_id;
}
#define DECLARE_NETWORKOBJECT_FACTORY(_class, _id) \
SimpleNetworkObjectFactoryClass<_class, _id> _class ## Factory
#endif //__NETWORK_OBJECT_FACTORY_H

View File

@@ -0,0 +1,201 @@
/*
** 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 : WWSaveLoad *
* *
* $Archive:: /Commando/Code/wwnet/networkobjectfactorymgr.cpp $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 5/18/01 2:35p $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "networkobjectfactorymgr.h"
#include "networkobjectfactory.h"
#include "wwdebug.h"
#include <string.h>
////////////////////////////////////////////////////////////////////////////
// Static member initialization
////////////////////////////////////////////////////////////////////////////
NetworkObjectFactoryClass *NetworkObjectFactoryMgrClass::_FactoryListHead = 0;
////////////////////////////////////////////////////////////////////////////
//
// Find_Factory
//
////////////////////////////////////////////////////////////////////////////
NetworkObjectFactoryClass *
NetworkObjectFactoryMgrClass::Find_Factory (uint32 class_id)
{
NetworkObjectFactoryClass *factory = 0;
//
// Loop through all the factories and see if we can
// find the one who owns the corresponding class-id.
//
for ( NetworkObjectFactoryClass *curr_factory = _FactoryListHead;
(factory == 0) && (curr_factory != 0);
curr_factory = curr_factory->NextFactory) {
//
// Is this the factory we were looking for?
//
if (curr_factory->Get_Class_ID () == class_id) {
factory = curr_factory;
}
}
return factory;
}
////////////////////////////////////////////////////////////////////////////
//
// Get_First
//
////////////////////////////////////////////////////////////////////////////
NetworkObjectFactoryClass *
NetworkObjectFactoryMgrClass::Get_First (void)
{
return _FactoryListHead;
}
////////////////////////////////////////////////////////////////////////////
//
// Get_Next
//
////////////////////////////////////////////////////////////////////////////
NetworkObjectFactoryClass *
NetworkObjectFactoryMgrClass::Get_Next (NetworkObjectFactoryClass *curr_factory)
{
NetworkObjectFactoryClass *factory = 0;
//
// Simply return the next factory in the chain
//
if (curr_factory != NULL) {
factory = curr_factory->NextFactory;
}
return factory;
}
////////////////////////////////////////////////////////////////////////////
//
// Register_Factory
//
////////////////////////////////////////////////////////////////////////////
void
NetworkObjectFactoryMgrClass::Register_Factory (NetworkObjectFactoryClass *factory)
{
WWASSERT (factory->NextFactory == 0);
WWASSERT (factory->PrevFactory == 0);
Link_Factory (factory);
return ;
}
////////////////////////////////////////////////////////////////////////////
//
// Unregister_Factory
//
////////////////////////////////////////////////////////////////////////////
void
NetworkObjectFactoryMgrClass::Unregister_Factory (NetworkObjectFactoryClass *factory)
{
WWASSERT (factory != 0);
Unlink_Factory (factory);
return ;
}
////////////////////////////////////////////////////////////////////////////
//
// Link_Factory
//
////////////////////////////////////////////////////////////////////////////
void
NetworkObjectFactoryMgrClass::Link_Factory (NetworkObjectFactoryClass *factory)
{
WWASSERT (factory->NextFactory == 0);
WWASSERT (factory->PrevFactory == 0);
// Adding this factory in front of the current head of the list
factory->NextFactory = _FactoryListHead;
// If the list wasn't empty, link the next factory back to this factory
if (factory->NextFactory != 0) {
factory->NextFactory->PrevFactory = factory;
}
// Point the head of the list at this factory now
_FactoryListHead = factory;
return ;
}
////////////////////////////////////////////////////////////////////////////
//
// Unlink_Factory
//
////////////////////////////////////////////////////////////////////////////
void
NetworkObjectFactoryMgrClass::Unlink_Factory (NetworkObjectFactoryClass *factory)
{
WWASSERT(factory != 0);
// Handle the factory's prev pointer:
if (factory->PrevFactory == 0) {
// this factory is the head
WWASSERT (_FactoryListHead == factory);
_FactoryListHead = factory->NextFactory;
} else {
// link it's prev with it's next
factory->PrevFactory->NextFactory = factory->NextFactory;
}
// Handle the factory's next pointer if its not at the end of the list:
if (factory->NextFactory != 0) {
factory->NextFactory->PrevFactory = factory->PrevFactory;
}
// factory is now un-linked
factory->NextFactory = 0;
factory->PrevFactory = 0;
return ;
}

View File

@@ -0,0 +1,87 @@
/*
** 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 : WWSaveLoad *
* *
* $Archive:: /Commando/Code/wwnet/networkobjectfactorymgr.h $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 5/18/01 2:35p $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef __NETWORK_OBJECT_FACTORY_MGR_H
#define __NETWORK_OBJECT_FACTORY_MGR_H
#include "always.h"
#include "bittype.h"
//////////////////////////////////////////////////////////////////////////////////
// Forward declarations
//////////////////////////////////////////////////////////////////////////////////
class NetworkObjectFactoryClass;
//////////////////////////////////////////////////////////////////////////////////
//
// NetworkObjectFactoryMgrClass
//
//////////////////////////////////////////////////////////////////////////////////
class NetworkObjectFactoryMgrClass
{
public:
/////////////////////////////////////////////////////////////////////
// Public methods
/////////////////////////////////////////////////////////////////////
static NetworkObjectFactoryClass * Find_Factory (uint32 class_id);
static void Register_Factory (NetworkObjectFactoryClass *factory);
static void Unregister_Factory (NetworkObjectFactoryClass *factory);
// Factory enumeration
static NetworkObjectFactoryClass * Get_First (void);
static NetworkObjectFactoryClass * Get_Next (NetworkObjectFactoryClass *current);
private:
/////////////////////////////////////////////////////////////////////
// Private methods
/////////////////////////////////////////////////////////////////////
static void Link_Factory (NetworkObjectFactoryClass *factory);
static void Unlink_Factory (NetworkObjectFactoryClass *factory);
/////////////////////////////////////////////////////////////////////
// Static member data
/////////////////////////////////////////////////////////////////////
static NetworkObjectFactoryClass * _FactoryListHead;
};
#endif //__NETWORK_OBJECT_FACTORY_MGR_H

View File

@@ -0,0 +1,437 @@
/*
** 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/wwnet/networkobjectmgr.cpp $*
* *
* $Author:: Tom_s $*
* *
* $Modtime:: 1/07/02 10:30a $*
* *
* $Revision:: 20 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "networkobjectmgr.h"
#include "networkobject.h"
////////////////////////////////////////////////////////////////
// Static member initialization
////////////////////////////////////////////////////////////////
NetworkObjectMgrClass::OBJECT_LIST NetworkObjectMgrClass::_ObjectList;
NetworkObjectMgrClass::OBJECT_LIST NetworkObjectMgrClass::_DeletePendingList;
int NetworkObjectMgrClass::_NewDynamicID = NETID_DYNAMIC_OBJECT_MIN;
int NetworkObjectMgrClass::_NewClientID = 0;
bool NetworkObjectMgrClass::_IsLevelLoading = false;
////////////////////////////////////////////////////////////////
//
// Register_Object
//
////////////////////////////////////////////////////////////////
void
NetworkObjectMgrClass::Register_Object (NetworkObjectClass *object)
{
int object_id = object->Get_Network_ID ();
if (object_id != 0) {
//
// Check to ensure the object isn't already in the list
//
int index = 0;
if (Find_Object (object_id, &index) == false) {
//
// Insert the object into the list
//
_ObjectList.Insert (index, object);
}
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Unregister_Object
//
////////////////////////////////////////////////////////////////
void
NetworkObjectMgrClass::Unregister_Object (NetworkObjectClass *object)
{
int object_id = object->Get_Network_ID ();
if (object_id != 0) {
//
// Try to find the object in the list
//
int index = 0;
if (Find_Object (object_id, &index)) {
//
// Remove the object from the list
//
_ObjectList.Delete (index);
}
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Find_Object
//
////////////////////////////////////////////////////////////////
NetworkObjectClass *
NetworkObjectMgrClass::Find_Object (int object_id)
{
NetworkObjectClass *object = NULL;
//
// Lookup the object in the sorted list
//
int index = 0;
if (Find_Object (object_id, &index)) {
object = _ObjectList[index];
}
return object;
}
////////////////////////////////////////////////////////////////
//
// Set_New_Dynamic_ID
//
////////////////////////////////////////////////////////////////
void
NetworkObjectMgrClass::Set_New_Dynamic_ID (int id)
{
WWASSERT(id >= NETID_DYNAMIC_OBJECT_MIN && id <= NETID_DYNAMIC_OBJECT_MAX);
_NewDynamicID = id;
}
////////////////////////////////////////////////////////////////
//
// Get_New_Dynamic_ID
//
////////////////////////////////////////////////////////////////
int
NetworkObjectMgrClass::Get_New_Dynamic_ID (void)
{
WWASSERT(_NewDynamicID >= NETID_DYNAMIC_OBJECT_MIN && _NewDynamicID < NETID_DYNAMIC_OBJECT_MAX);
/*
//TSS091001
NetworkObjectClass * p_object = Find_Object(_NewDynamicID);
//WWASSERT(p_object == NULL);
if (p_object != NULL) {
int iii;
iii = 111;
}
/**/
//TSS091201
NetworkObjectClass * p_object = Find_Object(_NewDynamicID);
while (p_object != NULL) {
/*
WWDEBUG_SAY(("NetworkObjectMgrClass::Get_New_Dynamic_ID :skipping id %d (already in use)\n",
_NewDynamicID));
*/
_NewDynamicID++;
p_object = Find_Object(_NewDynamicID);
}
return _NewDynamicID++;
}
////////////////////////////////////////////////////////////////
//
// Get_Current_Dynamic_ID
//
////////////////////////////////////////////////////////////////
int
NetworkObjectMgrClass::Get_Current_Dynamic_ID (void)
{
WWASSERT(_NewDynamicID >= NETID_DYNAMIC_OBJECT_MIN && _NewDynamicID <= NETID_DYNAMIC_OBJECT_MAX);
return _NewDynamicID;
}
////////////////////////////////////////////////////////////////
//
// Init_New_Client_ID
//
////////////////////////////////////////////////////////////////
void
NetworkObjectMgrClass::Init_New_Client_ID (int client_id)
{
WWASSERT(client_id > 0);
_NewClientID = NETID_CLIENT_OBJECT_MIN + (client_id - 1) * 100000;
}
////////////////////////////////////////////////////////////////
//
// Get_New_Client_ID
//
////////////////////////////////////////////////////////////////
int
NetworkObjectMgrClass::Get_New_Client_ID (void)
{
WWASSERT(_NewClientID >= NETID_CLIENT_OBJECT_MIN && _NewClientID < NETID_CLIENT_OBJECT_MAX);
return _NewClientID++;
}
////////////////////////////////////////////////////////////////
//
// Find_Object
//
////////////////////////////////////////////////////////////////
bool
NetworkObjectMgrClass::Find_Object (int id_to_find, int *index)
{
WWASSERT(index != NULL);
bool found = false;
(*index) = 0;
int min_index = 0;
int max_index = _ObjectList.Count () - 1;
//
// Keep looping until we've closed the window of possiblity
//
bool keep_going = (max_index >= min_index);
while (keep_going) {
//
// Calculate what slot we are currently looking at
//
int curr_index = min_index + ((max_index - min_index) / 2);
int curr_id = _ObjectList[curr_index]->Get_Network_ID ();
//
// Did we find the right slot?
//
if (id_to_find == curr_id) {
(*index) = curr_index;
keep_going = false;
found = true;
} else {
//
// Stop if we've narrowed the window to one entry
//
keep_going = (max_index > min_index);
//
// Move the window to the appropriate side
// of the test index.
//
if (id_to_find < curr_id) {
max_index = curr_index - 1;
(*index) = curr_index;
} else {
min_index = curr_index + 1;
(*index) = curr_index + 1;
}
}
}
return found;
}
////////////////////////////////////////////////////////////////
//
// Think
//
////////////////////////////////////////////////////////////////
void
NetworkObjectMgrClass::Think (void)
{
//
// Simply let each object think
//
for (int index = 0; index < _ObjectList.Count (); index ++) {
WWASSERT(_ObjectList[index] != NULL);
_ObjectList[index]->Network_Think ();
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Set_All_Delete_Pending
//
// This is for cleanup only.
//
////////////////////////////////////////////////////////////////
void
NetworkObjectMgrClass::Set_All_Delete_Pending (void)
{
WWDEBUG_SAY(("NetworkObjectMgrClass::Set_All_Delete_Pending\n"));
//
// Mark all netobjects as delete pending
//
for (int index = 0; index < _ObjectList.Count (); index ++) {
_ObjectList[index]->Set_Delete_Pending();
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Delete_Pending
//
////////////////////////////////////////////////////////////////
void
NetworkObjectMgrClass::Delete_Pending (void)
{
if (_IsLevelLoading) {
return ;
}
//WWDEBUG_SAY(("NetworkObjectMgrClass::Delete_Pending\n"));
//
// Delete each object that is pending...
//
for (int index = 0; index < _DeletePendingList.Count (); index ++) {
WWASSERT(_DeletePendingList[index] != NULL);
if (_DeletePendingList[index]->Is_Delete_Pending ()) {
_DeletePendingList[index]->Delete ();
}
}
// if (_DeletePendingList.Count()) {
// _DeletePendingList.Delete_All ();
// }
_DeletePendingList.Reset_Active(); // No need to resize the vector back to zero...
return ;
}
////////////////////////////////////////////////////////////////
//
// Delete_Client_Objects
//
////////////////////////////////////////////////////////////////
void
NetworkObjectMgrClass::Delete_Client_Objects (int client_id)
{
WWASSERT(client_id > 0);
//
// Delete each object that belongs to the given client
//
for (int index = 0; index < _ObjectList.Count (); index ++) {
WWASSERT(_ObjectList[index] != NULL);
if (_ObjectList[index]->Belongs_To_Client (client_id)) {
//TSS092301 _ObjectList[index]->Delete ();
_ObjectList[index]->Set_Delete_Pending();
}
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Restore_Dirty_Bits
//
////////////////////////////////////////////////////////////////
void
NetworkObjectMgrClass::Restore_Dirty_Bits (int client_id)
{
WWASSERT(client_id > 0);
//
// If a guy quits, we need to restore the dirty bits on each object so that
// if he rejoins he will be told about stuff again.
// For now I am going to use the topmost client id...
//
for (int index = 0; index < _ObjectList.Count (); index ++) {
NetworkObjectClass * p_object = _ObjectList[index];
WWASSERT(p_object != NULL);
BYTE generic_bits = p_object->Get_Object_Dirty_Bits(NetworkObjectClass::MAX_CLIENT_COUNT - 1);//TSS2001e
p_object->Set_Object_Dirty_Bits(client_id, generic_bits);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Register_Object_For_Deletion
//
////////////////////////////////////////////////////////////////
void
NetworkObjectMgrClass::Register_Object_For_Deletion (NetworkObjectClass *object)
{
if (_DeletePendingList.ID (object) == -1) {
_DeletePendingList.Add (object);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Reset_Import_State_Counts
//
////////////////////////////////////////////////////////////////
void
NetworkObjectMgrClass::Reset_Import_State_Counts(void)
{
for (int index = 0; index < _ObjectList.Count (); index ++) {
NetworkObjectClass * p_object = _ObjectList[index];
WWASSERT(p_object != NULL);
//
// Reset its import state count
//
p_object->Reset_Import_State_Count();
}
}

View File

@@ -0,0 +1,160 @@
/*
** 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/wwnet/networkobjectmgr.h $*
* *
* $Author:: Tom_s $*
* *
* $Modtime:: 11/24/01 10:47a $*
* *
* $Revision:: 14 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef __NETWORKOBJECTMGR_H
#define __NETWORKOBJECTMGR_H
#include "vector.h"
////////////////////////////////////////////////////////////////
// Forward declarations
////////////////////////////////////////////////////////////////
class NetworkObjectClass;
////////////////////////////////////////////////////////////////
// ID Ranges
////////////////////////////////////////////////////////////////
enum
{
NETID_DYNAMIC_OBJECT_MIN = 1500000000, // 600M dynamic
NETID_DYNAMIC_OBJECT_MAX = 2100000000,
NETID_STATIC_OBJECT_MIN = 2100000001, // 10M static
NETID_STATIC_OBJECT_MAX = 2110000000,
NETID_CLIENT_OBJECT_MIN = 2110000001, // 100K per client, 128 clients
//NETID_CLIENT_OBJECT_MAX = 2113100000,
NETID_CLIENT_OBJECT_MAX = 2122800001,
//
// Note: INT_MAX = 2147483647
//
};
////////////////////////////////////////////////////////////////
//
// NetworkObjectMgrClass
//
// Container for network objects. Used for sending and receiving
// object state updates.
//
////////////////////////////////////////////////////////////////
class NetworkObjectMgrClass
{
public:
////////////////////////////////////////////////////////////////
// Public methods
////////////////////////////////////////////////////////////////
//
// Object registration
//
static void Register_Object (NetworkObjectClass *object);
static void Unregister_Object (NetworkObjectClass *object);
//
// Delete registration support
//
static void Register_Object_For_Deletion (NetworkObjectClass *object);
static void Set_Is_Level_Loading (bool onoff) { _IsLevelLoading = onoff; }
//
// Timestep
//
static void Think (void);
//
// Deletion support
//
static void Set_All_Delete_Pending (void);//TSS092301
static void Delete_Pending (void);
static void Delete_Client_Objects (int client_id);
static void Restore_Dirty_Bits (int client_id);
//
// Object enumeration
//
static int Get_Object_Count (void) { return _ObjectList.Count (); }
static NetworkObjectClass * Get_Object (int index) { return _ObjectList[index]; }
static int Get_Pending_Object_Count (void) { return _DeletePendingList.Count (); }
//
// Object lookup
//
static NetworkObjectClass * Find_Object (int object_id);
//
// ID access
//
static int Get_New_Dynamic_ID (void);
static int Get_Current_Dynamic_ID (void);
static void Set_New_Dynamic_ID (int id);
static void Init_New_Client_ID (int client_id);
static int Get_New_Client_ID (void);
static void Reset_Import_State_Counts(void);
private:
////////////////////////////////////////////////////////////////
// Private methods
////////////////////////////////////////////////////////////////
static bool Find_Object (int id_to_find, int *index);
////////////////////////////////////////////////////////////////
// Private tyepdefs
////////////////////////////////////////////////////////////////
typedef DynamicVectorClass<NetworkObjectClass *> OBJECT_LIST;
////////////////////////////////////////////////////////////////
// Private member data
////////////////////////////////////////////////////////////////
static OBJECT_LIST _ObjectList;
static OBJECT_LIST _DeletePendingList;
static int _NewDynamicID;
static int _NewClientID;
static bool _IsLevelLoading;
};
#endif // __NETWORKOBJECTMGR_H

1814
Code/wwnet/packetmgr.cpp Normal file

File diff suppressed because it is too large Load Diff

359
Code/wwnet/packetmgr.h Normal file
View File

@@ -0,0 +1,359 @@
/*
** 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 : Command & Conquer *
* *
* $Archive:: /Commando/Code/wwnet/packetmgr.h $*
* *
* $Author:: Bhayes $*
* *
* $Modtime:: 2/18/02 10:48p $*
* *
* $Revision:: 16 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#pragma once
#ifndef _PACKETMGR_H
#define _PACKETMGR_H
#include "mutex.h"
#include "wwdebug.h"
#include "vector.h"
#include <winsock.h> // for SOCKET
#ifdef WWASSERT
#ifndef pm_assert
#define pm_assert WWASSERT
#endif //pm_assert
#else //WWASSERT
#define pm_assert assert
#endif //WWASSERT
#pragma pack(push)
#pragma pack(1)
#define WRAPPER_CRC
/*
** Header used at the beginning of a packet to identify the number of packets packed in this packet IYSWIM.
*/
struct PacketPackHeaderStruct {
/*
** Number of same length packets in this block.
*/
unsigned short NumPackets : 5;
/*
** Length of the packets.
*/
unsigned short PacketSize : 10;
/*
** More packets of a different length after these ones?
*/
unsigned short MorePackets : 1;
};
/*
** Header used at the beginning of every delta packet.
*/
struct PacketDeltaHeaderStruct {
/*
** Chunk pack bits are present.
*/
unsigned char ChunkPack : 1;
/*
** Byte pack bits are present.
*/
unsigned char BytePack : 1;
};
#pragma pack(pop)
/*
** Minimum MTU allowable on the internet is 576. IP Header is 20 bytes. UDP header is 8 bytes
** So our max packet size is 576 - 28 = 548
*/
#ifdef WRAPPER_CRC
#define PACKET_MANAGER_MTU 540
#else
#define PACKET_MANAGER_MTU 544
#endif //WRAPPER_CRC
#define PACKET_MANAGER_BUFFERS 256
#define PACKET_MANAGER_BUFFERS_WHEN_SERVER (32 * 32)
#define PACKET_MANAGER_RECEIVE_BUFFERS 128
#define PACKET_MANAGER_RECEIVE_BUFFERS_AS_SERVER (64 * 32)
#define PACKET_MANAGER_MAX_PACKETS 31
#define UDP_HEADER_SIZE 28
/*
** This class intercepts packets at the lowest level and applies delta based compression and packet combining to reduce
** low level bandwidth while being transparent to higher levels in the application.
**
** All packets are prefixed by a PacketHeaderStruct which is 2 bytes long. So sending a single packet incurs an extra
** overhead of two bytes. Normally, however, multiple small packets can be combined into a single large one which saves
** the IP and UDP overhead invlolved in sending the subsequent packets.
**
*/
class PacketManagerClass;
class PacketManagerClass
{
public:
/*
** Constructor.
*/
PacketManagerClass(void);
~PacketManagerClass(void);
/*
** Application interface.
*/
bool Take_Packet(unsigned char *packet, int packet_len, unsigned char *dest_ip, unsigned short dest_port, SOCKET socket);
int Get_Packet(SOCKET socket, unsigned char *packet_buffer, int packet_buffer_size, unsigned char *ip_address, unsigned short &port);
void Flush(bool forced = false);
void Set_Is_Server(bool is_server);
/*
** Bandwidth Management.
*/
void Reset_Stats(void);
void Update_Stats(bool forced = false);
unsigned long Get_Total_Raw_Bandwidth_In(void);
unsigned long Get_Total_Raw_Bandwidth_Out(void);
unsigned long Get_Total_Compressed_Bandwidth_In(void);
unsigned long Get_Total_Compressed_Bandwidth_Out(void);
unsigned long Get_Raw_Bandwidth_In(SOCKADDR_IN *address);
unsigned long Get_Raw_Bandwidth_Out(SOCKADDR_IN *address);
unsigned long Get_Compressed_Bandwidth_In(SOCKADDR_IN *address);
unsigned long Get_Compressed_Bandwidth_Out(SOCKADDR_IN *address);
unsigned long Get_Raw_Bytes_Out(SOCKADDR_IN *address);
void Set_Stats_Sampling_Frequency_Delay(unsigned long time_ms);
unsigned long Get_Stats_Sampling_Frequency_Delay(void) {return(StatsFrequency);};
/*
** Class configuration.
*/
void Set_Flush_Frequency(unsigned long freq) {FlushFrequency = freq;};
bool Toggle_Allow_Deltas(void) {
AllowDeltas = AllowDeltas ? false : true;
return(AllowDeltas);
};
bool Toggle_Allow_Combos(void) {
AllowCombos = AllowCombos ? false : true;
return(AllowCombos);
};
//
// TSS added 09/25/01
//
unsigned long Get_Flush_Frequency(void) {return FlushFrequency;}
bool Get_Allow_Deltas(void) {return AllowDeltas;}
bool Get_Allow_Combos(void) {return AllowCombos;}
void Disable_Optimizations(void);
enum ErrorStateEnum {
STATE_OK,
STATE_WS_BUFFERS_FULL,
};
ErrorStateEnum Get_Error_State(void);
private:
/*
** Delta compression.
*/
static int Build_Delta_Packet_Patch(unsigned char *base_packet, unsigned char *add_packet, unsigned char *delta_packet, int base_packet_size, int add_packet_size);
static int Reconstruct_From_Delta(unsigned char *base_packet, unsigned char *reconstructed_packet, unsigned char *delta_packet, int base_packet_size, int &delta_size);
bool Break_Packet(unsigned char *packet, int packet_len, unsigned char *ip_address, unsigned short port);
/*
** Bit packing.
*/
static inline int Add_Bit(bool bit, unsigned char * &bitstream, int &position);
static inline unsigned char Get_Bit(unsigned char * &bitstream, int &position);
/*
** Buffer allocation.
*/
int Get_Next_Free_Buffer_Index(void);
/*
** Error handling.
*/
void Clear_Socket_Error(SOCKET socket);
/*
** Stats management.
*/
struct BandwidthStatsStruct {
unsigned long IPAddress;
unsigned short Port;
unsigned long UncompressedBytesIn;
unsigned long UncompressedBytesOut;
unsigned long CompressedBytesIn;
unsigned long CompressedBytesOut;
unsigned long UncompressedBandwidthIn;
unsigned long UncompressedBandwidthOut;
unsigned long CompressedBandwidthIn;
unsigned long CompressedBandwidthOut;
bool operator == (BandwidthStatsStruct const &stats);
bool operator != (BandwidthStatsStruct const &stats);
};
int Get_Stats_Index(unsigned long ip_address, unsigned short port, bool can_create = true);
void Register_Packet_In(unsigned char *ip_address, unsigned short port, unsigned long compressed_size, unsigned long uncompressed_size);
void Register_Packet_Out(unsigned char *ip_address, unsigned short port, unsigned long compressed_size, unsigned long uncompressed_size);
/*
** Send buffers.
*/
typedef struct tPacketBufferType {
unsigned char Buffer [600];
} PacketBufferType;
class SendBufferClass {
public:
PacketBufferType *PacketBuffer;
unsigned char IPAddress[4];
unsigned short Port;
int PacketLength;
bool PacketReady;
int PacketSendLength;
SOCKET PacketSendSocket;
SendBufferClass(void) {
PacketBuffer = new PacketBufferType;
IPAddress[0] = 0;
IPAddress[1] = 0;
IPAddress[2] = 0;
IPAddress[3] = 0;
Port = 0;
PacketLength = 0;
PacketReady = false;
PacketSendLength = 0;
PacketSendSocket = INVALID_SOCKET;
};
~SendBufferClass(void) {
delete PacketBuffer;
PacketBuffer = NULL;
};
};
int NumSendBuffers;
SendBufferClass *SendBuffers;
//unsigned char *PacketBuffers; //[PACKET_MANAGER_BUFFERS][600];
//unsigned char *IPAddresses; //[PACKET_MANAGER_BUFFERS][4];
//unsigned short *Ports; //[PACKET_MANAGER_BUFFERS];
//int *PacketLengths //[PACKET_MANAGER_BUFFERS];
//bool *PacketReady; //[PACKET_MANAGER_BUFFERS];
//int *PacketSendLength; //[PACKET_MANAGER_BUFFERS];
//SOCKET *PacketSendSockets; //[PACKET_MANAGER_BUFFERS];
int NextPacket;
int NumPackets;
unsigned char BuildPacket[PACKET_MANAGER_MTU];
unsigned char DeltaPacket[PACKET_MANAGER_MTU + 128];
/*
** Receive buffers. Don't need so many since we only process one packet at a time.
*/
class ReceiveBufferClass {
public:
unsigned char ReceiveHoldingBuffer[600];
unsigned long ReceivePacketLength;
};
int NumReceiveBuffers;
ReceiveBufferClass *ReceiveBuffers;
//unsigned char ReceiveHoldingBuffers[PACKET_MANAGER_RECEIVE_BUFFERS][600];
//unsigned long ReceivePacketLengths[PACKET_MANAGER_RECEIVE_BUFFERS];
unsigned char ReceiveIPAddress[4];
unsigned short ReceivePort;
int NumReceivePackets;
int CurrentPacket;
SOCKET ReceiveSocket;
/*
** Send timing.
*/
unsigned long LastSendTime;
unsigned long FlushFrequency;
/*
** Bandwidth measurement.
*/
DynamicVectorClass<BandwidthStatsStruct> BandwidthList;
unsigned long TotalCompressedBandwidthIn;
unsigned long TotalCompressedBandwidthOut;
unsigned long TotalUncompressedBandwidthIn;
unsigned long TotalUncompressedBandwidthOut;
unsigned long StatsFrequency;
unsigned long LastStatsUpdate;
bool ResetStatsIn;
bool ResetStatsOut;
/*
** Debug
*/
bool AllowDeltas;
bool AllowCombos;
/*
** Winsock error handling.
*/
ErrorStateEnum ErrorState;
/*
** Thread safety
*/
CriticalSectionClass CriticalSection;
};
/*
** Single instance of the packet manager.
*/
extern PacketManagerClass PacketManager;
#endif //_PACKETMGR_H

57
Code/wwnet/packettype.h Normal file
View File

@@ -0,0 +1,57 @@
/*
** 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/>.
*/
//
// Filename: packettype.h
// Project: wwnet
// Author: Tom Spencer-Smith
// Date:
// Description:
//
//-----------------------------------------------------------------------------
#if defined(_MSV_VER)
#pragma once
#endif
#ifndef PACKETTYPE_H
#define PACKETTYPE_H
//
// Don't use these at application level
//
enum
{
PACKETTYPE_FIRST,
PACKETTYPE_UNRELIABLE = PACKETTYPE_FIRST,
PACKETTYPE_RELIABLE,
PACKETTYPE_ACK,
PACKETTYPE_KEEPALIVE,
PACKETTYPE_CONNECT_CS,
PACKETTYPE_ACCEPT_SC,
PACKETTYPE_REFUSAL_SC,
PACKETTYPE_FIREWALL_PROBE,
PACKETTYPE_LAST = PACKETTYPE_FIREWALL_PROBE,
PACKETTYPE_COUNT
};
//-----------------------------------------------------------------------------
#endif // PACKETTYPE_H

704
Code/wwnet/rhost.cpp Normal file
View File

@@ -0,0 +1,704 @@
/*
** 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/>.
*/
//
// Filename: rhost.cpp
// Project: wwnet
// Author: Tom Spencer-Smith
// Date: June 1998
// Description: Data about a remote host
//
//-----------------------------------------------------------------------------
#include "rhost.h" // I WANNA BE FIRST!
#include <stdlib.h>
#include <math.h>
#include "systimer.h"
#include "miscutil.h"
#include "mathutil.h"
#include "connect.h"
#include "wwdebug.h"
#include "packetmgr.h"
bool cRemoteHost::AllowExtraModemBandwidthThrottling = true;
int cRemoteHost::PriorityUpdateRate = 15;
//
// class defines
//
//-----------------------------------------------------------------------------
cRemoteHost::cRemoteHost() :
ReliablePacketSendId(0),
UnreliablePacketSendId(0),
ReliablePacketRcvId(0),
UnreliablePacketRcvId(0),
LastKeepaliveTimeMs(TIMEGETTIME()),
IsFlowControlEnabled(cConnection::Is_Flow_Control_Enabled()),
MustEvict(false),
LastReliableSendId(-2), // dummy value
LastUnreliableSendId(-2), // dummy value
ResendTimeoutMs(cNetUtil::Get_Default_Resend_Timeout_Ms()),
LastServiceCount(0),
LastContactTime(0),
TargetBps(0),
MaximumBps(0),
MaxInternalPingtimeMs(0),
AverageInternalPingtimeMs(0),
BandwidthMultiplier(1.0f),
AverageObjectPriority(0.5f),
IsLoading(false),
ExpectPacketFlood(false),
WasLoading(0),
CreationTime(TIMEGETTIME()),
TotalResends(0),
PriorityUpdateCounter(0),
ExtendedAveragePingTime(0),
ExtendedAverageCount(0),
LastAveragePingTime(0),
IsOutgoingFlooded(false),
TotalResentPacketsInQueue(0),
NextOutgoingFloodActionTime(0),
NumOutgoingFloods(0)
{
//WWDEBUG_SAY(("cRemoteHost::cRemoteHost\n"));
ZeroMemory(&Address, sizeof(SOCKADDR_IN));
if (IsFlowControlEnabled) {
//ThresholdPriority = cNetUtil::Get_Initial_Threshold_Priority();
//ThresholdPriority = 1.0;
ThresholdPriority = 0.5;
} else {
ThresholdPriority = 0.0;
}
//ThresholdPriority = 0.0;
TPIncrement = 0.01;//TSS2001d
Init_Stats();
}
//-----------------------------------------------------------------------------
void cRemoteHost::Init_Stats()
{
Stats.Init_Net_Stats();
//
// Possibly, pull back ResendTimeoutMs if we have had a chance to measure ping time yet.
//
Adjust_Resend_Timeout();
//if (MaxInternalPingtimeMs && AverageInternalPingtimeMs) {
// int candidate_resend_timeout_ms = min(3 * AverageInternalPingtimeMs,
// (int) (1.1 * MaxInternalPingtimeMs));
// if (candidate_resend_timeout_ms < ResendTimeoutMs) {
// WWASSERT(candidate_resend_timeout_ms < 0xffff);
// ResendTimeoutMs = candidate_resend_timeout_ms;
// //WWDEBUG_SAY((">> ResendTimeoutMs for rhost %d = %d\n", Id, ResendTimeoutMs));
// }
//}
//WWDEBUG_SAY(("ResendTimeoutMs for rhost %d = %d\n", Id, (unsigned long)ResendTimeoutMs));
WWDEBUG_SAY(("ResendTimeoutMs for rhost = %d\n", (unsigned long)ResendTimeoutMs));
TotalInternalPingtimeMs = 0;
NumInternalPings = 0;
AverageInternalPingtimeMs = 0;//-1;
MinInternalPingtimeMs = 1000000;
MaxInternalPingtimeMs = 0;
}
//-----------------------------------------------------------------------------
void cRemoteHost::Set_Last_Service_Count(int service_count)
{
WWASSERT(service_count >= 0);
LastServiceCount = service_count;
}
//-----------------------------------------------------------------------------
void cRemoteHost::Compute_List_Max(int list_type)
{
WWASSERT(list_type >= 0 && list_type < 4);
ListMax[list_type] = PacketList[list_type].Get_Count();
}
//-----------------------------------------------------------------------------
int cRemoteHost::Get_List_Max(int list_type)
{
WWASSERT(list_type >= 0 && list_type < 4);
return ListMax[list_type];
}
//-----------------------------------------------------------------------------
void cRemoteHost::Set_List_Processing_Time(int list_type, int processing_time_ms)
{
WWASSERT(list_type >= 0 && list_type < 4);
ListProcessingTime[list_type] = processing_time_ms;
}
//-----------------------------------------------------------------------------
int cRemoteHost::Get_List_Processing_Time(int list_type)
{
WWASSERT(list_type >= 0 && list_type < 4);
return ListProcessingTime[list_type];
}
//-----------------------------------------------------------------------------
SOCKADDR_IN & cRemoteHost::Get_Address()
{
return Address;
}
//-----------------------------------------------------------------------------
cRemoteHost::~cRemoteHost()
{
SLNode<cPacket> * objnode;
cPacket * p_packet;
for (int list_type = 0; list_type < 4; list_type++) {
for (objnode = PacketList[list_type].Head(); objnode != NULL;) {
p_packet = objnode->Data();
WWASSERT(p_packet != NULL);
objnode = objnode->Next();
PacketList[list_type].Remove(p_packet);
p_packet->Flush();
delete p_packet;
}
}
}
//------------------------------------------------------------------------------------
void cRemoteHost::Add_Packet(cPacket & packet, BYTE list_type)
{
WWASSERT(
list_type == RELIABLE_SEND_LIST ||
list_type == RELIABLE_RCV_LIST ||
list_type == UNRELIABLE_SEND_LIST ||
list_type == UNRELIABLE_RCV_LIST);
WWASSERT(packet.Get_Id() >= 0);
if (list_type == RELIABLE_SEND_LIST) {
//
// Test to make sure that additions to send list are in sequence.
//
if (LastReliableSendId != -2) {
WWASSERT(packet.Get_Id() == LastReliableSendId + 1);
}
LastReliableSendId = packet.Get_Id();
} else if (list_type == UNRELIABLE_SEND_LIST) {
if (LastUnreliableSendId != -2) {
WWASSERT(packet.Get_Id() == LastUnreliableSendId + 1);
}
LastUnreliableSendId = packet.Get_Id();
}
cPacket * p_packet = new cPacket;
WWASSERT(p_packet != NULL);
*p_packet = packet; // copy data
if (list_type == RELIABLE_SEND_LIST || list_type == UNRELIABLE_SEND_LIST) {
PacketList[list_type].Add_Tail(p_packet);
} else {
//
// Locate insertion point according to packet id
//
SLNode<cPacket> * objnode;
cPacket * obj = NULL;
for (objnode = PacketList[list_type].Head(); objnode; objnode = objnode->Next()) {
obj = objnode->Data();
WWASSERT(obj != NULL);
if (obj->Get_Id() > packet.Get_Id()) {
break;
}
}
//
// Insert packet at designated point
// TSS - this is inefficient: we have already parsed the
// list once
//
if (objnode == NULL) {
PacketList[list_type].Add_Tail(p_packet);
} else {
PacketList[list_type].Insert_Before(p_packet, obj);
}
}
}
//------------------------------------------------------------------------------------
void cRemoteHost::Remove_Packet(int packet_id, BYTE list_type)
{
WWASSERT(packet_id >= 0);
WWASSERT(
list_type == RELIABLE_SEND_LIST ||
list_type == RELIABLE_RCV_LIST ||
list_type == UNRELIABLE_SEND_LIST ||
list_type == UNRELIABLE_RCV_LIST);
SLNode<cPacket> * objnode;
for (objnode = PacketList[list_type].Head(); objnode != NULL;) {
cPacket * p_packet = objnode->Data();
WWASSERT(p_packet != NULL);
objnode = objnode->Next();
if (packet_id == p_packet->Get_Id()) {
if (list_type == RELIABLE_SEND_LIST) {// &&
// Packets that require a resend shouldn't count towards ping time calculations. It may be being removed because
// the ACK to the first send just came in and if we just resent it then the ping time will look really low so we get
// biased towards a low resend timeout value on connections of variable quality. ST - 12/7/2001 12:48PM
if (p_packet->Get_Resend_Count() == 0 || NumInternalPings == 0) {
unsigned long time = TIMEGETTIME();
int ping_time = time - p_packet->Get_Send_Time();
if (p_packet->Get_Resend_Count() != 0) {
WWASSERT(NumInternalPings == 0);
// If we are not getting any timing info at all then we need to do something. Use the first send time. It's going
// to make it big but that should cut down on the resends and let us get better timing info.
if (NumInternalPings == 0) {
ping_time = TIMEGETTIME() - p_packet->Get_First_Send_Time();
}
}
TotalInternalPingtimeMs += ping_time;
NumInternalPings++;
if (NumInternalPings > 0) {
AverageInternalPingtimeMs = cMathUtil::Round(TotalInternalPingtimeMs / (double) NumInternalPings);
} else {
AverageInternalPingtimeMs = 0;
}
if (ping_time < MinInternalPingtimeMs) {
MinInternalPingtimeMs = ping_time;
}
if (ping_time > MaxInternalPingtimeMs) {
MaxInternalPingtimeMs = ping_time;
#if (0)
int candidate_resend_timeout_ms = (int) (MaxInternalPingtimeMs
* 1.1);
if (candidate_resend_timeout_ms > ResendTimeoutMs) {
ResendTimeoutMs = candidate_resend_timeout_ms;
//WWDEBUG_SAY((">> ResendTimeoutMs for rhost %d = %d\n", Id, ResendTimeoutMs));
}
#endif //(0)
}
}
}
//if (list_type == RELIABLE_SEND_LIST) {
//WWDEBUG_SAY(("Removing packet %d from RELIABLE_SEND_LIST\n", packet_id));
//}
PacketList[list_type].Remove(p_packet);
p_packet->Flush();
delete p_packet;
break;
}
}
}
//------------------------------------------------------------------------------------
void cRemoteHost::Toggle_Flow_Control()
{
IsFlowControlEnabled = !IsFlowControlEnabled;
if (IsFlowControlEnabled) {
//ThresholdPriority = 1.0;
ThresholdPriority = 0.5;
} else {
ThresholdPriority = 0.0;
}
//ThresholdPriority = 0.0;
}
//------------------------------------------------------------------------------------
void cRemoteHost::Adjust_Flow_If_Necessary(float sample_time_ms)
{
if (!IsFlowControlEnabled) {
return;
}
#ifdef OBSOLETE
WWASSERT(TargetBps > 0);
float sample_target_bits = sample_time_ms / 1000.0f * TargetBps;
WWASSERT(sample_target_bits > 0);
//TSS101401
//float ratio = Stats.StatSnapshot[STAT_BitsSent] / sample_target_bits;
float ratio = PacketManager.Get_Compressed_Bandwidth_Out(&Get_Address()) / sample_target_bits;
if (ratio > MISCUTIL_EPSILON) {
double last_tp = TPIncrement;
if (ratio > 1)
{
TPIncrement = ::fabs(TPIncrement);
}
else
{
TPIncrement = -::fabs(TPIncrement);
}
if ((TPIncrement > 0 && last_tp > 0) ||
(TPIncrement < 0 && last_tp < 0)) {
if (::fabs(TPIncrement) < 0.025) {
TPIncrement *= 1.5;
}
} else {
if (::fabs(TPIncrement) > 0.0001) {
TPIncrement *= 0.5;
}
}
double new_tp = ThresholdPriority + TPIncrement;
if (new_tp < 0)
{
new_tp = 0;
}
else if (new_tp > 1)
{
new_tp = 1;
}
ThresholdPriority = new_tp;
/*
if (Id == 1) {
WWDEBUG_SAY(("ratio = %-7d / %-7d = %5.2f, TPIncrement = %9.7f, ThresholdPriority = %9.7f\n",
Stats.StatSnapshot[STAT_BitsSent], (int) sample_target_bits, ratio, TPIncrement, ThresholdPriority));
}
/**/
}
#endif //OBSOLETE
//
// Do a similar calculation for the new bandwidth per object per client distribution method.
//
float actual = (float) PacketManager.Get_Compressed_Bandwidth_Out(&Get_Address());
float desired = (float) TargetBps; //sample_time_ms / 1000.0f * TargetBps;
//if (Id > 1) {
//WWDEBUG_SAY(("actual = %f, desired = %f\n", actual, desired));
//}
if (ExpectPacketFlood) {
BandwidthMultiplier = 0.5f; //1.5f;
if (TIMEGETTIME() - FloodTimer > 8000) {
ExpectPacketFlood = false;
}
} else {
//
// If we are sending way more than we know the remote host can receive then we need to send much less.
//
if (actual > desired) {
BandwidthMultiplier = (BandwidthMultiplier * (desired / actual)) * 0.85f;
} else {
//
// Another case we have to trap is a low bandwidth connection that has been accidentally flooded by having several
// frames where we send more stuff than usual. It could also be a remote host that has incorrectly reported his
// downstream bandwidth and can't receive as much as we think we can send to him. Or it could simply be some temporary
// condition at either end of the connection or anywhere in between. If this happens we have to cut way back on sends
// so that the connection can catch up.
//
if (AllowExtraModemBandwidthThrottling) {
if (IsOutgoingFlooded) {
//
// We recently detected a outbound flooding condition. If we are still flooding then we have to take further action.
//
if (Is_Outgoing_Flooded()) {
Dam_The_Flood();
} else {
IsOutgoingFlooded = false;
}
} else {
//
// See if we are flooding now.
//
if (Is_Outgoing_Flooded()) {
IsOutgoingFlooded = true;
//
// Well, something is wrong. It's kind of hard to say exactly what the problem is so a remedy is only guesswork.
// In the short term we can try reducing the BandwidthMultiplier to clamp down on non-guaranteed packets. If the
// problem persists then we will have to reduce MaximumBps too.
//
NumOutgoingFloods++;
NextOutgoingFloodActionTime = 0;
Dam_The_Flood();
}
}
}
if (BandwidthMultiplier < 20.0f) {
if (actual < (desired * 0.7f)) {
/*
** Give bandwidth a quicker boost
*/
BandwidthMultiplier += 0.5f * (1.0f - (actual / desired));
if (BandwidthMultiplier > 20.0f) {
BandwidthMultiplier = 20.0f;
}
} else {
if (actual < (desired * 0.95f)) {
/*
** Gradually increase bandwidth usage until we are close to max utilization.
*/
BandwidthMultiplier += 0.1f * (1.0f - (actual / desired));
if (BandwidthMultiplier > 20.0f) {
BandwidthMultiplier = 20.0f;
}
}
}
}
}
}
}
//------------------------------------------------------------------------------------
bool cRemoteHost::Is_Outgoing_Flooded(void)
{
//
// Don't take action if the rhost is loading.
//
if (Was_Recently_Loading()) {
return(false);
}
//
// We can try to detect outgoing floods in two ways.
//
//
// 1. Look for a spike in the ping times. If we are sending more than the remote host can receive then ping times will
// quickly rise as each packet backs up at the receive end and thus gets ack'd later than usual.
//
if (ExtendedAverageCount) {
// average ping time over the life of the connection to use as a base line.
int average = (int)(ExtendedAveragePingTime / (unsigned)ExtendedAverageCount);
if ((LastAveragePingTime > 1500 && MaximumBps < 100000) || LastAveragePingTime > 500 && LastAveragePingTime > average * 3) {
//
// Ping time is bigger than we expect. If we are still receiving stuff from this host then chances are we are
// flooding him.
//
if (TIMEGETTIME() - LastContactTime < 1000) {
if (!IsOutgoingFlooded) {
WWDEBUG_SAY(("Detected abnormal ping times - assuming outbound connection to rhost %d is flooded\n", Id));
WWDEBUG_SAY(("Normal average ping time = %d, last average ping time = %d\n", average, LastAveragePingTime));
}
return(true);
}
}
}
//
// 2. An excessive number of packets in the out queue that are older than the average ping time. Since acks aren't
// coming back we resend more and so exacerbate the problem.
//
int total_in_queue = PacketList[RELIABLE_SEND_LIST].Get_Count();
// Let's say that if more than 90% of the packets in the queue have been resent then there is a problem.
if (total_in_queue > 20 && (TotalResentPacketsInQueue*10) > (total_in_queue*9)) {
//
// More resends than we expect. If we are still receiving stuff from this host then chances are we are
// flooding him.
//
if (TIMEGETTIME() - LastContactTime < 1000) {
WWDEBUG_SAY(("Detected abnormally high number of resends - assuming outbound connection to rhost %d is flooded\n", Id));
WWDEBUG_SAY(("Total packets in queue = %d, queue packets resent = %d\n", total_in_queue, TotalResentPacketsInQueue));
return(true);
}
}
return(false);
}
//------------------------------------------------------------------------------------
void cRemoteHost::Dam_The_Flood(void)
{
WWASSERT(IsOutgoingFlooded);
//
// How long since we last took action?
//
bool action_time = (TIMEGETTIME() > NextOutgoingFloodActionTime) ? true : false;
//
// Try something else every now and then until things improve.
//
if (action_time) {
//
// Try reducing BandwidthMultiplier. This will be a temporary change and will be allowed to revert once the connection
// recovers.
//
if (BandwidthMultiplier > 0.5f || NextOutgoingFloodActionTime == 0) {
BandwidthMultiplier = BandwidthMultiplier / 2.0f;
//
// Give this a half second or so to take effect.
//
if (BandwidthMultiplier <= 0.5f) {
//
// Reducing MaximumBps is a last resort. Give it a little more time to recover.
//
NextOutgoingFloodActionTime = 2000 + TIMEGETTIME();
} else {
NextOutgoingFloodActionTime = 600 + TIMEGETTIME();
}
} else {
//
// Try reducing MaximumBps. This will be a permanent change. 30,000ish should be our minimum since the min requirement is
// a 33600 modem.
//
if (MaximumBps > 30000) {
#ifdef WWDEBUG
int old_bps = MaximumBps;
#endif //WWDEBUG
if (MaximumBps > 100000) {
MaximumBps = 100000;
} else {
if (MaximumBps > 56000) {
MaximumBps = 56000;
} else {
//if (MaximumBps > 40000) {
// MaximumBps = 40000;
//} else {
if (MaximumBps > 33600) {
MaximumBps = 33600;
} else {
MaximumBps = 30000;
}
//}
}
}
WWDEBUG_SAY(("Reduced MaximumBps for rhost %d from %d to %d\n", Id, old_bps, MaximumBps));
/*
** Give this a few seconds to take effect before stepping down again.
*/
NextOutgoingFloodActionTime = 5000 + TIMEGETTIME();
}
}
}
}
//------------------------------------------------------------------------------------
void cRemoteHost::Set_Flood(bool state)
{
ExpectPacketFlood = state;
if (state) {
FloodTimer = TIMEGETTIME();
}
}
//------------------------------------------------------------------------------------
void cRemoteHost::Set_Is_Loading(bool state)
{
if (IsLoading && !state) {
WasLoading = TIMEGETTIME();
}
IsLoading = state;
}
//------------------------------------------------------------------------------------
bool cRemoteHost::Was_Recently_Loading(unsigned long time)
{
if (IsLoading) {
return(true);
}
if (time == 0) {
time = TIMEGETTIME();
}
if (time - WasLoading < 1000 * 8) {
return(true);
}
return(false);
}
//------------------------------------------------------------------------------------
void cRemoteHost::Adjust_Resend_Timeout(void)
{
if (NumInternalPings > 0) {
if (MaxInternalPingtimeMs && AverageInternalPingtimeMs) {
int candidate_resend_timeout_ms = min(3 * AverageInternalPingtimeMs, (int) (1.3f * (float)MaxInternalPingtimeMs));
// Make sure it stays inside a reasonable range.
candidate_resend_timeout_ms = min(candidate_resend_timeout_ms, 3000);
candidate_resend_timeout_ms = max(candidate_resend_timeout_ms, 333);
ResendTimeoutMs = (ResendTimeoutMs + candidate_resend_timeout_ms) / 2;
if (candidate_resend_timeout_ms < ResendTimeoutMs) {
WWASSERT(candidate_resend_timeout_ms < 0xffff);
ResendTimeoutMs = candidate_resend_timeout_ms;
//WWDEBUG_SAY((">> ResendTimeoutMs for rhost %d = %d\n", Id, ResendTimeoutMs));
}
//WWDEBUG_SAY(("ResendTimeoutMs for rhost = %d\n", (unsigned long)ResendTimeoutMs));
//
// Keep track of the last average we calculated plus the average ping over the life of the connection.
//
ExtendedAveragePingTime += (unsigned) TotalInternalPingtimeMs;
ExtendedAverageCount++;
LastAveragePingTime = AverageInternalPingtimeMs;
}
TotalInternalPingtimeMs = 0;
NumInternalPings = 0;
AverageInternalPingtimeMs = 0;//-1;
MinInternalPingtimeMs = 65535;
MaxInternalPingtimeMs = 0;
}
}

252
Code/wwnet/rhost.h Normal file
View File

@@ -0,0 +1,252 @@
/*
** 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/>.
*/
//
// Filename: rhost.h
// Project: wwnet
// Author: Tom Spencer-Smith
// Date: June 1998
// Description:
//
//-----------------------------------------------------------------------------
#if defined(_MSV_VER)
#pragma once
#endif
#ifndef RHOST_H
#define RHOST_H
#include "netstats.h"
#include "wwpacket.h"
#include "bittype.h"
#include "slist.h"
#include "wwdebug.h"
#include "win.h"
#include <winsock.h>
//const USHORT MAX_MESSAGE_TYPES = 256;
//
// ResendTimeoutMs is dynamic, and works like this: It starts out very low,
// say 10ms, as given by DEFAULT_RESEND_TIMEOUT_MS. Then it is kept 10%
// higher than the highest ping. Every 60s the stats are reset, and
// at that time ResendTimeoutMs is set to be min(3 * the average
// ping for that sample, max ping for that sample). This means that
// rare outlandishly large pings don't cause our ResendTimeoutMs to
// be too conservative.
// It's all very ad hoc of course and will need refinement...
//
enum {
RELIABLE_SEND_LIST = 0,
RELIABLE_RCV_LIST,
UNRELIABLE_SEND_LIST,
UNRELIABLE_RCV_LIST
};
class cRemoteHost
{
public:
cRemoteHost();
~cRemoteHost();
void Add_Packet(cPacket & packet, BYTE list_type);
void Remove_Packet(int reliable_packet_id, BYTE list_type);
void Toggle_Flow_Control();
void Init_Stats();
int Get_Last_Service_Count() {return LastServiceCount;}
void Set_Last_Service_Count(int service_count);
void Compute_List_Max(int list_type);
int Get_List_Max(int list_type);
void Set_List_Processing_Time(int list_type, int processing_time_ms);
int Get_List_Processing_Time(int list_type);
int Get_Last_Contact_Time() {return LastContactTime;}
void Set_Last_Contact_Time(int time) {LastContactTime = time;}
SOCKADDR_IN & Get_Address();
void Set_Address(SOCKADDR_IN & address) {Address = address;}
int Get_Target_Bps(void) const {return TargetBps;}
//void Set_Target_Bps(int bps) {WWASSERT(bps > 0); TargetBps = bps;}
void Set_Target_Bps(int bps) {TargetBps = bps;}
int Get_Maximum_Bps(void) const {return MaximumBps;}
//void Set_Maximum_Bps(int bps) {WWASSERT(bps > 0); MaximumBps = bps;}
void Set_Maximum_Bps(int bps) {MaximumBps = bps;}
cNetStats & Get_Stats() {return Stats;}
double Get_Threshold_Priority() const {return ThresholdPriority;}
float Get_Bandwidth_Multiplier(void) const {return BandwidthMultiplier;}
void Set_Average_Priority(float ave) {AverageObjectPriority = ave;}
float Get_Average_Priority(void) {return(AverageObjectPriority);}
USHORT Get_Resend_Timeout_Ms() const {return ResendTimeoutMs;}
//
// Ping
//
int Get_Num_Internal_Pings() const {return NumInternalPings;}
int Get_Total_Internal_Pingtime_Ms() const {return TotalInternalPingtimeMs;}
int Get_Average_Internal_Pingtime_Ms() const {return AverageInternalPingtimeMs;}
int Get_Min_Internal_Pingtime_Ms() const {return MinInternalPingtimeMs;}
int Get_Max_Internal_Pingtime_Ms() const {return MaxInternalPingtimeMs;}
int Get_Reliable_Packet_Send_Id() const {return ReliablePacketSendId;}
int Get_Unreliable_Packet_Send_Id() const {return UnreliablePacketSendId;}
int Get_Reliable_Packet_Rcv_Id() const {return ReliablePacketRcvId;}
int Get_Unreliable_Packet_Rcv_Id() const {return UnreliablePacketRcvId;}
void Set_Reliable_Packet_Send_Id(int id) {ReliablePacketSendId = id;}
void Set_Unreliable_Packet_Send_Id(int id) {UnreliablePacketSendId = id;}
void Set_Reliable_Packet_Rcv_Id(int id) {ReliablePacketRcvId = id;}
void Set_Unreliable_Packet_Rcv_Id(int id) {UnreliablePacketRcvId = id;}
void Increment_Reliable_Packet_Send_Id() {ReliablePacketSendId++;}
void Increment_Unreliable_Packet_Send_Id() {UnreliablePacketSendId++;}
void Increment_Reliable_Packet_Rcv_Id() {ReliablePacketRcvId++;}
void Increment_Unreliable_Packet_Rcv_Id() {UnreliablePacketRcvId++;}
unsigned long Get_Last_Keepalive_Time_Ms() const {return LastKeepaliveTimeMs;}
void Set_Last_Keepalive_Time_Ms(unsigned long time_ms) {LastKeepaliveTimeMs = time_ms;}
SList<cPacket> & Get_Packet_List(int index) {WWASSERT(index >= 0 && index < 4); return PacketList[index];}
bool Must_Evict() const {return MustEvict;}
void Set_Must_Evict(bool flag) {MustEvict = flag;}
void Adjust_Flow_If_Necessary(float sample_time_ms);
void Adjust_Resend_Timeout(void);
void Set_Id(int id) {WWASSERT(id >= 0); Id = id;}
int Get_Id(void) {WWASSERT(Id >= 0); return Id;}
void Set_Is_Loading(bool state);
bool Get_Is_Loading(void) {return(IsLoading);}
bool Was_Recently_Loading(unsigned long time = 0);
void Set_Flood(bool state);
bool Get_Flood(void) {return(ExpectPacketFlood);}
unsigned long Get_Creation_Time(void) {return(CreationTime);}
unsigned long Get_Total_Resends(void) {return(TotalResends);}
void Increment_Resends(void) {TotalResends++;}
void Set_Total_Resent_Packets_In_Queue (int resent_packets) {TotalResentPacketsInQueue = resent_packets;}
inline int Get_Priority_Update_Counter(void) {return(PriorityUpdateCounter);}
inline void Increment_Priority_Count(void) {PriorityUpdateCounter++; if (PriorityUpdateCounter > PriorityUpdateRate) PriorityUpdateCounter = 0;}
static void Set_Priority_Update_Rate(int rate) {PriorityUpdateRate = rate;}
static inline void Set_Allow_Extra_Modem_Bandwidth_Throttling(bool set) {AllowExtraModemBandwidthThrottling = set;}
private:
cRemoteHost(const cRemoteHost& rhs); // Disallow copy (compile/link time)
cRemoteHost& operator=(const cRemoteHost& rhs); // Disallow assignment (compile/link time)
void Dam_The_Flood(void);
bool Is_Outgoing_Flooded(void);
cNetStats Stats;
double ThresholdPriority;
double TPIncrement;
int LastReliableSendId;
int LastUnreliableSendId;
USHORT ResendTimeoutMs;
int NumInternalPings;
int TotalInternalPingtimeMs;
int AverageInternalPingtimeMs;
int MinInternalPingtimeMs;
int MaxInternalPingtimeMs;
SOCKADDR_IN Address;
int ReliablePacketSendId;
int UnreliablePacketSendId;
int ReliablePacketRcvId;
int UnreliablePacketRcvId;
SList<cPacket> PacketList[4]; // list of all player objects
int ListMax[4];
int ListProcessingTime[4];
unsigned long LastKeepaliveTimeMs;
bool MustEvict;
BOOL IsFlowControlEnabled;
int LastServiceCount;
int LastContactTime;
int TargetBps;
int MaximumBps;
int Id;
float BandwidthMultiplier;
float AverageObjectPriority;
bool IsLoading;
bool ExpectPacketFlood;
unsigned long FloodTimer;
unsigned long WasLoading;
unsigned long TotalResends;
unsigned long CreationTime;
int PriorityUpdateCounter;
//
// Variables for detecting outgoing packet floods.
//
unsigned long ExtendedAveragePingTime;
int ExtendedAverageCount;
int LastAveragePingTime;
bool IsOutgoingFlooded;
int TotalResentPacketsInQueue;
unsigned long NextOutgoingFloodActionTime;
int NumOutgoingFloods;
static bool AllowExtraModemBandwidthThrottling;
static int PriorityUpdateRate;
};
#endif // RHOST_H
//
// When this is set to true, sends to ALL will include this rhost.
// This is needed to allow a slot to be reserved for a rhost before
// using it extensively. Generally, when a client is accepted into a
// game he wil need to do an extensive data load during which there
// will be no network servicing. When that is finished he should signal
// readyness.
//
//bool IsReadyForAllData;
//int NumFailsInSampleTime;
//int NumConsecutiveTPCorrectionsDownwards;
//int NumConsecutiveTPCorrectionsUpwards;

79
Code/wwnet/singlepl.cpp Normal file
View File

@@ -0,0 +1,79 @@
/*
** 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/>.
*/
//
// Filename: singlepl.cpp
// Project: wwnet
// Author: Tom Spencer-Smith
// Date: Nov 1998
// Description:
//
//-----------------------------------------------------------------------------
#include "singlepl.h" // I WANNA BE FIRST!
#include "netutil.h"
#include "miscutil.h"
#include "wwdebug.h"
#include "wwpacket.h"
//
// class defines
//
bool cSinglePlayerData::IsSinglePlayer = false;
SList<cPacket> cSinglePlayerData::InputPacketList[];
//-----------------------------------------------------------------------------
void cSinglePlayerData::Init()
{
WWDEBUG_SAY(("cSinglePlayerData::cSinglePlayerData\n"));
IsSinglePlayer = true;
}
//------------------------------------------------------------------------------------
void cSinglePlayerData::Cleanup()
{
WWDEBUG_SAY(("cSinglePlayerData::~cSinglePlayerData\n"));
SLNode<cPacket> * objnode;
cPacket * p_packet;
for (int list_type = 0; list_type < 2; list_type++) {
for (objnode = InputPacketList[list_type].Head(); objnode != NULL;) {
p_packet = objnode->Data();
WWASSERT(p_packet != NULL);
objnode = objnode->Next();
InputPacketList[list_type].Remove(p_packet);
delete p_packet;
}
}
IsSinglePlayer = false;
}
//-----------------------------------------------------------------------------
SList<cPacket> * cSinglePlayerData::Get_Input_Packet_List(int type)
{
WWASSERT(type == CLIENT_LIST || type == SERVER_LIST);
return &InputPacketList[type];
}

75
Code/wwnet/singlepl.h Normal file
View File

@@ -0,0 +1,75 @@
/*
** 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/>.
*/
//
// Filename: singlepl.h
// Project: wwnet
// Author: Tom Spencer-Smith
// Date: Nov 1998
// Description: Single player stuff
//
// TODO:
// - merge into netutil ?
//
//
//-----------------------------------------------------------------------------
#if defined(_MSV_VER)
#pragma once
#endif
#ifndef SINGLEPL_H
#define SINGLEPL_H
#include "bittype.h"
#include "slist.h"
enum {
CLIENT_LIST = 0,
SERVER_LIST
};
class cPacket;
//
// This is used in single-player mode and is global, not owned by the
// C or S threads. A critical section is used to control access by
// the C and S threads. The client writes to the end of the Server list and
// reads from the beginning of the Client list. Vice versa for the server.
// Send_Packet handles everything transparently.
// TSS - linked list
//
class cSinglePlayerData
{
public:
static void Init();
static void Cleanup();
static bool Is_Single_Player() {return IsSinglePlayer;}
static SList<cPacket> * Get_Input_Packet_List(int type);
private:
static bool IsSinglePlayer;
static SList<cPacket> InputPacketList[2];
};
//-----------------------------------------------------------------------------
#endif // SINGLEPL_H

240
Code/wwnet/wwnet.dsp Normal file
View File

@@ -0,0 +1,240 @@
# Microsoft Developer Studio Project File - Name="wwnet" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Static Library" 0x0104
CFG=wwnet - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "wwnet.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "wwnet.mak" CFG="wwnet - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "wwnet - Win32 Release" (based on "Win32 (x86) Static Library")
!MESSAGE "wwnet - Win32 Debug" (based on "Win32 (x86) Static Library")
!MESSAGE "wwnet - Win32 Profile" (based on "Win32 (x86) Static Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""$/Commando/Code/wwnet", KBDBAAAA"
# PROP Scc_LocalPath "."
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "wwnet - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
# ADD CPP /nologo /MT /W4 /WX /GX- /Zi /O2 /Ob2 /I "..\\" /I "..\wwbitpack" /I "..\wwdebug" /I "..\wwutil" /I "..\wwlib" /I "..\wwmath" /I "..\wwsaveload" /I "..\wolapi" /I "..\wwonline" /D "NDEBUG" /D "_WINDOWS" /D "WIN32" /D "WIN32_LEAN_AND_MEAN" /FD /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409
# ADD RSC /l 0x409
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo /out:"..\Libs\Release\wwnet.lib"
!ELSEIF "$(CFG)" == "wwnet - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "wwnet___"
# PROP BASE Intermediate_Dir "wwnet___"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c
# ADD CPP /nologo /MTd /W4 /WX /Gm /Gi /GR- /GX- /ZI /Od /I "..\\" /I "..\wwbitpack" /I "..\wwdebug" /I "..\wwutil" /I "..\wwlib" /I "..\wwmath" /I "..\wwsaveload" /I "..\wolapi" /I "..\wwonline" /D "STRICT" /D "_DEBUG" /D "WWDEBUG" /D "WIN32_LEAN_AND_MEAN" /D "_WINDOWS" /D "WIN32" /FD /c
# SUBTRACT CPP /Fr /YX /Yc /Yu
# ADD BASE RSC /l 0x409
# ADD RSC /l 0x409
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo /out:"..\Libs\Debug\wwnet.lib"
!ELSEIF "$(CFG)" == "wwnet - Win32 Profile"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Profile"
# PROP BASE Intermediate_Dir "Profile"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Profile"
# PROP Intermediate_Dir "Profile"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "..\wwlzhl" /I "..\wwutil" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
# ADD CPP /nologo /MT /W4 /WX /GX- /Zi /O2 /Op /Ob2 /I "..\\" /I "..\wwbitpack" /I "..\wwdebug" /I "..\wwutil" /I "..\wwlib" /I "..\wwmath" /I "..\wwsaveload" /I "..\wolapi" /I "..\wwonline" /D "NDEBUG" /D "WWDEBUG" /D "_WINDOWS" /D "WIN32" /D "WIN32_LEAN_AND_MEAN" /FD /c
# SUBTRACT CPP /Fr /YX
# ADD BASE RSC /l 0x409
# ADD RSC /l 0x409
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo /out:"..\Libs\Release\wwnet.lib"
# ADD LIB32 /nologo /out:"..\Libs\Profile\wwnet.lib"
!ENDIF
# Begin Target
# Name "wwnet - Win32 Release"
# Name "wwnet - Win32 Debug"
# Name "wwnet - Win32 Profile"
# Begin Source File
SOURCE=.\BWBalance.cpp
# End Source File
# Begin Source File
SOURCE=.\BWBalance.h
# End Source File
# Begin Source File
SOURCE=.\connect.cpp
# End Source File
# Begin Source File
SOURCE=.\connect.h
# End Source File
# Begin Source File
SOURCE=.\fromaddress.h
# End Source File
# Begin Source File
SOURCE=.\msgstat.cpp
# End Source File
# Begin Source File
SOURCE=.\msgstat.h
# End Source File
# Begin Source File
SOURCE=.\msgstatlist.cpp
# End Source File
# Begin Source File
SOURCE=.\msgstatlist.h
# End Source File
# Begin Source File
SOURCE=.\msgstatlistgroup.cpp
# End Source File
# Begin Source File
SOURCE=.\msgstatlistgroup.h
# End Source File
# Begin Source File
SOURCE=.\netstats.cpp
# End Source File
# Begin Source File
SOURCE=.\netstats.h
# End Source File
# Begin Source File
SOURCE=.\netutil.cpp
# End Source File
# Begin Source File
SOURCE=.\netutil.h
# End Source File
# Begin Source File
SOURCE=.\networkobject.cpp
# End Source File
# Begin Source File
SOURCE=.\networkobject.h
# End Source File
# Begin Source File
SOURCE=.\networkobjectfactory.cpp
# End Source File
# Begin Source File
SOURCE=.\networkobjectfactory.h
# End Source File
# Begin Source File
SOURCE=.\networkobjectfactorymgr.cpp
# End Source File
# Begin Source File
SOURCE=.\networkobjectfactorymgr.h
# End Source File
# Begin Source File
SOURCE=.\networkobjectmgr.cpp
# End Source File
# Begin Source File
SOURCE=.\networkobjectmgr.h
# End Source File
# Begin Source File
SOURCE=.\packetmgr.cpp
# End Source File
# Begin Source File
SOURCE=.\packetmgr.h
# End Source File
# Begin Source File
SOURCE=.\packettype.h
# End Source File
# Begin Source File
SOURCE=.\rhost.cpp
# End Source File
# Begin Source File
SOURCE=.\rhost.h
# End Source File
# Begin Source File
SOURCE=.\singlepl.cpp
# End Source File
# Begin Source File
SOURCE=.\singlepl.h
# End Source File
# Begin Source File
SOURCE=.\wwpacket.cpp
# End Source File
# Begin Source File
SOURCE=.\wwpacket.h
# End Source File
# End Target
# End Project

349
Code/wwnet/wwpacket.cpp Normal file
View File

@@ -0,0 +1,349 @@
/*
** 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/>.
*/
//
// Filename: wwpacket.cpp
// Project: wwnet
// Author: Tom Spencer-Smith
// Date: June 1998
// Description: Adding and extracting data from byte streams
//
//------------------------------------------------------------------------------------
#include "wwpacket.h"
#include "win.h"
#include "systimer.h"
#include "crc.h"
#include "quat.h"
#include "fromaddress.h"
#include "packettype.h"
#include "bitpackids.h"
#define PACKET_ID_BITS 28 // Enough for about 100 packets per second for about a month.
#define PACKET_TYPE_BITS 4 // Enough for 16 packet types (we currently have 7 at 9/20/2001 11:33PM)
DEFINE_AUTO_POOL(cPacket, 256)
//
// Class statics
//
#ifdef WRAPPER_CRC
const USHORT cPacket::PACKET_HEADER_SIZE = 7;
#else //WRAPPER_CRC
const int cPacket::CRC_PLACEHOLDER = 99999;
const USHORT cPacket::PACKET_HEADER_SIZE = 11;
#endif //WRAPPER_CRC
int cPacket::RefCount = 0;
bool cPacket::EncoderInit = true;
const unsigned long cPacket::DefSendTime = 0xffffffff;
//------------------------------------------------------------------------------------
cPacket::cPacket() :
Type(UNDEFINED_TYPE),
Id(UNDEFINED_ID),
SenderId(UNDEFINED_ID),
SendTime(DefSendTime),
FirstSendTime(DefSendTime),
ResendCount(-1), // so that first send doesn't count as a resend
NumSends(1)
{
//cEncoderList::Set_Compression_Enabled(false);
RefCount++;
if (EncoderInit) {
Init_Encoder();
}
}
//---------------- --------------------------------------------------------------------
cPacket::~cPacket()
{
RefCount--;
}
//------------------------------------------------------------------------------------
//
// Assignment operator
//
cPacket& cPacket::operator=(const cPacket& source)
{
PFromAddressWrapper = source.PFromAddressWrapper;
Type = source.Type;
Id = source.Id;
SenderId = source.SenderId;
SendTime = source.SendTime;
FirstSendTime = source.FirstSendTime;
ResendCount = source.ResendCount;
#ifndef WRAPPER_CRC
IsCrcCorrect = source.IsCrcCorrect;
#endif //WRAPPER_CRC
BitStreamClass::operator=(source);
NumSends = source.NumSends;
return * this;
}
//------------------------------------------------------------------------------------
void cPacket::Add_Vector3(Vector3 & v)
{
WWASSERT(v.Is_Valid());
Add(v.X);
Add(v.Y);
Add(v.Z);
}
//------------------------------------------------------------------------------------
void cPacket::Get_Vector3(Vector3 & v)
{
Get(v.X);
Get(v.Y);
Get(v.Z);
WWASSERT(v.Is_Valid());
}
//------------------------------------------------------------------------------------
void cPacket::Add_Quaternion(Quaternion & q)
{
WWASSERT(q.Is_Valid());
Add(q.X);
Add(q.Y);
Add(q.Z);
Add(q.W);
}
//------------------------------------------------------------------------------------
void cPacket::Get_Quaternion(Quaternion & q)
{
Get(q.X);
Get(q.Y);
Get(q.Z);
Get(q.W);
WWASSERT(q.Is_Valid());
}
//------------------------------------------------------------------------------------
void cPacket::Set_Type(BYTE type)
{
WWASSERT(Type == UNDEFINED_TYPE || Type == type);
WWASSERT(type != UNDEFINED_TYPE);
Type = type;
}
//------------------------------------------------------------------------------------
void cPacket::Set_Id(int id)
{
WWASSERT(id != UNDEFINED_ID);
Id = id;
}
//------------------------------------------------------------------------------------
void cPacket::Set_Sender_Id(int sender_id)
{
SenderId = sender_id;
}
//------------------------------------------------------------------------------------
void cPacket::Set_Send_Time()
{
unsigned long time = TIMEGETTIME();
if (SendTime == DefSendTime) {
FirstSendTime = time;
}
SendTime = time;
}
//------------------------------------------------------------------------------------
void cPacket::Set_Num_Sends(int num_sends)
{
WWASSERT(num_sends > 0);
NumSends = num_sends;
}
/*
//------------------------------------------------------------------------------------
BYTE cPacket::Peek_Message_Type() const
{
//
// Note that using Get is the only valid way to read data from a packet!
//
cPacket temp_packet;
temp_packet = *this;
BYTE message_type;
temp_packet.Get(message_type);
temp_packet.Flush();
return message_type;
}
*/
void cPacket::Init_Encoder(void)
{
EncoderInit = false;
cEncoderList::Set_Precision(BITPACK_PACKET_ID, PACKET_ID_BITS);
cEncoderList::Set_Precision(BITPACK_PACKET_TYPE, PACKET_TYPE_BITS);
}
//------------------------------------------------------------------------------------
void cPacket::Construct_Full_Packet(cPacket & full_packet, cPacket & src_packet)
{
WWASSERT(
src_packet.Get_Type() >= PACKETTYPE_FIRST &&
src_packet.Get_Type() <= PACKETTYPE_LAST);
WWASSERT(src_packet.Get_Id() != UNDEFINED_ID);
#ifndef WRAPPER_CRC
full_packet.Add(CRC_PLACEHOLDER);
#endif //WRAPPER_CRC
full_packet.Add(src_packet.Get_Type(), BITPACK_PACKET_TYPE);
full_packet.Add(src_packet.Get_Id(), BITPACK_PACKET_ID);
full_packet.Add((BYTE)src_packet.Get_Sender_Id());
full_packet.Add((USHORT)src_packet.Get_Bit_Length());
int header_bit_length = full_packet.Get_Bit_Length();
WWASSERT(header_bit_length == PACKET_HEADER_SIZE * 8);
memcpy(
full_packet.Get_Data() + PACKET_HEADER_SIZE,
src_packet.Get_Data(),
src_packet.Get_Compressed_Size_Bytes());
unsigned int whole_bit_length = header_bit_length + src_packet.Get_Bit_Length();
full_packet.Set_Bit_Length(whole_bit_length);
//
// Compute a CRC for all the data following the CRC placeholder
//
//ULONG crc = CRC::Memory(
// (BYTE *) (full_packet.Get_Data() + sizeof(CRC_PLACEHOLDER)),
// full_packet.Get_Max_Size() - sizeof(CRC_PLACEHOLDER));
// Only CRC the meaningful data in the buffer - not the other 1300ish bytes as well. ST - 9/19/2001 11:18PM
#ifndef WRAPPER_CRC
ULONG crc = CRC::Memory((BYTE *) (full_packet.Get_Data() + sizeof(CRC_PLACEHOLDER)), (whole_bit_length / 8) - sizeof(CRC_PLACEHOLDER));
//
// Overwrite the crc placeholder with the computed crc.
//
cPacket temp_packet;
temp_packet.Add(crc);
memcpy(
full_packet.Get_Data(),
temp_packet.Get_Data(),
temp_packet.Get_Compressed_Size_Bytes());
#endif //WRAPPER_CRC
}
//------------------------------------------------------------------------------------
void cPacket::Construct_App_Packet(cPacket & packet, cPacket & full_packet)
{
#ifndef WRAPPER_CRC
int remote_crc;
#endif //WRAPPER_CRC
BYTE type;
int packet_id;
char sender_id;
USHORT bit_size;
#ifndef WRAPPER_CRC
full_packet.Get(remote_crc);
#endif //WRAPPER_CRC
full_packet.Get(type, BITPACK_PACKET_TYPE);
full_packet.Get(packet_id, BITPACK_PACKET_ID);
full_packet.Get(sender_id);
full_packet.Get(bit_size);
#ifdef WRAPPER_CRC
packet.Set_Type(type);
packet.Set_Id(packet_id);
packet.Set_Sender_Id(sender_id);
packet.Set_Bit_Length(bit_size);
packet.PFromAddressWrapper = full_packet.PFromAddressWrapper;
memcpy(packet.Get_Data(), full_packet.Get_Data() + PACKET_HEADER_SIZE, packet.Get_Compressed_Size_Bytes());
#else //WRAPPER_CRC
if (((bit_size / 8) + PACKET_HEADER_SIZE) < MAX_BUFFER_SIZE) {
//
// Only CRC the meaningful data in the buffer - not the other 1300ish bytes as well. ST - 9/19/2001 11:29PM
//
int local_crc = CRC::Memory((BYTE *) (full_packet.Get_Data() + sizeof(CRC_PLACEHOLDER)), ((bit_size / 8) + PACKET_HEADER_SIZE) - sizeof(CRC_PLACEHOLDER));
if (local_crc == remote_crc) {
packet.Set_Is_Crc_Correct(true);
packet.Set_Type(type);
packet.Set_Id(packet_id);
packet.Set_Sender_Id(sender_id);
packet.Set_Bit_Length(bit_size);
packet.PFromAddressWrapper = full_packet.PFromAddressWrapper;
memcpy(packet.Get_Data(), full_packet.Get_Data() + PACKET_HEADER_SIZE, packet.Get_Compressed_Size_Bytes());
} else {
packet.Set_Is_Crc_Correct(false);
}
} else {
packet.Set_Is_Crc_Correct(false);
}
#endif //WRAPPER_CRC
}
//ExecuteTime(0),
//ReturnCode(0),
//ExecuteTime = source.ExecuteTime;
//ReturnCode = source.ReturnCode;
//------------------------------------------------------------------------------------
/*
void cPacket::Set_Execute_Time(int execute_time)
{
WWASSERT(execute_time > 0);
ExecuteTime = execute_time;
}
*/
/*
if (!Is_Flushed()) {
WWDEBUG_SAY(("*** cPacket::~cPacket: !Is_Flushed()\n"));
DIE;
}
*/

159
Code/wwnet/wwpacket.h Normal file
View File

@@ -0,0 +1,159 @@
/*
** 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/>.
*/
//
// Filename: wwpacket.h
// Project: wwnet
// Author: Tom Spencer-Smith
// Date: June 1998
// Description:
//
//-----------------------------------------------------------------------------
#if defined(_MSV_VER)
#pragma once
#endif
#ifndef WWPACKET_H
#define WWPACKET_H
#include "bittype.h"
#include "bitstream.h"
#include "fromaddress.h"
#include "packetmgr.h"
#include "mempool.h"
#ifndef NULL
#define NULL 0L
#endif
class Vector3;
class Quaternion;
class cFromAddress;
//-----------------------------------------------------------------------------
class cPacket : public BitStreamClass, public AutoPoolClass<cPacket, 256>
{
public:
cPacket();
~cPacket();
cPacket& operator=(const cPacket& source);
enum {NO_ENCODER = -1};
enum {UNDEFINED_TYPE = 15};
enum {UNDEFINED_ID = -1};
int Get_Max_Size() const {return BitStreamClass::Get_Buffer_Size();}
void Set_Bit_Length(UINT bit_position){BitStreamClass::Set_Bit_Write_Position(bit_position);}
UINT Get_Bit_Length(void) {return BitStreamClass::Get_Bit_Write_Position();}
void Add_Vector3(Vector3 & v);
void Get_Vector3(Vector3 & v);
void Add_Quaternion(Quaternion & q);
void Get_Quaternion(Quaternion & q);
const cFromAddress * Get_From_Address_Wrapper() const {return &PFromAddressWrapper;}
cFromAddress * Get_From_Address_Wrapper() {return &PFromAddressWrapper;}
void Set_Type(BYTE type);
BYTE Get_Type() const {return Type;}
void Set_Id(int id);
int Get_Id() const {WWASSERT(Id != UNDEFINED_ID); return Id;}//NEW
void Set_Sender_Id(int sender_id);
int Get_Sender_Id() const {return SenderId;}
void Set_Send_Time(void);
unsigned long Get_Send_Time() const {return SendTime;}
void Clear_Resend_Count() {ResendCount = 0;}
void Increment_Resend_Count() {ResendCount++;}
int Get_Resend_Count() const {return ResendCount;}
#ifndef WRAPPER_CRC
bool Is_Crc_Correct() const {return IsCrcCorrect;}
#endif //WRAPPER_CRC
void Set_Num_Sends(int num_sends);
int Get_Num_Sends() const {return NumSends;}
unsigned long Get_First_Send_Time(void) const {return FirstSendTime;}
static void Init_Encoder(void);
static int Get_Ref_Count() {return RefCount;}
static void Construct_Full_Packet(cPacket & full_packet, cPacket & src_packet);
static void Construct_App_Packet(cPacket & packet, cPacket & full_packet);
static USHORT Get_Packet_Header_Size(void) {return (PACKET_HEADER_SIZE);}
//BYTE Peek_Message_Type() const;
static unsigned long Get_Default_Send_Time(void) {return(DefSendTime);}
private:
cPacket(const cPacket& source); // Disallow
#ifndef WRAPPER_CRC
void Set_Is_Crc_Correct(bool flag) {IsCrcCorrect = flag;}
static const int CRC_PLACEHOLDER;
#endif //WRAPPER_CRC
static const USHORT PACKET_HEADER_SIZE;
static const unsigned long DefSendTime;
cFromAddress PFromAddressWrapper;
BYTE Type;
int Id;
int SenderId;
unsigned long SendTime;
unsigned long FirstSendTime;
int ResendCount;
#ifndef WRAPPER_CRC
bool IsCrcCorrect;
#endif //WRAPPER_CRC
int NumSends;
static int RefCount;
static bool EncoderInit;
};
//-----------------------------------------------------------------------------
#endif // WWPACKET_H
//int ExecuteTime;
//int ReturnCode;
//void Set_Execute_Time(int execute_time);
//int Get_Execute_Time() const {return ExecuteTime;}
//void Set_Return_Code(int return_code) {ReturnCode = return_code;}
//int Get_Return_Code() const {return ReturnCode;}
//BYTE Get_Type() const {WWASSERT(Type != UNDEFINED_TYPE); return Type;}//NEW
//
// Maximum Transmission Unit (MTU) for LAN is 1500.
//
//const int CRC_PLACEHOLDER = 99999;
//const USHORT PACKET_HEADER_SIZE = 17;
//const USHORT MAX_LAN_PACKET_APP_DATA_SIZE = MAX_BUFFER_SIZE;
//const USHORT MAX_INTERNET_PACKET_APP_DATA_SIZE = 512;
//const USHORT MAX_PACKET_APP_DATA_SIZE = 512;