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
|
//========= Copyright Valve Corporation, All rights reserved. ============//
#ifndef _TF_GC_SHARED_H
#define _TF_GC_SHARED_H
#ifdef _WIN32
#pragma once
#endif
#include "gcsdk/msgprotobuf.h"
using namespace GCSDK;
#define MMLog(...) do { Log( __VA_ARGS__ ); } while(false)
//-----------------------------------------------------------------------------
// ReliableMessage - A message/job class that retry until confirmed, and be sent
// In order with other such messages.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Check for pending messages
//-----------------------------------------------------------------------------
static bool BPendingReliableMessages();
//-----------------------------------------------------------------------------
GCSDK::CGCClientJob *s_pCurrentConfirmJob = NULL;
CUtlQueue< GCSDK::CGCClientJob * > s_queuePendingConfirmJobs;
template < typename RELIABLE_MSG_CLASS, typename MSG_TYPE, ETFGCMsg E_MSG_TYPE, typename REPLY_TYPE, ETFGCMsg E_REPLY_TYPE>
class CJobReliableMessageBase : public GCSDK::CGCClientJob
{
public:
typedef CProtoBufMsg< MSG_TYPE > Msg_t;
typedef CProtoBufMsg< REPLY_TYPE > Reply_t;
CJobReliableMessageBase()
: GCSDK::CGCClientJob( GCClientSystem()->GetGCClient() )
, m_msg( E_MSG_TYPE )
, m_msgReply()
{}
Msg_t &Msg() { return m_msg; }
void Enqueue()
{
static_cast<RELIABLE_MSG_CLASS *>(this)->InitDebugString( m_strDebug );
MMLog( "[SendMsgUntilConfirmed] %s queued for %s\n", GetMsgName(), DebugString() );
if ( !s_pCurrentConfirmJob )
{
s_pCurrentConfirmJob = this;
this->StartJobDelayed( NULL );
}
else
{
// Queue, confirm jobs will kick next in queue as necessary
s_queuePendingConfirmJobs.Insert( this );
}
}
virtual bool BYieldingRunJob( void *pvStartParam )
{
Assert( s_pCurrentConfirmJob == this );
bool bRet = BYieldingRunJobInternal();
if ( s_queuePendingConfirmJobs.Count() )
{
// Kick off next job
s_pCurrentConfirmJob = s_queuePendingConfirmJobs.RemoveAtHead();
s_pCurrentConfirmJob->StartJob( NULL );
}
else
{
s_pCurrentConfirmJob = NULL;
}
return bRet;
}
bool BYieldingRunJobInternal()
{
MMLog( "[SendMsgUntilConfirmed] %s started for %s\n", GetMsgName(), DebugString() );
// Trigger OnPrepare
static_cast<RELIABLE_MSG_CLASS *>(this)->OnPrepare();
for ( ;; )
{
BYieldingWaitOneFrame();
// Create and load the message
// continuously attempt to send the message to the GC
BYldSendMessageAndGetReply_t result = BYldSendMessageAndGetReplyEx( m_msg, 30, &m_msgReply, E_REPLY_TYPE );
switch ( result )
{
case BYLDREPLY_SUCCESS:
MMLog( "[SendMsgUntilConfirmed] %s successfully sent for %s\n",
GetMsgName(), DebugString() );
// Trigger OnReply
static_cast<RELIABLE_MSG_CLASS *>(this)->OnReply( m_msgReply );
return true;
case BYLDREPLY_SEND_FAILED:
MMLog( "[SendMsgUntilConfirmed] %s send FAILED for %s -- retrying\n",
GetMsgName(), DebugString() );
break;
case BYLDREPLY_TIMEOUT:
MMLog( "[SendMsgUntilConfirmed] %s send TIMEOUT for %s -- retrying\n",
GetMsgName(), DebugString() );
break;
case BYLDREPLY_MSG_TYPE_MISMATCH:
MMLog( "[SendMsgUntilConfirmed] %s send TYPE MISMATCH for %s\n",
GetMsgName(), DebugString() );
Assert( !"Mismatched response type in reliable message" );
return true;
}
}
}
protected:
// Overrides
// Must be overridden by reliable message implementers. Debug string is e.g. "Match 12345, Lobby 4"
void InitDebugString( CUtlString &debugStr ) {}
const char *MsgName() { return "<unknown>"; }
// Optionally overridden
void OnReply( Reply_t &msgReply ) {}
// Called before sending, after previous messages in queue have flushed
void OnPrepare() {}
private:
const char *DebugString() { return m_strDebug.Get(); }
// Forward to override
const char *GetMsgName() { return static_cast<RELIABLE_MSG_CLASS *>(this)->MsgName(); }
Msg_t m_msg;
Reply_t m_msgReply;
CUtlString m_strDebug;
void _static_asserts() {
// Ensure we passed an override and provided provided these
#if __cplusplus >= 201103L && !defined ( OSX ) // (Don't have time to figure out what criteria the OS X toolchain has to not blow this)
static_assert( std::is_base_of< decltype( *this ), RELIABLE_MSG_CLASS >::value,
"RELIABLE_MSG_CLASS Must be an override of this base" );
static_assert( !std::is_same< decltype( &(decltype( *this )::InitDebugString) ),
decltype( &RELIABLE_MSG_CLASS::InitDebugString ) >::value && \
!std::is_same< decltype( &(decltype( *this )::MsgName) ),
decltype( &RELIABLE_MSG_CLASS::MsgName ) >::value,
"RELIABLE_MSG_CLASS class must override DebugString and MsgName" );
#endif // __cplusplus >= 201103L && !defined ( OSX )
}
};
static bool BPendingReliableMessages()
{
Assert( !s_queuePendingConfirmJobs.Count() || s_pCurrentConfirmJob );
return !!s_pCurrentConfirmJob || s_queuePendingConfirmJobs.Count();
}
#endif // _TF_GC_SHARED_H
|