mirror of
https://github.com/electronicarts/CnC_Renegade.git
synced 2025-12-15 23:21:40 -05:00
Initial commit of Command & Conquer Renegade source code.
This commit is contained in:
396
Code/wwnet/BWBalance.cpp
Normal file
396
Code/wwnet/BWBalance.cpp
Normal 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
105
Code/wwnet/BWBalance.h
Normal 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
2672
Code/wwnet/connect.cpp
Normal file
File diff suppressed because it is too large
Load Diff
278
Code/wwnet/connect.h
Normal file
278
Code/wwnet/connect.h
Normal 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 ¤t);
|
||||
#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
49
Code/wwnet/fromaddress.h
Normal 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
37
Code/wwnet/lan.cpp
Normal 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
120
Code/wwnet/msgstat.cpp
Normal 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
81
Code/wwnet/msgstat.h
Normal 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
192
Code/wwnet/msgstatlist.cpp
Normal 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
80
Code/wwnet/msgstatlist.h
Normal 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
|
||||
202
Code/wwnet/msgstatlistgroup.cpp
Normal file
202
Code/wwnet/msgstatlistgroup.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
81
Code/wwnet/msgstatlistgroup.h
Normal file
81
Code/wwnet/msgstatlistgroup.h
Normal 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
176
Code/wwnet/netstats.cpp
Normal 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
180
Code/wwnet/netstats.h
Normal 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
678
Code/wwnet/netutil.cpp
Normal 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
149
Code/wwnet/netutil.h
Normal 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();
|
||||
661
Code/wwnet/networkobject.cpp
Normal file
661
Code/wwnet/networkobject.cpp
Normal 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
352
Code/wwnet/networkobject.h
Normal 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;
|
||||
64
Code/wwnet/networkobjectfactory.cpp
Normal file
64
Code/wwnet/networkobjectfactory.cpp
Normal 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 ;
|
||||
}
|
||||
138
Code/wwnet/networkobjectfactory.h
Normal file
138
Code/wwnet/networkobjectfactory.h
Normal 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
|
||||
201
Code/wwnet/networkobjectfactorymgr.cpp
Normal file
201
Code/wwnet/networkobjectfactorymgr.cpp
Normal 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 ;
|
||||
}
|
||||
87
Code/wwnet/networkobjectfactorymgr.h
Normal file
87
Code/wwnet/networkobjectfactorymgr.h
Normal 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
|
||||
437
Code/wwnet/networkobjectmgr.cpp
Normal file
437
Code/wwnet/networkobjectmgr.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
160
Code/wwnet/networkobjectmgr.h
Normal file
160
Code/wwnet/networkobjectmgr.h
Normal 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
1814
Code/wwnet/packetmgr.cpp
Normal file
File diff suppressed because it is too large
Load Diff
359
Code/wwnet/packetmgr.h
Normal file
359
Code/wwnet/packetmgr.h
Normal 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
57
Code/wwnet/packettype.h
Normal 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
704
Code/wwnet/rhost.cpp
Normal 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
252
Code/wwnet/rhost.h
Normal 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
79
Code/wwnet/singlepl.cpp
Normal 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
75
Code/wwnet/singlepl.h
Normal 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
240
Code/wwnet/wwnet.dsp
Normal 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
349
Code/wwnet/wwpacket.cpp
Normal 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
159
Code/wwnet/wwpacket.h
Normal 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;
|
||||
Reference in New Issue
Block a user