/*
** Command & Conquer Generals Zero Hour(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 .
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
/**
* The connection manager maintains a collection of Connections to each player. Messages are queued up
* from here, as well as removed through here.
*/
#pragma once
#ifndef __CONNECTIONMANAGER_H
#define __CONNECTIONMANAGER_H
#include "GameNetwork/Connection.h"
#include "GameNetwork/NetCommandList.h"
#include "GameNetwork/Transport.h"
#include "GameNetwork/FrameDataManager.h"
#include "GameNetwork/FrameMetrics.h"
#include "GameNetwork/NetworkDefs.h"
#include "GameNetwork/DisconnectManager.h"
class GameInfo;
class NetCommandWrapperList;
typedef std::map FileCommandMap;
typedef std::map FileMaskMap;
typedef std::map FileProgressMap;
class ConnectionManager
{
public:
ConnectionManager();
~ConnectionManager();
virtual void init(); ///< Initialize this instance.
virtual void reset(); ///< Take this instance back to the initial state.
virtual void update(Bool isInGame); ///< Service the Connections being managed by this instance.
// End SubsystemInterface functions
void updateRunAhead(Int oldRunAhead, Int frameRate, Bool didSelfSlug, Int nextExecutionFrame); ///< Update the run ahead value. If we are the current packet router, issue the command.
void attachTransport(Transport *transport);
void parseUserList(const GameInfo *game);
void sendChat(UnicodeString text, Int playerMask, UnsignedInt executionFrame);
void sendDisconnectChat(UnicodeString text);
void sendLocalCommand(NetCommandMsg *msg, UnsignedByte relay = 0xff); ///< Send command to the players specified in the relay, goes through packet router.
void sendLocalCommandDirect(NetCommandMsg *msg, UnsignedByte relay); ///< Send command directly to the players specified, doesn't go through packet router.
void sendLocalGameMessage(GameMessage *msg, UnsignedInt frame);
void sendCommand(NetCommandMsg *msg);
Bool allCommandsReady(UnsignedInt frame, Bool justTesting = FALSE);
void handleAllCommandsReady(void);
NetCommandList *getFrameCommandList(UnsignedInt frame);
// void AddConnection(User *user, UnsignedInt slot);
void determineRouterFallbackPlan();
void zeroFrames(UnsignedInt startingFrame, UnsignedInt numFrames);
void destroyGameMessages();
// void createConnections(UnsignedInt numberOfPlayers, UnsignedInt localSlot);
void setLocalAddress(UnsignedInt ip, UnsignedInt port);
void initTransport();
void processFrameTick(UnsignedInt frame);
void handleLocalPlayerLeaving(UnsignedInt frame);
void sendFile(AsciiString path, UnsignedByte playerMask, UnsignedShort commandID);
UnsignedShort sendFileAnnounce(AsciiString path, UnsignedByte playerMask);
Int getFileTransferProgress(Int playerID, AsciiString path);
Bool areAllQueuesEmpty(void);
UnsignedInt getLocalPlayerID();
UnicodeString getPlayerName(Int playerNum);
Int getNumPlayers();
UnsignedInt getPacketRouterFallbackSlot(Int packetRouterNumber); ///< Returns the slot of the given packet router number in the fallback plan.
UnsignedInt getPacketRouterSlot(); ///< Returns the current packet router's slot.
PlayerLeaveCode disconnectPlayer(Int slot); ///< Disconnect this player immediately. This should only be called by the disconnect manager.
void disconnectLocalPlayer(); ///< Does whatever is necessary to get TheNetwork to realize that it should be leaving the game now.
void quitGame(); ///< Disconnect from the game RIGHT NOW!! Tell everyone else we are disconnecting.
void voteForPlayerDisconnect(Int slot); ///< Register a vote for a player to be disconnected.
void resendPendingCommands(); ///< Resend the pending commands to the packet router.
void setFrameGrouping(time_t frameGrouping); ///< Set the number of frames that are grouped together into packets.
PlayerLeaveCode processPlayerLeave(NetPlayerLeaveCommandMsg *msg);
Bool canILeave(); ///< Returns true if the local player is allowed to leave.
// Bandwidth metrics
Real getIncomingBytesPerSecond( void );
Real getIncomingPacketsPerSecond( void );
Real getOutgoingBytesPerSecond( void );
Real getOutgoingPacketsPerSecond( void );
Real getUnknownBytesPerSecond( void );
Real getUnknownPacketsPerSecond( void );
UnsignedInt getPacketArrivalCushion( void );
UnsignedInt getMinimumCushion();
void flushConnections();
void processChat(NetChatCommandMsg *msg); // this actually needs to be public because it is frame-synchronized
void updateLoadProgress( Int progress );
void loadProgressComplete( void );
void sendTimeOutGameStart( void );
Bool isPacketRouter( void );
Bool isPlayerConnected( Int playerID );
void notifyOthersOfCurrentFrame(Int frame);
void sendFrameDataToPlayer(UnsignedInt playerID, UnsignedInt startingFrame);
void sendSingleFrameToPlayer(UnsignedInt playerID, UnsignedInt frame);
void notifyOthersOfNewFrame(UnsignedInt frame);
UnsignedInt getNextPacketRouterSlot(UnsignedInt playerID); ///< returns the packet router player that comes after the given player.
Int getAverageFPS( void );
Int getSlotAverageFPS(Int slot);
#if defined(_DEBUG) || defined(_INTERNAL)
void debugPrintConnectionCommands();
#endif
// For disconnect blame assignment
UnsignedInt getPingFrame();
Int getPingsSent();
Int getPingsRecieved();
private:
void doRelay();
void doKeepAlive();
void sendRemoteCommand(NetCommandRef *msg);
void ackCommand(NetCommandRef *ref, UnsignedInt localSlot);
Bool processNetCommand(NetCommandRef *ref);
void processAckStage1(NetCommandMsg *msg);
void processAckStage2(NetCommandMsg *msg);
void processAck(NetCommandMsg *msg);
void processFrameInfo(NetFrameCommandMsg *msg);
void processRunAheadMetrics(NetRunAheadMetricsCommandMsg *msg);
void processDisconnectChat(NetDisconnectChatCommandMsg *msg);
void processProgress( NetProgressCommandMsg *msg );
void processLoadComplete( NetCommandMsg *msg );
void processTimeOutGameStart( NetCommandMsg *msg );
void processWrapper(NetCommandRef *ref);
void processFrameResendRequest(NetFrameResendRequestCommandMsg *msg);
void processFile(NetFileCommandMsg *ref);
void processFileAnnounce(NetFileAnnounceCommandMsg *ref);
void processFileProgress(NetFileProgressCommandMsg *ref);
// void doPerFrameMetrics(UnsignedInt frame);
void getMinimumFps(Int &minFps, Int &minFpsPlayer); ///< Returns the smallest FPS in the m_fpsAverages list.
Real getMaximumLatency(); ///< This actually sums the two biggest latencies in the m_latencyAverages list.
void requestFrameDataResend(Int playerID, UnsignedInt frame); ///< request of this player that he send the specified frame's data.
// The connections are set up like the slot list. The connection corresponding to the local
// player's position in the slot list will be NULL. Connections corresponding to slots that
// do not have a player will also be NULL.
Connection *m_connections[MAX_SLOTS];
Transport *m_transport;
UnsignedInt m_localSlot;
UnsignedInt m_packetRouterSlot;
UnsignedInt m_packetRouterFallback[MAX_SLOTS];
UnsignedInt m_localAddr;
UnsignedInt m_localPort;
User* m_localUser;
DisconnectManager *m_disconnectManager; ///< Controls the disconnect dialog.
FrameDataManager *m_frameData[MAX_SLOTS];
NetCommandList *m_pendingCommands;
NetCommandList *m_relayedCommands;
FrameMetrics m_frameMetrics;
NetCommandWrapperList *m_netCommandWrapperList;
// These variables are used to keep track of the other players' average fps and latency.
// yup.
Real m_latencyAverages[MAX_SLOTS];
Int m_fpsAverages[MAX_SLOTS];
Int m_minFpsPlayer;
Int m_minFps;
UnsignedInt m_smallestPacketArrivalCushion;
Bool m_didSelfSlug;
// -----------------------------------------------------------------------------
FileCommandMap s_fileCommandMap;
FileMaskMap s_fileRecipientMaskMap;
FileProgressMap s_fileProgressMap[MAX_SLOTS];
// -----------------------------------------------------------------------------
};
#endif