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
|