This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Commando/clienthintmanager.cpp

293 lines
9.2 KiB
C++
Raw Permalink Normal View History

/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando *
* *
* $Archive:: /Commando/Code/Commando/clienthintmanager.cpp $*
* *
* $Author:: Steve_t $*
* *
* $Modtime:: 12/09/01 6:40p $*
* *
* $Revision:: 6 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "clienthintmanager.h"
#include "cshint.h"
#include "cnetwork.h"
#include "combat.h"
#include "gameobjmanager.h"
#include "pscene.h"
#include "apppackettypes.h"
#include "vistable.h"
#include "useroptions.h"
#include "priority.h"
#include "apppacketstats.h"
//
// Class statics
//
//-----------------------------------------------------------------------------
void
cClientHintManager::Think
(
void
)
{
if (cNetwork::I_Am_Server() ||
COMBAT_SCENE == NULL ||
cUserOptions::ClientHintFactor.Get() < 1)
{
//
// Bail:
// Only dedicated clients send hints.
// Also, ClientHintFactor is disabled by using any value < 1.
//
return;
}
WWASSERT(cNetwork::I_Am_Only_Client());
SoldierGameObj * p_my_soldier = GameObjManager::Find_Soldier_Of_Client_ID(cNetwork::Get_My_Id());
if (p_my_soldier == NULL)
{
//
// Bail...
//
return;
}
//
// Do not allow hints to go out at too high a frequency.
//
static DWORD last_hint_time_ms = 0;
DWORD time_now_ms = TIMEGETTIME();
const DWORD MIN_HINT_DELAY_MS = 1000;
if (time_now_ms - last_hint_time_ms < MIN_HINT_DELAY_MS)
{
//
// Bail...
//
return;
}
//
// Examine all the vis-visible soldiers and vehicles. If we find one that hasn't been
// updated for a suspiciously long time, send a hint to the server, He'll follow
// up with an update.
//
Vector3 my_position;
p_my_soldier->Get_Position(&my_position);
my_position.Z += 1.5;
VisTableClass * pvs = COMBAT_SCENE->Get_Vis_Table(my_position);
int num_objects = 0;
//ULONG total_delay_ms = 0;
//ULONG maximum_delay_ms = 0;
//int longest_delayed_index = -1;
int count = NetworkObjectMgrClass::Get_Object_Count();
NetworkObjectClass **object_list = (NetworkObjectClass **) _alloca((count * sizeof(NetworkObjectClass*)) + 128);
WWASSERT(object_list != NULL);
SmartGameObj * player_ptr = GameObjManager::Find_Soldier_Of_Client_ID(cNetwork::Get_My_Id());
WWASSERT(player_ptr != NULL);
//
// Traverse the vehicles and soldiers and gather data about update rates.
//
for (int index = 0; index < count; index ++)
{
NetworkObjectClass * p_object = NetworkObjectMgrClass::Get_Object(index);
WWASSERT(p_object != NULL);
BYTE type = p_object->Get_App_Packet_Type();
if (type == APPPACKETTYPE_SOLDIER || type == APPPACKETTYPE_VEHICLE)
{
if (p_object->Get_Clientside_Update_Frequency()) {
int vis_id = p_object->Get_Vis_ID();
//
// If there is no vis data, proceed.
// If vis data does exist then only proceed with this object if it is vis-visible.
//
if (pvs == NULL || vis_id == -1 || pvs->Get_Bit(vis_id))
{
// Just add the object to the list on this pass.
object_list[num_objects++] = p_object;
// Work out the priority of this object from the clients perspective.
if (player_ptr) {
Vector3 position;
player_ptr->Get_Position(&position);
float priority = cPriority::Compute_Object_Priority(cNetwork::Get_My_Id(), position, p_object, true);
p_object->Set_Cached_Priority(priority);
}
#if (0)
ULONG delay_ms = time_now_ms - p_object->Get_Last_Clientside_Update_Time();
total_delay_ms += delay_ms;
num_objects++;
if (delay_ms > maximum_delay_ms)
{
maximum_delay_ms = delay_ms;
longest_delayed_index = index;
}
#endif //(0)
}
}
}
}
if (num_objects < 2) {
return;
}
//
// Sort the object list. Lowest priority first.
//
qsort(object_list, num_objects, sizeof(unsigned long), (int (__cdecl *)(const void *,const void *)) &Priority_Compare);
//
// Look for an object that has a higher priority than it's neighbor but is getting updated much less frequently
//
// Ignore the worst priority object. This might have to change....?
//
int most_broken_object_index = -1;
int worst_percentage = 0;
unsigned long time = TIMEGETTIME();
for (int i=1 ; i<num_objects ; i++) {
int higher_priority_rate = object_list[i]->Get_Clientside_Update_Frequency();
//
// Don't hint for objects with a recent updates. This should prevent us from hinting about objects we just hinted about.
//
if (time - object_list[i]->Get_Last_Clientside_Update_Time() > 1500) {
int lower_priority_rate = object_list[i-1]->Get_Clientside_Update_Frequency();
if (lower_priority_rate && higher_priority_rate) {
// Lower priority rate should be higher in terms of milliseconds.
if (lower_priority_rate < higher_priority_rate) {
// The higher priority object is being updated by the server less frequently. That doesn't seem right.
int percentage = (higher_priority_rate * 100) / lower_priority_rate;
if (percentage > worst_percentage) {
worst_percentage = percentage;
most_broken_object_index = i;
}
}
}
}
}
//
// Calculate the average delay for this set of soldiers and vehicles
//
//float average_delay_ms = 0;
//if (num_objects > 0)
//{
// average_delay_ms = total_delay_ms / (float) num_objects;
//}
//
// If the most OOD object is much more OOD than the average object, then request an update.
//
//if ((longest_delayed_index != -1) &&
// (maximum_delay_ms > cUserOptions::ClientHintFactor.Get() * average_delay_ms))
//{
float hint_factor = cUserOptions::ClientHintFactor.Get();
if (most_broken_object_index != -1 && worst_percentage > 100 + (10.0f * hint_factor)) {
//NetworkObjectClass * p_object = NetworkObjectMgrClass::Get_Object(longest_delayed_index);
NetworkObjectClass * p_object = object_list[most_broken_object_index];
WWASSERT(p_object != NULL);
cCsHint * p_hint = new cCsHint;
p_hint->Init(p_object->Get_Network_ID());
last_hint_time_ms = time_now_ms;
//WWDEBUG_SAY(("cClientHintManager::Think, requesting hint for object id %d, avg = %5.2f, max = %d\n",
// p_object->Get_Network_ID(), average_delay_ms, maximum_delay_ms));
WWDEBUG_SAY(("cClientHintManager::Think, requesting hint for object id %d (%s), priority = %5.2f, ave update rate = %dms\n",
p_object->Get_Network_ID(),
cAppPacketStats::Interpret_Type(p_object->Get_App_Packet_Type()),
p_object->Get_Cached_Priority(),
p_object->Get_Clientside_Update_Frequency()));
WWDEBUG_SAY((" Compared to object id %d with priority %5.2f, avg update rate %dms\n",
object_list[most_broken_object_index-1]->Get_Network_ID(),
object_list[most_broken_object_index-1]->Get_Cached_Priority(),
object_list[most_broken_object_index-1]->Get_Clientside_Update_Frequency()));
WWDEBUG_SAY((" Client hint factor = %5.2f\n", hint_factor));
}
}
//
// Qsort compare function for array of object pointers.
//
int __cdecl cClientHintManager::Priority_Compare(const void **object1, const void **object2)
{
WWASSERT(object1 != NULL);
WWASSERT(object2 != NULL);
NetworkObjectClass *n1 = (NetworkObjectClass*) *object1;
NetworkObjectClass *n2 = (NetworkObjectClass*) *object2;
float p1 = n1->Get_Cached_Priority();
float p2 = n2->Get_Cached_Priority();
if (p1 == p2) {
return(0);
}
if (p1 < p2) {
return(-1);
}
return(1);
}