/*
=============
SV_EmitPacketEntities
Writes a delta update of an entityState_t list to the message.
=============
*/
static void SV_EmitPacketEntities( clientSnapshot_t *from, clientSnapshot_t *to, msg_t *msg ) {
entityState_t *oldent, *newent;
int oldindex, newindex;
int oldnum, newnum;
int from_num_entities;
// generate the delta update
if ( !from ) {
from_num_entities = 0;
} else {
from_num_entities = from->num_entities;
}
newent = NULL;
oldent = NULL;
newindex = 0;
oldindex = 0;
while ( newindex < to->num_entities || oldindex < from_num_entities ) {
if ( newindex >= to->num_entities ) {
newnum = 9999;
} else {
newent = &svs.snapshotEntities[(to->first_entity+newindex) % svs.numSnapshotEntities];
newnum = newent->number;
}
if ( oldindex >= from_num_entities ) {
oldnum = 9999;
} else {
oldent = &svs.snapshotEntities[(from->first_entity+oldindex) % svs.numSnapshotEntities];
oldnum = oldent->number;
}
if ( newnum == oldnum ) {
// delta update from old position
// because the force parm is qfalse, this will not result
// in any bytes being emited if the entity has not changed at all
MSG_WriteDeltaEntity (msg, oldent, newent, qfalse );
oldindex++;
newindex++;
continue;
}
if ( newnum < oldnum ) {
// this is a new entity, send it from the baseline
MSG_WriteDeltaEntity (msg, &sv.svEntities[newnum].baseline, newent, qtrue );
newindex++;
continue;
}
if ( newnum > oldnum ) {
// the old entity isn't present in the new message
MSG_WriteDeltaEntity (msg, oldent, NULL, qtrue );
oldindex++;
continue;
}
}
MSG_WriteBits( msg, (MAX_GENTITIES-1), GENTITYNUM_BITS ); // end of packetentities
}
/*
================
SV_SendClientGameState
Sends the first message from the server to a connected client.
This will be sent on the initial connection and upon each new map load.
It will be resent if the client acknowledges a later message but has
the wrong gamestate.
================
*/
void SV_SendClientGameState( client_t *client ) {
int start;
entityState_t *base, nullstate;
msg_t msg;
byte msgBuffer[MAX_MSGLEN];
Com_DPrintf ("SV_SendGameState() for %s\n", client->name);
client->state = CS_PRIMED;
// when we receive the first packet from the client, we will
// notice that it is from a different serverid and that the
// gamestate message was not just sent, forcing a retransmit
client->gamestateMessageNum = client->netchan.outgoingSequence;
// clear the reliable message list for this client
client->reliableSequence = 0;
client->reliableAcknowledge = 0;
MSG_Init( &msg, msgBuffer, sizeof( msgBuffer ) );
// send the gamestate
MSG_WriteByte( &msg, svc_gamestate );
MSG_WriteLong( &msg, client->reliableSequence );
// write the configstrings
for ( start = 0 ; start < MAX_CONFIGSTRINGS ; start++ ) {
if (sv.configstrings[start][0]) {
MSG_WriteByte( &msg, svc_configstring );
MSG_WriteShort( &msg, start );
MSG_WriteString( &msg, sv.configstrings[start] );
}
}
// write the baselines
memset( &nullstate, 0, sizeof( nullstate ) );
for ( start = 0 ; start < MAX_GENTITIES; start++ ) {
base = &sv.svEntities[start].baseline;
if ( !base->number ) {
continue;
}
MSG_WriteByte( &msg, svc_baseline );
MSG_WriteDeltaEntity( &msg, &nullstate, base, qtrue );
}
MSG_WriteByte( &msg, 0 );
// check for overflow
if ( msg.overflowed ) {
Com_Printf ("WARNING: GameState overflowed for %s\n", client->name);
}
// deliver this to the client
SV_SendMessageToClient( &msg, client );
}
void SV_CreateClientGameStateMessage( client_t *client, msg_t *msg ) {
int start;
entityState_t *base, nullstate;
// NOTE, MRE: all server->client messages now acknowledge
// let the client know which reliable clientCommands we have received
MSG_WriteLong( msg, client->lastClientCommand );
// send any server commands waiting to be sent first.
// we have to do this cause we send the client->reliableSequence
// with a gamestate and it sets the clc.serverCommandSequence at
// the client side
SV_UpdateServerCommandsToClient( client, msg );
// send the gamestate
MSG_WriteByte( msg, svc_gamestate );
MSG_WriteLong( msg, client->reliableSequence );
// write the configstrings
for ( start = 0 ; start < MAX_CONFIGSTRINGS ; start++ ) {
if (sv.configstrings[start][0]) {
MSG_WriteByte( msg, svc_configstring );
MSG_WriteShort( msg, start );
MSG_WriteBigString( msg, sv.configstrings[start] );
}
}
// write the baselines
Com_Memset( &nullstate, 0, sizeof( nullstate ) );
for ( start = 0 ; start < MAX_GENTITIES; start++ ) {
base = &sv.svEntities[start].baseline;
if ( !base->number ) {
continue;
}
MSG_WriteByte( msg, svc_baseline );
MSG_WriteDeltaEntity( msg, &nullstate, base, qtrue );
}
MSG_WriteByte( msg, svc_EOF );
MSG_WriteLong( msg, client - svs.clients);
// write the checksum feed
MSG_WriteLong( msg, sv.checksumFeed);
// For old RMG system.
MSG_WriteShort ( msg, 0 );
}
/*
================
SV_SendClientGameState
Sends the first message from the server to a connected client.
This will be sent on the initial connection and upon each new map load.
It will be resent if the client acknowledges a later message but has
the wrong gamestate.
================
*/
void SV_SendClientGameState( client_t *client )
{
int start;
entityState_t *base, nullstate;
msg_t msg;
byte msgBuffer[ MAX_MSGLEN ];
Log::Debug( "SV_SendClientGameState() for %s", client->name );
Log::Debug( "Going from CS_CONNECTED to CS_PRIMED for %s", client->name );
client->state = clientState_t::CS_PRIMED;
// when we receive the first packet from the client, we will
// notice that it is from a different serverid and that the
// gamestate message was not just sent, forcing a retransmit
client->gamestateMessageNum = client->netchan.outgoingSequence;
MSG_Init( &msg, msgBuffer, sizeof( msgBuffer ) );
// NOTE, MRE: all server->client messages now acknowledge
// let the client know which reliable clientCommands we have received
MSG_WriteLong( &msg, client->lastClientCommand );
// send any server commands waiting to be sent first.
// we have to do this cause we send the client->reliableSequence
// with a gamestate and it sets the clc.serverCommandSequence at
// the client side
SV_UpdateServerCommandsToClient( client, &msg );
// send the gamestate
MSG_WriteByte( &msg, svc_gamestate );
MSG_WriteLong( &msg, client->reliableSequence );
// write the configstrings
for ( start = 0; start < MAX_CONFIGSTRINGS; start++ )
{
if ( sv.configstrings[ start ][ 0 ] )
{
MSG_WriteByte( &msg, svc_configstring );
MSG_WriteShort( &msg, start );
MSG_WriteBigString( &msg, sv.configstrings[ start ] );
}
}
// write the baselines
memset( &nullstate, 0, sizeof( nullstate ) );
for ( start = 0; start < MAX_GENTITIES; start++ )
{
base = &sv.svEntities[ start ].baseline;
if ( !base->number )
{
continue;
}
MSG_WriteByte( &msg, svc_baseline );
MSG_WriteDeltaEntity( &msg, &nullstate, base, true );
}
MSG_WriteByte( &msg, svc_EOF );
MSG_WriteLong( &msg, client - svs.clients );
// write the checksum feed
MSG_WriteLong( &msg, sv.checksumFeed );
// NERVE - SMF - debug info
Log::Debug( "Sending %i bytes in gamestate to client: %li", msg.cursize, ( long )( client - svs.clients ) );
// deliver this to the client
SV_SendMessageToClient( &msg, client );
}
/*
================
SV_SendClientGameState
Sends the first message from the server to a connected client.
This will be sent on the initial connection and upon each new map load.
It will be resent if the client acknowledges a later message but has
the wrong gamestate.
================
*/
void SV_SendClientGameState( client_t *client ) {
int start;
entityState_t *base, nullstate;
msg_t msg;
byte msgBuffer[MAX_MSGLEN];
// MW - my attempt to fix illegible server message errors caused by
// packet fragmentation of initial snapshot.
while(client->state&&client->netchan.unsentFragments)
{
// send additional message fragments if the last message
// was too large to send at once
Com_Printf ("[ISM]SV_SendClientGameState() [2] for %s, writing out old fragments\n", client->name);
SV_Netchan_TransmitNextFragment(&client->netchan);
}
Com_DPrintf ("SV_SendClientGameState() for %s\n", client->name);
Com_DPrintf( "Going from CS_CONNECTED to CS_PRIMED for %s\n", client->name );
client->state = CS_PRIMED;
client->pureAuthentic = 0;
// when we receive the first packet from the client, we will
// notice that it is from a different serverid and that the
// gamestate message was not just sent, forcing a retransmit
client->gamestateMessageNum = client->netchan.outgoingSequence;
MSG_Init( &msg, msgBuffer, sizeof( msgBuffer ) );
// NOTE, MRE: all server->client messages now acknowledge
// let the client know which reliable clientCommands we have received
MSG_WriteLong( &msg, client->lastClientCommand );
// send any server commands waiting to be sent first.
// we have to do this cause we send the client->reliableSequence
// with a gamestate and it sets the clc.serverCommandSequence at
// the client side
SV_UpdateServerCommandsToClient( client, &msg );
// send the gamestate
MSG_WriteByte( &msg, svc_gamestate );
MSG_WriteLong( &msg, client->reliableSequence );
// write the configstrings
for ( start = 0 ; start < MAX_CONFIGSTRINGS ; start++ ) {
if (sv.configstrings[start][0]) {
MSG_WriteByte( &msg, svc_configstring );
MSG_WriteShort( &msg, start );
MSG_WriteBigString( &msg, sv.configstrings[start] );
}
}
// write the baselines
Com_Memset( &nullstate, 0, sizeof( nullstate ) );
for ( start = 0 ; start < MAX_GENTITIES; start++ ) {
base = &sv.svEntities[start].baseline;
if ( !base->number ) {
continue;
}
MSG_WriteByte( &msg, svc_baseline );
MSG_WriteDeltaEntity( &msg, &nullstate, base, qtrue );
}
MSG_WriteByte( &msg, svc_EOF );
MSG_WriteLong( &msg, client - svs.clients);
// write the checksum feed
MSG_WriteLong( &msg, sv.checksumFeed);
//rwwRMG - send info for the terrain
if ( TheRandomMissionManager )
{
z_stream zdata;
// Send the height map
memset(&zdata, 0, sizeof(z_stream));
deflateInit ( &zdata, Z_MAX_COMPRESSION );
unsigned char heightmap[15000];
zdata.next_out = (unsigned char*)heightmap;
zdata.avail_out = 15000;
zdata.next_in = TheRandomMissionManager->GetLandScape()->GetHeightMap();
zdata.avail_in = TheRandomMissionManager->GetLandScape()->GetRealArea();
deflate(&zdata, Z_SYNC_FLUSH);
MSG_WriteShort ( &msg, (unsigned short)zdata.total_out );
MSG_WriteBits ( &msg, 1, 1 );
MSG_WriteData ( &msg, heightmap, zdata.total_out);
//.........这里部分代码省略.........
/*
Start a server-side demo.
This does it all, create the file and adjust the demo-related
stuff in client_t.
This is mostly ripped from sv_client.c/SV_SendClientGameState
and cl_main.c/CL_Record_f.
*/
static void SVD_StartDemoFile(client_t *client, const char *path)
{
int i, len;
entityState_t *base, nullstate;
msg_t msg;
byte buffer[MAX_MSGLEN];
fileHandle_t file;
#ifdef USE_DEMO_FORMAT_42
char *s;
int v, size;
#endif
Com_DPrintf("SVD_StartDemoFile\n");
assert(!client->demo_recording);
// create the demo file and write the necessary header
file = FS_FOpenFileWrite(path);
assert(file != 0);
/* File_write_header_demo // ADD this fx */
/* HOLBLIN entete demo */
#ifdef USE_DEMO_FORMAT_42
//@Barbatos: get the mod version from the server
s = Cvar_VariableString("g_modversion");
size = strlen( s );
len = LittleLong( size );
FS_Write( &len, 4, file );
FS_Write( s , size , file );
v = LittleLong( PROTOCOL_VERSION );
FS_Write ( &v, 4 , file );
len = 0;
len = LittleLong( len );
FS_Write ( &len, 4 , file );
FS_Write ( &len, 4 , file );
#endif
/* END HOLBLIN entete demo */
MSG_Init(&msg, buffer, sizeof(buffer));
MSG_Bitstream(&msg); // XXX server code doesn't do this, client code does
MSG_WriteLong(&msg, client->lastClientCommand); // TODO: or is it client->reliableSequence?
MSG_WriteByte(&msg, svc_gamestate);
MSG_WriteLong(&msg, client->reliableSequence);
for (i = 0; i < MAX_CONFIGSTRINGS; i++) {
if (sv.configstrings[i][0]) {
MSG_WriteByte(&msg, svc_configstring);
MSG_WriteShort(&msg, i);
MSG_WriteBigString(&msg, sv.configstrings[i]);
}
}
Com_Memset(&nullstate, 0, sizeof(nullstate));
for (i = 0 ; i < MAX_GENTITIES; i++) {
base = &sv.svEntities[i].baseline;
if (!base->number) {
continue;
}
MSG_WriteByte(&msg, svc_baseline);
MSG_WriteDeltaEntity(&msg, &nullstate, base, qtrue);
}
MSG_WriteByte(&msg, svc_EOF);
MSG_WriteLong(&msg, client - svs.clients);
MSG_WriteLong(&msg, sv.checksumFeed);
MSG_WriteByte(&msg, svc_EOF); // XXX server code doesn't do this, SV_Netchan_Transmit adds it!
len = LittleLong(client->netchan.outgoingSequence-1);
FS_Write(&len, 4, file);
len = LittleLong (msg.cursize);
FS_Write(&len, 4, file);
FS_Write(msg.data, msg.cursize, file);
#ifdef USE_DEMO_FORMAT_42
// add size of packet in the end for backward play /* holblin */
FS_Write(&len, 4, file);
#endif
FS_Flush(file);
// adjust client_t to reflect demo started
client->demo_recording = qtrue;
client->demo_file = file;
client->demo_waiting = qtrue;
//.........这里部分代码省略.........
请发表评论