| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622 |
- /*
- ** 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/messages.cpp $*
- * *
- * $Author:: Steve_t $*
- * *
- * $Modtime:: 2/22/02 7:15p $*
- * *
- * $Revision:: 229 $*
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "cnetwork.h"
- #include <stdio.h>
- #include "playermanager.h"
- #include "gameobjmanager.h"
- #include "textdisplay.h"
- #include "input.h"
- #include "combatgmode.h"
- #include "WWAudio.h"
- #include "systimer.h"
- #include "multihud.h"
- #include "useroptions.h"
- #include "devoptions.h"
- #include "god.h"
- #include "playertype.h"
- #include "translatedb.h"
- #include "string_ids.h"
- #include "wolgmode.h"
- #include "bitpackids.h"
- #include "msgstatlistgroup.h"
- #include "vistable.h"
- #include "pscene.h"
- #include "staticanimphys.h"
- #include "combat.h"
- #include "gametype.h"
- #include "colors.h"
- #include "networkobject.h"
- #include "building.h"
- #include "vendor.h"
- #include "networkobjectfactorymgr.h"
- #include "networkobjectfactory.h"
- #include "networkobjectmgr.h"
- #include "cstextobj.h"
- #include "loadingevent.h"
- #include "clientcontrol.h"
- #include "wwprofile.h"
- #include "changeteamevent.h"
- #include "DlgMPTeamSelect.h"
- #include "dlgmessagebox.h"
- #include "apppacketstats.h"
- #include "clientpingmanager.h"
- #include "priority.h"
- #include "crandom.h"
- #include "wwmath.h"
- #include "clienthintmanager.h"
- #include "packetmgr.h"
- #include "specialbuilds.h"
- #include "gameinitmgr.h"
- #include "dlgcncwinscreen.h"
- #include "consolemode.h"
- #include "CDKeyAuth.h"
- static int LastSortedSecond;
- static const float max_update_rate = 140.0f; // Priority 1 update rate
- static const float min_update_rate = 5000.0f; // Priority 0.001 update rate
- static const float unseen_update_rate = 10000.0f; // Priority 0 update rate.
- static const unsigned short infinity_update_rate = 0xffff; // Lowest update rate - no updates at all.
- //-----------------------------------------------------------------------------
- //
- // This is the most crucial place for server filtering
- //
- void cNetwork::Tell_Client_About_Dynamic_Objects
- (
- int client_id,
- Vector3 & dest_pos
- )
- {
- #ifndef BETACLIENT
- if (cDevOptions::UseNewTCADO.Is_False()) {
- WWPROFILE("TCADO");
- WWASSERT(client_id >= 0);
- WWASSERT(cNetwork::I_Am_Server());
- if (Get_Server_Rhost(client_id) == NULL) {
- return;
- }
- if (cNetwork::I_Am_Client() && client_id == cNetwork::Get_My_Id())
- {
- //
- // Server does not send to his own client.
- //
- return;
- }
- int i;
- VisTableClass *pvs = NULL;
- int count = 0;
- {
- WWPROFILE("GetVis");
- pvs = COMBAT_SCENE->Get_Vis_Table (dest_pos);
- count = NetworkObjectMgrClass::Get_Object_Count();
- }
- /*
- ** List of objects requiring frequent updates.
- */
- static DynamicVectorClass<NetworkObjectClass *> object_list;
- /*
- ** List of objects requiring guaranteed updates. We can't schedule these.
- */
- static DynamicVectorClass<NetworkObjectClass *> g_object_list;
- object_list.Clear();
- g_object_list.Clear();
- SoldierGameObj * player_ptr = GameObjManager::Find_Soldier_Of_Client_ID(client_id);
- {
- WWPROFILE("ListBuild");
- /*
- ** Go through the object list once and figure out all the priorities.
- */
- for (int index = 0; index < count; index ++) {
- NetworkObjectClass * p_object = NetworkObjectMgrClass::Get_Object(index);
- if (p_object == NULL) {
- continue;
- }
- float priority = 0.0f;
- if (p_object->Get_App_Packet_Type() == APPPACKETTYPE_SERVERFPS) {
- priority = 0.05f;
- p_object->Set_Cached_Priority(priority);
- object_list.Add(p_object);
- } else {
- /*
- ** Get the base priority. This is just distance from where the server thinks the client is.
- */
- priority = cPriority::Compute_Object_Priority(client_id, dest_pos, p_object);
- p_object->Set_Cached_Priority(priority);
- /*
- ** If we can't see it at all, ignore it unless the priority is really high. A really high priority might indicate
- ** an object update request from the client.
- */
- if (priority < 1.0f) {
- int vis_id = p_object->Get_Vis_ID ();
- if (pvs && vis_id != -1 && !pvs->Get_Bit(vis_id)) {
- priority = 0.0f;
- }
- }
- if (p_object->Get_Object_Dirty_Bit(client_id, NetworkObjectClass::BIT_CREATION) ||
- p_object->Get_Object_Dirty_Bit(client_id, NetworkObjectClass::BIT_RARE) ||
- p_object->Get_Object_Dirty_Bit(client_id, NetworkObjectClass::BIT_OCCASIONAL)) {
- g_object_list.Add(p_object);
- } else {
- if (p_object == player_ptr) {
- priority = 0.8f;
- }
- p_object->Set_Cached_Priority(priority);
- if (priority > 0.001f) {
- /*
- ** Work out the export size if we don't know it already.
- */
- if (p_object->Get_Frequent_Update_Export_Size() == 0) {
- cPacket packet;
- int bits_before = packet.Get_Bit_Write_Position();
- packet.Add(p_object->Get_Network_ID());
- packet.Add(p_object->Get_Object_Dirty_Bits(client_id));
- packet.Add(p_object->Is_Delete_Pending());
- int bits_now = packet.Get_Bit_Write_Position();
- p_object->Export_Frequent (packet);
- int bits_after = packet.Get_Bit_Write_Position();
- int packet_size = 0;
- if (bits_now < bits_after) {
- packet_size = (bits_after - bits_before) / 8;
- /*
- ** Add the packet header.
- */
- packet_size += cPacket::Get_Packet_Header_Size();
- p_object->Set_Frequent_Update_Export_Size(packet_size);
- } else {
- /*
- ** For some reason, some objects have a frequent bit set but there is no frequent update export.
- */
- p_object->Set_Frequent_Update_Export_Size(0);
- }
- }
- object_list.Add(p_object);
- }
- }
- }
- }
- }
- /*
- ** Now we have two lists. One list of non-guaranteed objects that we would like to send and another list of guaranteed
- ** objects we 'must' send.
- */
- /*
- ** Sort the non-guaranteed list.
- ** Bubble sort for now, quick sort later.
- */
- NetworkObjectClass *temp_obj;
- /*
- ** Get our budget in bits per second for this guy.
- */
- cRemoteHost *r_host = cNetwork::Get_Server_Rhost(client_id);
- int bits_per_second = r_host->Get_Target_Bps();
- /*
- ** Convert to bytes.
- */
- int bytes_per_second = bits_per_second >> 3;
- /*
- ** We have NetUpdateRate updates per second.
- */
- int net_update_rate = cUserOptions::NetUpdateRate.Get();
- /*
- ** Work out how many bytes we can send per update.
- */
- int avail_bytes_per_update = bytes_per_second / net_update_rate;
- /*
- ** Factor in the bandwidth multiplier.
- */
- float mult = Get_Server_Rhost(client_id)->Get_Bandwidth_Multiplier();
- if (r_host->Get_Flood()) {
- if (r_host->Get_Target_Bps() < 14400) {
- mult = 0.7f;
- } else {
- mult = 1.0f;
- }
- }
- avail_bytes_per_update = (int) (mult * (float)avail_bytes_per_update);
- unsigned long time = TIMEGETTIME();
- /*
- ** Don't use more than 50% of the available bytes per update for guaranteed packets.
- */
- unsigned long bytes_out = 0;
- unsigned long max_bytes = avail_bytes_per_update; //(avail_bytes_per_update >> 1); //* 3) / 4;
- /*
- ** Figure a compression ratio of 2:1
- */
- max_bytes += bytes_out << 1;
- {
- WWPROFILE("SendG");
- /*
- ** Send the guaranteed stuff first. This has to be done anyway so let's see how much of it there is.
- */
- bool full = false;
- for (i=0 ; i<g_object_list.Count() ; i++) {
- temp_obj = g_object_list[i];
- if (!full || temp_obj->Get_App_Packet_Type() == APPPACKETTYPE_CLIENTBBOEVENT) {
- bytes_out += (Send_Object_Update(temp_obj, client_id) >> 3);
- temp_obj->Set_Last_Update_Time(client_id, time);
- }
- /*
- ** See if we are using too much bandwidth and don't send more if we are.
- */
- if (!full && bytes_out > max_bytes) {
- #ifdef WWDEBUG
- if (cDevOptions::ExtraNetDebug.Is_True()) {
- WWDEBUG_SAY(("*** WARNING: Tell_Client_About_Dynamic_Objects - Insufficient bandwidth to send all guaranteed packets ***\n"));
- WWDEBUG_SAY(("*** After %d objects, bytes_out = %d, max_bytes = %d\n", i, bytes_out, max_bytes));
- }
- #endif //WWDEBUG
- avail_bytes_per_update >>= 1;
- full = true;
- //break;
- }
- }
- }
- /*
- ** Work out the average priority. We will need this later when balancing bandwidth budgets between clients.
- */
- float average_priority = 0.0f;
- int num_priorities = 0;
- {
- WWPROFILE("CalcRate");
- for (i=0 ; i<object_list.Count() ; i++) {
- temp_obj = object_list[i];
- if (temp_obj->Get_App_Packet_Type() != APPPACKETTYPE_SERVERFPS) {
- float pri = temp_obj->Get_Cached_Priority();
- /*
- ** Don't count unseen objects or objects we are getting hints for.
- */
- if (pri > 0.001f && pri < 1.0f) {
- average_priority += pri;
- num_priorities++;
- }
- }
- }
- if (num_priorities) {
- average_priority = average_priority / (float)num_priorities;
- Get_Server_Rhost(client_id)->Set_Average_Priority(average_priority);
- /*
- ** Remaining objects get updated based on priority.
- ** Just set the rate based on priority then we will go back in a second pass and adjust it according to remaining bandwidth.
- */
- float ms_low = max_update_rate;
- float ms_high = min_update_rate;
- float spread = ms_high - ms_low;
- int total_bps = 0;
- for (i=0 ; i<object_list.Count() ; i++) {
- temp_obj = object_list[i];
- float pri = temp_obj->Get_Cached_Priority();
- unsigned long update_rate = infinity_update_rate; //0;
- if (pri > 0.025f) { //01f) {
- update_rate = (unsigned long)(((1.0f - pri) * spread) + ms_low);
- }
- temp_obj->Set_Update_Rate(client_id, (unsigned short) update_rate);
- if (update_rate != infinity_update_rate) {
- int bps = (1000.0f / update_rate) * temp_obj->Get_Frequent_Update_Export_Size();
- total_bps += bps;
- }
- }
- /*
- ** Make that number a per update figure.
- */
- total_bps = total_bps / net_update_rate;
- /*
- ** Now, scale the update rate based on available bandwidth.
- */
- float factor = 1.0;
- if (total_bps) {
- factor = avail_bytes_per_update / total_bps;
- if (factor < 0.00001f) {
- factor = 0.00001f;
- }
- }
- unsigned short rate;
- float float_rate;
- for (i=0 ; i<object_list.Count() ; i++) {
- temp_obj = object_list[i];
- float obj_upd_rate = (float)temp_obj->Get_Update_Rate(client_id);
- if (obj_upd_rate != infinity_update_rate) {
- float_rate = obj_upd_rate / factor;
- rate = (unsigned short) float_rate;
- temp_obj->Set_Update_Rate(client_id, rate);
- }
- }
- } else {
- Get_Server_Rhost(client_id)->Set_Average_Priority(0.0f);
- }
- }
- {
- WWPROFILE("SendN");
- /*
- ** Send those packets whos time has come.
- */
- for (i=0 ; i<object_list.Count() ; i++) {
- temp_obj = object_list[i];
- unsigned long rate = (unsigned long)temp_obj->Get_Update_Rate(client_id);
- if (rate != (unsigned long)infinity_update_rate) {
- if (time - temp_obj->Get_Last_Update_Time(client_id) > rate) {
- Send_Object_Update(temp_obj, client_id);
- temp_obj->Set_Last_Update_Time(client_id, time);
- }
- }
- }
- }
- REF_PTR_RELEASE(pvs);
- } else {
- /*
- **
- ** Optimized version of TCADO
- **
- **
- **
- **
- */
- const unsigned char dirty_check = (NetworkObjectClass::BIT_FREQUENT ^ 0xffffffff) & (NetworkObjectClass::BIT_CREATION | NetworkObjectClass::BIT_RARE | NetworkObjectClass::BIT_OCCASIONAL);
- WWPROFILE("TCADO");
- WWASSERT(client_id >= 0);
- WWASSERT(cNetwork::I_Am_Server());
- if (Get_Server_Rhost(client_id) == NULL) {
- return;
- }
- if (cNetwork::I_Am_Client() && client_id == cNetwork::Get_My_Id())
- {
- //
- // Server does not send to his own client.
- //
- return;
- }
- cRemoteHost *r_host = cNetwork::Get_Server_Rhost(client_id);
- bool update_priorities = (r_host->Get_Priority_Update_Counter() == 0) ? true : false;
- r_host->Increment_Priority_Count();
- int i;
- VisTableClass *pvs = NULL;
- int count = 0;
- bool global_packet_allowance_full = false;
- NetworkObjectClass *temp_obj;
- unsigned long time = TIMEGETTIME();
- int global_count = 0;
- /*
- ** Get our budget in bits per second for this guy.
- */
- int bits_per_second = r_host->Get_Target_Bps();
- /*
- ** Adjust the cut off threshold for vis hidden objects further out if more bandwidth is available.
- */
- float min_vis_distance = 15.0f;
- if (bits_per_second > 60000) {
- min_vis_distance = 50.0f;
- }
- /*
- ** Convert to bytes.
- */
- int bytes_per_second = bits_per_second >> 3;
- /*
- ** We have NetUpdateRate updates per second.
- */
- int net_update_rate = cUserOptions::NetUpdateRate.Get();
- /*
- ** Work out how many bytes we can send per update.
- */
- int avail_bytes_per_update = bytes_per_second / net_update_rate;
- /*
- ** Don't use more than 50% of the available bytes per update for guaranteed packets.
- */
- unsigned long bytes_out = 0;
- unsigned long max_bytes = avail_bytes_per_update; //(avail_bytes_per_update >> 1); //* 3) / 4;
- /*
- ** Figure a compression ratio of 2:1 for guaranteed packets.
- */
- //max_bytes <<= 1;
- {
- WWPROFILE("GetVis");
- if (update_priorities) {
- pvs = COMBAT_SCENE->Get_Vis_Table(dest_pos);
- }
- }
- count = NetworkObjectMgrClass::Get_Object_Count();
- /*
- ** List of objects requiring frequent updates.
- */
- static DynamicVectorClass<NetworkObjectClass *> object_list(500);
- object_list.Reset_Active();
- object_list.Set_Growth_Step(100);
- SoldierGameObj * player_ptr = GameObjManager::Find_Soldier_Of_Client_ID(client_id);
- {
- WWPROFILE("ListBuild");
- /*
- ** Go through the object list once and figure out all the priorities.
- */
- for (int index = 0; index < count; index ++) {
- NetworkObjectClass * p_object = NetworkObjectMgrClass::Get_Object(index);
- if (p_object == NULL) {
- continue;
- }
- float priority = 0.0f;
- /*
- ** SERVERFPS events are low priority but must have some kind of priority.
- */
- if (p_object->Get_App_Packet_Type() == APPPACKETTYPE_SERVERFPS) {
- priority = 0.05f;
- p_object->Set_Cached_Priority_2(client_id, priority);
- object_list.Add(p_object);
- } else {
- unsigned char dirty = p_object->Get_Object_Dirty_Bits(client_id);
- if (dirty & dirty_check) {
- /*
- ** This is a guaranteed packet update. Just send it right away.
- */
- if (!global_packet_allowance_full || p_object->Get_App_Packet_Type() == APPPACKETTYPE_CLIENTBBOEVENT) {
- bytes_out += (Send_Object_Update(p_object, client_id) >> 3);
- p_object->Set_Last_Update_Time(client_id, time);
- }
- global_count++;
- /*
- ** See if we are using too much bandwidth and don't send any more guaranteed packets if we are.
- */
- if (!global_packet_allowance_full && bytes_out > max_bytes) {
- #ifdef WWDEBUG
- if (cDevOptions::ExtraNetDebug.Is_True()) {
- WWDEBUG_SAY(("*** WARNING: Tell_Client_About_Dynamic_Objects - Insufficient bandwidth to send all guaranteed packets ***\n"));
- WWDEBUG_SAY(("*** After %d objects, bytes_out = %d, max_bytes = %d\n", global_count, bytes_out, max_bytes));
- }
- #endif //WWDEBUG
- global_packet_allowance_full = true;
- }
- } else {
- /*
- ** Get the base priority. This is how important we think this object is to this client.
- */
- if ((dirty & NetworkObjectClass::BIT_FREQUENT) != 0) {
- priority = p_object->Get_Cached_Priority_2(client_id);
- if (p_object == player_ptr) {
- if (player_ptr->Is_In_Vehicle()) {
- priority = 0.1f;
- } else {
- priority = 0.8f;
- }
- } else {
- if (p_object->Get_Client_Hint_Count(client_id) > 0) {
- priority = 1.0f;
- p_object->Reset_Client_Hint_Count(client_id);
- } else {
- /*
- ** If we can't see it at all, ignore it unless the priority is really high. A really high priority might indicate
- ** an object update request from the client.
- */
- /*
- ** Even if the client can't see the object, it's probably a good idea to update it if it's really
- ** close to the client. Say 15 meters.
- */
- if (update_priorities) {
- int vis_id = p_object->Get_Vis_ID();
- bool hidden = false;
- if (pvs && vis_id != -1 && !pvs->Get_Bit(vis_id)) {
- hidden = true;
- }
- if (hidden) {
- int distance = cPriority::Get_Object_Distance_2(dest_pos, p_object);
- if (distance > min_vis_distance) {
- /*
- ** Allow some very infrequent updates for distant, hidden objects on broadband connections.
- */
- if (bits_per_second > 100000 && distance < 150.0f) {
- priority = 0.01f;
- } else {
- priority = 0.0f;
- }
- } else {
- /*
- ** It's hidden, but close (maybe as close at 15 meters), so it's probably somewhat important to us.
- */
- priority = 0.2f;
- }
- } else {
- /*
- ** Figure out the priority if it's time or if we couldn't see the object last frame but we can now.
- */
- //if (update_priorities || priority == 0.0f) {
- priority = cPriority::Compute_Object_Priority_2(client_id, dest_pos, p_object, false, player_ptr);
- p_object->Set_Cached_Priority_2(client_id, priority);
- //}
- }
- }
- }
- }
- p_object->Set_Cached_Priority_2(client_id, priority);
- if (priority > 0.001f) {
- /*
- ** Work out the export size if we don't know it already.
- */
- if (p_object->Get_Frequent_Update_Export_Size() == 0) {
- cPacket packet;
- int bits_before = packet.Get_Bit_Write_Position();
- packet.Add(p_object->Get_Network_ID());
- packet.Add(p_object->Get_Object_Dirty_Bits_2(client_id));
- packet.Add(p_object->Is_Delete_Pending());
- int bits_now = packet.Get_Bit_Write_Position();
- p_object->Export_Frequent (packet);
- int bits_after = packet.Get_Bit_Write_Position();
- int packet_size = 0;
- if (bits_now < bits_after) {
- packet_size = (bits_after - bits_before) / 8;
- /*
- ** Add the packet header.
- */
- packet_size += cPacket::Get_Packet_Header_Size();
- p_object->Set_Frequent_Update_Export_Size(packet_size);
- } else {
- /*
- ** For some reason, some objects have a frequent bit set but there is no frequent update export.
- */
- p_object->Set_Frequent_Update_Export_Size(0xff);
- }
- }
- /*
- ** Add this object to our update list.
- */
- if (p_object->Get_Frequent_Update_Export_Size() > 0 && p_object->Get_Frequent_Update_Export_Size() < 0xff) {
- object_list.Add(p_object);
- }
- }
- }
- }
- }
- }
- }
- /*
- ** Now we have a list of non-guaranteed objects that we would like to send.
- */
- /*
- ** Factor in the bandwidth multiplier.
- */
- float mult = Get_Server_Rhost(client_id)->Get_Bandwidth_Multiplier();
- if (r_host->Get_Flood()) {
- if (r_host->Get_Target_Bps() < 14400) {
- mult = 0.7f;
- } else {
- mult = 1.0f;
- }
- }
- avail_bytes_per_update = (int) (mult * (float)avail_bytes_per_update);
- /*
- ** If we used tons of bandwidth on guaranteed packets then throttle back on non-guaranteed stuff.
- */
- if (global_packet_allowance_full) {
- avail_bytes_per_update >>= 1;
- }
- /*
- ** Work out the average priority. We will need this later when balancing bandwidth budgets between clients.
- */
- float average_priority = 0.0f;
- int num_priorities = 0;
- {
- WWPROFILE("CalcRate");
- for (i=0 ; i<object_list.Count() ; i++) {
- temp_obj = object_list[i];
- if (temp_obj->Get_App_Packet_Type() != APPPACKETTYPE_SERVERFPS) {
- float pri = temp_obj->Get_Cached_Priority_2(client_id);
- /*
- ** Don't count unseen objects or objects we are getting hints for.
- */
- if (pri > 0.001f && pri < 1.0f) {
- average_priority += pri;
- num_priorities++;
- }
- }
- }
- if (num_priorities) {
- average_priority = average_priority / (float)num_priorities;
- Get_Server_Rhost(client_id)->Set_Average_Priority(average_priority);
- /*
- ** Remaining objects get updated based on priority.
- ** Just set the rate based on priority then we will go back in a second pass and adjust it according to remaining bandwidth.
- */
- float ms_low = max_update_rate;
- float ms_high = min_update_rate;
- float spread = ms_high - ms_low;
- int total_bps = 0;
- for (i=0 ; i<object_list.Count() ; i++) {
- temp_obj = object_list[i];
- float pri = temp_obj->Get_Cached_Priority_2(client_id);
- unsigned long update_rate = infinity_update_rate; //0;
- if (pri > 0.025f) { //01f) {
- update_rate = (unsigned long)(((1.0f - pri) * spread) + ms_low);
- } else {
- if (pri > 0.009f) {
- update_rate = min_update_rate;
- }
- }
- temp_obj->Set_Update_Rate(client_id, (unsigned short) update_rate);
- if (update_rate != infinity_update_rate) {
- int bps = (1000.0f / update_rate) * temp_obj->Get_Frequent_Update_Export_Size();
- total_bps += bps;
- }
- }
- /*
- ** Make that number a per update figure.
- */
- total_bps = total_bps / net_update_rate;
- /*
- ** Now, scale the update rate based on available bandwidth.
- */
- float factor = 1.0;
- if (total_bps) {
- factor = avail_bytes_per_update / total_bps;
- if (factor < 0.00001f) {
- factor = 0.00001f;
- }
- }
- unsigned short rate;
- float float_rate;
- for (i=0 ; i<object_list.Count() ; i++) {
- temp_obj = object_list[i];
- float obj_upd_rate = (float)temp_obj->Get_Update_Rate(client_id);
- if (obj_upd_rate != infinity_update_rate && obj_upd_rate < (min_update_rate + WWMATH_EPSILON)) {
- float_rate = obj_upd_rate / factor;
- rate = (unsigned short) float_rate;
- temp_obj->Set_Update_Rate(client_id, rate);
- }
- }
- } else {
- Get_Server_Rhost(client_id)->Set_Average_Priority(0.0f);
- }
- }
- {
- WWPROFILE("SendN");
- /*
- ** Send those packets whos time has come.
- */
- for (i=0 ; i<object_list.Count() ; i++) {
- temp_obj = object_list[i];
- unsigned long rate = (unsigned long)temp_obj->Get_Update_Rate(client_id);
- if (rate != (unsigned long)infinity_update_rate) {
- if (time - temp_obj->Get_Last_Update_Time(client_id) > rate) {
- Send_Object_Update(temp_obj, client_id);
- temp_obj->Set_Last_Update_Time(client_id, time);
- }
- }
- }
- }
- if (pvs) {
- REF_PTR_RELEASE(pvs);
- }
- }
- #endif // not BETACLIENT
- }
- //-----------------------------------------------------------------------------
- void cNetwork::Tell_Server_About_Dynamic_Objects
- (
- void
- )
- {
- #ifndef FREEDEDICATEDSERVER
- WWASSERT (cNetwork::I_Am_Client());
- //
- // Loop over each network object
- //
- int count = NetworkObjectMgrClass::Get_Object_Count();
- //int debug_count = 0;
- for (int index = 0; index < count; index ++) {
- NetworkObjectClass * object = NetworkObjectMgrClass::Get_Object(index);
- //
- // Should we send update information for this object?
- //
- if (object != NULL && object->Is_Client_Dirty(0))
- {
- //
- // Transmit the object's data to the server
- //
- Send_Object_Update(object, 0);
- //debug_count++;
- }
- }
- //unsigned long time = TIMEGETTIME() / 1000;
- //WWDEBUG_SAY(("Updated %d objects at %d\n", debug_count, time));
- #endif // !FREEDEDICATEDSERVER
- }
- //-----------------------------------------------------------------------------
- void cNetwork::Tell_Client_About_Delete_Notifications(int client_id)
- {
- #ifndef BETACLIENT
- WWASSERT (client_id >= 0);
- WWASSERT (cNetwork::I_Am_Server ());
- if (Get_Server_Rhost (client_id) == NULL) {
- return;
- }
- //
- // Loop over each network object
- //
- int count = NetworkObjectMgrClass::Get_Object_Count ();
- for (int index = 0; index < count; index ++) {
- NetworkObjectClass *object = NetworkObjectMgrClass::Get_Object (index);
- //
- // Is this object going to be deleted soon?
- //
- if (object != NULL && object->Is_Delete_Pending ()) {
- //
- // Does the local client need this information?
- //
- bool send = true;
- if (cNetwork::I_Am_Client() &&
- client_id == cNetwork::Get_My_Id ())
- {
- send = false;
- }
- if (send)
- {
- //
- // Transmit the object's data to the client
- //
- Send_Object_Update (object, client_id);
- }
- }
- }
- return ;
- #endif // not BETACLIENT
- }
- //-----------------------------------------------------------------------------
- int
- cNetwork::Send_Object_Update(NetworkObjectClass *object, int client_id)
- {
- WWPROFILE( "send obj upd" );
- WWASSERT(client_id >= 0);
- Debug_Network_Prolific((
- "Sending update for object %d to %s",
- object->Get_Network_ID (), Get_Client_String(client_id)));
- WWASSERT(object->Get_Network_ID() > 0);
- // Track number of bits sent.
- int bits_sent = 0;
- //
- // Build a packet that will contain enough information about
- // the object so the client will be able to import the data
- //
- cPacket packet;
- packet.Add(object->Get_Network_ID());
- packet.Add(object->Get_Object_Dirty_Bits(client_id));
- packet.Add(object->Is_Delete_Pending());
- //packet.Add(object->Get_App_Packet_Type());
- BYTE type = object->Get_App_Packet_Type();
- int bits_start = packet.Get_Bit_Write_Position();
- //
- // Send mode is unreliable unless creation/rare/occasional data is present.
- //
- int mode = SEND_UNRELIABLE;
- //
- // TSS101101
- //
- if (object->Is_Delete_Pending()) {
- mode = SEND_RELIABLE;
- }
- //
- // Add creation information to the packet (if necessary)
- //
- if (object->Get_Object_Dirty_Bit (client_id, NetworkObjectClass::BIT_CREATION)) {
- int bits_before = packet.Get_Bit_Write_Position();
- //
- // Add the class id of the object so it can be created on the client.
- //
- uint32 net_classid = object->Get_Network_Class_ID ();
- packet.Add (net_classid);
- //
- // Lookup the factory for this object
- //
- NetworkObjectFactoryClass *factory = NetworkObjectFactoryMgrClass::Find_Factory (net_classid);
- WWASSERT (factory != NULL);
- //
- // Store any data in the packet that's needed in order
- // to create the object on the client
- //
- factory->Prep_Packet (object, packet);
- //
- // Send object-specific data to the client
- //
- object->Export_Creation (packet);
- int bits_after = packet.Get_Bit_Write_Position();
- cAppPacketStats::Increment_Bits_Sent_Tier(type, PACKET_TIER_CREATION, bits_after - bits_before);
- mode = SEND_RELIABLE;
- }
- //
- // Add rare information to the packet (if necessary)
- //
- if (object->Get_Object_Dirty_Bit (client_id, NetworkObjectClass::BIT_RARE)) {
- int bits_before = packet.Get_Bit_Write_Position();
- object->Export_Rare (packet);
- int bits_after = packet.Get_Bit_Write_Position();
- cAppPacketStats::Increment_Bits_Sent_Tier(type, PACKET_TIER_RARE, bits_after - bits_before);
- mode = SEND_RELIABLE;
- }
- //
- // Add occasional information to the packet (if necessary)
- //
- if (object->Get_Object_Dirty_Bit (client_id, NetworkObjectClass::BIT_OCCASIONAL)) {
- int bits_before = packet.Get_Bit_Write_Position();
- object->Export_Occasional (packet);
- int bits_after = packet.Get_Bit_Write_Position();
- cAppPacketStats::Increment_Bits_Sent_Tier(type, PACKET_TIER_OCCASIONAL, bits_after - bits_before);
- mode = SEND_RELIABLE;
- }
- //
- // Add frequent information to the packet (if necessary)
- //
- if (object->Get_Object_Dirty_Bit (client_id, NetworkObjectClass::BIT_FREQUENT)) {
- int bits_before = packet.Get_Bit_Write_Position();
- object->Export_Frequent (packet);
- int bits_after = packet.Get_Bit_Write_Position();
- cAppPacketStats::Increment_Bits_Sent_Tier(type, PACKET_TIER_FREQUENT, bits_after - bits_before);
- }
- int bits_end = packet.Get_Bit_Write_Position();
- if (mode == SEND_RELIABLE && object->Get_Unreliable_Override() == true) {
- //
- // The normal rules would make us send this packet reliably, but this
- // has been explicitly overridden.
- //
- mode = SEND_UNRELIABLE;
- }
- //
- // Trying just not sending the packet if there is nothing in it.
- //
- if (object->Is_Delete_Pending() || (bits_end > bits_start)) {
- bits_sent = packet.Get_Bit_Write_Position();
- //
- // Send the packet to the client or server
- //
- if (client_id > 0) {
- //WWDEBUG_SAY(("Sending update for object %d\n", object->Get_Network_ID()));
- Server_Send_Packet(packet, mode, client_id);
- } else {
- Client_Send_Packet(packet, mode);
- }
- }
- #ifdef WWDEBUG
- if (client_id > 0) {
- //
- // It is useful to be able to generate a bunch of
- // extra bandwidth
- //
- for (int i = 0; i < cDevOptions::SpamCount.Get (); i ++) {
- WWDEBUG_SAY(("Sending spam\n"));
- Server_Send_Packet(packet, mode, client_id);
- }
- }
- #endif // WWDEBUG
- //
- // Reset the dirty bits for this client
- //
- object->Set_Object_Dirty_Bit (client_id, NetworkObjectClass::BIT_CREATION, false);
- object->Set_Object_Dirty_Bit (client_id, NetworkObjectClass::BIT_RARE, false);
- object->Set_Object_Dirty_Bit (client_id, NetworkObjectClass::BIT_OCCASIONAL, false);
- //if (client_id > 0)
- {
- //
- // This is a server send. Record stats.
- //
- /*
- //if (type == APPPACKETTYPE_UNKNOWN)
- if (type == APPPACKETTYPE_SIMPLE)
- {
- WWDEBUG_SAY(("WTF is this?\n"));
- }
- /**/
- //
- // Record stats
- //
- cAppPacketStats::Increment_Packets_Sent(type);
- cAppPacketStats::Increment_Bits_Sent(type, bits_end - bits_start);
- }
- return(bits_sent);
- }
- //-----------------------------------------------------------------------------
- void cNetwork::Intermission_Over_Processing(void)
- {
- WWDEBUG_SAY(("cNetwork::Intermission_Over_Processing\n"));
- WWASSERT(cNetwork::I_Am_Server());
- //
- // Terminate the win screen dialog
- //
- CNCWinScreenMenuClass::Close_Dialog ();
- //
- // Destroy any commando's, let them be recreated afresh
- //
- for (
- SLNode<cPlayer> * objnode = cPlayerManager::Get_Player_Object_List()->Head();
- objnode; objnode =
- objnode->Next()) {
- cPlayer * p_player = objnode->Data();
- WWASSERT(p_player != NULL);
- Delete_Player_Objects(p_player->Get_Id());
- if (p_player->Is_Human()) {
- //
- // Set their ingame flag to false.
- // They will each notify us when they have finished loading.
- //
- p_player->Set_Is_In_Game(false);
- if (cNetwork::PServerConnection) {
- cNetwork::PServerConnection->Set_Rhost_Is_In_Game(p_player->Get_Id(), false);
- }
- }
- }
- if (cGameData::Is_Manual_Exit()) {
- GameInitMgrClass::Set_Needs_Game_Exit_All(true);
- } else {
- //
- // If we have to quit out now then don't do a core restart.
- //
- if (The_Game () != NULL && The_Game ()->Is_Map_Cycle_Over () == false) {
- extern bool g_b_core_restart;
- g_b_core_restart = true;
- GameModeClass *game_mode = GameModeManager::Find("WOL");
- if (game_mode && game_mode->Is_Active()) {
- WolGameModeClass* wol_game = (WolGameModeClass*) game_mode;
- if (wol_game->Post_Game_Check()) {
- g_b_core_restart = false;
- }
- }
- } else {
- GameInitMgrClass::Set_Needs_Game_Exit (true);
- }
- }
- //
- // TSS092601
- //
- WWASSERT(PTheGameData != NULL);
- The_Game()->IsIntermission.Set(false);
- }
- //-----------------------------------------------------------------------------
- void cNetwork::End_Game_Test(void)
- {
- #ifndef BETACLIENT
- WWASSERT(cNetwork::I_Am_Server());
- if (IS_MISSION || !GameModeManager::Find("Combat")->Is_Active()) {
- return;
- }
- WWASSERT(PTheGameData != NULL);
- if (The_Game()->IsIntermission.Is_True()) {
- if (The_Game()->Get_Intermission_Time_Remaining() < WWMATH_EPSILON) {
- Intermission_Over_Processing();
- }
- } else {
- //
- // Sort teams and players once a second
- //
- bool is_game_over = The_Game()->Is_Game_Over();
- bool sort=is_game_over;
- int seconds=cMathUtil::Round(TimeManager::Get_Seconds());
- if (!sort) {
- if (seconds!=LastSortedSecond) {
- LastSortedSecond=seconds;
- sort=true;
- }
- }
- if (sort) {
- cTeamManager::Sort_Teams();
- cPlayerManager::Sort_Players(true);
- }
- if (is_game_over) {
- The_Game()->Game_Over_Processing();
- }
- }
- #endif // not BETACLIENT
- }
- //-----------------------------------------------------------------------------
- void cNetwork::Shared_Client_And_Server_Think(void)
- {
- WWASSERT(I_Am_Client() || I_Am_Server());
- WWASSERT(PTheGameData != NULL);
- The_Game()->Think();
- Hibernation_Think();
- }
- //-----------------------------------------------------------------------------
- bool cNetwork::Client_Think(void)
- {
- bool ret_code = false;
- #ifndef FREEDEDICATEDSERVER
- if (PClientConnection->Is_Destroy()) {
- Cleanup_Client();
- return(ret_code);
- }
- if (!PClientConnection->Have_Id()) {
- return(ret_code);
- }
- //
- // Client reload if necessary
- //
- WWASSERT(PTheGameData != NULL);
- if (I_Am_Only_Client() &&
- The_Game()->IsIntermission.Is_True() &&
- The_Game()->Get_Intermission_Time_Remaining() < WWMATH_EPSILON)
- {
- //
- // Terminate the win screen dialog
- //
- CNCWinScreenMenuClass::Close_Dialog ();
- //
- // See if we have the next map in the cycle.
- //
- bool quit = false;
- if (The_Game()->Is_Map_Cycle_Over()) {
- quit = true;
- } else {
- if (!The_Game()->Is_Map_Valid()) {
- quit = true;
- }
- }
- if (quit) {
- //
- // If the map looping is over, then quit the game
- //
- GameInitMgrClass::Set_Needs_Game_Exit (true);
- } else {
- extern bool g_b_core_restart;
- g_b_core_restart = true;
- }
- }
- cClientPingManager::Think();
- cClientHintManager::Think();
- WWASSERT(Receiver!= NULL);
- ret_code = Receiver->Client_Update_Dynamic_Objects();
- //
- // Pop up the team change dialog if it is appropriate and we have not already
- // done so.
- //
- if (!HaveDoneTeamChangeDialog && cChangeTeamEvent::Is_Change_Team_Possible()
- && DlgMsgBox::Get_Current_Count() == 0) {
- HaveDoneTeamChangeDialog = true;
- DlgMPTeamSelect::DoDialog(*The_Game());
- }
- //
- // Pop up the MOTD dialog if appropriate.
- //
- if (!HaveDoneMotdDialog) {
- if (I_Am_Only_Client()) {
- if ((DlgMsgBox::Get_Current_Count() == 0)
- && GameModeManager::Find("Combat") != NULL && GameModeManager::Find("Combat")->Is_Active()
- && The_Game() != NULL && ::wcslen(The_Game()->Get_Motd()) > 0) {
- DlgMsgBox::DoDialog(TRANSLATE (IDS_MENU_MOTD), The_Game()->Get_Motd());
- HaveDoneMotdDialog = true;
- }
- } else {
- HaveDoneMotdDialog = true;
- }
- }
- #endif // !FREEDEDICATEDSERVER
- return(ret_code);
- }
- //-----------------------------------------------------------------------------
- VisTableClass * cNetwork::Peek_Temp_Vis_Table(void)
- {
- bool alloc_new_table = false;
- if (VisTable == NULL) {
- alloc_new_table = true;
- } else if (VisTable->Get_Bit_Count() != COMBAT_SCENE->Get_Vis_Table_Size()) {
- alloc_new_table = true;
- }
- if (alloc_new_table) {
- REF_PTR_RELEASE(VisTable);
- VisTable = new VisTableClass(COMBAT_SCENE->Get_Vis_Table_Size(), 0);
- }
- WWASSERT(VisTable != NULL);
- VisTable->Reset_All();
- return VisTable;
- }
- //-----------------------------------------------------------------------------
- void cNetwork::Hibernation_Think(void)
- {
- if (COMBAT_SCENE != NULL) {
- //
- // The server can't let any player go into hibernation or updates will cease to be sent for that player.
- //
- if (I_Am_Server() && !IS_MISSION) {
- for (
- SLNode<BaseGameObj> * p_objnode = GameObjManager::Get_Game_Obj_List()->Head();
- p_objnode;
- p_objnode = p_objnode->Next()) {
- PhysicalGameObj * p_phys_go = p_objnode->Data()->As_PhysicalGameObj();
- if (p_phys_go != NULL) {
- p_phys_go->Reset_Hibernating();
- }
- }
- } else {
- VisTableClass * p_vis_table = Peek_Temp_Vis_Table();
- WWASSERT(p_vis_table != NULL);
- //
- // Build a union of all players' PVS's
- //
- for (
- SLNode<SmartGameObj> * p_smart_objnode = GameObjManager::Get_Smart_Game_Obj_List()->Head();
- p_smart_objnode;
- p_smart_objnode = p_smart_objnode->Next()) {
- WWASSERT(p_smart_objnode->Data() != NULL);
- SoldierGameObj * p_soldier = p_smart_objnode->Data()->As_SoldierGameObj();
- if (p_soldier != NULL && p_soldier->Has_Player()) {
- Vector3 player_pos;
- p_soldier->Get_Position(&player_pos);
- player_pos.Z += 2; // Start near the player's head
- VisTableClass * p_player_pvs = COMBAT_SCENE->Get_Vis_Table(player_pos);
- if (p_player_pvs == NULL) {
- p_vis_table = NULL;
- break;
- } else {
- p_vis_table->Merge(*p_player_pvs);
- REF_PTR_RELEASE(p_player_pvs);
- }
- }
- }
- //
- // Reset hibernating on anything visible
- //
- Vector3 star_pos(0,0,0);
- if ( COMBAT_STAR ) {
- COMBAT_STAR->Get_Position( &star_pos );
- }
- for (
- SLNode<BaseGameObj> * p_objnode = GameObjManager::Get_Game_Obj_List()->Head();
- p_objnode;
- p_objnode = p_objnode->Next()) {
- PhysicalGameObj * p_phys_go = p_objnode->Data()->As_PhysicalGameObj();
- if (p_phys_go != NULL) {
- PhysClass * p_phys_obj = p_phys_go->Peek_Physical_Object();
- if (p_phys_obj != NULL &&
- (p_vis_table == NULL || p_vis_table->Get_Bit(p_phys_obj->Get_Vis_Object_ID()))) {
- // Only if within 300m of the star;
- #define MIN_HIB_DISTANCE 300
- Vector3 pos;
- p_phys_go->Get_Position( &pos );
- pos -= star_pos;
- if ( pos.Length2() < MIN_HIB_DISTANCE * MIN_HIB_DISTANCE ) {
- p_phys_go->Reset_Hibernating();
- }
- }
- }
- }
- }
- }
- }
- //-----------------------------------------------------------------------------
- bool cNetwork::Server_Think(void)
- {
- WWASSERT(I_Am_Server());
- bool ret_code = false;
- {
- WWPROFILE( "God Think" );
- if (GameModeManager::Find( "Combat" )->Is_Active()) {
- cGod::Think();
- }
- }
- {
- WWPROFILE( "End_Game_Test" );
- End_Game_Test();
- }
- //
- // Update clients about dynamic & static objects
- //
- WWASSERT(Receiver!= NULL);
- //
- // TSS121101
- // The following I_Am_Server test seems to be necessary. In v 85
- // we were getting occasional I_Am_Server asserts on the server
- // inside Server_Update_Dynamic_Objects.
- //
- if (I_Am_Server())
- {
- {
- WWPROFILE( "svrupd dyn" );
- ret_code = Receiver->Server_Update_Dynamic_Objects();
- }
- {
- WWPROFILE( "svrupd del" );
- Receiver->Server_Send_Delete_Notifications();
- }
- }
- return(ret_code);
- }
- //-----------------------------------------------------------------------------
- void cNetwork::Server_Kill_Connection( int client_id )
- {
- WWASSERT(client_id >= 0);
- WWASSERT(I_Am_Server());
- PServerConnection->Destroy_Connection( client_id );
- WWDEBUG_SAY(("cNetwork::Server_Kill_Connection for client %d\n", client_id));
- }
- //-----------------------------------------------------------------------------
- void cNetwork::Delete_Player_Objects(int client_id)
- {
- WWDEBUG_SAY(("cNetwork::Delete_Player_Objects %d\n", client_id));
- WWASSERT(I_Am_Server());
- //
- // Delete all objects controlled by this guy; inform all clients.
- //
- SLNode<BaseGameObj> * objnode;
- for (objnode = GameObjManager::Get_Game_Obj_List()->Head(); objnode; objnode = objnode->Next()) {
- WWASSERT(objnode->Data() != NULL);
- PhysicalGameObj * p_phys_obj = objnode->Data()->As_PhysicalGameObj();
- if (p_phys_obj != NULL) {
- SmartGameObj * p_smart_obj = p_phys_obj->As_SmartGameObj();
- if (p_smart_obj != NULL &&
- (p_smart_obj->Get_Control_Owner() == client_id))
- {
- //
- // Destroy the object
- //
- p_smart_obj->Set_Delete_Pending ();
- }
- }
- }
- }
- //-----------------------------------------------------------------------------
- //
- // Call this after a client leaves or breaks connection.
- //
- //void cNetwork::Cleanup_After_Client(int client_id, bool is_departure_graceful)
- void cNetwork::Cleanup_After_Client(int client_id)
- {
- WWDEBUG_SAY(("cNetwork::Cleanup_After_Client %d\n", client_id));
- WWASSERT(I_Am_Server());
- Remove_Player(client_id);
- Delete_Player_Objects(client_id);
- NetworkObjectMgrClass::Delete_Client_Objects(client_id);
- //
- // We need to special-case these new delete notifications or delete_pending
- // will clear them first.
- //
- WWASSERT(Receiver!= NULL);
- Receiver->Server_Send_Delete_Notifications();
- NetworkObjectMgrClass::Restore_Dirty_Bits(client_id);
- CCDKeyAuth::DisconnectUser(client_id);
- }
- //-----------------------------------------------------------------------------
- void cNetwork::Remove_Player(int player_id)
- {
- WWDEBUG_SAY(("cNetwork::Remove_Player %d\n", player_id));
- WWASSERT(I_Am_Server());
- cPlayer * p_player = cPlayerManager::Find_Player(player_id);
- //
- // We can't remove the player structure because we wish to retain
- // it for scoring etc
- //
- if (p_player != NULL) {
- p_player->Increment_Total_Time();
- p_player->Set_Is_Active(false);
- StringClass str(128, true);
- p_player->Get_Name().Convert_To(str);
- ConsoleBox.Print_Maybe("Player %s left the game\n", str.Peek_Buffer());
- }
- if (p_player != NULL && IS_MULTIPLAY) {
- Test_For_Team_Defaulting(p_player);
- }
- }
- //-----------------------------------------------------------------------------
- void cNetwork::Test_For_Team_Defaulting(cPlayer * p_player)
- {
- //
- // If all players on the winning team bail, take punitive action:
- // reverse the team scores of Nod and GDI.
- //
- WWDEBUG_SAY(("cNetwork::Test_For_Team_Defaulting\n"));
- WWASSERT(p_player != NULL);
- WWASSERT(IS_MULTIPLAY);
- int count_nod = cPlayerManager::Tally_Team_Size(PLAYERTYPE_NOD);
- int count_gdi = cPlayerManager::Tally_Team_Size(PLAYERTYPE_GDI);
- cTeam * p_team_nod = cTeamManager::Find_Team(PLAYERTYPE_NOD);
- WWASSERT(p_team_nod != NULL);
- cTeam * p_team_gdi = cTeamManager::Find_Team(PLAYERTYPE_GDI);
- WWASSERT(p_team_gdi != NULL);
- float score_nod = p_team_nod->Get_Score();
- float score_gdi = p_team_gdi->Get_Score();
- if (p_player->Get_Player_Type() == PLAYERTYPE_NOD &&
- count_nod == 0 &&
- score_nod > score_gdi) {
- WWDEBUG_SAY(("Reversing Nod and GDI scores due to Nod defaulting.\n"));
- p_team_gdi->Set_Score(score_nod);
- p_team_nod->Set_Score(score_gdi);
- }
- else if (p_player->Get_Player_Type() == PLAYERTYPE_GDI &&
- count_gdi == 0 &&
- score_gdi > score_nod) {
- WWDEBUG_SAY(("Reversing Nod and GDI scores due to GDI defaulting.\n"));
- p_team_gdi->Set_Score(score_nod);
- p_team_nod->Set_Score(score_gdi);
- }
- }
|