summaryrefslogtreecommitdiff
path: root/ioq3-r437/src/server/sv_snapshot.c
diff options
context:
space:
mode:
Diffstat (limited to 'ioq3-r437/src/server/sv_snapshot.c')
-rw-r--r--ioq3-r437/src/server/sv_snapshot.c692
1 files changed, 0 insertions, 692 deletions
diff --git a/ioq3-r437/src/server/sv_snapshot.c b/ioq3-r437/src/server/sv_snapshot.c
deleted file mode 100644
index db5164bb..00000000
--- a/ioq3-r437/src/server/sv_snapshot.c
+++ /dev/null
@@ -1,692 +0,0 @@
-/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena 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 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-#include "server.h"
-
-
-/*
-=============================================================================
-
-Delta encode a client frame onto the network channel
-
-A normal server packet will look like:
-
-4 sequence number (high bit set if an oversize fragment)
-<optional reliable commands>
-1 svc_snapshot
-4 last client reliable command
-4 serverTime
-1 lastframe for delta compression
-1 snapFlags
-1 areaBytes
-<areabytes>
-<playerstate>
-<packetentities>
-
-=============================================================================
-*/
-
-/*
-=============
-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_WriteSnapshotToClient
-==================
-*/
-static void SV_WriteSnapshotToClient( client_t *client, msg_t *msg ) {
- clientSnapshot_t *frame, *oldframe;
- int lastframe;
- int i;
- int snapFlags;
-
- // this is the snapshot we are creating
- frame = &client->frames[ client->netchan.outgoingSequence & PACKET_MASK ];
-
- // try to use a previous frame as the source for delta compressing the snapshot
- if ( client->deltaMessage <= 0 || client->state != CS_ACTIVE ) {
- // client is asking for a retransmit
- oldframe = NULL;
- lastframe = 0;
- } else if ( client->netchan.outgoingSequence - client->deltaMessage
- >= (PACKET_BACKUP - 3) ) {
- // client hasn't gotten a good message through in a long time
- Com_DPrintf ("%s: Delta request from out of date packet.\n", client->name);
- oldframe = NULL;
- lastframe = 0;
- } else {
- // we have a valid snapshot to delta from
- oldframe = &client->frames[ client->deltaMessage & PACKET_MASK ];
- lastframe = client->netchan.outgoingSequence - client->deltaMessage;
-
- // the snapshot's entities may still have rolled off the buffer, though
- if ( oldframe->first_entity <= svs.nextSnapshotEntities - svs.numSnapshotEntities ) {
- Com_DPrintf ("%s: Delta request from out of date entities.\n", client->name);
- oldframe = NULL;
- lastframe = 0;
- }
- }
-
- MSG_WriteByte (msg, svc_snapshot);
-
- // NOTE, MRE: now sent at the start of every message from server to client
- // let the client know which reliable clientCommands we have received
- //MSG_WriteLong( msg, client->lastClientCommand );
-
- // send over the current server time so the client can drift
- // its view of time to try to match
- if( client->oldServerTime ) {
- // The server has not yet got an acknowledgement of the
- // new gamestate from this client, so continue to send it
- // a time as if the server has not restarted. Note from
- // the client's perspective this time is strictly speaking
- // incorrect, but since it'll be busy loading a map at
- // the time it doesn't really matter.
- MSG_WriteLong (msg, sv.time + client->oldServerTime);
- } else {
- MSG_WriteLong (msg, sv.time);
- }
-
- // what we are delta'ing from
- MSG_WriteByte (msg, lastframe);
-
- snapFlags = svs.snapFlagServerBit;
- if ( client->rateDelayed ) {
- snapFlags |= SNAPFLAG_RATE_DELAYED;
- }
- if ( client->state != CS_ACTIVE ) {
- snapFlags |= SNAPFLAG_NOT_ACTIVE;
- }
-
- MSG_WriteByte (msg, snapFlags);
-
- // send over the areabits
- MSG_WriteByte (msg, frame->areabytes);
- MSG_WriteData (msg, frame->areabits, frame->areabytes);
-
- // delta encode the playerstate
- if ( oldframe ) {
- MSG_WriteDeltaPlayerstate( msg, &oldframe->ps, &frame->ps );
- } else {
- MSG_WriteDeltaPlayerstate( msg, NULL, &frame->ps );
- }
-
- // delta encode the entities
- SV_EmitPacketEntities (oldframe, frame, msg);
-
- // padding for rate debugging
- if ( sv_padPackets->integer ) {
- for ( i = 0 ; i < sv_padPackets->integer ; i++ ) {
- MSG_WriteByte (msg, svc_nop);
- }
- }
-}
-
-
-/*
-==================
-SV_UpdateServerCommandsToClient
-
-(re)send all server commands the client hasn't acknowledged yet
-==================
-*/
-void SV_UpdateServerCommandsToClient( client_t *client, msg_t *msg ) {
- int i;
-
- // write any unacknowledged serverCommands
- for ( i = client->reliableAcknowledge + 1 ; i <= client->reliableSequence ; i++ ) {
- MSG_WriteByte( msg, svc_serverCommand );
- MSG_WriteLong( msg, i );
- MSG_WriteString( msg, client->reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] );
- }
- client->reliableSent = client->reliableSequence;
-}
-
-/*
-=============================================================================
-
-Build a client snapshot structure
-
-=============================================================================
-*/
-
-#define MAX_SNAPSHOT_ENTITIES 1024
-typedef struct {
- int numSnapshotEntities;
- int snapshotEntities[MAX_SNAPSHOT_ENTITIES];
-} snapshotEntityNumbers_t;
-
-/*
-=======================
-SV_QsortEntityNumbers
-=======================
-*/
-static int QDECL SV_QsortEntityNumbers( const void *a, const void *b ) {
- int *ea, *eb;
-
- ea = (int *)a;
- eb = (int *)b;
-
- if ( *ea == *eb ) {
- Com_Error( ERR_DROP, "SV_QsortEntityStates: duplicated entity" );
- }
-
- if ( *ea < *eb ) {
- return -1;
- }
-
- return 1;
-}
-
-
-/*
-===============
-SV_AddEntToSnapshot
-===============
-*/
-static void SV_AddEntToSnapshot( svEntity_t *svEnt, sharedEntity_t *gEnt, snapshotEntityNumbers_t *eNums ) {
- // if we have already added this entity to this snapshot, don't add again
- if ( svEnt->snapshotCounter == sv.snapshotCounter ) {
- return;
- }
- svEnt->snapshotCounter = sv.snapshotCounter;
-
- // if we are full, silently discard entities
- if ( eNums->numSnapshotEntities == MAX_SNAPSHOT_ENTITIES ) {
- return;
- }
-
- eNums->snapshotEntities[ eNums->numSnapshotEntities ] = gEnt->s.number;
- eNums->numSnapshotEntities++;
-}
-
-/*
-===============
-SV_AddEntitiesVisibleFromPoint
-===============
-*/
-static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *frame,
- snapshotEntityNumbers_t *eNums, qboolean portal ) {
- int e, i;
- sharedEntity_t *ent;
- svEntity_t *svEnt;
- int l;
- int clientarea, clientcluster;
- int leafnum;
- int c_fullsend;
- byte *clientpvs;
- byte *bitvector;
-
- // during an error shutdown message we may need to transmit
- // the shutdown message after the server has shutdown, so
- // specfically check for it
- if ( !sv.state ) {
- return;
- }
-
- leafnum = CM_PointLeafnum (origin);
- clientarea = CM_LeafArea (leafnum);
- clientcluster = CM_LeafCluster (leafnum);
-
- // calculate the visible areas
- frame->areabytes = CM_WriteAreaBits( frame->areabits, clientarea );
-
- clientpvs = CM_ClusterPVS (clientcluster);
-
- c_fullsend = 0;
-
- for ( e = 0 ; e < sv.num_entities ; e++ ) {
- ent = SV_GentityNum(e);
-
- // never send entities that aren't linked in
- if ( !ent->r.linked ) {
- continue;
- }
-
- if (ent->s.number != e) {
- Com_DPrintf ("FIXING ENT->S.NUMBER!!!\n");
- ent->s.number = e;
- }
-
- // entities can be flagged to explicitly not be sent to the client
- if ( ent->r.svFlags & SVF_NOCLIENT ) {
- continue;
- }
-
- // entities can be flagged to be sent to only one client
- if ( ent->r.svFlags & SVF_SINGLECLIENT ) {
- if ( ent->r.singleClient != frame->ps.clientNum ) {
- continue;
- }
- }
- // entities can be flagged to be sent to everyone but one client
- if ( ent->r.svFlags & SVF_NOTSINGLECLIENT ) {
- if ( ent->r.singleClient == frame->ps.clientNum ) {
- continue;
- }
- }
- // entities can be flagged to be sent to a given mask of clients
- if ( ent->r.svFlags & SVF_CLIENTMASK ) {
- if (frame->ps.clientNum >= 32)
- Com_Error( ERR_DROP, "SVF_CLIENTMASK: cientNum > 32\n" );
- if (~ent->r.singleClient & (1 << frame->ps.clientNum))
- continue;
- }
-
- svEnt = SV_SvEntityForGentity( ent );
-
- // don't double add an entity through portals
- if ( svEnt->snapshotCounter == sv.snapshotCounter ) {
- continue;
- }
-
- // broadcast entities are always sent
- if ( ent->r.svFlags & SVF_BROADCAST ) {
- SV_AddEntToSnapshot( svEnt, ent, eNums );
- continue;
- }
-
- // ignore if not touching a PV leaf
- // check area
- if ( !CM_AreasConnected( clientarea, svEnt->areanum ) ) {
- // doors can legally straddle two areas, so
- // we may need to check another one
- if ( !CM_AreasConnected( clientarea, svEnt->areanum2 ) ) {
- continue; // blocked by a door
- }
- }
-
- bitvector = clientpvs;
-
- // check individual leafs
- if ( !svEnt->numClusters ) {
- continue;
- }
- l = 0;
- for ( i=0 ; i < svEnt->numClusters ; i++ ) {
- l = svEnt->clusternums[i];
- if ( bitvector[l >> 3] & (1 << (l&7) ) ) {
- break;
- }
- }
-
- // if we haven't found it to be visible,
- // check overflow clusters that coudln't be stored
- if ( i == svEnt->numClusters ) {
- if ( svEnt->lastCluster ) {
- for ( ; l <= svEnt->lastCluster ; l++ ) {
- if ( bitvector[l >> 3] & (1 << (l&7) ) ) {
- break;
- }
- }
- if ( l == svEnt->lastCluster ) {
- continue; // not visible
- }
- } else {
- continue;
- }
- }
-
- // add it
- SV_AddEntToSnapshot( svEnt, ent, eNums );
-
- // if its a portal entity, add everything visible from its camera position
- if ( ent->r.svFlags & SVF_PORTAL ) {
- if ( ent->s.generic1 ) {
- vec3_t dir;
- VectorSubtract(ent->s.origin, origin, dir);
- if ( VectorLengthSquared(dir) > (float) ent->s.generic1 * ent->s.generic1 ) {
- continue;
- }
- }
- SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums, qtrue );
- }
-
- }
-}
-
-/*
-=============
-SV_BuildClientSnapshot
-
-Decides which entities are going to be visible to the client, and
-copies off the playerstate and areabits.
-
-This properly handles multiple recursive portals, but the render
-currently doesn't.
-
-For viewing through other player's eyes, clent can be something other than client->gentity
-=============
-*/
-static void SV_BuildClientSnapshot( client_t *client ) {
- vec3_t org;
- clientSnapshot_t *frame;
- snapshotEntityNumbers_t entityNumbers;
- int i;
- sharedEntity_t *ent;
- entityState_t *state;
- svEntity_t *svEnt;
- sharedEntity_t *clent;
- int clientNum;
- playerState_t *ps;
-
- // bump the counter used to prevent double adding
- sv.snapshotCounter++;
-
- // this is the frame we are creating
- frame = &client->frames[ client->netchan.outgoingSequence & PACKET_MASK ];
-
- // clear everything in this snapshot
- entityNumbers.numSnapshotEntities = 0;
- Com_Memset( frame->areabits, 0, sizeof( frame->areabits ) );
-
- // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=62
- frame->num_entities = 0;
-
- clent = client->gentity;
- if ( !clent || client->state == CS_ZOMBIE ) {
- return;
- }
-
- // grab the current playerState_t
- ps = SV_GameClientNum( client - svs.clients );
- frame->ps = *ps;
-
- // never send client's own entity, because it can
- // be regenerated from the playerstate
- clientNum = frame->ps.clientNum;
- if ( clientNum < 0 || clientNum >= MAX_GENTITIES ) {
- Com_Error( ERR_DROP, "SV_SvEntityForGentity: bad gEnt" );
- }
- svEnt = &sv.svEntities[ clientNum ];
-
- svEnt->snapshotCounter = sv.snapshotCounter;
-
- // find the client's viewpoint
- VectorCopy( ps->origin, org );
- org[2] += ps->viewheight;
-
- // add all the entities directly visible to the eye, which
- // may include portal entities that merge other viewpoints
- SV_AddEntitiesVisibleFromPoint( org, frame, &entityNumbers, qfalse );
-
- // if there were portals visible, there may be out of order entities
- // in the list which will need to be resorted for the delta compression
- // to work correctly. This also catches the error condition
- // of an entity being included twice.
- qsort( entityNumbers.snapshotEntities, entityNumbers.numSnapshotEntities,
- sizeof( entityNumbers.snapshotEntities[0] ), SV_QsortEntityNumbers );
-
- // now that all viewpoint's areabits have been OR'd together, invert
- // all of them to make it a mask vector, which is what the renderer wants
- for ( i = 0 ; i < MAX_MAP_AREA_BYTES/4 ; i++ ) {
- ((int *)frame->areabits)[i] = ((int *)frame->areabits)[i] ^ -1;
- }
-
- // copy the entity states out
- frame->num_entities = 0;
- frame->first_entity = svs.nextSnapshotEntities;
- for ( i = 0 ; i < entityNumbers.numSnapshotEntities ; i++ ) {
- ent = SV_GentityNum(entityNumbers.snapshotEntities[i]);
- state = &svs.snapshotEntities[svs.nextSnapshotEntities % svs.numSnapshotEntities];
- *state = ent->s;
- svs.nextSnapshotEntities++;
- // this should never hit, map should always be restarted first in SV_Frame
- if ( svs.nextSnapshotEntities >= 0x7FFFFFFE ) {
- Com_Error(ERR_FATAL, "svs.nextSnapshotEntities wrapped");
- }
- frame->num_entities++;
- }
-}
-
-
-/*
-====================
-SV_RateMsec
-
-Return the number of msec a given size message is supposed
-to take to clear, based on the current rate
-====================
-*/
-#define HEADER_RATE_BYTES 48 // include our header, IP header, and some overhead
-static int SV_RateMsec( client_t *client, int messageSize ) {
- int rate;
- int rateMsec;
-
- // individual messages will never be larger than fragment size
- if ( messageSize > 1500 ) {
- messageSize = 1500;
- }
- rate = client->rate;
- if ( sv_maxRate->integer ) {
- if ( sv_maxRate->integer < 1000 ) {
- Cvar_Set( "sv_MaxRate", "1000" );
- }
- if ( sv_maxRate->integer < rate ) {
- rate = sv_maxRate->integer;
- }
- }
- rateMsec = ( messageSize + HEADER_RATE_BYTES ) * 1000 / rate;
-
- return rateMsec;
-}
-
-/*
-=======================
-SV_SendMessageToClient
-
-Called by SV_SendClientSnapshot and SV_SendClientGameState
-=======================
-*/
-void SV_SendMessageToClient( msg_t *msg, client_t *client ) {
- int rateMsec;
-
- // record information about the message
- client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageSize = msg->cursize;
- client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageSent = svs.time;
- client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageAcked = -1;
-
- // send the datagram
- SV_Netchan_Transmit( client, msg ); //msg->cursize, msg->data );
-
- // set nextSnapshotTime based on rate and requested number of updates
-
- // local clients get snapshots every frame
- // TTimo - https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=491
- // added sv_lanForceRate check
- if ( client->netchan.remoteAddress.type == NA_LOOPBACK || (sv_lanForceRate->integer && Sys_IsLANAddress (client->netchan.remoteAddress)) ) {
- client->nextSnapshotTime = svs.time - 1;
- return;
- }
-
- // normal rate / snapshotMsec calculation
- rateMsec = SV_RateMsec( client, msg->cursize );
-
- if ( rateMsec < client->snapshotMsec ) {
- // never send more packets than this, no matter what the rate is at
- rateMsec = client->snapshotMsec;
- client->rateDelayed = qfalse;
- } else {
- client->rateDelayed = qtrue;
- }
-
- client->nextSnapshotTime = svs.time + rateMsec;
-
- // don't pile up empty snapshots while connecting
- if ( client->state != CS_ACTIVE ) {
- // a gigantic connection message may have already put the nextSnapshotTime
- // more than a second away, so don't shorten it
- // do shorten if client is downloading
- if ( !*client->downloadName && client->nextSnapshotTime < svs.time + 1000 ) {
- client->nextSnapshotTime = svs.time + 1000;
- }
- }
-}
-
-
-/*
-=======================
-SV_SendClientSnapshot
-
-Also called by SV_FinalMessage
-
-=======================
-*/
-void SV_SendClientSnapshot( client_t *client ) {
- byte msg_buf[MAX_MSGLEN];
- msg_t msg;
-
- // build the snapshot
- SV_BuildClientSnapshot( client );
-
- // bots need to have their snapshots build, but
- // the query them directly without needing to be sent
- if ( client->gentity && client->gentity->r.svFlags & SVF_BOT ) {
- return;
- }
-
- MSG_Init (&msg, msg_buf, sizeof(msg_buf));
- msg.allowoverflow = qtrue;
-
- // NOTE, MRE: all server->client messages now acknowledge
- // let the client know which reliable clientCommands we have received
- MSG_WriteLong( &msg, client->lastClientCommand );
-
- // (re)send any reliable server commands
- SV_UpdateServerCommandsToClient( client, &msg );
-
- // send over all the relevant entityState_t
- // and the playerState_t
- SV_WriteSnapshotToClient( client, &msg );
-
- // Add any download data if the client is downloading
- SV_WriteDownloadToClient( client, &msg );
-
- // check for overflow
- if ( msg.overflowed ) {
- Com_Printf ("WARNING: msg overflowed for %s\n", client->name);
- MSG_Clear (&msg);
- }
-
- SV_SendMessageToClient( &msg, client );
-}
-
-
-/*
-=======================
-SV_SendClientMessages
-=======================
-*/
-void SV_SendClientMessages( void ) {
- int i;
- client_t *c;
-
- // send a message to each connected client
- for (i=0, c = svs.clients ; i < sv_maxclients->integer ; i++, c++) {
- if (!c->state) {
- continue; // not connected
- }
-
- if ( svs.time < c->nextSnapshotTime ) {
- continue; // not time yet
- }
-
- // send additional message fragments if the last message
- // was too large to send at once
- if ( c->netchan.unsentFragments ) {
- c->nextSnapshotTime = svs.time +
- SV_RateMsec( c, c->netchan.unsentLength - c->netchan.unsentFragmentStart );
- SV_Netchan_TransmitNextFragment( c );
- continue;
- }
-
- // generate and send a new message
- SV_SendClientSnapshot( c );
- }
-}
-