summaryrefslogtreecommitdiff
path: root/engine/matchmaking.h
blob: 963034e03a0e83c82911b69bfc62f9ecbb57326d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================//
#ifndef MATCHMAKING_H
#define MATCHMAKING_H
#ifdef _WIN32
#pragma once
#endif

#ifdef _WIN32
#include "winerror.h"
#endif
#include "utlmap.h"
#include "inetmsghandler.h"
#include "netmessages.h"
#include "Session.h"
#include "engine/imatchmaking.h"

enum MMPACKETS
{
	PTH_CONNECT,
	PTH_SYSTEMLINK_SEARCH,
	HTP_SYSTEMLINK_REPLY
};

enum MMSTATE
{
	MMSTATE_INITIAL,
	MMSTATE_IDLE,
	MMSTATE_CREATING,
	MMSTATE_MODIFYING,
	MMSTATE_ACCEPTING_CONNECTIONS,
	MMSTATE_SEARCHING,
	MMSTATE_WAITING_QOS,
	MMSTATE_BROWSING,
	MMSTATE_SESSION_CONNECTING,
	MMSTATE_SESSION_CONNECTED,
	MMSTATE_SESSION_DISCONNECTING,

	MMSTATE_HOSTMIGRATE_STARTINGMIGRATION,
	MMSTATE_HOSTMIGRATE_MIGRATING,
	MMSTATE_HOSTMIGRATE_WAITINGFORCLIENTS,
	MMSTATE_HOSTMIGRATE_WAITINGFORHOST,

	MMSTATE_GAME_LOCKED,	// in ranked games, clients can't join after this point

	MMSTATE_PREGAME,
	MMSTATE_REPORTING_STATS,
	MMSTATE_POSTGAME,

	MMSTATE_GAME_ACTIVE,	// clients are no longer in the lobby

	MMSTATE_LOADING,
	MMSTATE_CONNECTED_TO_SERVER,
	MMSTATE_INGAME,
};

// For sending host data back in search results
enum eGameState
{
	GAMESTATE_INLOBBY,
	GAMESTATE_INPROGRESS
};

#define HEARTBEAT_INTERVAL_LONG		1.0		// send a heartbeat every second during gameplay
#define HEARTBEAT_INTERVAL_SHORT	0.1		// send a heartbeat ten times a second in the lobby
#define HEARTBEAT_TIMEOUT			10.0	// time out if a heartbeat isn't recieved for ten seconds 
#if defined( _DEBUG )
#define HEARTBEAT_TIMEOUT_LOADING	300		// in debug loads take much longer
#else
#define HEARTBEAT_TIMEOUT_LOADING	100		// allow for longer communication gaps during map load
#endif

#define STARTGAME_COUNTDOWN			15.0	// start game countdown timer
#define DISCONNECT_WAITTIME			1.0		// wait for the server to reply to our disconnect notification
#define QOSLOOKUP_WAITTIME			20.0	// wait to get quality of service data about session hosts
#define JOINREPLY_WAITTIME			15.0	// time to wait for the host to reply to our join request
#define REPORTSTATS_WAITTIME		20.0	// time to wait for clients to report their stats to live

// 360 TCR's require system link searches complete in less than 3 seconds
#define SYSTEMLINK_RETRYINTERVAL	1.f		// in seconds
#define SYSTEMLINK_MAXRETRIES		3		// number of tries before giving up

#define SESSIONMODIRY_MAXWAITTIME	10		// max time for clients to update their session properties
#define REGISTRATION_MAXWAITTIME	10		// max time for clients to register

#define HOSTMIGRATION_RETRYINTERVAL	1.0		// in seconds
#define HOSTMIGRATION_MAXRETRIES	10		// max migrate sends to clients
#define HOSTMIGRATION_MAXWAITTIME	10		// time to wait for a new host to contact us

#define MAX_SEARCHRESULTS			20		// Maximum number of results when searching for a session.

#define PING_MAX_GREEN				70
#define PING_MAX_YELLOW				140
#define PING_MAX_RED				250

#define VOICE_STATUS_OFF			0
#define VOICE_STATUS_IDLE			1
#define VOICE_STATUS_TALKING		2

// HACK: For simplicity, we know TF has two teams plus spectator.
#define MAX_TEAMS		3
#define MAX_PLAYERS		16

#define VOICE_ICON_BLINK_TIME 0.5

class CMatchmaking : public IMatchmaking, public IMatchmakingMessageHandler, public IClientMessageHandler, public INetChannelHandler, public IConnectionlessPacketHandler
{
public:
	CMatchmaking();
	~CMatchmaking();

	// IMatchmaking implementation
	virtual void	SessionNotification( const SESSION_NOTIFY notification, const int param = 0 );
	virtual void	AddSessionProperty( const uint nType, const char *pID, const char *pValue, const char *pValueType );
	virtual void	SetSessionProperties( KeyValues *pPropertyKeys );
	virtual void	SelectSession( uint sessionIdx );
	virtual void	ModifySession();
	virtual void	UpdateMuteList();
	virtual void	StartHost( bool bSystemLink = false );
	virtual void	StartClient( bool bSystemLink = false );
	virtual bool	StartGame();
	virtual bool	CancelStartGame();
	virtual void	ChangeTeam( const char *pTeamName );
	virtual void	TellClientsToConnect();
	virtual void	CancelCurrentOperation();
	virtual void	EndStatsReporting();

	virtual void	JoinInviteSessionByID( XNKID nSessionID );
	virtual void	JoinInviteSession( XSESSION_INFO *pHostInfo );
	virtual void	KickPlayerFromSession( uint64 id );
	
	// For GameUI
	virtual KeyValues *GetSessionProperties();

	// For voice chat
	virtual uint64	PlayerIdToXuid( int playerId );
	virtual bool	IsPlayerMuted( int iUserId, XUID id );

	// To determine host Quality-of-Service
	virtual MM_QOS_t GetQosWithLIVE();

	virtual bool	PreventFullServerStartup();

	// IConnectionlessPacketHandler implementation (Host/Client shared)
	virtual bool	ProcessConnectionlessPacket( netpacket_t * packet );

	// INetChannelHandler implementation
	virtual void	ConnectionStart(INetChannel *chan);	// called first time network channel is established
	virtual void	PacketEnd();						// all messages have been parsed

	// NetChannel message handlers
 	PROCESS_NET_MESSAGE( Tick ) { return true; }
 	PROCESS_NET_MESSAGE( SetConVar ) { return true; }
	PROCESS_NET_MESSAGE( StringCmd ) { return true; }
	PROCESS_NET_MESSAGE( SignonState ) { return true; }

	PROCESS_CLC_MESSAGE( VoiceData );
	PROCESS_CLC_MESSAGE( ClientInfo ) { return true; }
	PROCESS_CLC_MESSAGE( Move ) { return true; }
	PROCESS_CLC_MESSAGE( BaselineAck ) { return true; }
	PROCESS_CLC_MESSAGE( ListenEvents ) { return true; }
	PROCESS_CLC_MESSAGE( RespondCvarValue ) { return true; }
	PROCESS_CLC_MESSAGE( FileCRCCheck ) { return true; }
	PROCESS_CLC_MESSAGE( FileMD5Check ) { return true; }
	PROCESS_CLC_MESSAGE( SaveReplay ) { return true; }
	PROCESS_CLC_MESSAGE( CmdKeyValues ) { return true; }


	PROCESS_MM_MESSAGE( JoinResponse );
	PROCESS_MM_MESSAGE( ClientInfo );
	PROCESS_MM_MESSAGE( RegisterResponse );
	PROCESS_MM_MESSAGE(	Migrate );
	PROCESS_MM_MESSAGE( Mutelist );
	PROCESS_MM_MESSAGE( Checkpoint );
	PROCESS_MM_MESSAGE( Heartbeat ) { return true; }

	// (Not used)
	virtual void ConnectionClosing(const char *reason) {};							// network channel is being closed by remote site
	virtual void ConnectionCrashed(const char *reason) {};							// network error occurred
	virtual void PacketStart(int incoming_sequence, int outgoing_acknowledged) {};	// called each time a new packet arrived
	virtual void FileRequested(const char *fileName, unsigned int transferID ) {};	// other side request a file for download
	virtual void FileReceived(const char *fileName, unsigned int transferID ) {};	// we received a file
	virtual void FileDenied(const char *fileName, unsigned int transferID ) {};		// a file request was denied by other side
	virtual void FileSent(const char *fileName, unsigned int transferID ) {};		// a file was sent

	// Debugging helpers
	void	ShowSessionInfo();
	void	SendDevMessage( const char *message );

	void	SetSessionSlots( const uint nSlotsTotal, const uint nSlotsPrivate );
	void	RunFrame();
	void	EndGame();

	void	AddLocalPlayersToTeams();
	void	OnLevelLoadingFinished();

	void	TestSendMessage();
	void	TestStats();

	bool	GameIsActive();
	
	void	PrintVoiceStatus( void );

private:
	// NetChannel send
	void	SendMessage( INetMessage *msg, netadr_t *adr, bool bVoice = false );
	void	SendMessage( INetMessage *msg, CClientInfo *pClient, bool bVoice = false );
	void	SendToRemoteClients( INetMessage *msg, bool bVoice = false, XUID excludeXUID = -1 );

	// Session Host
	void	OnHostSessionCreated();
	void	UpdateAcceptingConnections();
	void	SendModifySessionMessage();
	void	EndSessionModify();
	bool	IsAcceptingConnections();
	void	HandleSystemLinkSearch( netpacket_t *pPacket );
	void	HandleJoinRequest( netpacket_t *pPacket );
	void	StartCountdown();
	void	CancelCountdown();
	void	UpdatePregame();
	void	UpdateRegistration();
	void	UpdateSessionModify();
	void	ProcessRegistrationResults();
	void	UpdateServerNegotiation();
	void	UpdateSessionReplyData( uint flags );
	void	SwitchToNextOpenTeam( CClientInfo *pClient );
	void	SetupTeams();
	int		ChooseTeam();
	int		GetPlayersNeeded();

	// Session Client
	bool	StartSystemLinkSearch();
	void	HandleSystemLinkReply( netpacket_t *pPacket );
	bool	SearchForSession();
	void	UpdateSearch();
	void	UpdateQosLookup();
	void	CancelSearch();
	void	CancelQosLookup();
	void	ClearSearchResults();
	void	SendJoinRequest( netadr_t *adr );
	bool	ConnectToHost();
	void	UpdateConnecting();
	void	ApplySessionProperties( int numContexts, int numProperties, XUSER_CONTEXT *pContexts, XUSER_PROPERTY *pProperties );

	// Host/Client shared
	bool	InitializeLocalClient( bool bIsHost );
	void	AddPlayersToSession( CClientInfo *pClient );
	void	SendPlayerInfoToLobby( CClientInfo *pClient, int iHostIdx = -1 );
	void	RemovePlayersFromSession( CClientInfo *pClient );
	void	ClientDropped( CClientInfo *pClient );
	void	PerformDisconnect();
	void	GenerateMutelist( MM_Mutelist *pMsg );
	int		FindOrCreateContext( const uint id );
	int		FindOrCreateProperty( const uint id );
	void	AddSessionPropertyInternal( KeyValues *pProperty );

	// Host Migration
	CClientInfo *SelectNewHost();
	void		StartHostMigration();
	void		BeginHosting();
	void		TellClientsToMigrate();
	void		SwitchToNewHost();
	void		EndMigration();

	// General utility functions
	void		Cleanup();
	void		SwitchToState( int newState );
	double		GetTime();
	void		SendHeartbeat();
	bool		SendHeartbeat( CClientInfo *pClient );
	void		ClientInfoToNetMessage( MM_ClientInfo *pInfo, const CClientInfo *pClient );
	void		NetMessageToClientInfo( CClientInfo *pClient, const MM_ClientInfo *pInfo );
	bool		GameIsLocked();
	bool		IsInMigration();
	bool		IsServer();
	bool		ConnectedToServer();

	// Netchannel handling
	INetChannel *CreateNetChannel( netadr_t *adr );
	INetChannel *AddRemoteChannel( netadr_t *adr );
	INetChannel *FindChannel( const unsigned int ip );
	CClientInfo *FindClient( netadr_t *adr ); 
	CClientInfo *FindClientByXUID( XUID xuid );
	void		SetChannelTimeout( netadr_t *adr, int timeout );
	void		RemoveRemoteChannel( netadr_t *adr, const char *pReason );
	void		MarkChannelForRemoval( netadr_t *adr );
	void		CleanupMarkedChannels();

	void		UpdateVoiceStatus( void );

	void		SetPreventFullServerStartup( bool bState, PRINTF_FORMAT_STRING char const *fmt, ... );

private:

	// Used by a systemlink host to reply to broadcast searches
	struct systemLinkInfo_s
	{
		char					szHostName[MAX_PLAYER_NAME_LENGTH];
		char					szScenario[MAX_MAP_NAME];
		int						gameState;
		int						gameTime;
		int						iScenarioIndex;
		XUID					xuid;
		XSESSION_SEARCHRESULT	Result;
	};

	hostData_s						m_HostData;				// pass host info back to searching clients
	CClientInfo						m_Host;					// the session host
	CClientInfo						m_Local;				// the local client
	CClientInfo						*m_pNewHost;			// new host when migrating the session
	CClientInfo						*m_pGameServer;			// the client that will act as the game server
	CUtlVector< CClientInfo* >		m_Remote;				// remote clients
	CUtlVector< char* >				m_pSystemLinkResults;	// results from a system link search
	XSESSION_SEARCHRESULT_HEADER	*m_pSearchResults;		// dynamic buffer to hold session search results

	// Arbitration registration
	XSESSION_REGISTRATION_RESULTS	*m_pRegistrationResults;

	// QoS Data
	BOOL               m_bQoSTesting;
	XNQOS              m_QoSResult;
	XNQOS*             m_pQoSResult;
	const XNADDR*      m_QoSxnaddr[MAX_SEARCHRESULTS];
	const XNKID*       m_QoSxnkid[MAX_SEARCHRESULTS];
	const XNKEY*       m_QoSxnkey[MAX_SEARCHRESULTS];

	CUtlMap< unsigned int, INetChannel* >m_Channels;
	CUtlVector< unsigned int > m_ChannelsToRemove;

	AsyncHandle_t	m_hSearchHandle;
	CSession		m_Session;
	int				m_CurrentState;
	int				m_PreMigrateState;
	double			m_fNextHeartbeatTime;
	double			m_fCountdownStartTime;
	double			m_fRegistrationTimer;
	bool			m_bCreatedLocalTalker;
	bool			m_bInitialized;
	bool			m_bCleanup;
	bool			m_bPreventFullServerStartup;
	bool			m_bEnteredLobby;
	int				m_nHostOwnerId;
	int				m_nGameSize;
	int				m_nTotalTeams;
	int				m_nPrivateSlots;
	int				m_nQOSProbeCount;
	double			m_fQOSProbeTimer;
	int				m_nSendCount;
	double			m_fSendTimer;
	double			m_fWaitTimer;
	double			m_fHeartbeatInterval;
	uint64			m_Nonce;		// used in system link queries

	int				CountPlayersOnTeam( int idxTeam );	// players on each team

	XUID				m_Mutelist[MAX_PLAYERS_PER_CLIENT][MAX_PLAYERS];
	CUtlVector< XUID >	m_MutedBy[MAX_PLAYERS_PER_CLIENT];

	// Contexts and properties
	CUtlVector< XUSER_CONTEXT >		m_SessionContexts;		// for session creation
	CUtlVector< XUSER_PROPERTY >	m_SessionProperties;	// for session creation
	CUtlVector< XUSER_PROPERTY >	m_PlayerStats;			
	KeyValues						*m_pSessionKeys;		// for GameUI lobby setup

	double			m_flVoiceBlinkTime;

	XSESSION_INFO	m_InviteSessionInfo;

	enum InviteState_t
	{
		INVITE_NONE,
		INVITE_PENDING,
		INVITE_VALIDATING,
		INVITE_AWAITING_STORAGE,
		INVITE_ACCEPTING
	} m_InviteState;
	
	struct InviteWaitingInfo_t
	{
		DWORD				m_UserIdx;
#if defined( _X360 )
		XUSER_SIGNIN_INFO	m_SignInInfo;
#endif
		DWORD				m_SignInState;
		BOOL				m_PrivilegeMultiplayer;
		int					m_InviteStorageDeviceSelected;
		int					m_bAcceptingInvite;
	} m_InviteWaitingInfo;
	
	void RunFrameInvite();
	void InviteCancel();
};
extern CMatchmaking *g_pMatchmaking;

#endif // MATCHMAKING_H