/*
Copyright (C) 1997-2001 Id Software, Inc.

This program 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.

This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#include "server.h"
#include "mvd.h"

/*
=============================================================================

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( deltaFrame_t *from, deltaFrame_t *to,
						   int clientNum, entity_state_t **baselines )
{
	entity_state_t	*oldent, *newent, *base;
	uint32	oldindex, newindex;
	int		oldnum, newnum;
	uint32	from_num_entities;
	msgEsFlags_t flags;
    uint32  i;

	if( !from )
		from_num_entities = 0;
	else
		from_num_entities = from->numEntities;

	newindex = 0;
	oldindex = 0;
	oldent = newent = 0;
	while( newindex < to->numEntities || oldindex < from_num_entities ) {
		if( newindex >= to->numEntities ) {
			newnum = 9999;
		} else {
            i = ( to->firstEntity + newindex ) % svs.numEntityStates;
			newent = &svs.entityStates[i];
			newnum = newent->number;
		}

		if( oldindex >= from_num_entities ) {
			oldnum = 9999;
		} else {
            i = ( from->firstEntity + oldindex ) % svs.numEntityStates;
			oldent = &svs.entityStates[i];
			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
			flags = 0;
			if( clientNum == CLIENTNUM_ANY ) {
				if( newent->number <= svs.maxGameClients &&
                    sv.state == ss_game )
                {
					edict_t *ent = EDICT_NUM( newent->number );
					if( ent->client->ps.pmove.pm_type < PM_DEAD ) {
						flags |= MSG_ES_FIRSTPERSON;
					}
				}
			} else {
				if( newent->number <= svs.maxGameClients ) {
					flags |= MSG_ES_NEWENTITY;
				}
				if( clientNum != CLIENTNUM_NONE ) {
					if( newent->number == clientNum + 1 ) {
						flags |= MSG_ES_FIRSTPERSON;
					}
				}
			}
			MSG_WriteDeltaEntity( oldent, newent, flags );
			oldindex++;
			newindex++;
			continue;
		}

		if( newnum < oldnum ) {	
			// this is a new entity, send it from the baseline
			flags = MSG_ES_FORCE|MSG_ES_NEWENTITY;
			if( clientNum == CLIENTNUM_ANY ) {
				if( newent->number <= svs.maxGameClients &&
                    sv.state == ss_game )
                {
					edict_t *ent = EDICT_NUM( newent->number );
					if( ent->client->ps.pmove.pm_type < PM_DEAD ) {
						flags |= MSG_ES_FIRSTPERSON;
					}
				}
			} else {
				if( clientNum != CLIENTNUM_NONE ) {
					if( newent->number == clientNum + 1 ) {
						flags |= MSG_ES_FIRSTPERSON;
					}
				}
			}
			base = baselines[newnum >> SV_BASELINES_SHIFT];
			if( base ) {
				base += newnum & SV_BASELINES_MASK;
			}
			MSG_WriteDeltaEntity( base, newent, flags );
			newindex++;
			continue;
		}

		if( newnum > oldnum ) {
			// the old entity isn't present in the new message
			MSG_WriteDeltaEntity( oldent, NULL, MSG_ES_FORCE );
			oldindex++;
			continue;
		}
	}

	MSG_WriteShort( 0 );	// end of packetentities
}

void SV_EmitPacketPlayers( deltaFrame_t *from, deltaFrame_t *to, msgPsFlags_t flags ) {
	playerStateEx_t	*oldps, *newps;
	uint32	oldindex, newindex;
	int		oldnum, newnum;
	uint32	from_num_players;
    uint32  i;

	if( !from ) {
		from_num_players = 0;
	} else {
		from_num_players = from->numPlayers;
	}

	newindex = 0;
	oldindex = 0;
	oldps = newps = NULL;
	while( newindex < to->numPlayers || oldindex < from_num_players ) {
		if( newindex >= to->numPlayers ) {
			newnum = 9999;
		} else {
            i = ( to->firstPlayer + newindex ) % svs.numPlayerStates;
			newps = &svs.playerStates[i];
			newnum = newps->number;
		}

		if( oldindex >= from_num_players ) {
			oldnum = 9999;
		} else {
            i = ( from->firstPlayer + oldindex ) % svs.numPlayerStates;
			oldps = &svs.playerStates[i];
			oldnum = oldps->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_WriteDeltaPlayerstate_Packet( oldps, newps, flags );
			oldindex++;
			newindex++;
			continue;
		}

		if( newnum < oldnum ) {
			// this is a new entity, send it from the baseline
			MSG_WriteDeltaPlayerstate_Packet( NULL, newps, flags | MSG_PS_FORCE );
			newindex++;
			continue;
		}

		if( newnum > oldnum ) {
			// the old entity isn't present in the new message
			MSG_WriteDeltaPlayerstate_Packet( oldps, NULL, flags | MSG_PS_FORCE );
			oldindex++;
			continue;
		}
	}

	MSG_WriteByte( CLIENTNUM_NONE );	// end of packetentities
}

static deltaFrame_t	*currentFrame;

/*
================
SV_DeltaBeginFrame
================
*/
void SV_DeltaBeginFrame( deltaFrame_t *frame ) {
	currentFrame = frame;

	frame->numEntities = 0;
	frame->firstEntity = svs.nextEntityStates;

	frame->numPlayers = 0;
	frame->firstPlayer = svs.nextPlayerStates;

}

/*
================
Delta_AllocEntity
================
*/
entity_state_t *SV_DeltaAllocEntity( void ) {
	entity_state_t *es;
		
	es = &svs.entityStates[svs.nextEntityStates % svs.numEntityStates];

	svs.nextEntityStates++;
	currentFrame->numEntities++;

	return es;
}

/*
================
Delta_AllocPlayer
================
*/
playerStateEx_t *SV_DeltaAllocPlayer( void ) {
	playerStateEx_t *ps;

	ps = &svs.playerStates[svs.nextPlayerStates % svs.numPlayerStates];

	svs.nextPlayerStates++;
	currentFrame->numPlayers++;

	return ps;
}

/*
==================
SV_WriteFrameToClient_Default
==================
*/
void SV_WriteFrameToClient_Default( client_t *client ) {
	client_frame_t		*frame, *oldframe;
	player_state_t		*oldstate;
	int					lastframe;

	// this is the frame we are creating
	frame = &client->frames[sv.framenum & UPDATE_MASK];
	client->frame_latency[sv.framenum & LATENCY_MASK] = -1;

	if( client->lastframe <= 0 ) {
		// client is asking for a retransmit
		oldframe = NULL;
		lastframe = -1;
	} else if( sv.framenum - client->lastframe > UPDATE_BACKUP - 1 ) {
		// 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 = -1;
	} else {
		// we have a valid message to delta from
		oldframe = &client->frames[client->lastframe & UPDATE_MASK];
		lastframe = client->lastframe;

		if( svs.nextEntityStates -
			oldframe->delta.firstEntity > svs.numEntityStates )
		{
			Com_DPrintf( "%s: delta request from out-of-date entities.\n",
                    client->name );
			oldframe = NULL;
			lastframe = -1;
		}
	}

	MSG_WriteByte( svc_frame );
	MSG_WriteLong( sv.framenum );
	MSG_WriteLong( lastframe );	// what we are delta'ing from
	MSG_WriteByte( client->surpressCount );	// rate dropped packets
	client->surpressCount = 0;

	// send over the areabits
	MSG_WriteByte( frame->areabytes );
	MSG_WriteData( frame->areabits, frame->areabytes );

	// delta encode the playerstate
	MSG_WriteByte( svc_playerinfo );
	if( oldframe ) {
		oldstate = &oldframe->ps.ps;
	} else {
		oldstate = NULL;
	}
	MSG_WriteDeltaPlayerstate_Default( oldstate, &frame->ps.ps );
	
	// delta encode the entities
	MSG_WriteByte( svc_packetentities );
	SV_EmitPacketEntities( ( deltaFrame_t * )oldframe, ( deltaFrame_t * )frame,
		CLIENTNUM_NONE, client->baselines );
}

/*
==================
SV_WriteFrameToClient_Enhanced
==================
*/
void SV_WriteFrameToClient_Enhanced( client_t *client ) {
	client_frame_t		*frame, *oldframe;
	uint32		extraflags;
	int			delta;
	byte *b1, *b2;
	msgPsFlags_t	psFlags;
	int clientNum;

	// this is the frame we are creating
	frame = &client->frames[sv.framenum & UPDATE_MASK];
	client->frame_latency[sv.framenum & LATENCY_MASK] = -1;

	if( client->lastframe <= 0 ) {
		// client is asking for a retransmit
		oldframe = NULL;
		delta = 31;
	} else if( sv.framenum - client->lastframe > UPDATE_BACKUP - 1 ) {
		// 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;
		delta = 31;
	} else {
		// we have a valid message to delta from
		oldframe = &client->frames[client->lastframe & UPDATE_MASK];
		delta = sv.framenum - client->lastframe;

		if( svs.nextEntityStates -
			oldframe->delta.firstEntity > svs.numEntityStates )
		{
			Com_DPrintf( "%s: delta request from out-of-date entities.\n",
                    client->name );
			oldframe = NULL;
			delta = 31;
		}
	}

	/* first byte to be patched */
	b1 = SZ_GetSpace( &msg_write, 1 );

	MSG_WriteLong( ( sv.framenum & FRAMENUM_MASK ) |
            ( delta << FRAMENUM_BITS ) );

	/* second byte to be patched */
	b2 = SZ_GetSpace( &msg_write, 1 );

	/* send over the areabits */
	MSG_WriteByte( frame->areabytes );
	MSG_WriteData( frame->areabits, frame->areabytes );

	/* ignore some parts of playerstate if not recording demo */
	psFlags = 0;
	if( !client->settings[CLS_RECORDING] ) {
		if( client->settings[CLS_NOGUN] ) {
			psFlags |= MSG_PS_IGNORE_GUNFRAMES;
			if( client->settings[CLS_NOGUN] != 2 ) {
				psFlags |= MSG_PS_IGNORE_GUNINDEX;
			}
		}
		if( client->settings[CLS_NOBLEND] ) {
			psFlags |= MSG_PS_IGNORE_BLEND;
		}
		if( frame->ps.ps.pmove.pm_type < PM_DEAD ) {
			if( !( frame->ps.ps.pmove.pm_flags & PMF_NO_PREDICTION ) ) {
				psFlags |= MSG_PS_IGNORE_VIEWANGLES;
			}
		} else {
			/* lying dead on a rotating platform? */
			psFlags |= MSG_PS_IGNORE_DELTAANGLES;
		}
	}

	if( client->protocol == PROTOCOL_VERSION_Q2PRO ) {
		if( client->settings[CLS_NOPREDICT] ) {
			psFlags |= MSG_PS_IGNORE_PREDICTION;
		}
		psFlags |= MSG_PS_WRITE_CLIENTNUM;
	}

	/* delta encode the playerstate */
	extraflags = MSG_WriteDeltaPlayerstate_Enhanced(
            oldframe ? &oldframe->ps : NULL, &frame->ps, psFlags );

	/* save 3 high bits of extraflags */
	*b1 = svc_frame | ( ( ( extraflags & 0x70 ) << 1 ) );

	/* save 4 low bits of extraflags */
	*b2 = ( client->surpressCount & SURPRESSCOUNT_MASK ) |
        ( ( extraflags & 0x0F ) << SURPRESSCOUNT_BITS );

	client->surpressCount = 0;

	/* delta encode the entities */
	clientNum = CLIENTNUM_NONE;
	if( client->protocol == PROTOCOL_VERSION_Q2PRO &&
        frame->ps.ps.pmove.pm_type < PM_DEAD )
    {
		clientNum = frame->ps.clientNum;
	}
	SV_EmitPacketEntities( ( deltaFrame_t * )oldframe, ( deltaFrame_t * )frame,
		clientNum, client->baselines );
}

/*
==================
SV_WriteFrameToClient_Mvd
==================
*/
void SV_WriteFrameToClient_Mvd( client_t *client ) {
	client_frame_t		*frame, *oldframe;
	int			delta;
	msgPsFlags_t flags;

	// this is the frame we are creating
	frame = &client->frames[sv.framenum & UPDATE_MASK];
	client->frame_latency[sv.framenum & LATENCY_MASK] = -1;

	if( client->lastframe <= 0 ) {
		// client is asking for a retransmit
		oldframe = NULL;
		delta = 31;
	} else if( sv.framenum - client->lastframe > UPDATE_BACKUP - 1 ) {
		// 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;
		delta = 31;
	} else {
		// we have a valid message to delta from
		oldframe = &client->frames[client->lastframe & UPDATE_MASK];
		delta = sv.framenum - client->lastframe;

		if( svs.nextEntityStates -
			oldframe->delta.firstEntity > svs.numEntityStates )
		{
			Com_DPrintf( "%s: delta request from out-of-date entities.\n",
                    client->name );
			oldframe = NULL;
			delta = 31;
		} else if( svs.nextPlayerStates -
			oldframe->delta.firstPlayer > svs.numPlayerStates )
		{
			Com_DPrintf( "%s: delta request from out-of-date players.\n",
                    client->name );
			oldframe = NULL;
			delta = 31;
		}
	}

	MSG_WriteByte( svc_frame );
	MSG_WriteLong( ( sv.framenum & FRAMENUM_MASK ) |
            ( delta << FRAMENUM_BITS ) );

	/* send over the portalbits */
	MSG_WriteByte( frame->areabytes );
	MSG_WriteData( frame->areabits, frame->areabytes );

	flags = MSG_PS_IGNORE_PREDICTION|MSG_PS_IGNORE_DELTAANGLES;
	if( mvd_noblend->integer ) {
		flags |= MSG_PS_IGNORE_BLEND;
	}
	if( mvd_nogun->integer ) {
		flags |= MSG_PS_IGNORE_GUNINDEX|MSG_PS_IGNORE_GUNFRAMES;
	}
	SV_EmitPacketPlayers( ( deltaFrame_t * )oldframe, ( deltaFrame_t * )frame,
            flags );
	SV_EmitPacketEntities( ( deltaFrame_t * )oldframe, ( deltaFrame_t * )frame,
		CLIENTNUM_ANY, client->baselines );

	client->surpressCount = 0;

}

/*
=============================================================================

Build a client frame structure

=============================================================================
*/

void SV_BuildMvdFrame( client_t *client ) {
	client_frame_t	*frame;
	edict_t *ent;
	entity_state_t *es;
	playerStateEx_t *ps;
	int i;

	// this is the frame we are creating
	frame = &client->frames[sv.framenum & UPDATE_MASK];
	frame->senttime = svs.realtime; // save it for ping calc later

    // store portalbits
	frame->areabytes = CM_WritePortalBits( &sv.cm, frame->areabits );

	// build up the list of visible entities
	SV_DeltaBeginFrame( ( deltaFrame_t * )frame );

	for( i = 1; i < ge->num_edicts; i++ ) {
		ent = EDICT_NUM( i );

		if( !( ent->svflags & SVF_NOCLIENT ) &&
			( ent->s.modelindex || ent->s.effects || ent->s.sound || ent->s.event ) )
		{
			es = SV_DeltaAllocEntity();
			*es = ent->s;
			es->number = i;
		}
	}

	for( i = 0 ; i < sv_maxclients->integer; i++ ) {
		if( MVD_PlayerIsActive( i ) ) {
			ps = SV_DeltaAllocPlayer();

			ent = EDICT_NUM( i + 1 );
			ps->ps = ent->client->ps;
			ps->number = i;
			ps->clientNum = i;
		}
	}
}

void SV_BuildProxyMvdFrame( client_t *client ) {
	int		i, j;
    mvdFrame_t  *mvdframe;
	client_frame_t	*frame;
	entity_state_t	*es;
	playerStateEx_t *ps;

	mvdframe = &mvd.frames[mvd.validPacketNum % mvd.frameBackup];

	// this is the frame we are creating
	frame = &client->frames[sv.framenum & UPDATE_MASK];
	frame->senttime = svs.realtime; // save it for ping calc later

	// copy portalbits
	frame->areabytes = mvdframe->numPortalBytes;
    memcpy( frame->areabits, mvdframe->portalbytes, mvdframe->numPortalBytes );

	SV_DeltaBeginFrame( ( deltaFrame_t * )frame );

	for( i = 0; i < mvdframe->numEntityStates; i++ ) {
		j = ( mvdframe->firstEntityState + i ) % mvd.maxEntityStates;
		es = SV_DeltaAllocEntity();
		*es = mvd.entityStates[j].s;
	}

	for( i = 0; i < mvdframe->numPlayerStates; i++ ) {
		j = ( mvdframe->firstPlayerState + i ) % mvd.maxPlayerStates;
		ps = SV_DeltaAllocPlayer();
		*ps = mvd.playerStates[j];
	}
}

#define BODY_QUEUE_SIZE 8

/*
=============
SV_BuildClientFrame

Decides which entities are going to be visible to the client, and
copies off the playerstat and areabits.
=============
*/
void SV_BuildClientFrame( client_t *client ) {
	int		e, i;
	vec3_t	org;
	edict_t	*ent;
	edict_t	*clent;
	client_frame_t	*frame;
	entity_state_t	*state;
    player_state_t  *ps;
	int		l;
	int		clientarea, clientcluster;
	cleaf_t	*leaf;
	byte	*clientphs;
	byte	*clientpvs;

	clent = client->edict;
	if( !clent->client )
		return;		// not in game yet

	// this is the frame we are creating
	frame = &client->frames[sv.framenum & UPDATE_MASK];

	frame->senttime = svs.realtime; // save it for ping calc later

	// find the client's PVS
    ps = &clent->client->ps;
	VectorMA( ps->viewoffset, 0.125f, ps->pmove.origin, org );

	leaf = CM_PointLeaf( &sv.cm, org );
	clientarea = CM_LeafArea( leaf );
	clientcluster = CM_LeafCluster( leaf );

	// calculate the visible areas
	frame->areabytes = CM_WriteAreaBits( &sv.cm, frame->areabits, clientarea );

	// grab the current player_state_t
	frame->ps.ps = *ps;
	frame->ps.number = client->clientNum;
	frame->ps.clientNum = client->clientNum;

	clientpvs = CM_FatPVS( &sv.cm, org );
	clientphs = CM_ClusterPHS( &sv.cm, clientcluster );

	// build up the list of visible entities
	SV_DeltaBeginFrame( ( deltaFrame_t * )frame );

	for( e = 1; e < ge->num_edicts; e++ ) {
		ent = EDICT_NUM( e );

		// ignore ents without visible models
		if( ent->svflags & SVF_NOCLIENT )
			continue;

		// ignore ents without visible models unless they have an effect
		if( !ent->s.modelindex && !ent->s.effects && !ent->s.sound ) {
			if( !ent->s.event ) {
				continue;
			}
			if( ent->s.event == EV_FOOTSTEP && client->settings[CLS_NOFOOTSTEPS] ) {
				continue;
			}
		}

		if( ( ent->s.effects & EF_GIB ) && client->settings[CLS_NOGIBS] ) {
			continue;
		}

		// ignore if not touching a PV leaf
		if( ent != clent ) {
			// check area
			if( !CM_AreasConnected( &sv.cm, clientarea, ent->areanum ) ) {	
				// doors can legally straddle two areas, so
				// we may need to check another one
				if( !ent->areanum2 || !CM_AreasConnected( &sv.cm, clientarea, ent->areanum2 ) )
					continue;		// blocked by a door
			}

			// beams just check one point for PHS
			if( ent->s.renderfx & RF_BEAM ) {
				l = ent->clusternums[0];
				if( !Q_IsBitSet( clientphs, l ) )
					continue;
			} else {
				if( ent->num_clusters == -1 ) {	
					// too many leafs for individual check, go by headnode
					if( !CM_HeadnodeVisible( CM_NodeNum( &sv.cm, ent->headnode ), clientpvs ) )
						continue;
				} else {	
					// check individual leafs
					for( i = 0; i < ent->num_clusters; i++ ) {
						l = ent->clusternums[i];
						if( Q_IsBitSet( clientpvs, l ) )
							break;
					}
					if( i == ent->num_clusters )
						continue;		// not visible
				}

				if( !ent->s.modelindex ) {	
					// don't send sounds if they will be attenuated away
					vec3_t	delta;
					float	len;

					VectorSubtract( org, ent->s.origin, delta );
					len = VectorLength( delta );
					if( len > 400 )
						continue;
				}
			}
		}

		if( ent->s.number != e ) {
			Com_DPrintf( "FIXING ENT->S.NUMBER!!!\n" );
			ent->s.number = e;
		}

		// add it to the circular client_entities array
		state = SV_DeltaAllocEntity();
		*state = ent->s;
		if( ent->s.event == EV_FOOTSTEP && client->settings[CLS_NOFOOTSTEPS] ) {
			state->event = 0;
		}

		if( sv_bodyque_hack->integer ) {
			if( state->number >= svs.maxGameClients + 1 &&
                state->number <= svs.maxGameClients + BODY_QUEUE_SIZE )
            {
				state->event = EV_OTHER_TELEPORT;
			}
		}

#if 0
		if( sv.state == ss_broadcast &&
            client->protocol != PROTOCOL_VERSION_Q2PRO &&
			e == frame->ps.clientNum + 1 )
		{
			/* clear the modelindex for the player entity this
             * client is following.
			 * FIXME: this way EF_FLAG effects are lost, etc */
			state->modelindex = 0;
		}
#endif

		// don't mark players missiles as solid
		if( ent->owner == client->edict )
			state->solid = 0;

		if( frame->delta.numEntities == MAX_PACKET_ENTITIES ) {
			break;
		}
	}
}

/*
=============
SV_BuildProxyClientFrame

=============
*/
void SV_BuildProxyClientFrame( client_t *client ) {
	int		i, j, k, l;
	vec3_t	org;
	mvdClient_t *mvdcl;
    mvdFrame_t  *mvdframe;
	mvdGamestate_t *gs;
	client_frame_t	*frame;
	entity_state_t	*state;
	entityStateEx_t *ent;
	player_state_t *ps;
	int		clientarea, clientcluster;
	cleaf_t	*leaf;
	byte	*clientphs;
	byte	*clientpvs;

	mvdcl = ( mvdClient_t * )client->edict->client;
	if( !mvdcl )
		return;		// not in game yet

	if( mvdcl->admin ) {
		mvdframe = &mvd.frames[mvd.validPacketNum % mvd.frameBackup];
		gs = &mvd.gamestates[mvd.gamestateSequence & GAMESTATE_MASK];
	} else {
		mvdframe = &mvd.frames[mvd.activePacketNum % mvd.frameBackup];
		gs = &mvd.gamestates[mvd.activeGamestateSequence & GAMESTATE_MASK];
	}

	if( !( mvdframe->flags & MFF_ACTIVE ) ) {
        SV_DropClient( client, "SV_BuildProxyClientFrame: inactive frame" );
        return;
    }

	if( !( mvdframe->flags & MFF_VALID ) ) {
        SV_DropClient( client, "SV_BuildProxyClientFrame: invalid frame" );
        return;
    }

	// this is the frame we are creating
	frame = &client->frames[sv.framenum & UPDATE_MASK];

	frame->senttime = svs.realtime; // save it for ping calc later

	// find the client's PVS
    ps = &mvdcl->ps;
	VectorMA( ps->viewoffset, 0.125f, ps->pmove.origin, org );

	leaf = CM_PointLeaf( &gs->cm, org );
	clientarea = CM_LeafArea( leaf );
	clientcluster = CM_LeafCluster( leaf );

	// copy areabits
	if( mvd.serverProtocol == PROTOCOL_VERSION_MVD ) {
		if( mvdcl->admin ) {
			frame->areabytes = 0;
		} else {
			frame->areabytes = CM_WriteAreaBits( &gs->cm, frame->areabits, clientarea );
		}
	} else {
		frame->areabytes = mvdframe->numPortalBytes;
		memcpy( frame->areabits, mvdframe->portalbytes, mvdframe->numPortalBytes );
	}

	// grab the current player_state_t
	frame->ps.ps = *ps;
	frame->ps.number = client->clientNum;
    frame->ps.clientNum = mvdcl->clientNum;

	clientpvs = CM_FatPVS( &gs->cm, org );
	clientphs = CM_ClusterPHS( &gs->cm, clientcluster );

	// build up the list of visible entities
	SV_DeltaBeginFrame( ( deltaFrame_t * )frame );

	for( i = 0; i < mvdframe->numEntityStates; i++ ) {
		j = ( mvdframe->firstEntityState + i ) % mvd.maxEntityStates;
		ent = &mvd.entityStates[j];

		// ignore ents without visible models unless they have an effect
		if( !ent->s.modelindex && !ent->s.effects && !ent->s.sound ) {
			if( !ent->s.event ) {
				continue;
			}
			if( ent->s.event == EV_FOOTSTEP && client->settings[CLS_NOFOOTSTEPS] )
            {
				continue;
			}
		}

		if( ( ent->s.effects & EF_GIB ) && client->settings[CLS_NOGIBS] ) {
			continue;
		}

		// ignore if not touching a PV leaf
		if( ent->linked ) {
			// check area
			if( !mvdcl->admin && !CM_AreasConnected( &gs->cm, clientarea, ent->areanum ) ) {	
				// doors can legally straddle two areas, so
				// we may need to check another one
				if( !ent->areanum2 || !CM_AreasConnected( &gs->cm, clientarea, ent->areanum2 ) ) {
					continue;		// blocked by a door
				}
			}

			// beams just check one point for PHS
			if( ent->s.renderfx & RF_BEAM ) {
				l = ent->clusternums[0];
				if( !Q_IsBitSet( clientphs, l ) ) {
					continue;
				}
			} else {
				if( ent->num_clusters == -1 ) {	
					// too many leafs for individual check, go by headnode
					if( !CM_HeadnodeVisible( ent->headnode, clientpvs ) ) {
						continue;
					}
				} else {	
					// check individual leafs
					for( k = 0; k < ent->num_clusters; k++ ) {
						l = ent->clusternums[k];
						if( Q_IsBitSet( clientpvs, l ) ) {
							break;
						}
					}
					if( k == ent->num_clusters ) {
						continue;		// not visible
					}
				}

				if( !ent->s.modelindex ) {	
					// don't send sounds if they will be attenuated away
					vec3_t	delta;
					float	len;

					VectorSubtract( org, ent->s.origin, delta );
					len = VectorLength( delta );
					if( len > 400 ) {
						continue;
					}
				}
			}
		}

		// add it to the circular client_entities array
		state = SV_DeltaAllocEntity();
		*state = ent->s;
        if( mvdframe->serverPacketNum == mvdcl->lastServerPacket ) {
            state->event = 0;
            if( !( state->renderfx & RF_BEAM ) ) {
                VectorCopy( state->origin, state->old_origin );
            }
        } else {
            if( state->event == EV_FOOTSTEP &&
                client->settings[CLS_NOFOOTSTEPS] )
            {
                state->event = 0;
            }
            
            if( sv_bodyque_hack->integer ) {
                if( state->number >= svs.maxGameClients + 1 &&
                    state->number <= svs.maxGameClients + BODY_QUEUE_SIZE )
                {
                    state->event = EV_OTHER_TELEPORT;
                }
            }
        }

		if( client->protocol != PROTOCOL_VERSION_Q2PRO &&
			state->number == frame->ps.clientNum + 1 )
		{
			/* clear the modelindex for the player entity this
             * client is following.
			 * FIXME: this way EF_FLAG effects are lost, etc */
			state->modelindex = 0;
		}

	}

    mvdcl->lastServerPacket = mvdframe->serverPacketNum;

}


