2012-11-26 18:58:24 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Doom 3 BFG Edition GPL Source Code
2012-11-28 15:47:07 +00:00
Copyright ( C ) 1993 - 2012 id Software LLC , a ZeniMax Media company .
2012-11-26 18:58:24 +00:00
2012-11-28 15:47:07 +00:00
This file is part of the Doom 3 BFG Edition GPL Source Code ( " Doom 3 BFG Edition Source Code " ) .
2012-11-26 18:58:24 +00:00
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 .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
# pragma hdrstop
# include "../idlib/precompiled.h"
2012-11-28 15:47:07 +00:00
idCVar net_verboseSnapshot ( " net_verboseSnapshot " , " 0 " , CVAR_INTEGER | CVAR_NOCHEAT , " Verbose snapshot code to help debug snapshot problems. Greater the number greater the spam " ) ;
idCVar net_verboseSnapshotCompression ( " net_verboseSnapshotCompression " , " 0 " , CVAR_INTEGER | CVAR_NOCHEAT , " Verbose snapshot code to help debug snapshot problems. Greater the number greater the spam " ) ;
idCVar net_verboseSnapshotReport ( " net_verboseSnapshotReport " , " 0 " , CVAR_INTEGER | CVAR_NOCHEAT , " Verbose snapshot code to help debug snapshot problems. Greater the number greater the spam " ) ;
2012-11-26 18:58:24 +00:00
idCVar net_ssTemplateDebug ( " net_ssTemplateDebug " , " 0 " , CVAR_BOOL , " Debug snapshot template states " ) ;
idCVar net_ssTemplateDebug_len ( " net_ssTemplateDebug_len " , " 32 " , CVAR_INTEGER , " Offset to start template state debugging " ) ;
idCVar net_ssTemplateDebug_start ( " net_ssTemplateDebug_start " , " 0 " , CVAR_INTEGER , " length of template state to print in debugging " ) ;
/*
= = = = = = = = = = = = = = = = = = = = = = = =
InDebugRange
Helper function for net_ssTemplateDebug debugging
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool InDebugRange ( int i )
{
2012-11-26 18:58:24 +00:00
return ( i > = net_ssTemplateDebug_start . GetInteger ( ) & & i < net_ssTemplateDebug_start . GetInteger ( ) + net_ssTemplateDebug_len . GetInteger ( ) ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
PrintAlign
Helper function for net_ssTemplateDebug debugging
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void PrintAlign ( const char * text )
{
2012-11-26 18:58:24 +00:00
idLib : : Printf ( " %25s: 0x " , text ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : objectState_t : : Print
Helper function for net_ssTemplateDebug debugging
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSnapShot : : objectState_t : : Print ( const char * name )
{
2012-11-26 18:58:24 +00:00
2012-11-28 15:47:07 +00:00
unsigned int start = ( unsigned int ) net_ssTemplateDebug_start . GetInteger ( ) ;
unsigned int end = Min ( ( unsigned int ) buffer . Size ( ) , start + net_ssTemplateDebug_len . GetInteger ( ) ) ;
2012-11-26 18:58:24 +00:00
PrintAlign ( va ( " %s: [sz %d] " , name , buffer . Size ( ) ) ) ;
2012-11-28 15:47:07 +00:00
for ( unsigned int i = start ; i < end ; i + + )
{
2012-11-26 18:58:24 +00:00
idLib : : Printf ( " %02X " , buffer [ i ] ) ;
}
2012-11-28 15:47:07 +00:00
idLib : : Printf ( " \n " ) ;
2012-11-26 18:58:24 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : objectBuffer_t : : Alloc
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSnapShot : : objectBuffer_t : : Alloc ( int s )
{
2012-11-26 18:58:24 +00:00
//assert( mem.IsMapHeap() );
2012-11-28 15:47:07 +00:00
if ( ! verify ( s < SIZE_NOT_STALE ) )
{
2012-11-26 18:58:24 +00:00
idLib : : FatalError ( " s >= SIZE_NOT_STALE " ) ;
}
_Release ( ) ;
2012-11-28 15:47:07 +00:00
data = ( byte * ) Mem_Alloc ( s + 1 , TAG_NETWORKING ) ;
2012-11-26 18:58:24 +00:00
size = s ;
data [ size ] = 1 ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : objectBuffer_t : : AddRef
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSnapShot : : objectBuffer_t : : _AddRef ( )
{
if ( data ! = NULL )
{
2012-11-26 18:58:24 +00:00
assert ( size > 0 ) ;
assert ( data [ size ] < 255 ) ;
data [ size ] + + ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : objectBuffer_t : : Release
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSnapShot : : objectBuffer_t : : _Release ( )
{
2012-11-26 18:58:24 +00:00
//assert( mem.IsMapHeap() );
2012-11-28 15:47:07 +00:00
if ( data ! = NULL )
{
2012-11-26 18:58:24 +00:00
assert ( size > 0 ) ;
2012-11-28 15:47:07 +00:00
if ( - - data [ size ] = = 0 )
{
2012-11-26 18:58:24 +00:00
Mem_Free ( data ) ;
}
data = NULL ;
size = 0 ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : objectBuffer_t : : operator =
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSnapShot : : objectBuffer_t : : operator = ( const idSnapShot : : objectBuffer_t & other )
{
2012-11-26 18:58:24 +00:00
//assert( mem.IsMapHeap() );
2012-11-28 15:47:07 +00:00
if ( this ! = & other )
{
2012-11-26 18:58:24 +00:00
_Release ( ) ;
data = other . data ;
size = other . size ;
_AddRef ( ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : idSnapShot
= = = = = = = = = = = = = = = = = = = = = = = =
*/
idSnapShot : : idSnapShot ( ) :
time ( 0 ) ,
recvTime ( 0 )
{
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : idSnapShot
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idSnapShot : : idSnapShot ( const idSnapShot & other ) : time ( 0 ) , recvTime ( 0 )
{
2012-11-26 18:58:24 +00:00
* this = other ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : ~ idSnapShot
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idSnapShot : : ~ idSnapShot ( )
{
2012-11-26 18:58:24 +00:00
Clear ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : Clear
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSnapShot : : Clear ( )
{
2012-11-26 18:58:24 +00:00
time = 0 ;
recvTime = 0 ;
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < objectStates . Num ( ) ; i + + )
{
2012-11-26 18:58:24 +00:00
FreeObjectState ( i ) ;
}
objectStates . Clear ( ) ;
allocatedObjs . Shutdown ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : operator =
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSnapShot : : operator = ( const idSnapShot & other )
{
2012-11-26 18:58:24 +00:00
//assert( mem.IsMapHeap() );
2012-11-28 15:47:07 +00:00
if ( this ! = & other )
{
for ( int i = other . objectStates . Num ( ) ; i < objectStates . Num ( ) ; i + + )
{
2012-11-26 18:58:24 +00:00
FreeObjectState ( i ) ;
}
objectStates . AssureSize ( other . objectStates . Num ( ) , NULL ) ;
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < objectStates . Num ( ) ; i + + )
{
const objectState_t & otherState = * other . objectStates [ i ] ;
if ( objectStates [ i ] = = NULL )
{
2012-11-26 18:58:24 +00:00
objectStates [ i ] = allocatedObjs . Alloc ( ) ;
}
2012-11-28 15:47:07 +00:00
objectState_t & state = * objectStates [ i ] ;
2012-11-26 18:58:24 +00:00
state . objectNum = otherState . objectNum ;
state . buffer = otherState . buffer ;
state . visMask = otherState . visMask ;
state . stale = otherState . stale ;
state . deleted = otherState . deleted ;
state . changedCount = otherState . changedCount ;
state . expectedSequence = otherState . expectedSequence ;
state . createdFromTemplate = otherState . createdFromTemplate ;
}
time = other . time ;
recvTime = other . recvTime ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : PeekDeltaSequence
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSnapShot : : PeekDeltaSequence ( const char * deltaMem , int deltaSize , int & sequence , int & baseSequence )
{
2012-11-26 18:58:24 +00:00
lzwCompressionData_t lzwData ;
idLZWCompressor lzwCompressor ( & lzwData ) ;
2012-11-28 15:47:07 +00:00
lzwCompressor . Start ( ( uint8 * ) deltaMem , deltaSize ) ;
2012-11-26 18:58:24 +00:00
lzwCompressor . ReadAgnostic ( sequence ) ;
lzwCompressor . ReadAgnostic ( baseSequence ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : ReadDeltaForJob
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool idSnapShot : : ReadDeltaForJob ( const char * deltaMem , int deltaSize , int visIndex , idSnapShot * templateStates )
{
2012-11-26 18:58:24 +00:00
bool report = net_verboseSnapshotReport . GetBool ( ) ;
net_verboseSnapshotReport . SetBool ( false ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
lzwCompressionData_t lzwData ;
idZeroRunLengthCompressor rleCompressor ;
idLZWCompressor lzwCompressor ( & lzwData ) ;
int bytesRead = 0 ; // how many uncompressed bytes we read in. Used to figure out compression ratio
2012-11-28 15:47:07 +00:00
lzwCompressor . Start ( ( uint8 * ) deltaMem , deltaSize ) ;
2012-11-26 18:58:24 +00:00
// Skip past sequence and baseSequence
int sequence = 0 ;
int baseSequence = 0 ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
lzwCompressor . ReadAgnostic ( sequence ) ;
lzwCompressor . ReadAgnostic ( baseSequence ) ;
lzwCompressor . ReadAgnostic ( time ) ;
bytesRead + = sizeof ( int ) * 3 ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
int objectNum = 0 ;
uint16 delta = 0 ;
2012-11-28 15:47:07 +00:00
while ( lzwCompressor . ReadAgnostic ( delta , true ) = = sizeof ( delta ) )
{
2012-11-26 18:58:24 +00:00
bytesRead + = sizeof ( delta ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
objectNum + = delta ;
2012-11-28 15:47:07 +00:00
if ( objectNum > = 0xFFFF )
{
2012-11-26 18:58:24 +00:00
// full delta
2012-11-28 15:47:07 +00:00
if ( net_verboseSnapshotCompression . GetBool ( ) )
{
2012-11-26 18:58:24 +00:00
float compRatio = static_cast < float > ( deltaSize ) / static_cast < float > ( bytesRead ) ;
idLib : : Printf ( " Snapshot (%d/%d). ReadSize: %d DeltaSize: %d Ratio: %.3f \n " , sequence , baseSequence , bytesRead , deltaSize , compRatio ) ;
}
return true ;
}
2012-11-28 15:47:07 +00:00
objectState_t & state = FindOrCreateObjectByID ( objectNum ) ;
2012-11-26 18:58:24 +00:00
objectSize_t newsize = 0 ;
lzwCompressor . ReadAgnostic ( newsize ) ;
bytesRead + = sizeof ( newsize ) ;
2012-11-28 15:47:07 +00:00
if ( newsize = = SIZE_STALE )
{
2012-11-26 18:58:24 +00:00
NET_VERBOSESNAPSHOT_PRINT ( " read delta: object %d goes stale \n " , objectNum ) ;
// sanity
bool oldVisible = ( state . visMask & ( 1 < < visIndex ) ) ! = 0 ;
2012-11-28 15:47:07 +00:00
if ( ! oldVisible )
{
2012-11-26 18:58:24 +00:00
NET_VERBOSESNAPSHOT_PRINT ( " ERROR: unexpected already stale \n " ) ;
}
state . visMask & = ~ ( 1 < < visIndex ) ;
state . stale = true ;
// We need to make sure we haven't freed stale objects.
assert ( state . buffer . Size ( ) > 0 ) ;
// no more data
continue ;
2012-11-28 15:47:07 +00:00
}
else if ( newsize = = SIZE_NOT_STALE )
{
2012-11-26 18:58:24 +00:00
NET_VERBOSESNAPSHOT_PRINT ( " read delta: object %d no longer stale \n " , objectNum ) ;
// sanity
bool oldVisible = ( state . visMask & ( 1 < < visIndex ) ) ! = 0 ;
2012-11-28 15:47:07 +00:00
if ( oldVisible )
{
2012-11-26 18:58:24 +00:00
NET_VERBOSESNAPSHOT_PRINT ( " ERROR: unexpected not stale \n " ) ;
}
state . visMask | = ( 1 < < visIndex ) ;
state . stale = false ;
// the latest state is packed in, get the new size and continue reading the new state
lzwCompressor . ReadAgnostic ( newsize ) ;
bytesRead + = sizeof ( newsize ) ;
}
2012-11-28 15:47:07 +00:00
objectState_t * objTemplateState = templateStates - > FindObjectByID ( objectNum ) ;
if ( newsize = = 0 )
{
2012-11-26 18:58:24 +00:00
// object deleted: reset state now so next one to use it doesn't have old data
state . deleted = false ;
state . stale = false ;
state . changedCount = 0 ;
state . expectedSequence = 0 ;
state . visMask = 0 ;
state . buffer . _Release ( ) ;
state . createdFromTemplate = false ;
2012-11-28 15:47:07 +00:00
if ( objTemplateState ! = NULL & & objTemplateState - > buffer . Size ( ) & & objTemplateState - > expectedSequence < baseSequence )
{
2012-11-26 18:58:24 +00:00
idLib : : PrintfIf ( net_ssTemplateDebug . GetBool ( ) , " Clearing old template state[%d] [%d<%d] \n " , objectNum , objTemplateState - > expectedSequence , baseSequence ) ;
objTemplateState - > deleted = false ;
objTemplateState - > stale = false ;
objTemplateState - > changedCount = 0 ;
objTemplateState - > expectedSequence = 0 ;
objTemplateState - > visMask = 0 ;
objTemplateState - > buffer . _Release ( ) ;
}
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
// new state?
bool debug = false ;
2012-11-28 15:47:07 +00:00
if ( state . buffer . Size ( ) = = 0 )
{
2012-11-26 18:58:24 +00:00
state . createdFromTemplate = true ;
// Brand new state
2012-11-28 15:47:07 +00:00
if ( objTemplateState ! = NULL & & objTemplateState - > buffer . Size ( ) > 0 & & sequence > = objTemplateState - > expectedSequence )
{
2012-11-26 18:58:24 +00:00
idLib : : PrintfIf ( net_ssTemplateDebug . GetBool ( ) , " \n Adding basestate for new object %d (for SS %d/%d. obj base created in ss %d) deltaSize: %d \n " , objectNum , sequence , baseSequence , objTemplateState - > expectedSequence , deltaSize ) ;
state . buffer = objTemplateState - > buffer ;
2012-11-28 15:47:07 +00:00
if ( net_ssTemplateDebug . GetBool ( ) )
{
2012-11-26 18:58:24 +00:00
state . Print ( " SPAWN STATE " ) ;
debug = true ;
PrintAlign ( " DELTA STATE " ) ;
}
}
2012-11-28 15:47:07 +00:00
else if ( net_ssTemplateDebug . GetBool ( ) )
{
idLib : : Printf ( " \n New snapobject[%d] in snapshot %d/%d but no basestate found locally so creating new \n " , objectNum , sequence , baseSequence ) ;
}
}
else
{
2012-11-26 18:58:24 +00:00
state . createdFromTemplate = false ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// the buffer shrank or stayed the same
objectBuffer_t newbuffer ( newsize ) ;
rleCompressor . Start ( NULL , & lzwCompressor , newsize ) ;
objectSize_t compareSize = Min ( state . buffer . Size ( ) , newsize ) ;
2012-11-28 15:47:07 +00:00
for ( objectSize_t i = 0 ; i < compareSize ; i + + )
{
2012-11-26 18:58:24 +00:00
byte b = rleCompressor . ReadByte ( ) ;
newbuffer [ i ] = state . buffer [ i ] + b ;
2012-11-28 15:47:07 +00:00
if ( debug & & InDebugRange ( i ) )
{
2012-11-26 18:58:24 +00:00
idLib : : Printf ( " %02X " , b ) ;
}
}
// Catch leftover
2012-11-28 15:47:07 +00:00
if ( newsize > compareSize )
{
2012-11-26 18:58:24 +00:00
rleCompressor . ReadBytes ( newbuffer . Ptr ( ) + compareSize , newsize - compareSize ) ;
2012-11-28 15:47:07 +00:00
if ( debug )
{
for ( objectSize_t i = compareSize ; i < newsize ; i + + )
{
if ( InDebugRange ( i ) )
{
2012-11-26 18:58:24 +00:00
idLib : : Printf ( " %02X " , newbuffer [ i ] ) ;
}
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
}
state . buffer = newbuffer ;
state . changedCount = sequence ;
bytesRead + = sizeof ( byte ) * newsize ;
2012-11-28 15:47:07 +00:00
if ( debug )
{
2012-11-26 18:58:24 +00:00
idLib : : Printf ( " \n " ) ;
2012-11-28 15:47:07 +00:00
state . Print ( " NEW STATE " ) ;
2012-11-26 18:58:24 +00:00
}
2012-11-28 15:47:07 +00:00
if ( report )
{
2012-11-26 18:58:24 +00:00
idLib : : Printf ( " Obj %d Compressed: Size %d \n " , objectNum , rleCompressor . CompressedSize ( ) ) ;
}
}
# ifdef SNAPSHOT_CHECKSUMS
extern uint32 SnapObjChecksum ( const uint8 * data , int length ) ;
2012-11-28 15:47:07 +00:00
if ( state . buffer . Size ( ) > 0 )
{
2012-11-26 18:58:24 +00:00
uint32 checksum = 0 ;
lzwCompressor . ReadAgnostic ( checksum ) ;
bytesRead + = sizeof ( checksum ) ;
2012-11-28 15:47:07 +00:00
if ( ! verify ( checksum = = SnapObjChecksum ( state . buffer . Ptr ( ) , state . buffer . Size ( ) ) ) )
{
idLib : : Error ( " Invalid snapshot checksum " ) ;
2012-11-26 18:58:24 +00:00
}
}
# endif
}
// partial delta
return false ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : SubmitObjectJob
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSnapShot : : SubmitObjectJob ( const submitDeltaJobsInfo_t & submitDeltaJobsInfo ,
objectState_t * newState ,
objectState_t * oldState ,
objParms_t * & baseObjParm ,
objParms_t * & curObjParm ,
objHeader_t * & curHeader ,
uint8 * & curObjDest ,
lzwParm_t * & curlzwParm
)
{
2012-11-26 18:58:24 +00:00
assert ( newState ! = NULL | | oldState ! = NULL ) ;
assert_16_byte_aligned ( curHeader ) ;
assert_16_byte_aligned ( curObjDest ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
int32 dataSize = newState ! = NULL ? newState - > buffer . Size ( ) : 0 ;
int totalSize = OBJ_DEST_SIZE_ALIGN16 ( dataSize ) ;
2012-11-28 15:47:07 +00:00
if ( curObjParm - submitDeltaJobsInfo . objParms > = submitDeltaJobsInfo . maxObjParms )
{
idLib : : Error ( " Out of parms for snapshot jobs. \n " ) ;
2012-11-26 18:58:24 +00:00
}
// Check to see if we are out of dest write space, and need to flush the jobs
bool needToSubmit = ( curObjDest - submitDeltaJobsInfo . objMemory ) + totalSize > = submitDeltaJobsInfo . maxObjMemory ;
needToSubmit | = ( curHeader - submitDeltaJobsInfo . headers > = submitDeltaJobsInfo . maxHeaders ) ;
2012-11-28 15:47:07 +00:00
if ( needToSubmit )
{
2012-11-26 18:58:24 +00:00
// If this obj will put us over the limit, then submit the jobs now, and start over re-using the same buffers
SubmitLZWJob ( submitDeltaJobsInfo , baseObjParm , curObjParm , curlzwParm , true ) ;
curHeader = submitDeltaJobsInfo . headers ;
curObjDest = submitDeltaJobsInfo . objMemory ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Setup obj parms
assert ( submitDeltaJobsInfo . visIndex < 256 ) ;
curObjParm - > visIndex = submitDeltaJobsInfo . visIndex ;
curObjParm - > destHeader = curHeader ;
curObjParm - > dest = curObjDest ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
memset ( & curObjParm - > newState , 0 , sizeof ( curObjParm - > newState ) ) ;
memset ( & curObjParm - > oldState , 0 , sizeof ( curObjParm - > oldState ) ) ;
2012-11-28 15:47:07 +00:00
if ( newState ! = NULL )
{
2012-11-26 18:58:24 +00:00
assert ( newState - > buffer . Size ( ) < = 65535 ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
curObjParm - > newState . valid = 1 ;
curObjParm - > newState . data = newState - > buffer . Ptr ( ) ;
curObjParm - > newState . size = newState - > buffer . Size ( ) ;
curObjParm - > newState . objectNum = newState - > objectNum ;
curObjParm - > newState . visMask = newState - > visMask ;
}
2012-11-28 15:47:07 +00:00
if ( oldState ! = NULL )
{
2012-11-26 18:58:24 +00:00
assert ( oldState - > buffer . Size ( ) < = 65535 ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
curObjParm - > oldState . valid = 1 ;
curObjParm - > oldState . data = oldState - > buffer . Ptr ( ) ;
curObjParm - > oldState . size = oldState - > buffer . Size ( ) ;
curObjParm - > oldState . objectNum = oldState - > objectNum ;
curObjParm - > oldState . visMask = oldState - > visMask ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
assert_16_byte_aligned ( curObjParm ) ;
assert_16_byte_aligned ( curObjParm - > newState . data ) ;
assert_16_byte_aligned ( curObjParm - > oldState . data ) ;
SnapshotObjectJob ( curObjParm ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Advance past header + data
curObjDest + = totalSize ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Advance parm pointer
2012-11-28 15:47:07 +00:00
curObjParm + + ;
2012-11-26 18:58:24 +00:00
// Advance header pointer
curHeader + + ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : SubmitLZWJob
Take the current list of delta ' d + zlre processed objects , and write them to the lzw stream .
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSnapShot : : SubmitLZWJob (
const submitDeltaJobsInfo_t & writeDeltaInfo ,
objParms_t * & baseObjParm , // Pointer to the first obj parm for the current stream
objParms_t * & curObjParm , // Current obj parm
lzwParm_t * & curlzwParm , // Current delta parm
2012-11-26 18:58:24 +00:00
bool saveDictionary
2012-11-28 15:47:07 +00:00
)
{
2012-11-26 18:58:24 +00:00
int numObjects = curObjParm - baseObjParm ;
2012-11-28 15:47:07 +00:00
if ( numObjects = = 0 )
{
2012-11-26 18:58:24 +00:00
return ; // Nothing to do
}
2012-11-28 15:47:07 +00:00
if ( curlzwParm - writeDeltaInfo . lzwParms > = writeDeltaInfo . maxDeltaParms )
{
2012-11-26 18:58:24 +00:00
idLib : : Error ( " SubmitLZWJob: Not enough lzwParams. \n " ) ;
return ; // Can't do anymore
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
curlzwParm - > numObjects = numObjects ;
curlzwParm - > headers = writeDeltaInfo . headers ; // We always start grabbing from the beggining of the memory (it's reused, with fences to protect memory sharing)
curlzwParm - > curTime = this - > GetTime ( ) ;
curlzwParm - > baseTime = writeDeltaInfo . oldSnap - > GetTime ( ) ;
curlzwParm - > baseSequence = writeDeltaInfo . baseSequence ;
curlzwParm - > fragmented = ( curlzwParm ! = writeDeltaInfo . lzwParms ) ;
curlzwParm - > saveDictionary = saveDictionary ;
curlzwParm - > ioData = writeDeltaInfo . lzwInOutData ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
LZWJob ( curlzwParm ) ;
curlzwParm + + ;
// Set base so it now points to where the parms start for the new stream
baseObjParm = curObjParm ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : GetTemplateState
Helper function for getting template objectState .
newState parameter is optional and is just used for debugging / printf comparison of the template and actual state
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idSnapShot : : objectState_t * idSnapShot : : GetTemplateState ( int objNum , idSnapShot * templateStates , idSnapShot : : objectState_t * newState /*=NULL*/ )
{
objectState_t * oldState = NULL ;
2012-11-26 18:58:24 +00:00
int spawnedStateIndex = templateStates - > FindObjectIndexByID ( objNum ) ;
2012-11-28 15:47:07 +00:00
if ( spawnedStateIndex > = 0 )
{
2012-11-26 18:58:24 +00:00
oldState = templateStates - > objectStates [ spawnedStateIndex ] ;
2012-11-28 15:47:07 +00:00
if ( net_ssTemplateDebug . GetBool ( ) )
{
2012-11-26 18:58:24 +00:00
idLib : : Printf ( " \n GetTemplateState[%d] \n " , objNum ) ;
oldState - > Print ( " SPAWN STATE " ) ;
2012-11-28 15:47:07 +00:00
if ( newState ! = NULL )
{
2012-11-26 18:58:24 +00:00
newState - > Print ( " CUR STATE " ) ;
}
}
}
return oldState ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : SubmitWriteDeltaToJobs
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSnapShot : : SubmitWriteDeltaToJobs ( const submitDeltaJobsInfo_t & submitDeltaJobInfo )
{
objParms_t * curObjParms = submitDeltaJobInfo . objParms ;
objParms_t * baseObjParms = submitDeltaJobInfo . objParms ;
lzwParm_t * curlzwParms = submitDeltaJobInfo . lzwParms ;
objHeader_t * curHeader = submitDeltaJobInfo . headers ;
uint8 * curObjMemory = submitDeltaJobInfo . objMemory ;
2012-11-26 18:58:24 +00:00
submitDeltaJobInfo . lzwInOutData - > numlzwDeltas = 0 ;
submitDeltaJobInfo . lzwInOutData - > lzwBytes = 0 ;
submitDeltaJobInfo . lzwInOutData - > fullSnap = false ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
int j = 0 ;
int numOldStates = submitDeltaJobInfo . oldSnap - > objectStates . Num ( ) ;
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < objectStates . Num ( ) ; i + + )
{
objectState_t & newState = * objectStates [ i ] ;
if ( ! verify ( newState . buffer . Size ( ) > 0 ) )
{
2012-11-26 18:58:24 +00:00
// you CANNOT have a valid ss obj state w/ size = 0: this will be interpreted as a delete in ::ReadDelta and this will completely throw
// off delta compression, eventually resulting in a checksum error that is a pain to track down.
idLib : : Warning ( " Snap obj [%d] state.size <= 0... skipping " , newState . objectNum ) ;
continue ;
}
2012-11-28 15:47:07 +00:00
if ( j > = numOldStates )
{
2012-11-26 18:58:24 +00:00
// We no longer have old objects to compare to.
// All objects are new from this point on.
2012-11-28 15:47:07 +00:00
objectState_t * oldState = GetTemplateState ( newState . objectNum , submitDeltaJobInfo . templateStates , & newState ) ;
2012-11-26 18:58:24 +00:00
SubmitObjectJob ( submitDeltaJobInfo , & newState , oldState , baseObjParms , curObjParms , curHeader , curObjMemory , curlzwParms ) ;
continue ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// write any deleted entities up to this one
2012-11-28 15:47:07 +00:00
for ( ; j < numOldStates & & newState . objectNum > submitDeltaJobInfo . oldSnap - > objectStates [ j ] - > objectNum ; j + + )
{
objectState_t & oldState = * submitDeltaJobInfo . oldSnap - > objectStates [ j ] ;
if ( ( oldState . stale & & ! oldState . deleted ) | | oldState . buffer . Size ( ) < = 0 )
{
2012-11-26 18:58:24 +00:00
continue ; // Don't delete objects that are stale and not marked as deleted
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
SubmitObjectJob ( submitDeltaJobInfo , NULL , & oldState , baseObjParms , curObjParms , curHeader , curObjMemory , curlzwParms ) ;
}
2012-11-28 15:47:07 +00:00
if ( j > = numOldStates )
{
2012-11-26 18:58:24 +00:00
continue ; // Went past end of old list deleting objects
}
// Beyond this point, we may have old state to compare against
2012-11-28 15:47:07 +00:00
objectState_t & submittedOldState = * submitDeltaJobInfo . oldSnap - > objectStates [ j ] ;
objectState_t * oldState = & submittedOldState ;
if ( newState . objectNum = = oldState - > objectNum )
{
if ( oldState - > buffer . Size ( ) = = 0 )
{
2012-11-26 18:58:24 +00:00
// New state (even though snapObj existed, its size was zero)
oldState = GetTemplateState ( newState . objectNum , submitDeltaJobInfo . templateStates , & newState ) ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
SubmitObjectJob ( submitDeltaJobInfo , & newState , oldState , baseObjParms , curObjParms , curHeader , curObjMemory , curlzwParms ) ;
j + + ;
2012-11-28 15:47:07 +00:00
}
else
{
// Different object, this one is new,
2012-11-26 18:58:24 +00:00
// Spawned
oldState = GetTemplateState ( newState . objectNum , submitDeltaJobInfo . templateStates , & newState ) ;
SubmitObjectJob ( submitDeltaJobInfo , & newState , oldState , baseObjParms , curObjParms , curHeader , curObjMemory , curlzwParms ) ;
}
}
// Finally, remove any entities at the end
2012-11-28 15:47:07 +00:00
for ( ; j < submitDeltaJobInfo . oldSnap - > objectStates . Num ( ) ; j + + )
{
objectState_t & oldState = * submitDeltaJobInfo . oldSnap - > objectStates [ j ] ;
if ( ( oldState . stale & & ! oldState . deleted ) | | oldState . buffer . Size ( ) < = 0 )
{
2012-11-26 18:58:24 +00:00
continue ; // Don't delete objects that are stale and not marked as deleted
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
SubmitObjectJob ( submitDeltaJobInfo , NULL , & oldState , baseObjParms , curObjParms , curHeader , curObjMemory , curlzwParms ) ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Submit any objects that are left over (will be all if they all fit up to this point)
SubmitLZWJob ( submitDeltaJobInfo , baseObjParms , curObjParms , curlzwParms , false ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : ReadDelta
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool idSnapShot : : ReadDelta ( idFile * file , int visIndex )
{
2012-11-26 18:58:24 +00:00
file - > ReadBig ( time ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
int objectNum = 0 ;
uint16 delta = 0 ;
2012-11-28 15:47:07 +00:00
while ( file - > ReadBig ( delta ) = = sizeof ( delta ) )
{
2012-11-26 18:58:24 +00:00
objectNum + = delta ;
2012-11-28 15:47:07 +00:00
if ( objectNum > = 0xFFFF )
{
2012-11-26 18:58:24 +00:00
// full delta
return true ;
}
2012-11-28 15:47:07 +00:00
objectState_t & state = FindOrCreateObjectByID ( objectNum ) ;
2012-11-26 18:58:24 +00:00
objectSize_t newsize = 0 ;
file - > ReadBig ( newsize ) ;
2012-11-28 15:47:07 +00:00
if ( newsize = = SIZE_STALE )
{
2012-11-26 18:58:24 +00:00
NET_VERBOSESNAPSHOT_PRINT ( " read delta: object %d goes stale \n " , objectNum ) ;
// sanity
bool oldVisible = ( state . visMask & ( 1 < < visIndex ) ) ! = 0 ;
2012-11-28 15:47:07 +00:00
if ( ! oldVisible )
{
2012-11-26 18:58:24 +00:00
NET_VERBOSESNAPSHOT_PRINT ( " ERROR: unexpected already stale \n " ) ;
}
state . visMask & = ~ ( 1 < < visIndex ) ;
state . stale = true ;
// We need to make sure we haven't freed stale objects.
assert ( state . buffer . Size ( ) > 0 ) ;
// no more data
continue ;
2012-11-28 15:47:07 +00:00
}
else if ( newsize = = SIZE_NOT_STALE )
{
2012-11-26 18:58:24 +00:00
NET_VERBOSESNAPSHOT_PRINT ( " read delta: object %d no longer stale \n " , objectNum ) ;
// sanity
bool oldVisible = ( state . visMask & ( 1 < < visIndex ) ) ! = 0 ;
2012-11-28 15:47:07 +00:00
if ( oldVisible )
{
2012-11-26 18:58:24 +00:00
NET_VERBOSESNAPSHOT_PRINT ( " ERROR: unexpected not stale \n " ) ;
}
state . visMask | = ( 1 < < visIndex ) ;
state . stale = false ;
// the latest state is packed in, get the new size and continue reading the new state
file - > ReadBig ( newsize ) ;
}
2012-11-28 15:47:07 +00:00
if ( newsize = = 0 )
{
2012-11-26 18:58:24 +00:00
// object deleted
state . buffer . _Release ( ) ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
objectBuffer_t newbuffer ( newsize ) ;
objectSize_t compareSize = Min ( newsize , state . buffer . Size ( ) ) ;
2012-11-28 15:47:07 +00:00
for ( objectSize_t i = 0 ; i < compareSize ; i + + )
{
2012-11-26 18:58:24 +00:00
uint8 delta = 0 ;
file - > ReadBig < byte > ( delta ) ;
newbuffer [ i ] = state . buffer [ i ] + delta ;
}
2012-11-28 15:47:07 +00:00
if ( newsize > compareSize )
{
file - > Read ( newbuffer . Ptr ( ) + compareSize , newsize - compareSize ) ;
2012-11-26 18:58:24 +00:00
}
2012-11-28 15:47:07 +00:00
state . buffer = newbuffer ;
2012-11-26 18:58:24 +00:00
state . changedCount + + ;
}
# ifdef SNAPSHOT_CHECKSUMS
2012-11-28 15:47:07 +00:00
if ( state . buffer . Size ( ) > 0 )
{
2012-11-26 18:58:24 +00:00
unsigned int checksum = 0 ;
file - > ReadBig ( checksum ) ;
assert ( checksum = = MD5_BlockChecksum ( state . buffer . Ptr ( ) , state . buffer . Size ( ) ) ) ;
}
2012-11-28 15:47:07 +00:00
# endif
2012-11-26 18:58:24 +00:00
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// partial delta
return false ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : WriteObject
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSnapShot : : WriteObject ( idFile * file , int visIndex , objectState_t * newState , objectState_t * oldState , int & lastobjectNum )
{
2012-11-26 18:58:24 +00:00
assert ( newState ! = NULL | | oldState ! = NULL ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
bool visChange = false ; // visibility changes will be signified with a 0xffff state size
bool visSendState = false ; // the state is sent when an entity is no longer stale
2012-11-28 15:47:07 +00:00
// Compute visibility changes
2012-11-26 18:58:24 +00:00
// (we need to do this before writing out object id, because we may not need to write out the id if we early out)
// (when we don't write out the id, we assume this is an "ack" when we deserialize the objects)
2012-11-28 15:47:07 +00:00
if ( newState ! = NULL & & oldState ! = NULL )
{
2012-11-26 18:58:24 +00:00
// Check visibility
assert ( newState - > objectNum = = oldState - > objectNum ) ;
2012-11-28 15:47:07 +00:00
if ( visIndex > 0 )
{
2012-11-26 18:58:24 +00:00
bool oldVisible = ( oldState - > visMask & ( 1 < < visIndex ) ) ! = 0 ;
bool newVisible = ( newState - > visMask & ( 1 < < visIndex ) ) ! = 0 ;
// Force visible if we need to either create or destroy this object
2012-11-28 15:47:07 +00:00
newVisible | = ( newState - > buffer . Size ( ) = = 0 ) ! = ( oldState - > buffer . Size ( ) = = 0 ) ;
2012-11-26 18:58:24 +00:00
2012-11-28 15:47:07 +00:00
if ( ! oldVisible & & ! newVisible )
{
// object is stale and ack'ed for this client, write nothing (see 'same object' below)
2012-11-26 18:58:24 +00:00
return ;
2012-11-28 15:47:07 +00:00
}
else if ( oldVisible & & ! newVisible )
{
2012-11-26 18:58:24 +00:00
NET_VERBOSESNAPSHOT_PRINT ( " object %d to client %d goes stale \n " , newState - > objectNum , visIndex ) ;
visChange = true ;
visSendState = false ;
2012-11-28 15:47:07 +00:00
}
else if ( ! oldVisible & & newVisible )
{
2012-11-26 18:58:24 +00:00
NET_VERBOSESNAPSHOT_PRINT ( " object %d to client %d no longer stale \n " , newState - > objectNum , visIndex ) ;
visChange = true ;
visSendState = true ;
2012-11-28 15:47:07 +00:00
}
2012-11-26 18:58:24 +00:00
}
// Same object, write a delta (never early out during vis changes)
2012-11-28 15:47:07 +00:00
if ( ! visChange & & newState - > buffer . Size ( ) = = oldState - > buffer . Size ( ) & &
( ( newState - > buffer . Ptr ( ) = = oldState - > buffer . Ptr ( ) ) | | memcmp ( newState - > buffer . Ptr ( ) , oldState - > buffer . Ptr ( ) , newState - > buffer . Size ( ) ) = = 0 ) )
{
2012-11-26 18:58:24 +00:00
// same state, write nothing
return ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Get the id of the object we are writing out
uint16 objectNum ;
2012-11-28 15:47:07 +00:00
if ( newState ! = NULL )
{
2012-11-26 18:58:24 +00:00
objectNum = newState - > objectNum ;
2012-11-28 15:47:07 +00:00
}
else if ( oldState ! = NULL )
{
2012-11-26 18:58:24 +00:00
objectNum = oldState - > objectNum ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
objectNum = 0 ;
}
assert ( objectNum = = 0 | | objectNum > lastobjectNum ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Write out object id (using delta)
uint16 objectDelta = objectNum - lastobjectNum ;
file - > WriteBig ( objectDelta ) ;
lastobjectNum = objectNum ;
2012-11-28 15:47:07 +00:00
if ( newState = = NULL )
{
2012-11-26 18:58:24 +00:00
// Deleted, write 0 size
assert ( oldState ! = NULL ) ;
file - > WriteBig < objectSize_t > ( 0 ) ;
2012-11-28 15:47:07 +00:00
}
else if ( oldState = = NULL )
{
2012-11-26 18:58:24 +00:00
// New object, write out full state
assert ( newState ! = NULL ) ;
// delta against an empty snap
file - > WriteBig ( newState - > buffer . Size ( ) ) ;
file - > Write ( newState - > buffer . Ptr ( ) , newState - > buffer . Size ( ) ) ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
// Compare to last object
assert ( newState ! = NULL & & oldState ! = NULL ) ;
assert ( newState - > objectNum = = oldState - > objectNum ) ;
2012-11-28 15:47:07 +00:00
if ( visChange )
{
2012-11-26 18:58:24 +00:00
// fake size indicates vis state change
// NOTE: we may still send a real size and a state below, for 'no longer stale' transitions
// TMP: send 0xFFFF for going stale and 0xFFFF - 1 for no longer stale
file - > WriteBig < objectSize_t > ( visSendState ? SIZE_NOT_STALE : SIZE_STALE ) ;
}
2012-11-28 15:47:07 +00:00
if ( ! visChange | | visSendState )
{
2012-11-26 18:58:24 +00:00
objectSize_t compareSize = Min ( newState - > buffer . Size ( ) , oldState - > buffer . Size ( ) ) ; // Get the number of bytes that overlap
file - > WriteBig ( newState - > buffer . Size ( ) ) ; // Write new size
// Compare bytes that overlap
2012-11-28 15:47:07 +00:00
for ( objectSize_t b = 0 ; b < compareSize ; b + + )
{
2012-11-26 18:58:24 +00:00
file - > WriteBig < byte > ( ( 0xFF + 1 + newState - > buffer [ b ] - oldState - > buffer [ b ] ) & 0xFF ) ;
}
// Write leftover
2012-11-28 15:47:07 +00:00
if ( newState - > buffer . Size ( ) > compareSize )
{
2012-11-26 18:58:24 +00:00
file - > Write ( newState - > buffer . Ptr ( ) + oldState - > buffer . Size ( ) , newState - > buffer . Size ( ) - compareSize ) ;
}
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
# ifdef SNAPSHOT_CHECKSUMS
2012-11-28 15:47:07 +00:00
if ( ( ! visChange | | visSendState ) & & newState ! = NULL )
{
2012-11-26 18:58:24 +00:00
assert ( newState - > buffer . Size ( ) > 0 ) ;
unsigned int checksum = MD5_BlockChecksum ( newState - > buffer . Ptr ( ) , newState - > buffer . Size ( ) ) ;
file - > WriteBig ( checksum ) ;
}
# endif
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : PrintReport
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSnapShot : : PrintReport ( )
{
2012-11-26 18:58:24 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : WriteDelta
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool idSnapShot : : WriteDelta ( idSnapShot & old , int visIndex , idFile * file , int maxLength , int optimalLength )
{
2012-11-26 18:58:24 +00:00
file - > WriteBig ( time ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
int objectHeaderSize = sizeof ( uint16 ) + sizeof ( objectSize_t ) ;
# ifdef SNAPSHOT_CHECKSUMS
objectHeaderSize + = sizeof ( unsigned int ) ;
# endif
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
int lastobjectNum = 0 ;
int j = 0 ;
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < objectStates . Num ( ) ; i + + )
{
objectState_t & newState = * objectStates [ i ] ;
if ( optimalLength > 0 & & file - > Length ( ) > = optimalLength )
{
2012-11-26 18:58:24 +00:00
return false ;
}
2012-11-28 15:47:07 +00:00
if ( ! verify ( newState . buffer . Size ( ) < maxLength ) )
{
2012-11-26 18:58:24 +00:00
// If the new state's size is > the max packet size, we'll never be able to send it!
idLib : : Warning ( " Snap obj [%d] state.size > max packet length. Skipping... " , newState . objectNum ) ;
continue ;
2012-11-28 15:47:07 +00:00
}
else if ( ! verify ( newState . buffer . Size ( ) > 0 ) )
{
2012-11-26 18:58:24 +00:00
// you CANNOT have a valid ss obj state w/ size = 0: this will be interpreted as a delete in ::ReadDelta and this will completely throw
// off delta compression, eventually resulting in a checksum error that is a pain to track down.
idLib : : Warning ( " Snap obj [%d] state.size <= 0... skipping " , newState . objectNum ) ;
continue ;
}
2012-11-28 15:47:07 +00:00
if ( file - > Length ( ) + objectHeaderSize + newState . buffer . Size ( ) > = maxLength )
{
2012-11-26 18:58:24 +00:00
return false ;
}
2012-11-28 15:47:07 +00:00
if ( j > = old . objectStates . Num ( ) )
{
2012-11-26 18:58:24 +00:00
// delta against an empty snap
WriteObject ( file , visIndex , & newState , NULL , lastobjectNum ) ;
continue ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// write any deleted entities up to this one
2012-11-28 15:47:07 +00:00
for ( ; newState . objectNum > old . objectStates [ j ] - > objectNum ; j + + )
{
if ( file - > Length ( ) + objectHeaderSize > = maxLength )
{
2012-11-26 18:58:24 +00:00
return false ;
}
2012-11-28 15:47:07 +00:00
objectState_t & oldState = * old . objectStates [ j ] ;
2012-11-26 18:58:24 +00:00
WriteObject ( file , visIndex , NULL , & oldState , lastobjectNum ) ;
}
// Beyond this point, we have old state to compare against
2012-11-28 15:47:07 +00:00
objectState_t & oldState = * old . objectStates [ j ] ;
if ( newState . objectNum = = oldState . objectNum )
{
2012-11-26 18:58:24 +00:00
// FIXME: We don't need to early out if WriteObject determines that we won't send the object due to being stale
2012-11-28 15:47:07 +00:00
if ( file - > Length ( ) + objectHeaderSize + newState . buffer . Size ( ) > = maxLength )
{
2012-11-26 18:58:24 +00:00
return false ;
}
WriteObject ( file , visIndex , & newState , & oldState , lastobjectNum ) ;
j + + ;
2012-11-28 15:47:07 +00:00
}
else
{
if ( file - > Length ( ) + objectHeaderSize + newState . buffer . Size ( ) > = maxLength )
{
2012-11-26 18:58:24 +00:00
return false ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Different object, this one is new, write the full state
WriteObject ( file , visIndex , & newState , NULL , lastobjectNum ) ;
}
}
// Finally, remove any entities at the end
2012-11-28 15:47:07 +00:00
for ( ; j < old . objectStates . Num ( ) ; j + + )
{
if ( file - > Length ( ) + objectHeaderSize > = maxLength )
{
2012-11-26 18:58:24 +00:00
return false ;
}
2012-11-28 15:47:07 +00:00
if ( optimalLength > 0 & & file - > Length ( ) > = optimalLength )
{
2012-11-26 18:58:24 +00:00
return false ;
}
2012-11-28 15:47:07 +00:00
objectState_t & oldState = * old . objectStates [ j ] ;
2012-11-26 18:58:24 +00:00
WriteObject ( file , visIndex , NULL , & oldState , lastobjectNum ) ;
}
2012-11-28 15:47:07 +00:00
if ( file - > Length ( ) + 2 > = maxLength )
{
2012-11-26 18:58:24 +00:00
return false ;
}
uint16 objectDelta = 0xFFFF - lastobjectNum ;
file - > WriteBig ( objectDelta ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : AddObject
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idSnapShot : : objectState_t * idSnapShot : : S_AddObject ( int objectNum , uint32 visMask , const char * data , int _size , const char * tag )
{
2012-11-26 18:58:24 +00:00
objectSize_t size = _size ;
2012-11-28 15:47:07 +00:00
objectState_t & state = FindOrCreateObjectByID ( objectNum ) ;
2012-11-26 18:58:24 +00:00
state . visMask = visMask ;
2012-11-28 15:47:07 +00:00
if ( state . buffer . Size ( ) = = size & & state . buffer . NumRefs ( ) = = 1 )
{
2012-11-26 18:58:24 +00:00
// re-use the same buffer
memcpy ( state . buffer . Ptr ( ) , data , size ) ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
objectBuffer_t buffer ( size ) ;
memcpy ( buffer . Ptr ( ) , data , size ) ;
state . buffer = buffer ;
}
return & state ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : CopyObject
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool idSnapShot : : CopyObject ( const idSnapShot & oldss , int objectNum , bool forceStale )
{
2012-11-26 18:58:24 +00:00
int oldIndex = oldss . FindObjectIndexByID ( objectNum ) ;
2012-11-28 15:47:07 +00:00
if ( oldIndex = = - 1 )
{
2012-11-26 18:58:24 +00:00
return false ;
}
2012-11-28 15:47:07 +00:00
const objectState_t & oldState = * oldss . objectStates [ oldIndex ] ;
objectState_t & newState = FindOrCreateObjectByID ( objectNum ) ;
2012-11-26 18:58:24 +00:00
newState . buffer = oldState . buffer ;
newState . visMask = oldState . visMask ;
newState . stale = oldState . stale ;
newState . deleted = oldState . deleted ;
newState . changedCount = oldState . changedCount ;
newState . expectedSequence = oldState . expectedSequence ;
newState . createdFromTemplate = oldState . createdFromTemplate ;
2012-11-28 15:47:07 +00:00
if ( forceStale )
{
2012-11-26 18:58:24 +00:00
newState . visMask = 0 ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : CompareObject
start , end , and oldStart can optionally be passed in to compare subsections of the object
default parameters will compare entire object
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
int idSnapShot : : CompareObject ( const idSnapShot * oldss , int objectNum , int start , int end , int oldStart )
{
if ( oldss = = NULL )
{
2012-11-26 18:58:24 +00:00
return 0 ;
}
assert ( FindObjectIndexByID ( objectNum ) > = 0 ) ;
2012-11-28 15:47:07 +00:00
objectState_t & newState = FindOrCreateObjectByID ( objectNum ) ;
2012-11-26 18:58:24 +00:00
int oldIndex = oldss - > FindObjectIndexByID ( objectNum ) ;
2012-11-28 15:47:07 +00:00
if ( oldIndex = = - 1 )
{
2012-11-26 18:58:24 +00:00
return ( end = = 0 ? newState . buffer . Size ( ) : end - start ) ; // Didn't exist in the old state, so we take the hit on the entire size
}
2012-11-28 15:47:07 +00:00
objectState_t & oldState = const_cast < objectState_t & > ( * oldss - > objectStates [ oldIndex ] ) ;
2012-11-26 18:58:24 +00:00
int bytes = 0 ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
int oldOffset = oldStart - start ;
int commonSize = ( newState . buffer . Size ( ) < = oldState . buffer . Size ( ) - oldOffset ) ? newState . buffer . Size ( ) : oldState . buffer . Size ( ) - oldOffset ;
2012-11-28 15:47:07 +00:00
if ( end = = 0 )
{
2012-11-26 18:58:24 +00:00
// default 0 means compare the whole thing
end = commonSize ;
// Get leftover (if any)
bytes = ( newState . buffer . Size ( ) > oldState . buffer . Size ( ) ) ? ( newState . buffer . Size ( ) - oldState . buffer . Size ( ) ) : 0 ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
// else only compare up to end or the max buffer and dont include leftover
end = Min ( commonSize , end ) ;
}
2012-11-28 15:47:07 +00:00
for ( int b = start ; b < end ; b + + )
{
if ( verify ( b > = 0 & & b < ( int ) newState . buffer . Size ( ) & & b + oldOffset > = 0 & & b + oldOffset < ( int ) oldState . buffer . Size ( ) ) )
{
2012-11-26 18:58:24 +00:00
bytes + = ( newState . buffer [ b ] ! = oldState . buffer [ b + oldOffset ] ) ? 1 : 0 ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
return bytes ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : GetObjectMsgByIndex
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
int idSnapShot : : GetObjectMsgByIndex ( int i , idBitMsg & msg , bool ignoreIfStale ) const
{
if ( i < 0 | | i > = objectStates . Num ( ) )
{
2012-11-26 18:58:24 +00:00
return - 1 ;
}
2012-11-28 15:47:07 +00:00
objectState_t & state = * objectStates [ i ] ;
if ( state . stale & & ignoreIfStale )
{
2012-11-26 18:58:24 +00:00
return - 1 ;
}
msg . InitRead ( state . buffer . Ptr ( ) , state . buffer . Size ( ) ) ;
return state . objectNum ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : ObjectIsStaleByIndex
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool idSnapShot : : ObjectIsStaleByIndex ( int i ) const
{
if ( i < 0 | | i > = objectStates . Num ( ) )
{
2012-11-26 18:58:24 +00:00
return false ;
}
return objectStates [ i ] - > stale ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : ObjectChangedCountByIndex
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
int idSnapShot : : ObjectChangedCountByIndex ( int i ) const
{
if ( i < 0 | | i > = objectStates . Num ( ) )
{
2012-11-26 18:58:24 +00:00
return false ;
}
return objectStates [ i ] - > changedCount ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : FindObjectIndexByID
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
int idSnapShot : : FindObjectIndexByID ( int objectNum ) const
{
2012-11-26 18:58:24 +00:00
int i = BinarySearch ( objectNum ) ;
2012-11-28 15:47:07 +00:00
if ( i > = 0 & & i < objectStates . Num ( ) & & objectStates [ i ] - > objectNum = = objectNum )
{
2012-11-26 18:58:24 +00:00
return i ;
}
return - 1 ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : BinarySearch
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
int idSnapShot : : BinarySearch ( int objectNum ) const
{
2012-11-26 18:58:24 +00:00
int lo = 0 ;
int hi = objectStates . Num ( ) ;
2012-11-28 15:47:07 +00:00
while ( hi ! = lo )
{
2012-11-26 18:58:24 +00:00
int mid = ( hi + lo ) > > 1 ;
2012-11-28 15:47:07 +00:00
if ( objectStates [ mid ] - > objectNum = = objectNum )
{
2012-11-26 18:58:24 +00:00
return mid ; // Early out if we can
}
2012-11-28 15:47:07 +00:00
if ( objectStates [ mid ] - > objectNum < objectNum )
{
2012-11-26 18:58:24 +00:00
lo = mid + 1 ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
hi = mid ;
}
}
return hi ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : FindOrCreateObjectByID
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idSnapShot : : objectState_t & idSnapShot : : FindOrCreateObjectByID ( int objectNum )
{
2012-11-26 18:58:24 +00:00
//assert( mem.IsMapHeap() );
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
int i = BinarySearch ( objectNum ) ;
2012-11-28 15:47:07 +00:00
if ( i > = 0 & & i < objectStates . Num ( ) & & objectStates [ i ] - > objectNum = = objectNum )
{
2012-11-26 18:58:24 +00:00
return * objectStates [ i ] ;
}
2012-11-28 15:47:07 +00:00
objectState_t * newstate = allocatedObjs . Alloc ( ) ;
2012-11-26 18:58:24 +00:00
newstate - > objectNum = objectNum ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
objectStates . Insert ( newstate , i ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
return * objectStates [ i ] ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : FindObjectByID
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idSnapShot : : objectState_t * idSnapShot : : FindObjectByID ( int objectNum ) const
{
2012-11-26 18:58:24 +00:00
//assert( mem.IsMapHeap() );
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
int i = BinarySearch ( objectNum ) ;
2012-11-28 15:47:07 +00:00
if ( i > = 0 & & i < objectStates . Num ( ) & & objectStates [ i ] - > objectNum = = objectNum )
{
2012-11-26 18:58:24 +00:00
return objectStates [ i ] ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
return NULL ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : CleanupEmptyStates
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSnapShot : : CleanupEmptyStates ( )
{
for ( int i = objectStates . Num ( ) - 1 ; i > = 0 ; i - - )
{
if ( objectStates [ i ] - > buffer . Size ( ) = = 0 )
{
2012-11-26 18:58:24 +00:00
FreeObjectState ( i ) ;
2012-11-28 15:47:07 +00:00
objectStates . RemoveIndex ( i ) ;
2012-11-26 18:58:24 +00:00
}
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : UpdateExpectedSeq
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSnapShot : : UpdateExpectedSeq ( int newSeq )
{
for ( int i = 0 ; i < objectStates . Num ( ) ; i + + )
{
if ( objectStates [ i ] - > expectedSequence = = - 2 )
{
2012-11-26 18:58:24 +00:00
objectStates [ i ] - > expectedSequence = newSeq ;
}
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : FreeObjectState
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSnapShot : : FreeObjectState ( int index )
{
2012-11-26 18:58:24 +00:00
assert ( objectStates [ index ] ! = NULL ) ;
//assert( mem.IsMapHeap() );
objectStates [ index ] - > buffer . _Release ( ) ;
allocatedObjs . Free ( objectStates [ index ] ) ;
objectStates [ index ] = NULL ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSnapShot : : ApplyToExistingState
Take uncompressed state in msg and add it to existing state
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSnapShot : : ApplyToExistingState ( int objId , idBitMsg & msg )
{
objectState_t * objectState = FindObjectByID ( objId ) ;
if ( ! verify ( objectState ! = NULL ) )
{
return ;
2012-11-26 18:58:24 +00:00
}
2012-11-28 15:47:07 +00:00
if ( ! objectState - > createdFromTemplate )
{
2012-11-26 18:58:24 +00:00
// We were created this from a template, so we shouldn't be applying it again
2012-11-28 15:47:07 +00:00
if ( net_ssTemplateDebug . GetBool ( ) )
{
idLib : : Printf ( " NOT ApplyToExistingState[%d] because object was created from existing base state. %d \n " , objId , objectState - > expectedSequence ) ;
2012-11-26 18:58:24 +00:00
objectState - > Print ( " SS STATE " ) ;
}
return ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Debug print the template (spawn) and delta state
2012-11-28 15:47:07 +00:00
if ( net_ssTemplateDebug . GetBool ( ) )
{
2012-11-26 18:58:24 +00:00
idLib : : Printf ( " \n ApplyToExistingState[%d]. buffer size: %d msg size: %d \n " , objId , objectState - > buffer . Size ( ) , msg . GetSize ( ) ) ;
objectState - > Print ( " DELTA STATE " ) ;
PrintAlign ( " SPAWN STATE " ) ;
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < msg . GetSize ( ) ; i + + )
{
if ( InDebugRange ( i ) )
{
2012-11-26 18:58:24 +00:00
idLib : : Printf ( " %02X " , msg . GetReadData ( ) [ i ] ) ;
}
}
idLib : : Printf ( " \n " ) ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Actually apply it
2012-11-28 15:47:07 +00:00
for ( objectSize_t i = 0 ; i < Min ( objectState - > buffer . Size ( ) , msg . GetSize ( ) ) ; i + + )
{
2012-11-26 18:58:24 +00:00
objectState - > buffer [ i ] + = msg . GetReadData ( ) [ i ] ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Debug print the final state
2012-11-28 15:47:07 +00:00
if ( net_ssTemplateDebug . GetBool ( ) )
{
2012-11-26 18:58:24 +00:00
objectState - > Print ( " NEW STATE " ) ;
idLib : : Printf ( " \n " ) ;
}
}
#if 0
2012-11-28 15:47:07 +00:00
CONSOLE_COMMAND ( serializeQTest , " Serialization Sanity Test " , 0 )
{
2012-11-26 18:58:24 +00:00
byte buffer [ 1024 ] ;
2012-11-28 15:47:07 +00:00
memset ( buffer , 0 , sizeof ( buffer ) ) ;
float values [ ] = { 0.0001f , 0.001f , 0.01f , 0.1f , 0.2f , 0.3f , 0.4f , 0.5f , 0.6f , 0.7f , 0.8f , 0.9f , 0.999f ,
1.0f , 1.01f , 1.1f , 10.0f , 10.1f , 10.101f , 100.0f , 101.0f , 101.1f , 101.101f
} ;
int num = sizeof ( values ) / sizeof ( float ) ;
idLib : : Printf ( " \n ^3Testing SerializeQ and SerializeUQ \n " ) ;
2012-11-26 18:58:24 +00:00
{
idBitMsg writeBitMsg ;
2012-11-28 15:47:07 +00:00
writeBitMsg . InitWrite ( buffer , sizeof ( buffer ) ) ;
2012-11-26 18:58:24 +00:00
idSerializer writeSerializer ( writeBitMsg , true ) ;
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < num ; i + + )
{
2012-11-26 18:58:24 +00:00
writeSerializer . SerializeUQ ( values [ i ] , 255.0f , 16 ) ;
writeSerializer . SerializeQ ( values [ i ] , 128.0f , 16 ) ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
{
idBitMsg readBitMsg ;
readBitMsg . InitRead ( buffer , sizeof ( buffer ) ) ;
idSerializer readSerializer ( readBitMsg , false ) ;
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < num ; i + + )
{
2012-11-26 18:58:24 +00:00
float resultUQ = - 999.0f ;
float resultQ = - 999.0f ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
readSerializer . SerializeUQ ( resultUQ , 255.0f , 16 ) ;
readSerializer . SerializeQ ( resultQ , 128.0f , 16 ) ;
2012-11-28 15:47:07 +00:00
float errorUQ = idMath : : Fabs ( ( resultUQ - values [ i ] ) ) / values [ i ] ;
float errorQ = idMath : : Fabs ( ( resultQ - values [ i ] ) ) / values [ i ] ;
idLib : : Printf ( " %s%f SerializeUQ: %f. Error: %f \n " , errorUQ > 0.1f ? " ^1 " : " " , values [ i ] , resultUQ , errorUQ ) ;
idLib : : Printf ( " %s%f SerializeQ: %f. Error: %f \n " , errorQ > 0.1f ? " ^1 " : " " , values [ i ] , resultQ , errorQ ) ;
2012-11-26 18:58:24 +00:00
}
}
}
# endif