doom3-bfg/neo/sys/sys_lobby.h
Daniel Gibson b38aff8995 Fix sending clients from lobby to game when lobby and game are on same server
In the current case (only "direct" lobby backend, i.e. connect to a
server directly), lobby and game are always on the same server anyway..

It used to send the IP of the first network interface.. that kinda works
on Windows and FreeBSD in LANs (i.e. not over the internet or even
behind a NAT), but not at all on Linux, because the first device seems
to be the loopback device there (at least on my machine)..
Now it sends net_ip (so it should even work behind NAT) or, if net_ip is
set to "localhost" (the default), 0.0.0.0 is sent, which the client
interprets as "just use the IP of the lobby you're already connected to"
2013-03-17 23:35:12 +01:00

1004 lines
39 KiB
C++

/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code 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.
Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "sys_lobby_backend.h"
#define INVALID_LOBBY_USER_NAME " " // Used to be "INVALID" but Sony might not like that.
class idSessionCallbacks;
class idDebugGraph;
/*
========================
idLobby
========================
*/
class idLobby : public idLobbyBase
{
public:
idLobby();
enum lobbyType_t
{
TYPE_PARTY = 0,
TYPE_GAME = 1,
TYPE_GAME_STATE = 2,
TYPE_INVALID = 0xff
};
enum lobbyState_t
{
STATE_IDLE,
STATE_CREATE_LOBBY_BACKEND,
STATE_SEARCHING,
STATE_OBTAINING_ADDRESS,
STATE_CONNECT_HELLO_WAIT,
STATE_FINALIZE_CONNECT,
STATE_FAILED,
NUM_STATES
};
enum failedReason_t
{
FAILED_UNKNOWN,
FAILED_CONNECT_FAILED,
FAILED_MIGRATION_CONNECT_FAILED,
};
void Initialize( lobbyType_t sessionType_, class idSessionCallbacks* callbacks );
void StartHosting( const idMatchParameters& parms );
void StartFinding( const idMatchParameters& parms_ );
void Pump();
void ProcessSnapAckQueue();
void Shutdown( bool retainMigrationInfo = false, bool skipGoodbye = false ); // Goto idle state
void HandlePacket( lobbyAddress_t& remoteAddress, idBitMsg fragMsg, idPacketProcessor::sessionId_t sessionID );
lobbyState_t GetState()
{
return state;
}
virtual bool HasActivePeers() const;
virtual bool IsLobbyFull() const
{
return NumFreeSlots() == 0;
}
int NumFreeSlots() const;
public:
enum reliablePlayerToPlayer_t
{
//RELIABLE_PLAYER_TO_PLAYER_VOICE_EVENT,
RELIABLE_PLAYER_TO_PLAYER_GAME_DATA,
// Game messages would be reserved here in the same way that RELIABLE_GAME_DATA is.
// I'm worried about using up the 0xff values we have for reliable type, so I'm not
// going to reserve anything here just yet.
NUM_RELIABLE_PLAYER_TO_PLAYER,
};
enum reliableType_t
{
RELIABLE_HELLO, // host to peer : connection established
RELIABLE_USER_CONNECTED, // host to peer : a new session user connected
RELIABLE_USER_DISCONNECTED, // host to peer : a session user disconnected
RELIABLE_START_LOADING, // host to peer : peer should begin loading the map
RELIABLE_LOADING_DONE, // peer to host : finished loading map
RELIABLE_IN_GAME, // peer to host : first full snap received, in game now
RELIABLE_SNAPSHOT_ACK, // peer to host : got a snapshot
RELIABLE_RESOURCE_ACK, // peer to host : got some new resources
RELIABLE_CONNECT_AND_MOVE_TO_LOBBY, // host to peer : connect to this server
RELIABLE_PARTY_CONNECT_OK, // host to peer
RELIABLE_PARTY_LEAVE_GAME_LOBBY, // host to peer : leave game lobby
RELIABLE_MATCH_PARMS, // host to peer : update in match parms
RELIABLE_UPDATE_MATCH_PARMS, // peer to host : peer updating match parms
// User join in progress msg's (join in progress for the party/game lobby, not inside a match)
RELIABLE_USER_CONNECT_REQUEST, // peer to host: local user wants to join session in progress
RELIABLE_USER_CONNECT_DENIED, // host to peer: user join session in progress denied (not enough slots)
// User leave in progress msg's (leave in progress for the party/game lobby, not inside a match)
RELIABLE_USER_DISCONNECT_REQUEST, // peer to host: request host to remove user from session
RELIABLE_KICK_PLAYER, // host to peer : kick a player
RELIABLE_MATCHFINISHED, // host to peer - Match is in post looking at score board
RELIABLE_ENDMATCH, // host to peer - End match, and go to game lobby
RELIABLE_ENDMATCH_PREMATURE, // host to peer - End match prematurely, and go to game lobby (onl possible in unrated/custom games)
RELIABLE_SESSION_USER_MODIFIED, // peer to host : user changed something (emblem, name, etc)
RELIABLE_UPDATE_SESSION_USER, // host to peers : inform all peers of the change
RELIABLE_HEADSET_STATE, // * to * : headset state change for user
RELIABLE_VOICE_STATE, // * to * : voice state changed for user pair (mute, unmute, etc)
RELIABLE_PING, // * to * : send host->peer, then reflected
RELIABLE_PING_VALUES, // host to peers : ping data from lobbyUser_t for everyone
RELIABLE_BANDWIDTH_VALUES, // peer to host: data back about bandwidth test
RELIABLE_ARBITRATE, // host to peer : start arbitration
RELIABLE_ARBITRATE_OK, // peer to host : ack arbitration request
RELIABLE_POST_STATS, // host to peer : here, write these stats now (hacky)
RELIABLE_MIGRATION_GAME_DATA, // host to peers: game data to use incase of a migration
RELIABLE_START_MATCH_GAME_LOBBY_HOST, // game lobby host to game state lobby host: start the match, since all players are in
RELIABLE_DUMMY_MSG, // used as a placeholder for old removed msg's
RELIABLE_PLAYER_TO_PLAYER_BEGIN,
// use reliablePlayerToPlayer_t
RELIABLE_PLAYER_TO_PLAYER_END = RELIABLE_PLAYER_TO_PLAYER_BEGIN + NUM_RELIABLE_PLAYER_TO_PLAYER,
// * to * : misc reliable game data above this
RELIABLE_GAME_DATA = RELIABLE_PLAYER_TO_PLAYER_END
};
// JGM: Reliable type in packet is a byte and there are a lot of reliable game messages.
// Feel free to bump this up since it's arbitrary anyway, but take a look at gameReliable_t.
// At the moment, both Doom and Rage have around 32 gameReliable_t values.
compile_time_assert( RELIABLE_GAME_DATA < 64 );
static const char* stateToString[ NUM_STATES ];
// Consts
static const int PEER_HEARTBEAT_IN_SECONDS = 5; // Make sure something was sent every 5 seconds, so we don't time out
static const int CONNECT_REQUEST_FREQUENCY_IN_SECONDS = 5; // Frequency at which we resend a request to connect to a server (will increase in frequency over time down to MIN_CONNECT_FREQUENCY_IN_SECONDS)
static const int MIN_CONNECT_FREQUENCY_IN_SECONDS = 1; // Min frequency of connection attempts
static const int MAX_CONNECT_ATTEMPTS = 5;
static const int BANDWIDTH_REPORTING_MAX = 10240; // make bps to report receiving (clamp if higher). For quantizing
static const int BANDWIDTH_REPORTING_BITS = 16; // number of bits to use for bandwidth reporting
static const int MAX_BPS_HISTORY = 32; // size of outgoing bps history to maintain for each client
static const int MAX_SNAP_SIZE = idPacketProcessor::MAX_MSG_SIZE;
static const int MAX_SNAPSHOT_QUEUE = 64;
static const int OOB_HELLO = 0;
static const int OOB_GOODBYE = 1;
static const int OOB_GOODBYE_W_PARTY = 2;
static const int OOB_GOODBYE_FULL = 3;
static const int OOB_RESOURCE_LIST = 4;
static const int OOB_VOICE_AUDIO = 5;
static const int OOB_MATCH_QUERY = 6;
static const int OOB_MATCH_QUERY_ACK = 7;
static const int OOB_SYSTEMLINK_QUERY = 8;
static const int OOB_MIGRATE_INVITE = 9;
static const int OOB_BANDWIDTH_TEST = 10;
enum connectionState_t
{
CONNECTION_FREE = 0, // Free peer slot
CONNECTION_CONNECTING = 1, // Waiting for response from host for initial connection
CONNECTION_ESTABLISHED = 2, // Connection is established and active
};
struct peer_t
{
peer_t()
{
loaded = false;
inGame = false;
networkChecksum = 0;
lastSnapTime = 0;
snapHz = 0.0f;
numResources = 0;
lastHeartBeat = 0;
connectionState = CONNECTION_FREE;
packetProc = NULL;
snapProc = NULL;
nextPing = 0; // do it asap
lastPingRtt = 0;
sessionID = idPacketProcessor::SESSION_ID_INVALID;
startResourceLoadTime = 0;
nextThrottleCheck = 0;
maxSnapQueueSize = 0;
throttledSnapRate = 0;
pauseSnapshots = false;
receivedBps = -1.0f;
maxSnapBps = -1.0f;
receivedThrottle = 0;
receivedThrottleTime = 0;
throttleSnapsForXSeconds = 0;
recoverPing = 0;
failedPingRecoveries = 0;
rightBeforeSnapsPing = 0;
bandwidthTestLastSendTime = 0;
bandwidthSequenceNum = 0;
bandwidthTestBytes = 0;
bandwidthChallengeStartSendTime = 0;
bandwidthChallengeResults = false;
bandwidthChallengeSendComplete = false;
numSnapsSent = 0;
ResetConnectState();
};
void ResetConnectState()
{
lastResourceTime = 0;
lastSnapTime = 0;
snapHz =
lastProcTime = 0;
lastInBandProcTime = 0;
lastFragmentSendTime = 0;
needToSubmitPendingSnap = false;
lastSnapJobTime = true;
startResourceLoadTime = 0;
receivedBps = -1.0;
maxSnapBps = -1.0f;
receivedThrottle = 0;
receivedThrottleTime = 0;
throttleSnapsForXSeconds = 0;
recoverPing = 0;
failedPingRecoveries = 0;
rightBeforeSnapsPing = 0;
bandwidthTestLastSendTime = 0;
bandwidthSequenceNum = 0;
bandwidthTestBytes = 0;
bandwidthChallengeStartSendTime = 0;
bandwidthChallengeResults = false;
bandwidthChallengeSendComplete = false;
memset( sentBpsHistory, 0, sizeof( sentBpsHistory ) );
receivedBpsIndex = 0;
debugGraphs.Clear();
}
void ResetAllData()
{
ResetConnectState();
ResetMatchData();
}
void ResetMatchData()
{
loaded = false;
networkChecksum = 0;
inGame = false;
numResources = 0;
needToSubmitPendingSnap = false;
throttledSnapRate = 0;
maxSnapQueueSize = 0;
receivedBpsIndex = -1;
numSnapsSent = 0;
pauseSnapshots = false;
// Reset the snapshot processor
if( snapProc != NULL )
{
snapProc->Reset( false );
}
}
void Print()
{
idLib::Printf( " lastResourceTime: %d\n", lastResourceTime );
idLib::Printf( " lastSnapTime: %d\n", lastSnapTime );
idLib::Printf( " lastProcTime: %d\n", lastProcTime );
idLib::Printf( " lastInBandProcTime: %d\n", lastInBandProcTime );
idLib::Printf( " lastFragmentSendTime: %d\n", lastFragmentSendTime );
idLib::Printf( " needToSubmitPendingSnap: %d\n", needToSubmitPendingSnap );
idLib::Printf( " lastSnapJobTime: %d\n", lastSnapJobTime );
}
bool IsActive() const
{
return connectionState != CONNECTION_FREE;
}
bool IsConnected() const
{
return connectionState == CONNECTION_ESTABLISHED;
}
connectionState_t GetConnectionState() const;
connectionState_t connectionState;
bool loaded; // true if this peer has finished loading the map
bool inGame; // true if this peer received the first snapshot, and is in-game
int lastSnapTime; // Last time a snapshot was sent on the network to this peer
float snapHz;
int lastProcTime; // Used to determine when a packet was processed for sending to this peer
int lastInBandProcTime; // Last time a in-band packet was processed for sending
int lastFragmentSendTime; // Last time a fragment was sent out (fragments are processed msg's, waiting to be fully sent)
// DG: use int instead of long for 64bit compatibility
unsigned int networkChecksum; // Checksum used to determine if a peer loaded the network resources the EXACT same as the server did
// DG end
int pauseSnapshots;
lobbyAddress_t address;
int numResources; // number of network resources we know the peer has
idPacketProcessor* packetProc; // Processes packets for this peer
idSnapshotProcessor* snapProc; // Processes snapshots for this peer
idStaticList< idDebugGraph*, 4 > debugGraphs; //
int lastResourceTime; // Used to throttle the sending of resources
int lastHeartBeat;
int nextPing; // next Sys_Milliseconds when I'll send this peer a RELIABLE_PING
int lastPingRtt;
bool needToSubmitPendingSnap;
int lastSnapJobTime; // Last time a snapshot was sent to the joblist for this peer
int startResourceLoadTime; // Used to determine how long a peer has been loading resources
int maxSnapQueueSize; // how big has the snap queue gotten?
int throttledSnapRate; // effective snap rate for this peer
int nextThrottleCheck;
int numSnapsSent;
float sentBpsHistory[ MAX_BPS_HISTORY ];
int receivedBpsIndex;
float receivedBps; // peer's reported bps (they tell us their effective downstream)
float maxSnapBps;
float receivedThrottle; // amount of accumlated time this client has been lagging behind
int receivedThrottleTime; // last time we did received based throttle calculations
int throttleSnapsForXSeconds;
int recoverPing;
int failedPingRecoveries;
int rightBeforeSnapsPing;
int bandwidthChallengeStartSendTime; // time we sent first packet of bw challenge to this peer
int bandwidthTestLastSendTime; // last time in MS we sent them a bw challenge packet
int bandwidthTestBytes; // used to measure number of bytes we sent them
int bandwidthSequenceNum; // number of challenge sequences we sent them
bool bandwidthChallengeResults; // we got results back
bool bandwidthChallengeSendComplete; // we finished sending everything
idPacketProcessor::sessionId_t sessionID;
};
const char* GetLobbyName()
{
switch( lobbyType )
{
case TYPE_PARTY:
return "TYPE_PARTY";
case TYPE_GAME:
return "TYPE_GAME";
case TYPE_GAME_STATE:
return "TYPE_GAME_STATE";
}
return "LOBBY_INVALID";
}
virtual lobbyUserID_t AllocLobbyUserSlotForBot( const char* botName ); // find a open user slot for the bot, and return the userID.
virtual void RemoveBotFromLobbyUserList( lobbyUserID_t lobbyUserID ); // release the session user slot, so that it can be claimed by a player, etc.
virtual bool GetLobbyUserIsBot( lobbyUserID_t lobbyUserID ) const; // check to see if the lobby user is a bot or not
virtual int GetNumLobbyUsers() const
{
return userList.Num();
}
virtual int GetNumActiveLobbyUsers() const;
virtual bool AllPeersInGame() const;
lobbyUser_t* GetLobbyUser( int index )
{
return ( index >= 0 && index < GetNumLobbyUsers() ) ? userList[index] : NULL;
}
const lobbyUser_t* GetLobbyUser( int index ) const
{
return ( index >= 0 && index < GetNumLobbyUsers() ) ? userList[index] : NULL;
}
virtual bool IsLobbyUserConnected( int index ) const
{
return !IsLobbyUserDisconnected( index );
}
virtual int PeerIndexFromLobbyUser( lobbyUserID_t lobbyUserID ) const;
virtual int GetPeerTimeSinceLastPacket( int peerIndex ) const;
virtual int PeerIndexForHost() const
{
return host;
}
virtual int PeerIndexOnHost() const
{
return peerIndexOnHost; // Returns -1 if we are the host
}
virtual const idMatchParameters& GetMatchParms() const
{
return parms;
}
lobbyType_t GetActingGameStateLobbyType() const;
// If IsHost is true, we are a host accepting connections from peers
bool IsHost() const
{
return isHost;
}
// If IsPeer is true, we are a peer, with an active connection to a host
bool IsPeer() const
{
if( host == -1 )
{
return false; // Can't possibly be a peer if we haven't setup a host
}
assert( !IsHost() );
return peers[host].IsConnected();
}
bool IsConnectingPeer() const
{
if( host == -1 )
{
return false; // Can't possibly be a peer if we haven't setup a host
}
assert( !IsHost() );
return peers[host].connectionState == CONNECTION_CONNECTING;
}
// IsRunningAsHostOrPeer means we are either an active host, and can accept connections from peers, or we are a peer with an active connection to a host
bool IsRunningAsHostOrPeer() const
{
return IsHost() || IsPeer();
}
bool IsLobbyActive() const
{
return IsRunningAsHostOrPeer();
}
struct reliablePlayerToPlayerHeader_t
{
int fromSessionUserIndex;
int toSessionUserIndex;
reliablePlayerToPlayerHeader_t();
// Both read and write return false if the data is invalid.
// The state of the msg and object are undefined if false is returned.
// The network packets contain userIds, and Read/Write will translate from userId to a
// sessionUserIndex. The sessionUserIndex should be the same on all peers, but the
// userId has to be used in case the target player quits while the message is on the
// wire from the originating peer to the server.
bool Read( idLobby* lobby, idBitMsg& msg );
bool Write( idLobby* lobby, idBitMsg& msg );
};
int GetTotalOutgoingRate(); // returns total instant outgoing bandwidth in B/s
//private:
public: // Turning this on for now, for the sake of getting this up and running to see where things are
// State functions
void State_Idle();
void State_Create_Lobby_Backend();
void State_Searching();
void State_Obtaining_Address();
void State_Finalize_Connect();
void State_Connect_Hello_Wait();
void SetState( lobbyState_t newState );
void StartCreating();
int FindPeer( const lobbyAddress_t& remoteAddress, idPacketProcessor::sessionId_t sessionID, bool ignoreSessionID = false );
int FindAnyPeer( const lobbyAddress_t& remoteAddress ) const;
int FindFreePeer() const;
int AddPeer( const lobbyAddress_t& remoteAddress, idPacketProcessor::sessionId_t sessionID );
void DisconnectPeerFromSession( int p );
void SetPeerConnectionState( int p, connectionState_t newState, bool skipGoodbye = false );
void DisconnectAllPeers();
virtual void SendReliable( int type, idBitMsg& msg, bool callReceiveReliable = true, peerMask_t sessionUserMask = MAX_UNSIGNED_TYPE( peerMask_t ) );
virtual void SendReliableToLobbyUser( lobbyUserID_t lobbyUserID, int type, idBitMsg& msg );
virtual void SendReliableToHost( int type, idBitMsg& msg );
void SendGoodbye( const lobbyAddress_t& remoteAddress, bool wasFull = false );
void QueueReliableMessage( int peerNum, byte type )
{
QueueReliableMessage( peerNum, type, NULL, 0 );
}
void QueueReliableMessage( int p, byte type, const byte* data, int dataLen );
virtual int GetNumConnectedPeers() const;
virtual int GetNumConnectedPeersInGame() const;
void SendMatchParmsToPeers();
static bool IsReliablePlayerToPlayerType( byte type );
void HandleReliablePlayerToPlayerMsg( int peerNum, idBitMsg& msg, int type );
void HandleReliablePlayerToPlayerMsg( const reliablePlayerToPlayerHeader_t& info, idBitMsg& msg, int reliableType );
void SendConnectionLess( const lobbyAddress_t& remoteAddress, byte type )
{
SendConnectionLess( remoteAddress, type, NULL, 0 );
}
void SendConnectionLess( const lobbyAddress_t& remoteAddress, byte type, const byte* data, int dataLen );
void SendConnectionRequest();
void ConnectTo( const lobbyConnectInfo_t& connectInfo, bool fromInvite );
void HandleGoodbyeFromPeer( int peerNum, lobbyAddress_t& remoteAddress, int msgType );
void HandleConnectionAttemptFailed();
bool ConnectToNextSearchResult();
bool CheckVersion( idBitMsg& msg, lobbyAddress_t peerAddress );
bool VerifyNumConnectingUsers( idBitMsg& msg );
bool VerifyLobbyUserIDs( idBitMsg& msg );
int HandleInitialPeerConnection( idBitMsg& msg, const lobbyAddress_t& peerAddress, int peerNum );
void InitStateLobbyHost();
void SendMembersToLobby( lobbyType_t destLobbyType, const lobbyConnectInfo_t& connectInfo, bool waitForOtherMembers );
void SendMembersToLobby( idLobby& destLobby, bool waitForOtherMembers );
void SendPeerMembersToLobby( int peerIndex, lobbyType_t destLobbyType, const lobbyConnectInfo_t& connectInfo, bool waitForOtherMembers );
void SendPeerMembersToLobby( int peerIndex, lobbyType_t destLobbyType, bool waitForOtherMembers );
void NotifyPartyOfLeavingGameLobby();
uint32 GetPartyTokenAsHost();
virtual void DrawDebugNetworkHUD() const;
virtual void DrawDebugNetworkHUD2() const;
virtual void DrawDebugNetworkHUD_ServerSnapshotMetrics( bool draw );
void CheckHeartBeats();
bool IsLosingConnectionToHost() const;
bool IsMigratedStatsGame() const;
bool ShouldRelaunchMigrationGame() const;
bool ShouldShowMigratingDialog() const;
bool IsMigrating() const;
// Pings
struct pktPing_t
{
int timestamp;
};
void PingPeers();
void SendPingValues();
void PumpPings();
void HandleReliablePing( int p, idBitMsg& msg );
void HandlePingReply( int p, const pktPing_t& ping );
void HandlePingValues( idBitMsg& msg );
void HandleBandwidhTestValue( int p, idBitMsg& msg );
void HandleMigrationGameData( idBitMsg& msg );
void HandleHeadsetStateChange( int fromPeer, idBitMsg& msg );
bool SendAnotherFragment( int p );
bool CanSendMoreData( int p );
void ProcessOutgoingMsg( int p, const void* data, int size, bool isOOB, int userData );
void ResendReliables( int p );
void PumpPackets();
void UpdateMatchParms( const idMatchParameters& p );
// SessionID helpers
idPacketProcessor::sessionId_t EncodeSessionID( uint32 key ) const;
void DecodeSessionID( idPacketProcessor::sessionId_t sessionID, uint32& key ) const;
idPacketProcessor::sessionId_t GenerateSessionID() const;
bool SessionIDCanBeUsedForInBand( idPacketProcessor::sessionId_t sessionID ) const;
idPacketProcessor::sessionId_t IncrementSessionID( idPacketProcessor::sessionId_t sessionID ) const;
void HandleHelloAck( int p, idBitMsg& msg );
virtual const char* GetLobbyUserName( lobbyUserID_t lobbyUserID ) const;
virtual bool GetLobbyUserWeaponAutoReload( lobbyUserID_t lobbyUserID ) const;
virtual bool GetLobbyUserWeaponAutoSwitch( lobbyUserID_t lobbyUserID ) const;
virtual int GetLobbyUserSkinIndex( lobbyUserID_t lobbyUserID ) const;
virtual int GetLobbyUserLevel( lobbyUserID_t lobbyUserID ) const;
virtual int GetLobbyUserQoS( lobbyUserID_t lobbyUserID ) const;
virtual int GetLobbyUserTeam( lobbyUserID_t lobbyUserID ) const;
virtual bool SetLobbyUserTeam( lobbyUserID_t lobbyUserID, int teamNumber );
virtual int GetLobbyUserPartyToken( lobbyUserID_t lobbyUserID ) const;
virtual idPlayerProfile* GetProfileFromLobbyUser( lobbyUserID_t lobbyUserID );
virtual idLocalUser* GetLocalUserFromLobbyUser( lobbyUserID_t lobbyUserID );
virtual int GetNumLobbyUsersOnTeam( int teamNumber ) const;
const char* GetPeerName( int peerNum ) const;
virtual const char* GetHostUserName() const;
void HandleReliableMsg( int p, idBitMsg& msg, const lobbyAddress_t* remoteAddress = NULL );
// Bandwidth / Qos / Throttling
void BeginBandwidthTest();
bool BandwidthTestStarted();
void ServerUpdateBandwidthTest();
void ClientUpdateBandwidthTest();
void ThrottlePeerSnapRate( int peerNum );
//
// sys_session_instance_users.cpp
//
lobbyUser_t* AllocUser( const lobbyUser_t& defaults );
void FreeUser( lobbyUser_t* user );
bool VerifyUser( const lobbyUser_t* lobbyUser ) const;
void FreeAllUsers();
void RegisterUser( lobbyUser_t* lobbyUser );
void UnregisterUser( lobbyUser_t* lobbyUser );
bool IsSessionUserLocal( const lobbyUser_t* lobbyUser ) const;
bool IsSessionUserIndexLocal( int i ) const;
int GetLobbyUserIndexByID( lobbyUserID_t lobbyUserId, bool ignoreLobbyType = false ) const;
lobbyUser_t* GetLobbyUserByID( lobbyUserID_t lobbyUserId, bool ignoreLobbyType = false );
// Helper function to create a lobby user from a local user
lobbyUser_t CreateLobbyUserFromLocalUser( const idLocalUser* localUser );
// This function is designed to initialize the session users of type lobbyType (TYPE_GAME or TYPE_PARTY)
// to the current list of local users that are being tracked by the sign-in manager
void InitSessionUsersFromLocalUsers( bool onlineMatch );
// Convert an local userhandle to a session user (-1 if there is no session user with this handle)
int GetLobbyUserIndexByLocalUserHandle( const localUserHandle_t localUserHandle ) const;
// This takes a session user, and converts to a controller user
idLocalUser* GetLocalUserFromLobbyUserIndex( int lobbyUserIndex );
// Takes a controller user, and converts to a session user (will return NULL if there is no session user for this controller user)
lobbyUser_t* GetSessionUserFromLocalUser( const idLocalUser* controller );
void RemoveUsersWithDisconnectedPeers();
void RemoveSessionUsersByIDList( idList< lobbyUserID_t >& usersToRemoveByID );
void SendNewUsersToPeers( int skipPeer, int userStart, int numUsers );
void SendPeersMicStatusToNewUsers( int peerNumber );
void AddUsersFromMsg( idBitMsg& msg, int fromPeer );
void UpdateSessionUserOnPeers( idBitMsg& msg );
void HandleUpdateSessionUser( idBitMsg& msg );
void CreateUserUpdateMessage( int userIndex, idBitMsg& msg );
void UpdateLocalSessionUsers();
int PeerIndexForSessionUserIndex( int sessionUserIndex ) const;
void HandleUserConnectFailure( int p, idBitMsg& inMsg, int reliableType );
void ProcessUserDisconnectMsg( idBitMsg& msg );
void CompactDisconnectedUsers();
// Sends a request to the host to join a local user to a session
void RequestLocalUserJoin( idLocalUser* localUser );
// Sends a request to the host to remove a session user from the session
void RequestSessionUserDisconnect( int sessionUserIndex );
// This function sycs the session users with the current list of of local users on the signin manager.
// It will remove the session users that are either no longer on the signin manager, or it
// will remove them if they are no longer allowed to be in the session.
// If it finds a local users that are not in a particular session, it will add that user if allowed.
void SyncLobbyUsersWithLocalUsers( bool allowJoin, bool onlineMatch );
bool ValidateConnectedUser( const lobbyUser_t* user ) const;
virtual bool IsLobbyUserDisconnected( int userIndex ) const;
virtual bool IsLobbyUserValid( lobbyUserID_t lobbyUserID ) const;
virtual bool IsLobbyUserLoaded( lobbyUserID_t lobbyUserID ) const;
virtual bool LobbyUserHasFirstFullSnap( lobbyUserID_t lobbyUserID ) const;
virtual lobbyUserID_t GetLobbyUserIdByOrdinal( int userIndex ) const;
virtual int GetLobbyUserIndexFromLobbyUserID( lobbyUserID_t lobbyUserID ) const;
virtual void EnableSnapshotsForLobbyUser( lobbyUserID_t lobbyUserID );
virtual bool IsPeerDisconnected( int peerIndex ) const
{
return !peers[peerIndex].IsConnected();
}
float GetAverageSessionLevel();
float GetAverageLocalUserLevel( bool onlineOnly );
void QueueReliablePlayerToPlayerMessage( int fromSessionUserIndex, int toSessionUserIndex, reliablePlayerToPlayer_t type, const byte* data, int dataLen );
virtual void KickLobbyUser( lobbyUserID_t lobbyUserID );
int GetNumConnectedUsers() const;
//
// sys_session_instance_migrate.cpp
//
bool IsBetterHost( int ping1, lobbyUserID_t userId1, int ping2, lobbyUserID_t userId2 );
int FindMigrationInviteIndex( lobbyAddress_t& address );
void UpdateHostMigration();
void BuildMigrationInviteList( bool inviteOldHost );
void PickNewHost( bool forceMe = false, bool inviteOldHost = false );
void PickNewHostInternal( bool forceMe, bool inviteOldHost );
void BecomeHost();
void EndMigration();
void ResetAllMigrationState();
void SendMigrationGameData();
bool GetMigrationGameData( idBitMsg& msg, bool reading );
bool GetMigrationGameDataUser( lobbyUserID_t lobbyUserID, idBitMsg& msg, bool reading );
//
// Snapshots
// sys_session_instance_snapshot.cpp
//
void UpdateSnaps();
bool SendCompletedSnaps();
bool SendResources( int p );
bool SubmitPendingSnap( int p );
void SendCompletedPendingSnap( int p );
void CheckPeerThrottle( int p );
void ApplySnapshotDelta( int p, int snapshotNumber );
bool ApplySnapshotDeltaInternal( int p, int snapshotNumber );
void SendSnapshotToPeer( idSnapShot& ss, int p );
bool AllPeersHaveBaseState();
void ThrottleSnapsForXSeconds( int p, int seconds, bool recoverPing );
bool FirstSnapHasBeenSent( int p );
virtual bool EnsureAllPeersHaveBaseState();
virtual bool AllPeersHaveStaleSnapObj( int objId );
virtual bool AllPeersHaveExpectedSnapObj( int objId );
virtual void MarkSnapObjDeleted( int objId );
virtual void RefreshSnapObj( int objId );
void ResetBandwidthStats();
void DetectSaturation( int p );
virtual void AddSnapObjTemplate( int objID, idBitMsg& msg );
static const int MAX_PEERS = MAX_PLAYERS;
//------------------------
// Pings
//------------------------
struct pktPingValues_t
{
idArray<short, MAX_PEERS> pings;
};
static const int PING_INTERVAL_MS = 3000;
int lastPingValuesRecvTime; // so clients can display something when server stops pinging
int nextSendPingValuesTime; // the next time to send RELIABLE_PING_VALUES
static const int MIGRATION_GAME_DATA_INTERVAL_MS = 1000;
int nextSendMigrationGameTime; // when to send next migration game data
int nextSendMigrationGamePeer; // who to send next migration game data to
lobbyType_t lobbyType;
lobbyState_t state; // State of this lobby
failedReason_t failedReason;
int host; // which peer is the host of this type of session (-1 if we are the host)
int peerIndexOnHost; // -1 if we are the host
lobbyAddress_t hostAddress; // address of the host for this type of session
bool isHost; // true if we are the host
idLobbyBackend* lobbyBackend;
int helloStartTime; // Used to determine when the first hello was sent
int lastConnectRequest; // Used to determine when the last hello was sent
int connectionAttempts; // Number of connection attempts
bool needToDisplayMigrateMsg; // If true, we migrated as host, so we need to display the msg as soon as the lobby is active
gameDialogMessages_t migrationDlg; // current migration dialog we should be showing
uint8 migrateMsgFlags; // cached match flags from the old game we migrated from, so we know what type of msg to display
bool joiningMigratedGame; // we are joining a migrated game and need to tell the session mgr if we succeed or fail
// ------------------------
// Bandwidth challenge
// ------------------------
int bandwidthChallengeEndTime; // When the challenge will end/timeout
int bandwidthChallengeStartTime; // time in MS the challenge started
bool bandwidthChallengeFinished; // (HOST) test is finished and we received results back from all peers (or timed out)
int bandwidthChallengeNumGoodSeq; // (PEER) num of good, in order packets we recevieved
int lastSnapBspHistoryUpdateSequence;
void SaveDisconnectedUser( const lobbyUser_t& user ); // This is needed to get the a user's gamertag after disconnection.
idSessionCallbacks* sessionCB;
enum migrationState_t
{
MIGRATE_NONE,
MIGRATE_PICKING_HOST,
MIGRATE_BECOMING_HOST,
};
struct migrationInvite_t
{
migrationInvite_t()
{
lastInviteTime = -1;
pingMs = 0;
migrationGameData = -1;
}
lobbyAddress_t address;
int pingMs;
lobbyUserID_t userId;
int lastInviteTime;
int migrationGameData;
};
struct migrationInfo_t
{
migrationInfo_t()
{
state = MIGRATE_NONE;
ourPingMs = 0;
ourUserId = lobbyUserID_t();
}
migrationState_t state;
idStaticList< migrationInvite_t, MAX_PEERS > invites;
int migrationStartTime;
int ourPingMs;
lobbyUserID_t ourUserId;
struct persistUntilGameEnds_t
{
persistUntilGameEnds_t()
{
Clear();
}
void Clear()
{
wasMigratedHost = false;
wasMigratedJoin = false;
wasMigratedGame = false;
ourGameData = -1;
hasGameData = false;
hasRelaunchedMigratedGame = false;
memset( gameData, 0, sizeof( gameData ) );
memset( gameDataUser, 0, sizeof( gameDataUser ) );
}
int ourGameData;
bool wasMigratedHost; // we are hosting a migrated session
bool wasMigratedJoin; // we joined a migrated session
bool wasMigratedGame; // If true, we migrated from a game
bool hasRelaunchedMigratedGame;
// A generic blob of data that the gamechallenge (or anything else) can read and write to for host migration
static const int MIGRATION_GAME_DATA_SIZE = 32;
byte gameData[ MIGRATION_GAME_DATA_SIZE ];
static const int MIGRATION_GAME_DATA_USER_SIZE = 64;
byte gameDataUser[ MAX_PLAYERS ][ MIGRATION_GAME_DATA_USER_SIZE ];
bool hasGameData;
} persistUntilGameEndsData;
};
struct disconnectedUser_t
{
lobbyUserID_t lobbyUserID; // Locally generated to be unique, and internally keeps the local user handle
char gamertag[lobbyUser_t::MAX_GAMERTAG];
};
migrationInfo_t migrationInfo;
bool showHostLeftTheSession;
bool connectIsFromInvite;
idList< lobbyConnectInfo_t > searchResults;
typedef idStaticList< lobbyUser_t*, MAX_PLAYERS > idLobbyUserList;
typedef idStaticList< lobbyUser_t, MAX_PLAYERS > idLobbyUserPool;
idLobbyUserList userList; // list of currently connected users to this lobby
idLobbyUserList freeUsers; // list of free users
idLobbyUserPool userPool;
idList< disconnectedUser_t> disconnectedUsers; // List of users which were connected, but aren't anymore, for printing their name on the hud
idStaticList< peer_t, MAX_PEERS > peers; // Unique machines connected to this lobby
uint32 partyToken;
idMatchParameters parms;
bool loaded; // Used for game sessions, whether this machine is loaded or not
bool respondToArbitrate; // true when the host has requested us to arbitrate our session (for TYPE_GAME only)
bool everyoneArbitrated;
bool waitForPartyOk;
bool startLoadingFromHost;
//------------------------
// Snapshot jobs
//------------------------
static const int SNAP_OBJ_JOB_MEMORY = 1024 * 128; // 128k of obj memory
lzwCompressionData_t* lzwData; // Shared across all snapshot jobs
uint8* objMemory; // Shared across all snapshot jobs
bool haveSubmittedSnaps; // True if we previously submitted snaps to jobs
idSnapShot* localReadSS;
struct snapDeltaAck_t
{
int p;
int snapshotNumber;
};
idStaticList< snapDeltaAck_t, 16 > snapDeltaAckQueue;
};
/*
========================
idSessionCallbacks
========================
*/
class idSessionCallbacks
{
public:
virtual idLobby& GetPartyLobby() = 0;
virtual idLobby& GetGameLobby() = 0;
virtual idLobby& GetActingGameStateLobby() = 0;
virtual idLobby* GetLobbyFromType( idLobby::lobbyType_t lobbyType ) = 0;
virtual int GetUniquePlayerId() const = 0;
virtual idSignInManagerBase& GetSignInManager() = 0;
virtual void SendRawPacket( const lobbyAddress_t& to, const void* data, int size, bool useDirectPort ) = 0;
virtual bool BecomingHost( idLobby& lobby ) = 0; // Called when a lobby is about to become host
virtual void BecameHost( idLobby& lobby ) = 0; // Called when a lobby becomes a host
virtual bool BecomingPeer( idLobby& lobby ) = 0; // Called when a lobby is about to become peer
virtual void BecamePeer( idLobby& lobby ) = 0; // Called when a lobby becomes a peer
virtual void FailedGameMigration( idLobby& lobby ) = 0;
virtual void MigrationEnded( idLobby& lobby ) = 0;
virtual void GoodbyeFromHost( idLobby& lobby, int peerNum, const lobbyAddress_t& remoteAddress, int msgType ) = 0;
virtual uint32 GetSessionOptions() = 0;
virtual bool AnyPeerHasAddress( const lobbyAddress_t& remoteAddress ) const = 0;
virtual idSession::sessionState_t GetState() const = 0;
virtual void ClearMigrationState() = 0;
// Called when the lobby receives a RELIABLE_ENDMATCH msg
virtual void EndMatchInternal( bool premature = false ) = 0;
// Called when the game lobby receives leaderboard stats
virtual void RecvLeaderboardStats( idBitMsg& msg ) = 0;
// Called once the lobby received its first full snap (used to advance from LOADING to INGAME state)
virtual void ReceivedFullSnap() = 0;
// Called when lobby received RELIABLE_PARTY_LEAVE_GAME_LOBBY msg
virtual void LeaveGameLobby() = 0;
virtual void PrePickNewHost( idLobby& lobby, bool forceMe, bool inviteOldHost ) = 0;
virtual bool PreMigrateInvite( idLobby& lobby ) = 0;
virtual void HandleOobVoiceAudio( const lobbyAddress_t& from, const idBitMsg& msg ) = 0;
// ConnectAndMoveToLobby is called when the lobby receives a RELIABLE_CONNECT_AND_MOVE_TO_LOBBY
virtual void ConnectAndMoveToLobby( idLobby::lobbyType_t destLobbyType, const lobbyConnectInfo_t& connectInfo, bool waitForPartyOk ) = 0;
virtual class idVoiceChatMgr* GetVoiceChat() = 0;
virtual void HandleServerQueryRequest( lobbyAddress_t& remoteAddr, idBitMsg& msg, int msgType ) = 0;
virtual void HandleServerQueryAck( lobbyAddress_t& remoteAddr, idBitMsg& msg ) = 0;
virtual void HandlePeerMatchParamUpdate( int peer, int msg ) = 0;
virtual idLobbyBackend* CreateLobbyBackend( const idMatchParameters& p, float skillLevel, idLobbyBackend::lobbyBackendType_t lobbyType ) = 0;
virtual idLobbyBackend* FindLobbyBackend( const idMatchParameters& p, int numPartyUsers, float skillLevel, idLobbyBackend::lobbyBackendType_t lobbyType ) = 0;
virtual idLobbyBackend* JoinFromConnectInfo( const lobbyConnectInfo_t& connectInfo , idLobbyBackend::lobbyBackendType_t lobbyType ) = 0;
virtual void DestroyLobbyBackend( idLobbyBackend* lobbyBackend ) = 0;
};