//-----------------------------------------------------------------------------
//
//  $Logfile:: /Source/sv_ents.c                                              $
// $Revision:: 5                                                              $
//   $Author:: Burgermac                                                      $
//     $Date:: 2/5/02 7:40 PM                                                 $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// DESCRIPTION:
//
//#include <alloca.h>
#include "server.h"

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

Encode a client frame onto the network channel

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

/*
=======================================================
SV_WriteConsoleStateToClient

Writes a delta update of console_state_t to the message
========================================================
*/
static void SV_WriteConsoleStateToClient (client_frame_t *from, client_frame_t *to, sizebuf_t *msg, client_t *client)
{
	console_state_t *newcon, *oldcon;
	console_buffer_state_t *bs;
	netconbuffer_t *svbuff;
	//netconsole_t *svcon;
	int from_num_consoles;
	int newindex, oldindex;
	int newnum, oldnum;

	MSG_WriteByte (msg, svc_consolestates);

	if (!from)
		from_num_consoles = 0;
	else
		from_num_consoles = from->num_consoles;

	bs = NULL;/* silence compiler */
	newcon = oldcon = NULL;/* silence compiler */
	newindex = 0;
	oldindex = 0;
	while (newindex < to->num_consoles || oldindex < from_num_consoles)
	{
		if (newindex >= to->num_consoles)
		{
			newnum = 9999;
		}
		else
		{
			newcon = &svs.client_consoles[(to->first_console+newindex)%svs.num_client_consoles];
			newnum = newcon->number;
			svbuff = CONSOLEBUFFER_NUM(newnum);
			//svcon  = CONSOLE_NUM(newnum);
			bs = &svbuff->s;
		}

		if (oldindex >= from_num_consoles)
		{
			oldnum = 9999;
		}
		else
		{
			oldcon = &svs.client_consoles[(from->first_console+oldindex)%svs.num_client_consoles];
			oldnum = oldcon->number;
		}

		if (newnum == oldnum)
		{
			// delta update from old console
			//SV_Printf ("delta %i\n", newnum);
			MSG_WriteDeltaConsole (oldcon, newcon, bs, msg, false, &client->current_buffer_index[newnum],true);
			oldindex++;
			newindex++;
			continue;
		}

		if (newnum < oldnum)
		{
			// this is a new console, send it from the baseline
			//SV_Printf ("baseline %i\n", newnum);
			MSG_WriteDeltaConsole (&sv.console_baselines[newnum], newcon, bs, msg, true, &client->current_buffer_index[newnum],false);
			newindex++;
			continue;
		}

		if (newnum > oldnum)
		{
			oldindex++;
			continue;
			// error - consoles should never be removed.
			//Com_Error (ERR_FATAL, "SV_WriteConsoleToClientState: Console has been removed.");
		}
	}
	MSG_WriteShort (msg, 0);	// end of console states
}

/*
=======================================================
SV_WriteSurfaceStateToClient

Writes a delta update of surface_state_t to the message
========================================================
*/
static void SV_WriteSurfaceStateToClient (client_frame_t *from, client_frame_t *to, sizebuf_t *msg, client_t *client)
{
	surface_state_t *newsurf, *oldsurf;
	int from_num_surfaces;
	int newindex, oldindex;
	int newnum, oldnum;

	MSG_WriteByte (msg, svc_surfacestates);

	if (!from)
		from_num_surfaces = 0;
	else
		from_num_surfaces = from->num_surfaces;

	newsurf = oldsurf = NULL;/* silence compiler */
	newindex = 0;
	oldindex = 0;

	while (newindex < to->num_surfaces || oldindex < from_num_surfaces)
	{
		if (newindex >= to->num_surfaces)
			newnum = 9999;
		else
		{
			newsurf = &svs.client_surfaces[(to->first_surface+newindex)%svs.num_client_surfaces];
			newnum = newsurf->number;
		}

		if (oldindex >= from_num_surfaces)
			oldnum = 9999;
		else
		{
			oldsurf = &svs.client_surfaces[(from->first_surface+oldindex)%svs.num_client_surfaces];
			oldnum = oldsurf->number;
		}

		if (newnum == oldnum)
		{
			// delta update from old surface
			//SV_Printf ("delta %i\n", newnum);
			MSG_WriteDeltaSurface (oldsurf, newsurf, msg, false);
			oldindex++;
			newindex++;
			continue;
		}

		if (newnum < oldnum)
		{
			// this is a new console, send it from the baseline
			//SV_Printf ("baseline %i\n", newnum);
			MSG_WriteDeltaSurface (&sv.surface_baselines[newnum], newsurf, msg, false);
			newindex++;
			continue;
		}

		if (newnum > oldnum)
		{
			oldindex++;
			continue;
		}
	}
	MSG_WriteShort (msg, -1);	// end of surface states
}

/*
=============
SV_EmitPacketEntities

Writes a delta update of an entity_state_t list to the message.
=============
*/
static 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;
	int		bits;

	MSG_WriteByte (msg, svc_packetentities);

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

	oldent = newent = NULL;/* silence compiler */
	newindex = 0;
	oldindex = 0;

	while (newindex < to->num_entities || oldindex < from_num_entities)
	{
		if (newindex >= to->num_entities)
			newnum = 9999;
		else
		{
			newent = &svs.client_entities[(to->first_entity+newindex)%svs.num_client_entities];
			newnum = newent->number;
		}

		if (oldindex >= from_num_entities)
			oldnum = 9999;
		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
			//SV_Printf ("delta %i\n", newnum);
			MSG_WriteDeltaEntity (oldent, newent, msg, false, newent->number <= maxclients->value);
			oldindex++;
			newindex++;
			continue;
		}

		if (newnum < oldnum)
		{	// this is a new entity, send it from the baseline
			//SV_Printf ("baseline %i\n", newnum);
			MSG_WriteDeltaEntity (&sv.baselines[newnum], newent, msg, true, false);
			newindex++;
			continue;
		}

		if (newnum > oldnum)
		{	// the old entity isn't present in the new message
			//SV_Printf ("remove %i\n", oldnum);
			bits = U_REMOVE;
			if (oldnum >= 256)
				bits |= U_NUMBER16 | U_MOREBITS1;

			MSG_WriteByte (msg,	bits&255 );
			if (bits & 0x0000ff00)
				MSG_WriteByte (msg,	(bits>>8)&255 );

			if (bits & U_NUMBER16)
				MSG_WriteShort (msg, oldnum);
			else
				MSG_WriteByte (msg, oldnum);

			oldindex++;
			continue;
		}
	}

	MSG_WriteShort (msg, 0);	// end of packetentities
}


/*
=============
SV_WritePlayerstateToClient

=============
*/
static void SV_WritePlayerstateToClient (client_frame_t *from, client_frame_t *to, sizebuf_t *msg)
{
	int				i;
	int				pflags;
	player_state_t	*ps, *ops;
	player_state_t	dummy;
	int				statbits;

	ps = &to->ps;
	if (!from)
	{
		memset (&dummy, 0, sizeof(dummy));
		ops = &dummy;
	}
	else
		ops = &from->ps;

	//
	// determine what needs to be sent
	//
	pflags = 0;

	if (ps->pmove.pm_type != ops->pmove.pm_type)
		pflags |= PS_M_TYPE;

	if (ps->pmove.origin[0] != ops->pmove.origin[0]
		|| ps->pmove.origin[1] != ops->pmove.origin[1]
		|| ps->pmove.origin[2] != ops->pmove.origin[2] )
		pflags |= PS_M_ORIGIN;

	if (ps->pmove.velocity[0] != ops->pmove.velocity[0]
		|| ps->pmove.velocity[1] != ops->pmove.velocity[1]
		|| ps->pmove.velocity[2] != ops->pmove.velocity[2] )
		pflags |= PS_M_VELOCITY;

	if (ps->pmove.pm_time != ops->pmove.pm_time)
		pflags |= PS_M_TIME;

	if (ps->pmove.pm_flags != ops->pmove.pm_flags)
	{
		pflags |= PS_M_FLAGS;
		if (ps->pmove.pm_flags > 0x100)
		{
			ps->pmove.pm_flags |= PMF_MOREBITS;
		}
	}

	if ((ps->pmove.gravity != ops->pmove.gravity) || (ps->pmove.gravity_axis != ops->pmove.gravity_axis))
		pflags |= PS_M_GRAVITY;

	if (ps->pmove.delta_angles[0] != ops->pmove.delta_angles[0]
		|| ps->pmove.delta_angles[1] != ops->pmove.delta_angles[1]
		|| ps->pmove.delta_angles[2] != ops->pmove.delta_angles[2] )
		pflags |= PS_M_DELTA_ANGLES;


	if (ps->viewoffset[0] != ops->viewoffset[0]
		|| ps->viewoffset[1] != ops->viewoffset[1]
		|| ps->viewoffset[2] != ops->viewoffset[2] )
		pflags |= PS_VIEWOFFSET;

	if (ps->viewangles[0] != ops->viewangles[0]
		|| ps->viewangles[1] != ops->viewangles[1]
		|| ps->viewangles[2] != ops->viewangles[2] )
		pflags |= PS_VIEWANGLES;

	if (ps->kick_angles[0] != ops->kick_angles[0]
		|| ps->kick_angles[1] != ops->kick_angles[1]
		|| ps->kick_angles[2] != ops->kick_angles[2] )
		pflags |= PS_KICKANGLES;

	if (ps->blend[0] != ops->blend[0]
		|| ps->blend[1] != ops->blend[1]
		|| ps->blend[2] != ops->blend[2]
		|| ps->blend[3] != ops->blend[3] )
		pflags |= PS_BLEND;

	if (ps->fov != ops->fov)
		pflags |= PS_FOV;

	if (ps->rdflags != ops->rdflags)
		pflags |= PS_RDFLAGS;

	if (( ps->current_music_mood != ops->current_music_mood ) ||
		( ps->fallback_music_mood != ops->fallback_music_mood ))
		{
			pflags |= PS_MUSICMOOD;
		}
	if (ps->gunoffset[0] != ops->gunoffset[0] ||
		ps->gunoffset[1] != ops->gunoffset[1] ||
		ps->gunoffset[2] != ops->gunoffset[2] ||
		ps->gunangles[0] != ops->gunangles[0] ||
		ps->gunangles[1] != ops->gunangles[1] ||
		ps->gunangles[2] != ops->gunangles[2])
			pflags |= PS_GUNOFFSET;

	//
	// write it
	//

	MSG_WriteByte (msg, svc_playerinfo);

	MSG_WriteShort (msg, pflags);
	//
	// write the pmove_state_t
	//
	if (pflags & PS_M_TYPE)
		MSG_WriteByte (msg, ps->pmove.pm_type);

	if (pflags & PS_M_ORIGIN)
	{
		MSG_WriteShort (msg, ps->pmove.origin[0]);
		MSG_WriteShort (msg, ps->pmove.origin[1]);
		MSG_WriteShort (msg, ps->pmove.origin[2]);
	}

	if (pflags & PS_M_VELOCITY)
	{
		MSG_WriteShort (msg, ps->pmove.velocity[0]);
		MSG_WriteShort (msg, ps->pmove.velocity[1]);
		MSG_WriteShort (msg, ps->pmove.velocity[2]);
	}

	if (pflags & PS_M_TIME)
		MSG_WriteByte (msg, ps->pmove.pm_time);

	if (pflags & PS_M_FLAGS)
		MSG_WriteByte (msg, ps->pmove.pm_flags&0xff);
	if ( ps->pmove.pm_flags & PMF_MOREBITS )
		MSG_WriteByte (msg, (ps->pmove.pm_flags>>8));

	if (pflags & PS_M_GRAVITY)
	{
		int gravity;

		gravity = ( ps->pmove.gravity & GRAVITY_MASK ) | ( ( ps->pmove.gravity_axis << GRAVITY_AXIS_SHIFT ) & GRAVITY_AXIS_MASK );
		MSG_WriteShort (msg, gravity);
	}

	if (pflags & PS_M_DELTA_ANGLES)
	{
		MSG_WriteShort (msg, ps->pmove.delta_angles[0]);
		MSG_WriteShort (msg, ps->pmove.delta_angles[1]);
		MSG_WriteShort (msg, ps->pmove.delta_angles[2]);
	}

	//
	// write the rest of the player_state_t
	//
	if (pflags & PS_VIEWOFFSET)
	{
		MSG_WriteChar (msg, ps->viewoffset[0]*4);
		MSG_WriteChar (msg, ps->viewoffset[1]*4);
		MSG_WriteShort (msg, ps->viewoffset[2]*4);
	}

	if (pflags & PS_VIEWANGLES)
	{
		MSG_WriteAngle16 (msg, ps->viewangles[0]);
		MSG_WriteAngle16 (msg, ps->viewangles[1]);
		MSG_WriteAngle16 (msg, ps->viewangles[2]);
	}

	if (pflags & PS_KICKANGLES)
	{
		MSG_WriteChar (msg, ps->kick_angles[0]*4);
		MSG_WriteChar (msg, ps->kick_angles[1]*4);
		MSG_WriteChar (msg, ps->kick_angles[2]*4);
	}

	if ( pflags & PS_MUSICMOOD )
	{
		int b;

		b = ( ps->current_music_mood & 0xf ) + ( ( ps->fallback_music_mood & 0xf ) << 4 );
		MSG_WriteByte( msg, b );
	}

	if (pflags & PS_GUNOFFSET)
	{
		MSG_WriteChar (msg, ps->gunoffset[0]*4);
		MSG_WriteChar (msg, ps->gunoffset[1]*4);
		MSG_WriteChar (msg, ps->gunoffset[2]*4);
		MSG_WriteChar (msg, ps->gunangles[0]*4);
		MSG_WriteChar (msg, ps->gunangles[1]*4);
		MSG_WriteChar (msg, ps->gunangles[2]*4);
	}

	if (pflags & PS_BLEND)
	{
		MSG_WriteByte (msg, ps->blend[0]*255);
		MSG_WriteByte (msg, ps->blend[1]*255);
		MSG_WriteByte (msg, ps->blend[2]*255);
		MSG_WriteByte (msg, ps->blend[3]*255);
	}
	if (pflags & PS_FOV)
		MSG_WriteByte (msg, ps->fov);
	if (pflags & PS_RDFLAGS)
		MSG_WriteByte (msg, ps->rdflags);

	// send stats
	statbits = 0;
	for (i=0 ; i<MAX_STATS ; i++)
		if (ps->stats[i] != ops->stats[i])
			statbits |= 1<<i;
	MSG_WriteLong (msg, statbits);
	for (i=0 ; i<MAX_STATS ; i++)
		if (statbits & (1<<i) )
			MSG_WriteShort (msg, ps->stats[i]);
}


/*
==================
SV_WriteFrameToClient
==================
*/
void SV_WriteFrameToClient (client_t *client, sizebuf_t *msg)
{
	client_frame_t		*frame, *oldframe;
	int					lastframe;

//	Com_Printf ("%i -> %i\n", client->lastframe, sv.framenum);
	// this is the frame we are creating
	frame = &client->frames[sv.framenum & UPDATE_MASK];

	if (client->lastframe <= 0)
	{	// client is asking for a retransmit
		oldframe = NULL;
		lastframe = -1;
	}
	else if (sv.framenum - client->lastframe >= (UPDATE_BACKUP - 3) )
	{	// client hasn't gotten a good message through in a long time
//		Com_Printf ("%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;
	}

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

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

	// delta encode the playerstate
	SV_WritePlayerstateToClient (oldframe, frame, msg);

#ifndef SEPERATE_SURFSANDCONSOLE
	// delta encode the consoles
	SV_WriteConsoleStateToClient(oldframe, frame, msg, client);

	// delta encode the surfaces
	SV_WriteSurfaceStateToClient(oldframe, frame, msg, client);
#endif

	// delta encode the entities
	SV_EmitPacketEntities (oldframe, frame, msg);
}

/*
==================
SV_WriteExtraDataToClient
==================
*/
void SV_WriteExtraDataToClient (client_t *client, sizebuf_t *msg)
{
	client_frame_t		*frame, *oldframe;
	//int					lastframe;

//	Com_Printf ("%i -> %i\n", client->lastframe, sv.framenum);
	// this is the frame we are creating
	frame = &client->frames[sv.framenum & UPDATE_MASK];

	if (client->lastframe <= 0)
	{	// client is asking for a retransmit
		oldframe = NULL;
		//lastframe = -1;
	}
	else if (sv.framenum - client->lastframe >= (UPDATE_MASK-1) )
	{	// client hasn't gotten a good message through in a long time
//		Com_Printf ("%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;
	}

	MSG_WriteByte (msg, svc_extradata);

	// delta encode the consoles
	SV_WriteConsoleStateToClient(oldframe, frame, msg, client);

	// delta encode the surfaces
	SV_WriteSurfaceStateToClient(oldframe, frame, msg, client);
}

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

Build a client frame structure

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

static byte	fatpvs[65536/8];	// 32767 is MAX_MAP_LEAFS

/*
============
SV_FatPVS

The client will interpolate the view position,
so we can't use a single PVS point
===========
*/
static 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++)
	{
		mins[i] = org[i] - 8;
		maxs[i] = org[i] + 8;
	}

	count = CM_BoxLeafnums (mins, maxs, leafs, 64, NULL);
	if (count < 1)
		Com_Error (ERR_FATAL, "SV_FatPVS: count < 1");
	longs = (CM_NumClusters()+31)>>5;

	// convert leafs to clusters
	for (i=0 ; i<count ; i++)
		leafs[i] = CM_LeafCluster(leafs[i]);

	memcpy(fatpvs, CM_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;		// allready have the cluster we want
		src = CM_ClusterPVS(leafs[i]);
		for (j=0 ; j<longs ; j++)
			((long *)fatpvs)[j] |= ((long *)src)[j];
	}
}

void SV_EdictEndFrame(edict_t *edict)
{
	edict->s.renderfx &= ~RF_FRAMELERP;
	edict->s.prevframe = -1;
	if ( !( edict->s.renderfx & RF_BEAM ) && !( edict->s.effects & EF_BEAMEFFECT ) )
	{
		VectorCopy( edict->s.origin, edict->s.old_origin );
	}
}


/*
=============
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, j;
	vec3_t	org;
	edict_t	*ent;
	edict_t	*clent;
	client_frame_t	*frame;
	entity_state_t	*state;
	int		l;
	int		clientarea, clientcluster;
	int		leafnum;
	int		c_fullsend;
	byte	*clientphs;
	byte	*bitvector;
	int num_possible=0;
	console_state_t *con_state;
	surface_state_t *surface_state;
	gravityaxis_t grav;
	int clnum;
	// 2015 code
	qboolean addongame;

	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


	// 2015 code 
	if ( game2015->value )
		addongame = true;
	else
		addongame = false;

	clnum = clent->s.number;

	// find the client's PVS
	grav = gravity_axis[ clent->client->ps.pmove.gravity_axis ];
	org[grav.x] = clent->client->ps.pmove.origin[grav.x]*0.125 + clent->client->ps.viewoffset[0];
	org[grav.y] = clent->client->ps.pmove.origin[grav.y]*0.125 + clent->client->ps.viewoffset[1] * grav.sign;
	org[grav.z] = clent->client->ps.pmove.origin[grav.z]*0.125 + clent->client->ps.viewoffset[2] * grav.sign;

	leafnum = CM_PointLeafnum (org);
	clientarea = CM_LeafArea (leafnum);
	clientcluster = CM_LeafCluster (leafnum);

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

	// grab the current player_state_t
	frame->ps = clent->client->ps;


	SV_FatPVS (org);
	clientphs = CM_ClusterPHS (clientcluster);

	// build up the list of visible entities
	frame->num_entities = 0;
	frame->first_entity = svs.next_client_entities;

	c_fullsend = 0;

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

		if (clent->svflags & SVF_HIDEOWNER)
		{
			if (clent->owner == ent)
				continue;
		}

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

		// 2015 code 
		// only send secondary view model entities
		if ( addongame )
		{
			// don't draw this entity to its parent
			if(ent->svflags & SVF_NOTOWNER)
			{
				if (ent->owner == clent)
					continue;
			}
		}

		// ignore ents without visible models unless they have an effect
		if (!ent->s.modelindex && !ent->s.effects && !ent->s.sound && !(ent->svflags & SVF_PLAYERCLIP) )
			continue;

		if (ent->svflags & SVF_ONLYPARENT)
		{
			if (ent->owner != clent)
				continue;
		}
		num_possible++;

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

			// beams just check one point for PHS
			if ( (ent->s.renderfx & RF_BEAM) || (ent->s.effects & EF_BEAMEFFECT) )
			{
				l = ent->clusternums[0];
				if ( !(clientphs[l >> 3] & (1 << (l&7) )) )
					continue;
			}
			else
			{
				// FIXME: if an ent has a model and a sound, but isn't
				// in the PVS, only the PHS, clear the model
				if (ent->s.sound)
				{
					bitvector = fatpvs;	//clientphs;
				}
				else
					bitvector = fatpvs;

				if (ent->num_clusters == -1)
				{	// too many leafs for individual check, go by headnode
					if (!CM_HeadnodeVisible (ent->headnode, bitvector))
						continue;
					c_fullsend++;
				}
				else
				{	// check individual leafs
					for (i=0 ; i < ent->num_clusters ; i++)
					{
						l = ent->clusternums[i];
						if (bitvector[l >> 3] & (1 << (l&7) ))
							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;
					int   attn;

					VectorSubtract (org, ent->s.origin, delta);
					len = VectorLength (delta);
					attn = ent->s.sound>>14;
					if(( attn==3 && len > 400) ||
						( attn==2 && len > 1000) ||
						( attn==1 && len > 2000))
					{
						continue;
					}
				}
			}
		}

		// add it to the circular client_entities array
		state = &svs.client_entities[svs.next_client_entities%svs.num_client_entities];
		if (ent->s.number != e)
		{
			Com_DPrintf (DEVELOPER_MSG_NET, "FIXING ENT->S.NUMBER!!!\n");
			ent->s.number = e;
		}
		*state = ent->s;

		// copy over vieworigin and viewangles
		VectorCopy( state->vieworigin, state->origin );
		VectorCopy( state->viewangles, state->angles );

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

		svs.next_client_entities++;
		frame->num_entities++;
	}

	if (sv_showents->value)
	{
		Com_Printf ("Client: %s sent %4d culled %4d\n", client->name, frame->num_entities, num_possible - frame->num_entities);
	}

	// Set up the consoles.

	frame->num_consoles = 0;
	frame->first_console = svs.next_client_consoles;

	for (i=1;i<=ge->num_consoles;i++)
	{
		netconsole_t *svcon;
		svcon  = CONSOLE_NUM(i);
		if (svcon->inuse)
		{
			con_state = &svs.client_consoles[svs.next_client_consoles%svs.num_client_consoles];
			if ( !( svcon->s.spawnflags & 8) )
			{
				if ( svcon->s.console_owner > 0 )
				{
					ent = EDICT_NUM( svcon->s.console_owner );
					// check area
					if (!CM_AreasConnected (clientarea, ent->areanum))
					{	// doors can legally straddle two areas, so
						// we may need to check another one
						if (!ent->areanum2 || !CM_AreasConnected (clientarea, ent->areanum2))
						{
							continue;		// blocked by a door
						}
					}

					bitvector = fatpvs;

					if (ent->num_clusters == -1)
					{	// too many leafs for individual check, go by headnode
						if (!CM_HeadnodeVisible (ent->headnode, bitvector))
						{
							continue;
						}
					}
					else
					{	// check individual leafs
						for (j=0 ; j < ent->num_clusters ; j++)
						{
							l = ent->clusternums[j];
							if (bitvector[l >> 3] & (1 << (l&7) ))
							{
								break;
							}
						}
						if (j == ent->num_clusters)
						{
							continue;		// not visible
						}
					}
				}
			}
			*con_state = svcon->s;
			svs.next_client_consoles++;
			frame->num_consoles++;
		}
	}

	// Set up the surfaces

	frame->num_surfaces = 0;
	frame->first_surface = svs.next_client_surfaces;

	for ( i=1; i<=ge->num_surfaces; i++ )
	{
		netsurface_t *svsurf;

		svsurf  = SURFACE_NUM( i );
		if ( svsurf->inuse )
		{
			csurface_t	*surface;
			int			surfacearea, surfacecluster;

			surface_state = &svs.client_surfaces[ svs.next_client_surfaces%svs.num_client_surfaces ];
	      
			surface        = &map_surfaces[ svsurf->s.groupnumber ];
			surfacearea    = CM_LeafArea( surface->leaf );
			if ( surfacearea > 0 )
			{
				surfacecluster = CM_LeafCluster( surface->leaf );

				if (!CM_AreasConnected (clientarea, surfacearea))
				{	
					continue;		// blocked by a door
				}

				if ( !(clientphs[surfacecluster >> 3] & (1 << (surfacecluster&7) )) )
					continue;
			}

			*surface_state = svsurf->s;
			svs.next_client_surfaces++;
			frame->num_surfaces++;
			svsurf->s.changed = false;
		}
	}
	// Com_Printf("Out of %i surfaces, %i are delta'd\n", ge->num_surfaces, frame->num_surfaces );
}


/*
==================
SV_RecordDemoMessage

Save everything in the world out without deltas.
Used for recording footage for merged or assembled demos
==================
*/
void SV_RecordDemoMessage (void)
{
	int			e;
	edict_t		*ent;
	entity_state_t	nostate;
	sizebuf_t	buf;
//	byte *		buf_data = alloca(sizeof(byte) * 32768);
	static byte		buf_data[32768];
	int			len;

	if (!svs.demofile)
		return;

	memset (&nostate, 0, sizeof(nostate));
	SZ_Init (&buf, buf_data, sizeof(buf_data));

	// write a frame message that doesn't contain a player_state_t
	MSG_WriteByte (&buf, svc_frame);
	MSG_WriteLong (&buf, sv.framenum);

	MSG_WriteByte (&buf, svc_packetentities);

	e = 1;
	ent = EDICT_NUM(e);
	while (e < ge->num_edicts)
	{
		// ignore ents without visible models unless they have an effect
		if (ent->inuse &&
			ent->s.number &&
			(ent->s.modelindex || ent->s.effects || ent->s.sound) &&
			!(ent->svflags & SVF_NOCLIENT))
		{
			MSG_WriteDeltaEntity (&nostate, &ent->s, &buf, false, true);
		}

		e++;
		ent = EDICT_NUM(e);
	}

	MSG_WriteShort (&buf, 0);		// end of packetentities

	// now add the accumulated multicast information
	SZ_Write (&buf, svs.demo_multicast.data, svs.demo_multicast.cursize);
	SZ_Clear (&svs.demo_multicast);

	// now write the entire message to the file, prefixed by the length
	len = LittleLong (buf.cursize);
	fwrite (&len, 4, 1, svs.demofile);
	fwrite (buf.data, buf.cursize, 1, svs.demofile);
}
