/*
**	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/comnetrcvinst.cpp                   $*
 *                                                                                             *
 *                      $Author:: Steve_t                                                     $*
 *                                                                                             *
 *                     $Modtime:: 2/22/02 7:00p                                               $*
 *                                                                                             *
 *                    $Revision:: 118                                                        $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include "comnetrcvinst.h"

#include "gameobjmanager.h"
#include "playermanager.h"
#include "cnetwork.h"
#include "WWAudio.H"
#include "ccamera.h"
#include "gametype.h"
#include "textdisplay.h"
#include "wwprofile.h"
#include "serverfps.h"
#include "useroptions.h"
#include "serversettings.h"
#include "consolemode.h"
#include "demosupport.h"

//-----------------------------------------------------------------------------
void	CombatNetworkReceiverInstanceClass::Print( const char *format, ... )
{
	va_list arg_list;
	va_start (arg_list, format);
	StringClass string;
	string.Format_Args( format, arg_list );

	ConsoleBox.Print_Maybe(string);
	if (!ConsoleBox.Is_Exclusive()) {
		WWASSERT( Get_Text_Display() );
		Get_Text_Display()->Print_System( string );
	}
	va_end (arg_list);
}

//-----------------------------------------------------------------------------
void	CombatNetworkReceiverInstanceClass::Print( const Vector3 & color, const char *format, ... )
{
	WWASSERT( Get_Text_Display() );
	va_list arg_list;
	va_start (arg_list, format);
	StringClass string;
	string.Format_Args( format, arg_list );
	WideStringClass ws(string, true);
	ConsoleBox.Add_Message(&ws, (Vector3*)&color);
	if (!ConsoleBox.Is_Exclusive()) {
		WWASSERT( Get_Text_Display() );
		Get_Text_Display()->Print( string, color );
	}
	va_end (arg_list);
}

//-----------------------------------------------------------------------------
void CombatNetworkReceiverInstanceClass::Server_Send_Delete_Notifications(void)
{
   WWASSERT(cNetwork::I_Am_Server());

	if (IS_SOLOPLAY) {
		return;
	}

   //
   // Figure out what data to send to each player (client)
   //
   for (
		SLNode<cPlayer> * player_node = cPlayerManager::Get_Player_Object_List()->Head();
		player_node;
		player_node = player_node->Next()) {

		cPlayer * p_player = player_node->Data();
      WWASSERT(p_player != NULL);

      int client_id = p_player->Get_Id();

		if (client_id < 0) {
			//
			// AI... no sends
			//
			continue;
		}

		if (p_player->Get_Is_Active().Is_False()) {
			continue;
		}

		if (p_player->Get_Is_In_Game().Is_False()) {
			//
			// Loading... delay send
			//
			continue;
		}

		cNetwork::Tell_Client_About_Delete_Notifications(client_id);
   }

	return ;
}

//-----------------------------------------------------------------------------
bool CombatNetworkReceiverInstanceClass::Server_Update_Dynamic_Objects(bool is_urgent)
{
   WWASSERT(cNetwork::I_Am_Server());

	if (IS_SOLOPLAY) {
		return(false);
	}

	DEMO_SECURITY_CHECK;

	//
	// Limit the maximum rate at which the server sends updates.
	// This code will result in updates at a little less than NetUpdateRate.
	// This saves bandwidth at the cost of added latency.
	//
	static DWORD last_update_time = 0;
   DWORD time_now = TIMEGETTIME();
	if (!is_urgent && (time_now - last_update_time < 1000 / (float) cUserOptions::NetUpdateRate.Get())) {
		return(false);
	}
	last_update_time = time_now;

	//
	// cRemoteHost figures out when to update priorities based on this so keep it in sync with the NetUpdateRate.
	//
	cRemoteHost::Set_Priority_Update_Rate(cUserOptions::NetUpdateRate.Get());

   //
   // TSS - bug
	// Must handle sniper... also, should use camera position
   // rather than commando position
   //

   //
   // Figure out what data to send to each player (client)
   //
   for (
		SLNode<cPlayer> * player_node = cPlayerManager::Get_Player_Object_List()->Head();
		player_node;
		player_node = player_node->Next()) {

		cPlayer * p_player = player_node->Data();
      WWASSERT(p_player != NULL);

      int client_id = p_player->Get_Id();

		if (client_id < 0) {
			//
			// AI... no sends
			//
			continue;
		}

		if (p_player->Get_Is_Active().Is_False()) {
			continue;
		}

		if (!is_urgent && p_player->Get_Is_In_Game().Is_False()) {
			//
			// Loading... delay sends
			//
			continue;
		}

		//is_urgent = true;//XXX

		if (!is_urgent) {
			//
			// Do not send to the client at a rate much higher than his framerate
			// This code will only impact if his framerate is below NetUpdateRate.
			// This code should help prevent players whose framerate bogs from
			// getting packet-swamped.
			//
			int min_delay_ms = 0;
			if (p_player->Get_Fps() > 0) {
				const float arbitrary_scale_factor = 1.0;
				min_delay_ms = (int)(arbitrary_scale_factor * 1000 / (float) p_player->Get_Fps());
			}
			int time_elapsed_ms = time_now - p_player->Get_Last_Update_Time_Ms();
			if (time_elapsed_ms < min_delay_ms) {
				continue;
			}
		}

		p_player->Set_Last_Update_Time_Ms(time_now);

      SmartGameObj * p_soldier = GameObjManager::Find_Soldier_Of_Client_ID(client_id);

		Vector3 dest_pos;
      if (p_soldier == NULL) {
			//
			// Act like the guy is far, far away.
			// He'll only receive priority 1 messages.
			//
			dest_pos.Set(Vector3(0, 0, -10000));
		} else {
         p_soldier->Get_Position(&dest_pos);

			//
			// His eyes are say 1.5 m above his feet
			//
			dest_pos.Z += 1.5;
		}

      cNetwork::Tell_Client_About_Dynamic_Objects(client_id, dest_pos);
   }
	return(true);
}

//-----------------------------------------------------------------------------
bool CombatNetworkReceiverInstanceClass::Client_Update_Dynamic_Objects(bool is_urgent)
{
   //
	// TSS092001
	// Do not send updates to the server at a rate higher than NetUpdateRate.
	// If the server framerate is below this, match the client send rate to it.
	//

	/*TSS092101*/
	WWASSERT(cNetwork::I_Am_Client());

	static DWORD last_update_time_ms = 0;
   DWORD time_now_ms = TIMEGETTIME();
	DWORD time_elapsed_ms = time_now_ms - last_update_time_ms;

	int max_updates_per_second = cUserOptions::NetUpdateRate.Get();
	WWASSERT(cServerFps::Get_Instance() != NULL);
	int server_fps = cServerFps::Get_Instance()->Get_Fps();
	if (server_fps > 0 && server_fps < cUserOptions::NetUpdateRate.Get()) {
		max_updates_per_second = server_fps;
	}

	DWORD min_delay_ms = (DWORD)(1000 / (float) max_updates_per_second);

	//is_urgent = true;//XXX

	if (is_urgent || (time_elapsed_ms >= min_delay_ms)) {
		last_update_time_ms = time_now_ms;
		cNetwork::Tell_Server_About_Dynamic_Objects();
		return(true);
	}
	return(false);
	/**/
}