This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/engine/server/sv_frame.c

534 lines
15 KiB
C
Raw Normal View History

2008-07-09 22:00:00 +02:00
//=======================================================================
// Copyright XashXT Group 2008 <20>
// sv_frame.c - server world snapshot
//=======================================================================
#include "common.h"
2008-07-16 22:00:00 +02:00
#include "server.h"
static byte fatpvs[MAX_MAP_LEAFS/8];
2008-08-02 22:00:00 +02:00
static byte *clientpvs;
static byte *clientphs;
static byte *bitvector;
2008-07-16 22:00:00 +02:00
/*
============
SV_FatPVS
The client will interpolate the view position,
so we can't use a single PVS point
===========
2008-08-11 22:00:00 +02:00
*/
2008-07-16 22:00:00 +02:00
void SV_FatPVS( vec3_t org )
{
int leafs[64];
int i, j, count;
int longs;
byte *src;
vec3_t mins, maxs;
for( i = 0; i < 3; i++ )
{
2008-08-10 22:00:00 +02:00
mins[i] = org[i] - 1;
maxs[i] = org[i] + 1;
2008-07-16 22:00:00 +02:00
}
count = pe->BoxLeafnums( mins, maxs, leafs, 64, NULL );
if( count < 1 ) Host_Error( "SV_FatPVS: invalid leafcount\n" );
longs = (pe->NumClusters() + 31)>>5;
// convert leafs to clusters
for( i = 0; i < count; i++) leafs[i] = pe->LeafCluster( leafs[i] );
Mem_Copy( fatpvs, pe->ClusterPVS( leafs[0] ), longs<<2 );
// or in all the other leaf bits
for( i = 1; i < count; i++ )
{
for( j = 0; j < i; j++ )
{
if( leafs[i] == leafs[j] )
break;
}
if( j != i ) continue; // already have the cluster we want
src = pe->ClusterPVS( leafs[i] );
for( j = 0; j < longs; j++ ) ((long *)fatpvs)[j] |= ((long *)src)[j];
}
}
/*
=============================================================================
Copy PRVM values into entity state
=============================================================================
*/
void SV_UpdateEntityState( edict_t *ent )
{
2008-08-02 22:00:00 +02:00
edict_t *client;
2008-07-16 22:00:00 +02:00
// copy progs values to state
ent->priv.sv->s.number = ent->priv.sv->serialnumber;
ent->priv.sv->s.solid = ent->priv.sv->solid;
VectorCopy (ent->progs.sv->origin, ent->priv.sv->s.origin);
VectorCopy (ent->progs.sv->angles, ent->priv.sv->s.angles);
VectorCopy (ent->progs.sv->old_origin, ent->priv.sv->s.old_origin);
2008-07-30 22:00:00 +02:00
ent->priv.sv->s.model.index = (int)ent->progs.sv->modelindex;
ent->priv.sv->s.health = ent->progs.sv->health;
ent->priv.sv->s.model.skin = (short)ent->progs.sv->skin; // studio model skin
ent->priv.sv->s.model.body = (byte)ent->progs.sv->body; // studio model submodel
ent->priv.sv->s.model.frame = ent->progs.sv->frame; // any model current frame
2008-08-05 22:00:00 +02:00
ent->priv.sv->s.model.gaitsequence = (int)ent->progs.sv->gaitsequence;// player sequence, that will be playing on client
2008-07-30 22:00:00 +02:00
ent->priv.sv->s.model.sequence = (byte)ent->progs.sv->sequence; // studio model sequence
ent->priv.sv->s.effects = (uint)ent->progs.sv->effects; // shared client and render flags
ent->priv.sv->s.renderfx = (int)ent->progs.sv->renderfx; // renderer flags
2008-08-10 22:00:00 +02:00
ent->priv.sv->s.renderamt = ent->progs.sv->renderamt; // alpha value
ent->priv.sv->s.model.framerate = ent->progs.sv->framerate;
ent->priv.sv->s.model.animtime = (int)(1000.0 * ent->progs.sv->animtime) * 0.001; // sequence time
2008-08-02 22:00:00 +02:00
ent->priv.sv->s.aiment = ent->progs.sv->aiment; // viewmodel parent
2008-07-31 22:00:00 +02:00
2008-08-02 22:00:00 +02:00
if( ent->priv.sv->s.ed_type == ED_VIEWMODEL )
{
// copy v_model state from client to viemodel entity
client = PRVM_EDICT_NUM( ent->progs.sv->aiment );
// update both arrays, because viewmodel are hidden for qc-coders
ent->priv.sv->s.model.index = ent->progs.sv->modelindex = SV_ModelIndex(PRVM_GetString(client->progs.sv->v_model));
ent->priv.sv->s.model.frame = ent->progs.sv->frame = client->progs.sv->v_frame;
ent->priv.sv->s.model.body = ent->progs.sv->body = client->progs.sv->v_body;
ent->priv.sv->s.model.skin = ent->progs.sv->skin = client->progs.sv->v_skin;
ent->priv.sv->s.model.sequence = ent->progs.sv->sequence = client->progs.sv->v_sequence;
VectorCopy( ent->progs.sv->origin, ent->priv.sv->s.old_origin );
ent->priv.sv->s.model.colormap = ent->progs.sv->colormap = client->progs.sv->colormap;
}
}
bool SV_EdictNeedsUpdate( edict_t *ent, edict_t *clent, int clientarea )
{
int i, l;
2008-08-10 22:00:00 +02:00
//NOTE: client index on client expected that entity will be valid
if( ent->priv.sv->s.ed_type == ED_CLIENT )
return true;
2008-08-02 22:00:00 +02:00
// send viewmodel entity always
// NOTE: never apply LinkEdict to viewmodel entity, because
// we wan't see it in list of entities returned with SV_AreaEdicts
if( ent->priv.sv->s.ed_type == ED_VIEWMODEL )
return true;
// ignore if not touching a PV leaf
if( ent != clent )
{
// check area
if( !pe->AreasConnected( clientarea, ent->priv.sv->areanum ))
{
// doors can legally straddle two areas, so
// we may need to check another one
int areanum2 = ent->priv.sv->areanum2;
if( !areanum2 || !pe->AreasConnected( clientarea, areanum2 ))
return false; // blocked by a door
}
// FIXME: if an ent has a model and a sound, but isn't
// in the PVS, only the PHS, clear the model
if( ent->priv.sv->s.soundindex ) bitvector = clientphs;
else if( sv_fatpvs->integer ) bitvector = fatpvs;
else bitvector = clientpvs;
// check individual leafs
if( !ent->priv.sv->num_clusters ) return false;
for( i = 0, l = 0; i < ent->priv.sv->num_clusters; i++ )
{
l = ent->priv.sv->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 == ent->priv.sv->num_clusters )
{
if( ent->priv.sv->lastcluster )
{
for( ; l <= ent->priv.sv->lastcluster; l++ )
{
if( bitvector[l>>3] & (1<<(l&7)))
break;
}
if( l == ent->priv.sv->lastcluster )
return false; // not visible
}
else return false;
}
if( !ent->progs.sv->modelindex )
{
// don't send sounds if they will be attenuated away
vec3_t org, delta, entorigin;
float len;
if(VectorIsNull( ent->progs.sv->origin ))
{
VectorAdd( ent->progs.sv->mins, ent->progs.sv->maxs, entorigin );
VectorScale( entorigin, 0.5, entorigin );
}
else
{
VectorCopy( ent->progs.sv->origin, entorigin );
}
VectorCopy( clent->priv.sv->s.origin, org );
VectorAdd( org, clent->priv.sv->s.viewoffset, org );
VectorSubtract( org, entorigin, delta );
len = VectorLength( delta );
if( len > 400 ) return false;
}
}
return true;
2008-07-16 22:00:00 +02:00
}
/*
=============================================================================
Encode a client frame onto the network channel
=============================================================================
*/
/*
=============
SV_EmitPacketEntities
Writes a delta update of an entity_state_t list to the message->
=============
*/
void SV_EmitPacketEntities( client_frame_t *from, client_frame_t *to, sizebuf_t *msg )
{
entity_state_t *oldent, *newent;
int oldindex, newindex;
int oldnum, newnum;
int from_num_entities;
MSG_WriteByte( msg, svc_packetentities );
if( !from ) from_num_entities = 0;
else from_num_entities = from->num_entities;
newindex = 0;
oldindex = 0;
while( newindex < to->num_entities || oldindex < from_num_entities )
{
if( newindex >= to->num_entities ) newnum = MAX_ENTNUMBER;
else
{
newent = &svs.client_entities[(to->first_entity+newindex)%svs.num_client_entities];
newnum = newent->number;
}
if( oldindex >= from_num_entities ) oldnum = MAX_ENTNUMBER;
else
{
oldent = &svs.client_entities[(from->first_entity+oldindex)%svs.num_client_entities];
oldnum = oldent->number;
}
if( newnum == oldnum )
{
// delta update from old position
// because the force parm is false, this will not result
// in any bytes being emited if the entity has not changed at all
// note that players are always 'newentities', this updates their oldorigin always
// and prevents warping
2008-08-11 22:00:00 +02:00
MSG_WriteDeltaEntity( oldent, newent, msg, false, (newent->ed_type == ED_CLIENT));
2008-07-16 22:00:00 +02:00
oldindex++;
newindex++;
continue;
}
if( newnum < oldnum )
{
// this is a new entity, send it from the baseline
2008-07-17 22:00:00 +02:00
MSG_WriteDeltaEntity( &svs.baselines[newnum], newent, msg, true, true );
2008-07-16 22:00:00 +02:00
newindex++;
continue;
}
if( newnum > oldnum )
{
MSG_WriteDeltaEntity( oldent, NULL, msg, true, true );
oldindex++;
continue;
}
}
MSG_WriteBits( msg, 0, NET_WORD ); // end of packetentities
}
/*
==================
SV_WriteFrameToClient
==================
*/
void SV_WriteFrameToClient( sv_client_t *cl, sizebuf_t *msg )
{
client_frame_t *frame, *oldframe;
int lastframe;
// this is the frame we are creating
frame = &cl->frames[sv.framenum & UPDATE_MASK];
if( cl->lastframe <= 0 )
{
// client is asking for a retransmit
oldframe = NULL;
lastframe = -1;
}
else if( sv.framenum - cl->lastframe >= (UPDATE_BACKUP - 3))
{
// client hasn't gotten a good message through in a long time
oldframe = NULL;
lastframe = -1;
}
else
{ // we have a valid message to delta from
oldframe = &cl->frames[cl->lastframe & UPDATE_MASK];
lastframe = cl->lastframe;
}
MSG_WriteByte( msg, svc_frame );
MSG_WriteLong( msg, sv.framenum );
MSG_WriteLong( msg, lastframe ); // what we are delta'ing from
MSG_WriteByte( msg, cl->surpressCount ); // rate dropped packets
cl->surpressCount = 0;
// send over the areabits
2008-08-02 22:00:00 +02:00
MSG_WriteByte( msg, frame->areabits_size );
MSG_WriteData( msg, frame->areabits, frame->areabits_size );
2008-07-16 22:00:00 +02:00
2008-08-02 22:00:00 +02:00
// just send an client index
// it's safe, because PRVM_NUM_FOR_EDICT always equal ed->serialnumber,
// thats shared across network
2008-08-11 22:00:00 +02:00
#if 0
MSG_WriteByte( msg, svc_playerinfo );
2008-08-02 22:00:00 +02:00
MSG_WriteByte( msg, frame->index );
2008-08-11 22:00:00 +02:00
#else
// delta encode the playerstate
MSG_WriteDeltaPlayerstate( &oldframe->ps, &frame->ps, msg );
#endif
2008-07-16 22:00:00 +02:00
// delta encode the entities
SV_EmitPacketEntities( oldframe, frame, msg );
}
/*
=============================================================================
Build a client frame structure
=============================================================================
*/
/*
=============
SV_BuildClientFrame
Decides which entities are going to be visible to the client, and
copies off the playerstat and areabits.
=============
*/
void SV_BuildClientFrame( sv_client_t *cl )
{
vec3_t org;
edict_t *ent;
edict_t *clent;
client_frame_t *frame;
entity_state_t *state;
2008-08-02 22:00:00 +02:00
int e, clientarea;
2008-07-16 22:00:00 +02:00
int clientcluster;
int leafnum;
clent = cl->edict;
if( !clent->priv.sv->client )
return; // not in game yet
// this is the frame we are creating
frame = &cl->frames[sv.framenum & UPDATE_MASK];
frame->msg_sent = svs.realtime; // save it for ping calc later
// find the client's PVS
2008-07-31 22:00:00 +02:00
VectorCopy( clent->priv.sv->s.origin, org );
VectorAdd( org, clent->priv.sv->s.viewoffset, org );
2008-07-16 22:00:00 +02:00
// calculate fat pvs
2008-08-18 22:00:00 +02:00
if( sv_fatpvs->integer == 1 ) SV_FatPVS( org );
if( sv_fatpvs->integer == 2 ) pe->FatPVS( org, 64, fatpvs, sizeof(fatpvs), false );
2008-07-16 22:00:00 +02:00
leafnum = pe->PointLeafnum( org );
clientarea = pe->LeafArea( leafnum );
clientcluster = pe->LeafCluster( leafnum );
// calculate the visible areas
2008-08-02 22:00:00 +02:00
frame->areabits_size = pe->WriteAreaBits( frame->areabits, clientarea );
2008-08-11 22:00:00 +02:00
#if 0
2008-08-02 22:00:00 +02:00
// grab the current player index
frame->index = PRVM_NUM_FOR_EDICT( clent );
2008-08-11 22:00:00 +02:00
#else
// grab the current player state
frame->ps = clent->priv.sv->s;
#endif
2008-07-16 22:00:00 +02:00
clientpvs = pe->ClusterPVS( clientcluster );
clientphs = pe->ClusterPHS( clientcluster );
// build up the list of visible entities
frame->num_entities = 0;
frame->first_entity = svs.next_client_entities;
for( e = 1; e < prog->num_edicts; e++ )
{
ent = PRVM_EDICT_NUM(e);
// ignore ents without visible models unless they have an effect
if( !ent->progs.sv->modelindex && !ent->progs.sv->effects && !ent->priv.sv->s.soundindex )
continue;
2008-08-02 22:00:00 +02:00
if(!SV_EdictNeedsUpdate( ent, clent, clientarea ))
continue;
2008-07-16 22:00:00 +02:00
// add it to the circular client_entities array
state = &svs.client_entities[svs.next_client_entities % svs.num_client_entities];
2008-08-02 22:00:00 +02:00
if( ent->priv.sv->serialnumber != e )
2008-07-16 22:00:00 +02:00
{
MsgDev( D_WARN, "SV_BuildClientFrame: invalid number %d\n", ent->priv.sv->serialnumber );
ent->priv.sv->serialnumber = e; // ptr to current entity such as entnumber
}
SV_UpdateEntityState( ent );
*state = ent->priv.sv->s;
// don't mark players missiles as solid
if( PRVM_PROG_TO_EDICT( ent->progs.sv->owner) == cl->edict )
state->solid = 0;
svs.next_client_entities++;
frame->num_entities++;
}
}
/*
===============================================================================
FRAME UPDATES
===============================================================================
*/
/*
=======================
SV_SendClientDatagram
=======================
*/
bool SV_SendClientDatagram( sv_client_t *cl )
{
byte msg_buf[MAX_MSGLEN];
sizebuf_t msg;
SV_BuildClientFrame( cl );
MSG_Init( &msg, msg_buf, sizeof(msg_buf));
// send over all the relevant entity_state_t
2008-07-30 22:00:00 +02:00
// and the player state
2008-07-16 22:00:00 +02:00
SV_WriteFrameToClient( cl, &msg );
// copy the accumulated multicast datagram
// for this client out to the message
// it is necessary for this to be after the WriteEntities
// so that entity references will be current
if( cl->datagram.overflowed ) MsgDev( D_WARN, "datagram overflowed for %s\n", cl->name );
else MSG_WriteData( &msg, cl->datagram.data, cl->datagram.cursize );
MSG_Clear( &cl->datagram );
if( msg.overflowed )
{
// must have room left for the packet header
MsgDev( D_WARN, "msg overflowed for %s\n", cl->name );
MSG_Clear( &msg );
}
// send the datagram
Netchan_Transmit( &cl->netchan, msg.cursize, msg.data );
// record the size for rate estimation
// record information about the message
cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].msg_size = msg.cursize;
cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].msg_sent = svs.realtime;
return true;
}
/*
=======================
SV_RateDrop
Returns true if the client is over its current
bandwidth estimation and should not be sent another packet
=======================
*/
bool SV_RateDrop( sv_client_t *cl )
{
int i, total = 0;
// never drop over the loopback
if( NET_IsLocalAddress( cl->netchan.remote_address ))
return false;
if( NET_IsLANAddress( cl->netchan.remote_address ))
return false;
for( i = 0; i < UPDATE_BACKUP; i++ )
total += cl->frames[i].msg_size;
if( total > cl->rate )
{
cl->surpressCount++;
cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].msg_size = 0;
return true;
}
return false;
}
/*
=======================
SV_SendClientMessages
=======================
*/
void SV_SendClientMessages( void )
{
sv_client_t *cl;
int i;
// send a message to each connected client
2008-07-31 22:00:00 +02:00
for( i = 0, cl = svs.clients; i < Host_MaxClients(); i++, cl++ )
2008-07-16 22:00:00 +02:00
{
if( !cl->state ) continue;
// if the reliable message overflowed, drop the client
if( cl->netchan.message.overflowed )
{
MSG_Clear( &cl->netchan.message );
MSG_Clear( &cl->datagram );
2008-08-10 22:00:00 +02:00
SV_BroadcastPrintf( PRINT_CONSOLE, "%s overflowed\n", cl->name );
2008-07-16 22:00:00 +02:00
SV_DropClient( cl );
}
2008-08-02 22:00:00 +02:00
if( cl->state == cs_spawned )
2008-07-16 22:00:00 +02:00
{
// don't overrun bandwidth
if( SV_RateDrop( cl )) continue;
SV_SendClientDatagram( cl );
}
else
{
// just update reliable if needed
if( cl->netchan.message.cursize || svs.realtime - cl->netchan.last_sent > 1000 )
Netchan_Transmit( &cl->netchan, 0, NULL );
}
}
}