661 lines
No EOL
17 KiB
C++
661 lines
No EOL
17 KiB
C++
/*
|
|
** 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
|