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
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#include "sv_sessioninfopublisher.h"
#include "sv_replaycontext.h"
#include "sv_recordingsessionblock.h"
#include "sv_recordingsession.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//----------------------------------------------------------------------------------------
CSessionInfoPublisher::CSessionInfoPublisher( CServerRecordingSession *pSession )
: m_pSession( pSession ),
m_flSessionInfoPublishTime( 0.0f ),
m_itLastCompletedBlockWrittenToBuffer( ~0 ),
m_pFilePublisher( NULL ),
m_bShouldPublish( false )
{
}
CSessionInfoPublisher::~CSessionInfoPublisher()
{
}
void CSessionInfoPublisher::Publish()
{
m_bShouldPublish = true;
}
bool CSessionInfoPublisher::IsDone() const
{
return !m_bShouldPublish && m_pFilePublisher == NULL;
}
void CSessionInfoPublisher::OnStopRecord( bool bAborting )
{
}
void CSessionInfoPublisher::AbortPublish()
{
m_bShouldPublish = false;
if ( m_pFilePublisher && !m_pFilePublisher->IsDone() )
{
m_pFilePublisher->AbortAndCleanup();
}
}
void CSessionInfoPublisher::RefreshSessionInfoBlockData( CUtlBuffer &buf )
{
const CBaseRecordingSession::BlockContainer_t &vecBlocks = m_pSession->GetBlocks();
// Start from head if this is the first time we're writing, otherwise start
// from block after the last one written.
const int itStart = m_itLastCompletedBlockWrittenToBuffer == vecBlocks.InvalidIndex() ?
0 : m_itLastCompletedBlockWrittenToBuffer + 1;
for( int i = itStart; i < vecBlocks.Count(); ++i )
{
const CServerRecordingSessionBlock *pBlock = SV_CastBlock( vecBlocks[ i ] );
// NOTE: This will SeekPut() on buf, based on the block's reconstruction index.
pBlock->WriteSessionInfoDataToBuffer( buf );
// Cache the last block written whose state isn't going to change
if ( pBlock->m_nRemoteStatus == CBaseRecordingSessionBlock::STATUS_READYFORDOWNLOAD &&
i > m_itLastCompletedBlockWrittenToBuffer )
{
m_itLastCompletedBlockWrittenToBuffer = i;
}
IF_REPLAY_DBG( Warning( "Writing block w/ recon index %i to session info buffer\n", pBlock->m_iReconstruction ) );
}
}
void CSessionInfoPublisher::Think()
{
// Existing publisher?
if ( m_pFilePublisher )
{
// Finished?
if ( m_pFilePublisher->IsDone() )
{
// Free/clear
delete m_pFilePublisher;
m_pFilePublisher = NULL;
}
else
{
// Let the publisher think
m_pFilePublisher->Think();
}
}
// Publish needed?
if ( !m_bShouldPublish )
return;
// Already publishing?
if ( m_pFilePublisher )
return;
DBG( "Publishing session info...\n" );
// Write outstanding blocks to the buffer
RefreshSessionInfoBlockData( m_bufSessionInfo );
// We now know the uncompressed payload size
const int nPayloadSize = m_bufSessionInfo.TellPut();
// Create as much of the header as possible now - the rest will be written in AdjustHeader()
// once the publisher knows the md5 digest and the compression result.
Assert( m_pSession->m_strName.Length() < MAX_SESSIONNAME_LENGTH ); // The only way this name is going to get very long is if
V_strcpy_safe( m_SessionInfoHeader.m_szSessionName, m_pSession->m_strName.Get() );
m_SessionInfoHeader.m_nNumBlocks = m_pSession->GetNumBlocks();
m_SessionInfoHeader.m_bRecording = m_pSession->m_bRecording;
m_SessionInfoHeader.m_uPayloadSizeUC = nPayloadSize;
// Format a path & filename that points to the tmp dir, with <session name>.dmx on the end
CFmtStr fmtTmpSessionInfoFile( "%s%s.%s", SV_GetTmpDir(), m_pSession->m_strName.Get(), GENERIC_FILE_EXTENSION );
// Publish the file now (asynchronous)
PublishFileParams_t params;
params.m_pOutFilename = fmtTmpSessionInfoFile.Access(),
params.m_pSrcData = (uint8 *)m_bufSessionInfo.Base();
params.m_nSrcSize = nPayloadSize;
params.m_pCallbackHandler = this;
params.m_nCompressorType = COMPRESSORTYPE_LZSS;
params.m_bHash = true;
params.m_bFreeSrcData = false;
params.m_bDeleteFile = false;
params.m_pHeaderData = &m_SessionInfoHeader;
params.m_nHeaderSize = sizeof( SessionInfoHeader_t );
params.m_pUserData = NULL;
m_pFilePublisher = SV_PublishFile( params );
// Reset flag
m_bShouldPublish = false;
}
void CSessionInfoPublisher::OnPublishComplete( const IFilePublisher *pPublisher, void *pUserData )
{
Assert( !pUserData );
// Handle publish failure
if ( pPublisher->GetStatus() != IFilePublisher::PUBLISHSTATUS_OK )
{
g_pServerReplayContext->OnPublishFailed();
}
}
void CSessionInfoPublisher::OnPublishAborted( const IFilePublisher *pPublisher )
{
Assert( pPublisher == m_pFilePublisher );
g_pServerReplayContext->OnPublishFailed();
}
void CSessionInfoPublisher::AdjustHeader( const IFilePublisher *pPublisher, void *pHeaderData )
{
SessionInfoHeader_t *pHeader = static_cast< SessionInfoHeader_t * >( pHeaderData );
// Set compressor type - will return COMPRESSORTYPE_INVALID if compression failed.
pHeader->m_nCompressorType = pPublisher->GetCompressorType();
pHeader->m_uPayloadSize = pPublisher->GetCompressedSize();
// Get MD5 digest
pPublisher->GetHash( pHeader->m_aHash );
}
void CSessionInfoPublisher::PublishAllSynchronous()
{
while ( !IsDone() )
{
Think();
}
}
//----------------------------------------------------------------------------------------
|