clienthintmanager.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** Confidential - Westwood Studios ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Commando *
  23. * *
  24. * $Archive:: /Commando/Code/Commando/clienthintmanager.cpp $*
  25. * *
  26. * $Author:: Steve_t $*
  27. * *
  28. * $Modtime:: 12/09/01 6:40p $*
  29. * *
  30. * $Revision:: 6 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "clienthintmanager.h"
  36. #include "cshint.h"
  37. #include "cnetwork.h"
  38. #include "combat.h"
  39. #include "gameobjmanager.h"
  40. #include "pscene.h"
  41. #include "apppackettypes.h"
  42. #include "vistable.h"
  43. #include "useroptions.h"
  44. #include "priority.h"
  45. #include "apppacketstats.h"
  46. //
  47. // Class statics
  48. //
  49. //-----------------------------------------------------------------------------
  50. void
  51. cClientHintManager::Think
  52. (
  53. void
  54. )
  55. {
  56. if (cNetwork::I_Am_Server() ||
  57. COMBAT_SCENE == NULL ||
  58. cUserOptions::ClientHintFactor.Get() < 1)
  59. {
  60. //
  61. // Bail:
  62. // Only dedicated clients send hints.
  63. // Also, ClientHintFactor is disabled by using any value < 1.
  64. //
  65. return;
  66. }
  67. WWASSERT(cNetwork::I_Am_Only_Client());
  68. SoldierGameObj * p_my_soldier = GameObjManager::Find_Soldier_Of_Client_ID(cNetwork::Get_My_Id());
  69. if (p_my_soldier == NULL)
  70. {
  71. //
  72. // Bail...
  73. //
  74. return;
  75. }
  76. //
  77. // Do not allow hints to go out at too high a frequency.
  78. //
  79. static DWORD last_hint_time_ms = 0;
  80. DWORD time_now_ms = TIMEGETTIME();
  81. const DWORD MIN_HINT_DELAY_MS = 1000;
  82. if (time_now_ms - last_hint_time_ms < MIN_HINT_DELAY_MS)
  83. {
  84. //
  85. // Bail...
  86. //
  87. return;
  88. }
  89. //
  90. // Examine all the vis-visible soldiers and vehicles. If we find one that hasn't been
  91. // updated for a suspiciously long time, send a hint to the server, He'll follow
  92. // up with an update.
  93. //
  94. Vector3 my_position;
  95. p_my_soldier->Get_Position(&my_position);
  96. my_position.Z += 1.5;
  97. VisTableClass * pvs = COMBAT_SCENE->Get_Vis_Table(my_position);
  98. int num_objects = 0;
  99. //ULONG total_delay_ms = 0;
  100. //ULONG maximum_delay_ms = 0;
  101. //int longest_delayed_index = -1;
  102. int count = NetworkObjectMgrClass::Get_Object_Count();
  103. NetworkObjectClass **object_list = (NetworkObjectClass **) _alloca((count * sizeof(NetworkObjectClass*)) + 128);
  104. WWASSERT(object_list != NULL);
  105. SmartGameObj * player_ptr = GameObjManager::Find_Soldier_Of_Client_ID(cNetwork::Get_My_Id());
  106. WWASSERT(player_ptr != NULL);
  107. //
  108. // Traverse the vehicles and soldiers and gather data about update rates.
  109. //
  110. for (int index = 0; index < count; index ++)
  111. {
  112. NetworkObjectClass * p_object = NetworkObjectMgrClass::Get_Object(index);
  113. WWASSERT(p_object != NULL);
  114. BYTE type = p_object->Get_App_Packet_Type();
  115. if (type == APPPACKETTYPE_SOLDIER || type == APPPACKETTYPE_VEHICLE)
  116. {
  117. if (p_object->Get_Clientside_Update_Frequency()) {
  118. int vis_id = p_object->Get_Vis_ID();
  119. //
  120. // If there is no vis data, proceed.
  121. // If vis data does exist then only proceed with this object if it is vis-visible.
  122. //
  123. if (pvs == NULL || vis_id == -1 || pvs->Get_Bit(vis_id))
  124. {
  125. // Just add the object to the list on this pass.
  126. object_list[num_objects++] = p_object;
  127. // Work out the priority of this object from the clients perspective.
  128. if (player_ptr) {
  129. Vector3 position;
  130. player_ptr->Get_Position(&position);
  131. float priority = cPriority::Compute_Object_Priority(cNetwork::Get_My_Id(), position, p_object, true);
  132. p_object->Set_Cached_Priority(priority);
  133. }
  134. #if (0)
  135. ULONG delay_ms = time_now_ms - p_object->Get_Last_Clientside_Update_Time();
  136. total_delay_ms += delay_ms;
  137. num_objects++;
  138. if (delay_ms > maximum_delay_ms)
  139. {
  140. maximum_delay_ms = delay_ms;
  141. longest_delayed_index = index;
  142. }
  143. #endif //(0)
  144. }
  145. }
  146. }
  147. }
  148. if (num_objects < 2) {
  149. return;
  150. }
  151. //
  152. // Sort the object list. Lowest priority first.
  153. //
  154. qsort(object_list, num_objects, sizeof(unsigned long), (int (__cdecl *)(const void *,const void *)) &Priority_Compare);
  155. //
  156. // Look for an object that has a higher priority than it's neighbor but is getting updated much less frequently
  157. //
  158. // Ignore the worst priority object. This might have to change....?
  159. //
  160. int most_broken_object_index = -1;
  161. int worst_percentage = 0;
  162. unsigned long time = TIMEGETTIME();
  163. for (int i=1 ; i<num_objects ; i++) {
  164. int higher_priority_rate = object_list[i]->Get_Clientside_Update_Frequency();
  165. //
  166. // Don't hint for objects with a recent updates. This should prevent us from hinting about objects we just hinted about.
  167. //
  168. if (time - object_list[i]->Get_Last_Clientside_Update_Time() > 1500) {
  169. int lower_priority_rate = object_list[i-1]->Get_Clientside_Update_Frequency();
  170. if (lower_priority_rate && higher_priority_rate) {
  171. // Lower priority rate should be higher in terms of milliseconds.
  172. if (lower_priority_rate < higher_priority_rate) {
  173. // The higher priority object is being updated by the server less frequently. That doesn't seem right.
  174. int percentage = (higher_priority_rate * 100) / lower_priority_rate;
  175. if (percentage > worst_percentage) {
  176. worst_percentage = percentage;
  177. most_broken_object_index = i;
  178. }
  179. }
  180. }
  181. }
  182. }
  183. //
  184. // Calculate the average delay for this set of soldiers and vehicles
  185. //
  186. //float average_delay_ms = 0;
  187. //if (num_objects > 0)
  188. //{
  189. // average_delay_ms = total_delay_ms / (float) num_objects;
  190. //}
  191. //
  192. // If the most OOD object is much more OOD than the average object, then request an update.
  193. //
  194. //if ((longest_delayed_index != -1) &&
  195. // (maximum_delay_ms > cUserOptions::ClientHintFactor.Get() * average_delay_ms))
  196. //{
  197. float hint_factor = cUserOptions::ClientHintFactor.Get();
  198. if (most_broken_object_index != -1 && worst_percentage > 100 + (10.0f * hint_factor)) {
  199. //NetworkObjectClass * p_object = NetworkObjectMgrClass::Get_Object(longest_delayed_index);
  200. NetworkObjectClass * p_object = object_list[most_broken_object_index];
  201. WWASSERT(p_object != NULL);
  202. cCsHint * p_hint = new cCsHint;
  203. p_hint->Init(p_object->Get_Network_ID());
  204. last_hint_time_ms = time_now_ms;
  205. //WWDEBUG_SAY(("cClientHintManager::Think, requesting hint for object id %d, avg = %5.2f, max = %d\n",
  206. // p_object->Get_Network_ID(), average_delay_ms, maximum_delay_ms));
  207. WWDEBUG_SAY(("cClientHintManager::Think, requesting hint for object id %d (%s), priority = %5.2f, ave update rate = %dms\n",
  208. p_object->Get_Network_ID(),
  209. cAppPacketStats::Interpret_Type(p_object->Get_App_Packet_Type()),
  210. p_object->Get_Cached_Priority(),
  211. p_object->Get_Clientside_Update_Frequency()));
  212. WWDEBUG_SAY((" Compared to object id %d with priority %5.2f, avg update rate %dms\n",
  213. object_list[most_broken_object_index-1]->Get_Network_ID(),
  214. object_list[most_broken_object_index-1]->Get_Cached_Priority(),
  215. object_list[most_broken_object_index-1]->Get_Clientside_Update_Frequency()));
  216. WWDEBUG_SAY((" Client hint factor = %5.2f\n", hint_factor));
  217. }
  218. }
  219. //
  220. // Qsort compare function for array of object pointers.
  221. //
  222. int __cdecl cClientHintManager::Priority_Compare(const void **object1, const void **object2)
  223. {
  224. WWASSERT(object1 != NULL);
  225. WWASSERT(object2 != NULL);
  226. NetworkObjectClass *n1 = (NetworkObjectClass*) *object1;
  227. NetworkObjectClass *n2 = (NetworkObjectClass*) *object2;
  228. float p1 = n1->Get_Cached_Priority();
  229. float p2 = n2->Get_Cached_Priority();
  230. if (p1 == p2) {
  231. return(0);
  232. }
  233. if (p1 < p2) {
  234. return(-1);
  235. }
  236. return(1);
  237. }