mirror of
https://github.com/Shpoike/Quakespasm.git
synced 2024-11-14 00:50:38 +00:00
26c1b63c00
If an entity is visilbe from MAX_ENT_LEAFS (32) or more leafs, don't try to vis-cull it, just send it to the client. This should completely eliminate any flickering entities, no matter how many leafs they're visible from. This could potentially increase packet sizes a bit.. but ent->num_leafs == 32 never happens in id1 epsiode 1, so it will not cause any increase on those maps. hip1m1 has one entity (a rotator) that is caught by this change and will be always sent. see e.g. http://forums.inside3d.com/viewtopic.php?f=1&t=5554 http://www.celephais.net/board/view_thread.php?id=60452&start=1235 git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1069 af15c1b1-3010-417e-b628-4374ebc0bcbd
1435 lines
35 KiB
C
1435 lines
35 KiB
C
/*
|
|
Copyright (C) 1996-2001 Id Software, Inc.
|
|
Copyright (C) 2002-2009 John Fitzgibbons and others
|
|
Copyright (C) 2010-2014 QuakeSpasm developers
|
|
|
|
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.
|
|
|
|
*/
|
|
// sv_main.c -- server main program
|
|
|
|
#include "quakedef.h"
|
|
|
|
server_t sv;
|
|
server_static_t svs;
|
|
|
|
static char localmodels[MAX_MODELS][8]; // inline model names for precache
|
|
|
|
int sv_protocol = PROTOCOL_FITZQUAKE; //johnfitz
|
|
|
|
extern qboolean pr_alpha_supported; //johnfitz
|
|
|
|
//============================================================================
|
|
|
|
/*
|
|
===============
|
|
SV_Protocol_f
|
|
===============
|
|
*/
|
|
void SV_Protocol_f (void)
|
|
{
|
|
int i;
|
|
|
|
switch (Cmd_Argc())
|
|
{
|
|
case 1:
|
|
Con_Printf ("\"sv_protocol\" is \"%i\"\n", sv_protocol);
|
|
break;
|
|
case 2:
|
|
i = atoi(Cmd_Argv(1));
|
|
if (i != PROTOCOL_NETQUAKE && i != PROTOCOL_FITZQUAKE)
|
|
Con_Printf ("sv_protocol must be %i or %i\n", PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE);
|
|
else
|
|
{
|
|
sv_protocol = i;
|
|
if (sv.active)
|
|
Con_Printf ("changes will not take effect until the next level load.\n");
|
|
}
|
|
break;
|
|
default:
|
|
Con_SafePrintf ("usage: sv_protocol <protocol>\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
SV_Init
|
|
===============
|
|
*/
|
|
void SV_Init (void)
|
|
{
|
|
int i;
|
|
extern cvar_t sv_maxvelocity;
|
|
extern cvar_t sv_gravity;
|
|
extern cvar_t sv_nostep;
|
|
extern cvar_t sv_freezenonclients;
|
|
extern cvar_t sv_friction;
|
|
extern cvar_t sv_edgefriction;
|
|
extern cvar_t sv_stopspeed;
|
|
extern cvar_t sv_maxspeed;
|
|
extern cvar_t sv_accelerate;
|
|
extern cvar_t sv_idealpitchscale;
|
|
extern cvar_t sv_aim;
|
|
extern cvar_t sv_altnoclip; //johnfitz
|
|
|
|
Cvar_RegisterVariable (&sv_maxvelocity);
|
|
Cvar_RegisterVariable (&sv_gravity);
|
|
Cvar_RegisterVariable (&sv_friction);
|
|
Cvar_SetCallback (&sv_gravity, Host_Callback_Notify);
|
|
Cvar_SetCallback (&sv_friction, Host_Callback_Notify);
|
|
Cvar_RegisterVariable (&sv_edgefriction);
|
|
Cvar_RegisterVariable (&sv_stopspeed);
|
|
Cvar_RegisterVariable (&sv_maxspeed);
|
|
Cvar_SetCallback (&sv_maxspeed, Host_Callback_Notify);
|
|
Cvar_RegisterVariable (&sv_accelerate);
|
|
Cvar_RegisterVariable (&sv_idealpitchscale);
|
|
Cvar_RegisterVariable (&sv_aim);
|
|
Cvar_RegisterVariable (&sv_nostep);
|
|
Cvar_RegisterVariable (&sv_freezenonclients);
|
|
Cvar_RegisterVariable (&sv_altnoclip); //johnfitz
|
|
|
|
Cmd_AddCommand ("sv_protocol", &SV_Protocol_f); //johnfitz
|
|
|
|
for (i=0 ; i<MAX_MODELS ; i++)
|
|
sprintf (localmodels[i], "*%i", i);
|
|
}
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
EVENT MESSAGES
|
|
|
|
=============================================================================
|
|
*/
|
|
|
|
/*
|
|
==================
|
|
SV_StartParticle
|
|
|
|
Make sure the event gets sent to all clients
|
|
==================
|
|
*/
|
|
void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count)
|
|
{
|
|
int i, v;
|
|
|
|
if (sv.datagram.cursize > MAX_DATAGRAM-16)
|
|
return;
|
|
MSG_WriteByte (&sv.datagram, svc_particle);
|
|
MSG_WriteCoord (&sv.datagram, org[0]);
|
|
MSG_WriteCoord (&sv.datagram, org[1]);
|
|
MSG_WriteCoord (&sv.datagram, org[2]);
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
v = dir[i]*16;
|
|
if (v > 127)
|
|
v = 127;
|
|
else if (v < -128)
|
|
v = -128;
|
|
MSG_WriteChar (&sv.datagram, v);
|
|
}
|
|
MSG_WriteByte (&sv.datagram, count);
|
|
MSG_WriteByte (&sv.datagram, color);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
SV_StartSound
|
|
|
|
Each entity can have eight independant sound sources, like voice,
|
|
weapon, feet, etc.
|
|
|
|
Channel 0 is an auto-allocate channel, the others override anything
|
|
allready running on that entity/channel pair.
|
|
|
|
An attenuation of 0 will play full volume everywhere in the level.
|
|
Larger attenuations will drop off. (max 4 attenuation)
|
|
|
|
==================
|
|
*/
|
|
void SV_StartSound (edict_t *entity, int channel, const char *sample, int volume, float attenuation)
|
|
{
|
|
int sound_num, ent;
|
|
int i, field_mask;
|
|
|
|
if (volume < 0 || volume > 255)
|
|
Host_Error ("SV_StartSound: volume = %i", volume);
|
|
|
|
if (attenuation < 0 || attenuation > 4)
|
|
Host_Error ("SV_StartSound: attenuation = %f", attenuation);
|
|
|
|
if (channel < 0 || channel > 7)
|
|
Host_Error ("SV_StartSound: channel = %i", channel);
|
|
|
|
if (sv.datagram.cursize > MAX_DATAGRAM-16)
|
|
return;
|
|
|
|
// find precache number for sound
|
|
for (sound_num = 1; sound_num < MAX_SOUNDS && sv.sound_precache[sound_num]; sound_num++)
|
|
{
|
|
if (!strcmp(sample, sv.sound_precache[sound_num]))
|
|
break;
|
|
}
|
|
|
|
if (sound_num == MAX_SOUNDS || !sv.sound_precache[sound_num])
|
|
{
|
|
Con_Printf ("SV_StartSound: %s not precacheed\n", sample);
|
|
return;
|
|
}
|
|
|
|
ent = NUM_FOR_EDICT(entity);
|
|
|
|
field_mask = 0;
|
|
if (volume != DEFAULT_SOUND_PACKET_VOLUME)
|
|
field_mask |= SND_VOLUME;
|
|
if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)
|
|
field_mask |= SND_ATTENUATION;
|
|
|
|
//johnfitz -- PROTOCOL_FITZQUAKE
|
|
if (ent >= 8192)
|
|
{
|
|
if (sv.protocol == PROTOCOL_NETQUAKE)
|
|
return; //don't send any info protocol can't support
|
|
else
|
|
field_mask |= SND_LARGEENTITY;
|
|
}
|
|
if (sound_num >= 256 || channel >= 8)
|
|
{
|
|
if (sv.protocol == PROTOCOL_NETQUAKE)
|
|
return; //don't send any info protocol can't support
|
|
else
|
|
field_mask |= SND_LARGESOUND;
|
|
}
|
|
//johnfitz
|
|
|
|
// directed messages go only to the entity the are targeted on
|
|
MSG_WriteByte (&sv.datagram, svc_sound);
|
|
MSG_WriteByte (&sv.datagram, field_mask);
|
|
if (field_mask & SND_VOLUME)
|
|
MSG_WriteByte (&sv.datagram, volume);
|
|
if (field_mask & SND_ATTENUATION)
|
|
MSG_WriteByte (&sv.datagram, attenuation*64);
|
|
|
|
//johnfitz -- PROTOCOL_FITZQUAKE
|
|
if (field_mask & SND_LARGEENTITY)
|
|
{
|
|
MSG_WriteShort (&sv.datagram, ent);
|
|
MSG_WriteByte (&sv.datagram, channel);
|
|
}
|
|
else
|
|
MSG_WriteShort (&sv.datagram, (ent<<3) | channel);
|
|
if (field_mask & SND_LARGESOUND)
|
|
MSG_WriteShort (&sv.datagram, sound_num);
|
|
else
|
|
MSG_WriteByte (&sv.datagram, sound_num);
|
|
//johnfitz
|
|
|
|
for (i = 0; i < 3; i++)
|
|
MSG_WriteCoord (&sv.datagram, entity->v.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i]));
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
CLIENT SPAWNING
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
/*
|
|
================
|
|
SV_SendServerinfo
|
|
|
|
Sends the first message from the server to a connected client.
|
|
This will be sent on the initial connection and upon each server load.
|
|
================
|
|
*/
|
|
void SV_SendServerinfo (client_t *client)
|
|
{
|
|
const char **s;
|
|
char message[2048];
|
|
int i; //johnfitz
|
|
|
|
MSG_WriteByte (&client->message, svc_print);
|
|
sprintf (message, "%c\nFITZQUAKE %1.2f SERVER (%i CRC)\n", 2, FITZQUAKE_VERSION, pr_crc); //johnfitz -- include fitzquake version
|
|
MSG_WriteString (&client->message,message);
|
|
|
|
MSG_WriteByte (&client->message, svc_serverinfo);
|
|
MSG_WriteLong (&client->message, sv.protocol); //johnfitz -- sv.protocol instead of PROTOCOL_VERSION
|
|
MSG_WriteByte (&client->message, svs.maxclients);
|
|
|
|
if (!coop.value && deathmatch.value)
|
|
MSG_WriteByte (&client->message, GAME_DEATHMATCH);
|
|
else
|
|
MSG_WriteByte (&client->message, GAME_COOP);
|
|
|
|
MSG_WriteString (&client->message, PR_GetString(sv.edicts->v.message));
|
|
|
|
//johnfitz -- only send the first 256 model and sound precaches if protocol is 15
|
|
for (i=0,s = sv.model_precache+1 ; *s; s++,i++)
|
|
if (sv.protocol != PROTOCOL_NETQUAKE || i < 256)
|
|
MSG_WriteString (&client->message, *s);
|
|
MSG_WriteByte (&client->message, 0);
|
|
|
|
for (i=0,s = sv.sound_precache+1 ; *s ; s++,i++)
|
|
if (sv.protocol != PROTOCOL_NETQUAKE || i < 256)
|
|
MSG_WriteString (&client->message, *s);
|
|
MSG_WriteByte (&client->message, 0);
|
|
//johnfitz
|
|
|
|
// send music
|
|
MSG_WriteByte (&client->message, svc_cdtrack);
|
|
MSG_WriteByte (&client->message, sv.edicts->v.sounds);
|
|
MSG_WriteByte (&client->message, sv.edicts->v.sounds);
|
|
|
|
// set view
|
|
MSG_WriteByte (&client->message, svc_setview);
|
|
MSG_WriteShort (&client->message, NUM_FOR_EDICT(client->edict));
|
|
|
|
MSG_WriteByte (&client->message, svc_signonnum);
|
|
MSG_WriteByte (&client->message, 1);
|
|
|
|
client->sendsignon = true;
|
|
client->spawned = false; // need prespawn, spawn, etc
|
|
}
|
|
|
|
/*
|
|
================
|
|
SV_ConnectClient
|
|
|
|
Initializes a client_t for a new net connection. This will only be called
|
|
once for a player each game, not once for each level change.
|
|
================
|
|
*/
|
|
void SV_ConnectClient (int clientnum)
|
|
{
|
|
edict_t *ent;
|
|
client_t *client;
|
|
int edictnum;
|
|
struct qsocket_s *netconnection;
|
|
int i;
|
|
float spawn_parms[NUM_SPAWN_PARMS];
|
|
|
|
client = svs.clients + clientnum;
|
|
|
|
Con_DPrintf ("Client %s connected\n", NET_QSocketGetAddressString(client->netconnection));
|
|
|
|
edictnum = clientnum+1;
|
|
|
|
ent = EDICT_NUM(edictnum);
|
|
|
|
// set up the client_t
|
|
netconnection = client->netconnection;
|
|
|
|
if (sv.loadgame)
|
|
memcpy (spawn_parms, client->spawn_parms, sizeof(spawn_parms));
|
|
memset (client, 0, sizeof(*client));
|
|
client->netconnection = netconnection;
|
|
|
|
strcpy (client->name, "unconnected");
|
|
client->active = true;
|
|
client->spawned = false;
|
|
client->edict = ent;
|
|
client->message.data = client->msgbuf;
|
|
client->message.maxsize = sizeof(client->msgbuf);
|
|
client->message.allowoverflow = true; // we can catch it
|
|
|
|
if (sv.loadgame)
|
|
memcpy (client->spawn_parms, spawn_parms, sizeof(spawn_parms));
|
|
else
|
|
{
|
|
// call the progs to get default spawn parms for the new client
|
|
PR_ExecuteProgram (pr_global_struct->SetNewParms);
|
|
for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
|
|
client->spawn_parms[i] = (&pr_global_struct->parm1)[i];
|
|
}
|
|
|
|
SV_SendServerinfo (client);
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
SV_CheckForNewClients
|
|
|
|
===================
|
|
*/
|
|
void SV_CheckForNewClients (void)
|
|
{
|
|
struct qsocket_s *ret;
|
|
int i;
|
|
|
|
//
|
|
// check for new connections
|
|
//
|
|
while (1)
|
|
{
|
|
ret = NET_CheckNewConnections ();
|
|
if (!ret)
|
|
break;
|
|
|
|
//
|
|
// init a new client structure
|
|
//
|
|
for (i=0 ; i<svs.maxclients ; i++)
|
|
if (!svs.clients[i].active)
|
|
break;
|
|
if (i == svs.maxclients)
|
|
Sys_Error ("Host_CheckForNewClients: no free clients");
|
|
|
|
svs.clients[i].netconnection = ret;
|
|
SV_ConnectClient (i);
|
|
|
|
net_activeconnections++;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
FRAME UPDATES
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
==================
|
|
SV_ClearDatagram
|
|
|
|
==================
|
|
*/
|
|
void SV_ClearDatagram (void)
|
|
{
|
|
SZ_Clear (&sv.datagram);
|
|
}
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
The PVS must include a small area around the client to allow head bobbing
|
|
or other small motion on the client side. Otherwise, a bob might cause an
|
|
entity that should be visible to not show up, especially when the bob
|
|
crosses a waterline.
|
|
|
|
=============================================================================
|
|
*/
|
|
|
|
int fatbytes;
|
|
byte fatpvs[MAX_MAP_LEAFS/8];
|
|
|
|
void SV_AddToFatPVS (vec3_t org, mnode_t *node, qmodel_t *worldmodel) //johnfitz -- added worldmodel as a parameter
|
|
{
|
|
int i;
|
|
byte *pvs;
|
|
mplane_t *plane;
|
|
float d;
|
|
|
|
while (1)
|
|
{
|
|
// if this is a leaf, accumulate the pvs bits
|
|
if (node->contents < 0)
|
|
{
|
|
if (node->contents != CONTENTS_SOLID)
|
|
{
|
|
pvs = Mod_LeafPVS ( (mleaf_t *)node, worldmodel); //johnfitz -- worldmodel as a parameter
|
|
for (i=0 ; i<fatbytes ; i++)
|
|
fatpvs[i] |= pvs[i];
|
|
}
|
|
return;
|
|
}
|
|
|
|
plane = node->plane;
|
|
d = DotProduct (org, plane->normal) - plane->dist;
|
|
if (d > 8)
|
|
node = node->children[0];
|
|
else if (d < -8)
|
|
node = node->children[1];
|
|
else
|
|
{ // go down both
|
|
SV_AddToFatPVS (org, node->children[0], worldmodel); //johnfitz -- worldmodel as a parameter
|
|
node = node->children[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
SV_FatPVS
|
|
|
|
Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the
|
|
given point.
|
|
=============
|
|
*/
|
|
byte *SV_FatPVS (vec3_t org, qmodel_t *worldmodel) //johnfitz -- added worldmodel as a parameter
|
|
{
|
|
fatbytes = (worldmodel->numleafs+31)>>3;
|
|
Q_memset (fatpvs, 0, fatbytes);
|
|
SV_AddToFatPVS (org, worldmodel->nodes, worldmodel); //johnfitz -- worldmodel as a parameter
|
|
return fatpvs;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
SV_VisibleToClient -- johnfitz
|
|
|
|
PVS test encapsulated in a nice function
|
|
=============
|
|
*/
|
|
qboolean SV_VisibleToClient (edict_t *client, edict_t *test, qmodel_t *worldmodel)
|
|
{
|
|
byte *pvs;
|
|
vec3_t org;
|
|
int i;
|
|
|
|
VectorAdd (client->v.origin, client->v.view_ofs, org);
|
|
pvs = SV_FatPVS (org, worldmodel);
|
|
|
|
for (i=0 ; i < test->num_leafs ; i++)
|
|
if (pvs[test->leafnums[i] >> 3] & (1 << (test->leafnums[i]&7) ))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
/*
|
|
=============
|
|
SV_WriteEntitiesToClient
|
|
|
|
=============
|
|
*/
|
|
void SV_WriteEntitiesToClient (edict_t *clent, sizebuf_t *msg)
|
|
{
|
|
int e, i;
|
|
int bits;
|
|
byte *pvs;
|
|
vec3_t org;
|
|
float miss;
|
|
edict_t *ent;
|
|
|
|
// find the client's PVS
|
|
VectorAdd (clent->v.origin, clent->v.view_ofs, org);
|
|
pvs = SV_FatPVS (org, sv.worldmodel);
|
|
|
|
// send over all entities (excpet the client) that touch the pvs
|
|
ent = NEXT_EDICT(sv.edicts);
|
|
for (e=1 ; e<sv.num_edicts ; e++, ent = NEXT_EDICT(ent))
|
|
{
|
|
|
|
if (ent != clent) // clent is ALLWAYS sent
|
|
{
|
|
// ignore ents without visible models
|
|
if (!ent->v.modelindex || !PR_GetString(ent->v.model)[0])
|
|
continue;
|
|
|
|
//johnfitz -- don't send model>255 entities if protocol is 15
|
|
if (sv.protocol == PROTOCOL_NETQUAKE && (int)ent->v.modelindex & 0xFF00)
|
|
continue;
|
|
|
|
// ignore if not touching a PV leaf
|
|
for (i=0 ; i < ent->num_leafs ; i++)
|
|
if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) ))
|
|
break;
|
|
|
|
// ericw -- added ent->num_leafs < MAX_ENT_LEAFS condition.
|
|
//
|
|
// if ent->num_leafs == MAX_ENT_LEAFS, the ent is visible from too many leafs
|
|
// for us to say whether it's in the PVS, so don't try to vis cull it.
|
|
// this commonly happens with rotators, because they often have huge bboxes
|
|
// spanning the entire map, or really tall lifts, etc.
|
|
if (i == ent->num_leafs && ent->num_leafs < MAX_ENT_LEAFS)
|
|
continue; // not visible
|
|
}
|
|
|
|
//johnfitz -- max size for protocol 15 is 18 bytes, not 16 as originally
|
|
//assumed here. And, for protocol 85 the max size is actually 24 bytes.
|
|
if (msg->cursize + 24 > msg->maxsize)
|
|
{
|
|
//johnfitz -- less spammy overflow message
|
|
if (!dev_overflows.packetsize || dev_overflows.packetsize + CONSOLE_RESPAM_TIME < realtime )
|
|
{
|
|
Con_Printf ("Packet overflow!\n");
|
|
dev_overflows.packetsize = realtime;
|
|
}
|
|
goto stats;
|
|
//johnfitz
|
|
}
|
|
|
|
// send an update
|
|
bits = 0;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
miss = ent->v.origin[i] - ent->baseline.origin[i];
|
|
if ( miss < -0.1 || miss > 0.1 )
|
|
bits |= U_ORIGIN1<<i;
|
|
}
|
|
|
|
if ( ent->v.angles[0] != ent->baseline.angles[0] )
|
|
bits |= U_ANGLE1;
|
|
|
|
if ( ent->v.angles[1] != ent->baseline.angles[1] )
|
|
bits |= U_ANGLE2;
|
|
|
|
if ( ent->v.angles[2] != ent->baseline.angles[2] )
|
|
bits |= U_ANGLE3;
|
|
|
|
if (ent->v.movetype == MOVETYPE_STEP)
|
|
bits |= U_STEP; // don't mess up the step animation
|
|
|
|
if (ent->baseline.colormap != ent->v.colormap)
|
|
bits |= U_COLORMAP;
|
|
|
|
if (ent->baseline.skin != ent->v.skin)
|
|
bits |= U_SKIN;
|
|
|
|
if (ent->baseline.frame != ent->v.frame)
|
|
bits |= U_FRAME;
|
|
|
|
if (ent->baseline.effects != ent->v.effects)
|
|
bits |= U_EFFECTS;
|
|
|
|
if (ent->baseline.modelindex != ent->v.modelindex)
|
|
bits |= U_MODEL;
|
|
|
|
//johnfitz -- alpha
|
|
if (pr_alpha_supported)
|
|
{
|
|
// TODO: find a cleaner place to put this code
|
|
eval_t *val;
|
|
val = GetEdictFieldValue(ent, "alpha");
|
|
if (val)
|
|
ent->alpha = ENTALPHA_ENCODE(val->_float);
|
|
}
|
|
|
|
//don't send invisible entities unless they have effects
|
|
if (ent->alpha == ENTALPHA_ZERO && !ent->v.effects)
|
|
continue;
|
|
//johnfitz
|
|
|
|
//johnfitz -- PROTOCOL_FITZQUAKE
|
|
if (sv.protocol != PROTOCOL_NETQUAKE)
|
|
{
|
|
|
|
if (ent->baseline.alpha != ent->alpha) bits |= U_ALPHA;
|
|
if (bits & U_FRAME && (int)ent->v.frame & 0xFF00) bits |= U_FRAME2;
|
|
if (bits & U_MODEL && (int)ent->v.modelindex & 0xFF00) bits |= U_MODEL2;
|
|
if (ent->sendinterval) bits |= U_LERPFINISH;
|
|
if (bits >= 65536) bits |= U_EXTEND1;
|
|
if (bits >= 16777216) bits |= U_EXTEND2;
|
|
}
|
|
//johnfitz
|
|
|
|
if (e >= 256)
|
|
bits |= U_LONGENTITY;
|
|
|
|
if (bits >= 256)
|
|
bits |= U_MOREBITS;
|
|
|
|
//
|
|
// write the message
|
|
//
|
|
MSG_WriteByte (msg, bits | U_SIGNAL);
|
|
|
|
if (bits & U_MOREBITS)
|
|
MSG_WriteByte (msg, bits>>8);
|
|
|
|
//johnfitz -- PROTOCOL_FITZQUAKE
|
|
if (bits & U_EXTEND1)
|
|
MSG_WriteByte(msg, bits>>16);
|
|
if (bits & U_EXTEND2)
|
|
MSG_WriteByte(msg, bits>>24);
|
|
//johnfitz
|
|
|
|
if (bits & U_LONGENTITY)
|
|
MSG_WriteShort (msg,e);
|
|
else
|
|
MSG_WriteByte (msg,e);
|
|
|
|
if (bits & U_MODEL)
|
|
MSG_WriteByte (msg, ent->v.modelindex);
|
|
if (bits & U_FRAME)
|
|
MSG_WriteByte (msg, ent->v.frame);
|
|
if (bits & U_COLORMAP)
|
|
MSG_WriteByte (msg, ent->v.colormap);
|
|
if (bits & U_SKIN)
|
|
MSG_WriteByte (msg, ent->v.skin);
|
|
if (bits & U_EFFECTS)
|
|
MSG_WriteByte (msg, ent->v.effects);
|
|
if (bits & U_ORIGIN1)
|
|
MSG_WriteCoord (msg, ent->v.origin[0]);
|
|
if (bits & U_ANGLE1)
|
|
MSG_WriteAngle(msg, ent->v.angles[0]);
|
|
if (bits & U_ORIGIN2)
|
|
MSG_WriteCoord (msg, ent->v.origin[1]);
|
|
if (bits & U_ANGLE2)
|
|
MSG_WriteAngle(msg, ent->v.angles[1]);
|
|
if (bits & U_ORIGIN3)
|
|
MSG_WriteCoord (msg, ent->v.origin[2]);
|
|
if (bits & U_ANGLE3)
|
|
MSG_WriteAngle(msg, ent->v.angles[2]);
|
|
|
|
//johnfitz -- PROTOCOL_FITZQUAKE
|
|
if (bits & U_ALPHA)
|
|
MSG_WriteByte(msg, ent->alpha);
|
|
if (bits & U_FRAME2)
|
|
MSG_WriteByte(msg, (int)ent->v.frame >> 8);
|
|
if (bits & U_MODEL2)
|
|
MSG_WriteByte(msg, (int)ent->v.modelindex >> 8);
|
|
if (bits & U_LERPFINISH)
|
|
MSG_WriteByte(msg, (byte)(Q_rint((ent->v.nextthink-sv.time)*255)));
|
|
//johnfitz
|
|
}
|
|
|
|
//johnfitz -- devstats
|
|
stats:
|
|
if (msg->cursize > 1024 && dev_peakstats.packetsize <= 1024)
|
|
Con_Warning ("%i byte packet exceeds standard limit of 1024.\n", msg->cursize);
|
|
dev_stats.packetsize = msg->cursize;
|
|
dev_peakstats.packetsize = q_max(msg->cursize, dev_peakstats.packetsize);
|
|
//johnfitz
|
|
}
|
|
|
|
/*
|
|
=============
|
|
SV_CleanupEnts
|
|
|
|
=============
|
|
*/
|
|
void SV_CleanupEnts (void)
|
|
{
|
|
int e;
|
|
edict_t *ent;
|
|
|
|
ent = NEXT_EDICT(sv.edicts);
|
|
for (e=1 ; e<sv.num_edicts ; e++, ent = NEXT_EDICT(ent))
|
|
{
|
|
ent->v.effects = (int)ent->v.effects & ~EF_MUZZLEFLASH;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
SV_WriteClientdataToMessage
|
|
|
|
==================
|
|
*/
|
|
void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg)
|
|
{
|
|
int bits;
|
|
int i;
|
|
edict_t *other;
|
|
int items;
|
|
eval_t *val;
|
|
|
|
//
|
|
// send a damage message
|
|
//
|
|
if (ent->v.dmg_take || ent->v.dmg_save)
|
|
{
|
|
other = PROG_TO_EDICT(ent->v.dmg_inflictor);
|
|
MSG_WriteByte (msg, svc_damage);
|
|
MSG_WriteByte (msg, ent->v.dmg_save);
|
|
MSG_WriteByte (msg, ent->v.dmg_take);
|
|
for (i=0 ; i<3 ; i++)
|
|
MSG_WriteCoord (msg, other->v.origin[i] + 0.5*(other->v.mins[i] + other->v.maxs[i]));
|
|
|
|
ent->v.dmg_take = 0;
|
|
ent->v.dmg_save = 0;
|
|
}
|
|
|
|
//
|
|
// send the current viewpos offset from the view entity
|
|
//
|
|
SV_SetIdealPitch (); // how much to look up / down ideally
|
|
|
|
// a fixangle might get lost in a dropped packet. Oh well.
|
|
if ( ent->v.fixangle )
|
|
{
|
|
MSG_WriteByte (msg, svc_setangle);
|
|
for (i=0 ; i < 3 ; i++)
|
|
MSG_WriteAngle (msg, ent->v.angles[i] );
|
|
ent->v.fixangle = 0;
|
|
}
|
|
|
|
bits = 0;
|
|
|
|
if (ent->v.view_ofs[2] != DEFAULT_VIEWHEIGHT)
|
|
bits |= SU_VIEWHEIGHT;
|
|
|
|
if (ent->v.idealpitch)
|
|
bits |= SU_IDEALPITCH;
|
|
|
|
// stuff the sigil bits into the high bits of items for sbar, or else
|
|
// mix in items2
|
|
val = GetEdictFieldValue(ent, "items2");
|
|
|
|
if (val)
|
|
items = (int)ent->v.items | ((int)val->_float << 23);
|
|
else
|
|
items = (int)ent->v.items | ((int)pr_global_struct->serverflags << 28);
|
|
|
|
bits |= SU_ITEMS;
|
|
|
|
if ( (int)ent->v.flags & FL_ONGROUND)
|
|
bits |= SU_ONGROUND;
|
|
|
|
if ( ent->v.waterlevel >= 2)
|
|
bits |= SU_INWATER;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
if (ent->v.punchangle[i])
|
|
bits |= (SU_PUNCH1<<i);
|
|
if (ent->v.velocity[i])
|
|
bits |= (SU_VELOCITY1<<i);
|
|
}
|
|
|
|
if (ent->v.weaponframe)
|
|
bits |= SU_WEAPONFRAME;
|
|
|
|
if (ent->v.armorvalue)
|
|
bits |= SU_ARMOR;
|
|
|
|
// if (ent->v.weapon)
|
|
bits |= SU_WEAPON;
|
|
|
|
//johnfitz -- PROTOCOL_FITZQUAKE
|
|
if (sv.protocol != PROTOCOL_NETQUAKE)
|
|
{
|
|
if (bits & SU_WEAPON && SV_ModelIndex(PR_GetString(ent->v.weaponmodel)) & 0xFF00) bits |= SU_WEAPON2;
|
|
if ((int)ent->v.armorvalue & 0xFF00) bits |= SU_ARMOR2;
|
|
if ((int)ent->v.currentammo & 0xFF00) bits |= SU_AMMO2;
|
|
if ((int)ent->v.ammo_shells & 0xFF00) bits |= SU_SHELLS2;
|
|
if ((int)ent->v.ammo_nails & 0xFF00) bits |= SU_NAILS2;
|
|
if ((int)ent->v.ammo_rockets & 0xFF00) bits |= SU_ROCKETS2;
|
|
if ((int)ent->v.ammo_cells & 0xFF00) bits |= SU_CELLS2;
|
|
if (bits & SU_WEAPONFRAME && (int)ent->v.weaponframe & 0xFF00) bits |= SU_WEAPONFRAME2;
|
|
if (bits & SU_WEAPON && ent->alpha != ENTALPHA_DEFAULT) bits |= SU_WEAPONALPHA; //for now, weaponalpha = client entity alpha
|
|
if (bits >= 65536) bits |= SU_EXTEND1;
|
|
if (bits >= 16777216) bits |= SU_EXTEND2;
|
|
}
|
|
//johnfitz
|
|
|
|
// send the data
|
|
|
|
MSG_WriteByte (msg, svc_clientdata);
|
|
MSG_WriteShort (msg, bits);
|
|
|
|
//johnfitz -- PROTOCOL_FITZQUAKE
|
|
if (bits & SU_EXTEND1) MSG_WriteByte(msg, bits>>16);
|
|
if (bits & SU_EXTEND2) MSG_WriteByte(msg, bits>>24);
|
|
//johnfitz
|
|
|
|
if (bits & SU_VIEWHEIGHT)
|
|
MSG_WriteChar (msg, ent->v.view_ofs[2]);
|
|
|
|
if (bits & SU_IDEALPITCH)
|
|
MSG_WriteChar (msg, ent->v.idealpitch);
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
if (bits & (SU_PUNCH1<<i))
|
|
MSG_WriteChar (msg, ent->v.punchangle[i]);
|
|
if (bits & (SU_VELOCITY1<<i))
|
|
MSG_WriteChar (msg, ent->v.velocity[i]/16);
|
|
}
|
|
|
|
// [always sent] if (bits & SU_ITEMS)
|
|
MSG_WriteLong (msg, items);
|
|
|
|
if (bits & SU_WEAPONFRAME)
|
|
MSG_WriteByte (msg, ent->v.weaponframe);
|
|
if (bits & SU_ARMOR)
|
|
MSG_WriteByte (msg, ent->v.armorvalue);
|
|
if (bits & SU_WEAPON)
|
|
MSG_WriteByte (msg, SV_ModelIndex(PR_GetString(ent->v.weaponmodel)));
|
|
|
|
MSG_WriteShort (msg, ent->v.health);
|
|
MSG_WriteByte (msg, ent->v.currentammo);
|
|
MSG_WriteByte (msg, ent->v.ammo_shells);
|
|
MSG_WriteByte (msg, ent->v.ammo_nails);
|
|
MSG_WriteByte (msg, ent->v.ammo_rockets);
|
|
MSG_WriteByte (msg, ent->v.ammo_cells);
|
|
|
|
if (standard_quake)
|
|
{
|
|
MSG_WriteByte (msg, ent->v.weapon);
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<32;i++)
|
|
{
|
|
if ( ((int)ent->v.weapon) & (1<<i) )
|
|
{
|
|
MSG_WriteByte (msg, i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//johnfitz -- PROTOCOL_FITZQUAKE
|
|
if (bits & SU_WEAPON2)
|
|
MSG_WriteByte (msg, SV_ModelIndex(PR_GetString(ent->v.weaponmodel)) >> 8);
|
|
if (bits & SU_ARMOR2)
|
|
MSG_WriteByte (msg, (int)ent->v.armorvalue >> 8);
|
|
if (bits & SU_AMMO2)
|
|
MSG_WriteByte (msg, (int)ent->v.currentammo >> 8);
|
|
if (bits & SU_SHELLS2)
|
|
MSG_WriteByte (msg, (int)ent->v.ammo_shells >> 8);
|
|
if (bits & SU_NAILS2)
|
|
MSG_WriteByte (msg, (int)ent->v.ammo_nails >> 8);
|
|
if (bits & SU_ROCKETS2)
|
|
MSG_WriteByte (msg, (int)ent->v.ammo_rockets >> 8);
|
|
if (bits & SU_CELLS2)
|
|
MSG_WriteByte (msg, (int)ent->v.ammo_cells >> 8);
|
|
if (bits & SU_WEAPONFRAME2)
|
|
MSG_WriteByte (msg, (int)ent->v.weaponframe >> 8);
|
|
if (bits & SU_WEAPONALPHA)
|
|
MSG_WriteByte (msg, ent->alpha); //for now, weaponalpha = client entity alpha
|
|
//johnfitz
|
|
}
|
|
|
|
/*
|
|
=======================
|
|
SV_SendClientDatagram
|
|
=======================
|
|
*/
|
|
qboolean SV_SendClientDatagram (client_t *client)
|
|
{
|
|
byte buf[MAX_DATAGRAM];
|
|
sizebuf_t msg;
|
|
|
|
msg.data = buf;
|
|
msg.maxsize = sizeof(buf);
|
|
msg.cursize = 0;
|
|
|
|
//johnfitz -- if client is nonlocal, use smaller max size so packets aren't fragmented
|
|
if (Q_strcmp(NET_QSocketGetAddressString(client->netconnection), "LOCAL") != 0)
|
|
msg.maxsize = DATAGRAM_MTU;
|
|
//johnfitz
|
|
|
|
MSG_WriteByte (&msg, svc_time);
|
|
MSG_WriteFloat (&msg, sv.time);
|
|
|
|
// add the client specific data to the datagram
|
|
SV_WriteClientdataToMessage (client->edict, &msg);
|
|
|
|
SV_WriteEntitiesToClient (client->edict, &msg);
|
|
|
|
// copy the server datagram if there is space
|
|
if (msg.cursize + sv.datagram.cursize < msg.maxsize)
|
|
SZ_Write (&msg, sv.datagram.data, sv.datagram.cursize);
|
|
|
|
// send the datagram
|
|
if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1)
|
|
{
|
|
SV_DropClient (true);// if the message couldn't send, kick off
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=======================
|
|
SV_UpdateToReliableMessages
|
|
=======================
|
|
*/
|
|
void SV_UpdateToReliableMessages (void)
|
|
{
|
|
int i, j;
|
|
client_t *client;
|
|
|
|
// check for changes to be sent over the reliable streams
|
|
for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
|
|
{
|
|
if (host_client->old_frags != host_client->edict->v.frags)
|
|
{
|
|
for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
|
|
{
|
|
if (!client->active)
|
|
continue;
|
|
MSG_WriteByte (&client->message, svc_updatefrags);
|
|
MSG_WriteByte (&client->message, i);
|
|
MSG_WriteShort (&client->message, host_client->edict->v.frags);
|
|
}
|
|
|
|
host_client->old_frags = host_client->edict->v.frags;
|
|
}
|
|
}
|
|
|
|
for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
|
|
{
|
|
if (!client->active)
|
|
continue;
|
|
SZ_Write (&client->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize);
|
|
}
|
|
|
|
SZ_Clear (&sv.reliable_datagram);
|
|
}
|
|
|
|
|
|
/*
|
|
=======================
|
|
SV_SendNop
|
|
|
|
Send a nop message without trashing or sending the accumulated client
|
|
message buffer
|
|
=======================
|
|
*/
|
|
void SV_SendNop (client_t *client)
|
|
{
|
|
sizebuf_t msg;
|
|
byte buf[4];
|
|
|
|
msg.data = buf;
|
|
msg.maxsize = sizeof(buf);
|
|
msg.cursize = 0;
|
|
|
|
MSG_WriteChar (&msg, svc_nop);
|
|
|
|
if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1)
|
|
SV_DropClient (true); // if the message couldn't send, kick off
|
|
client->last_message = realtime;
|
|
}
|
|
|
|
/*
|
|
=======================
|
|
SV_SendClientMessages
|
|
=======================
|
|
*/
|
|
void SV_SendClientMessages (void)
|
|
{
|
|
int i;
|
|
|
|
// update frags, names, etc
|
|
SV_UpdateToReliableMessages ();
|
|
|
|
// build individual updates
|
|
for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
|
|
{
|
|
if (!host_client->active)
|
|
continue;
|
|
|
|
if (host_client->spawned)
|
|
{
|
|
if (!SV_SendClientDatagram (host_client))
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// the player isn't totally in the game yet
|
|
// send small keepalive messages if too much time has passed
|
|
// send a full message when the next signon stage has been requested
|
|
// some other message data (name changes, etc) may accumulate
|
|
// between signon stages
|
|
if (!host_client->sendsignon)
|
|
{
|
|
if (realtime - host_client->last_message > 5)
|
|
SV_SendNop (host_client);
|
|
continue; // don't send out non-signon messages
|
|
}
|
|
}
|
|
|
|
// check for an overflowed message. Should only happen
|
|
// on a very fucked up connection that backs up a lot, then
|
|
// changes level
|
|
if (host_client->message.overflowed)
|
|
{
|
|
SV_DropClient (true);
|
|
host_client->message.overflowed = false;
|
|
continue;
|
|
}
|
|
|
|
if (host_client->message.cursize || host_client->dropasap)
|
|
{
|
|
if (!NET_CanSendMessage (host_client->netconnection))
|
|
{
|
|
// I_Printf ("can't write\n");
|
|
continue;
|
|
}
|
|
|
|
if (host_client->dropasap)
|
|
SV_DropClient (false); // went to another level
|
|
else
|
|
{
|
|
if (NET_SendMessage (host_client->netconnection
|
|
, &host_client->message) == -1)
|
|
SV_DropClient (true); // if the message couldn't send, kick off
|
|
SZ_Clear (&host_client->message);
|
|
host_client->last_message = realtime;
|
|
host_client->sendsignon = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// clear muzzle flashes
|
|
SV_CleanupEnts ();
|
|
}
|
|
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
SERVER SPAWNING
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
/*
|
|
================
|
|
SV_ModelIndex
|
|
|
|
================
|
|
*/
|
|
int SV_ModelIndex (const char *name)
|
|
{
|
|
int i;
|
|
|
|
if (!name || !name[0])
|
|
return 0;
|
|
|
|
for (i=0 ; i<MAX_MODELS && sv.model_precache[i] ; i++)
|
|
if (!strcmp(sv.model_precache[i], name))
|
|
return i;
|
|
if (i==MAX_MODELS || !sv.model_precache[i])
|
|
Sys_Error ("SV_ModelIndex: model %s not precached", name);
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
================
|
|
SV_CreateBaseline
|
|
================
|
|
*/
|
|
void SV_CreateBaseline (void)
|
|
{
|
|
int i;
|
|
edict_t *svent;
|
|
int entnum;
|
|
int bits; //johnfitz -- PROTOCOL_FITZQUAKE
|
|
|
|
for (entnum = 0; entnum < sv.num_edicts ; entnum++)
|
|
{
|
|
// get the current server version
|
|
svent = EDICT_NUM(entnum);
|
|
if (svent->free)
|
|
continue;
|
|
if (entnum > svs.maxclients && !svent->v.modelindex)
|
|
continue;
|
|
|
|
//
|
|
// create entity baseline
|
|
//
|
|
VectorCopy (svent->v.origin, svent->baseline.origin);
|
|
VectorCopy (svent->v.angles, svent->baseline.angles);
|
|
svent->baseline.frame = svent->v.frame;
|
|
svent->baseline.skin = svent->v.skin;
|
|
if (entnum > 0 && entnum <= svs.maxclients)
|
|
{
|
|
svent->baseline.colormap = entnum;
|
|
svent->baseline.modelindex = SV_ModelIndex("progs/player.mdl");
|
|
svent->baseline.alpha = ENTALPHA_DEFAULT; //johnfitz -- alpha support
|
|
}
|
|
else
|
|
{
|
|
svent->baseline.colormap = 0;
|
|
svent->baseline.modelindex = SV_ModelIndex(PR_GetString(svent->v.model));
|
|
svent->baseline.alpha = svent->alpha; //johnfitz -- alpha support
|
|
}
|
|
|
|
//johnfitz -- PROTOCOL_FITZQUAKE
|
|
bits = 0;
|
|
if (sv.protocol == PROTOCOL_NETQUAKE) //still want to send baseline in PROTOCOL_NETQUAKE, so reset these values
|
|
{
|
|
if (svent->baseline.modelindex & 0xFF00)
|
|
svent->baseline.modelindex = 0;
|
|
if (svent->baseline.frame & 0xFF00)
|
|
svent->baseline.frame = 0;
|
|
svent->baseline.alpha = ENTALPHA_DEFAULT;
|
|
}
|
|
else //decide which extra data needs to be sent
|
|
{
|
|
if (svent->baseline.modelindex & 0xFF00)
|
|
bits |= B_LARGEMODEL;
|
|
if (svent->baseline.frame & 0xFF00)
|
|
bits |= B_LARGEFRAME;
|
|
if (svent->baseline.alpha != ENTALPHA_DEFAULT)
|
|
bits |= B_ALPHA;
|
|
}
|
|
//johnfitz
|
|
|
|
//
|
|
// add to the message
|
|
//
|
|
//johnfitz -- PROTOCOL_FITZQUAKE
|
|
if (bits)
|
|
MSG_WriteByte (&sv.signon, svc_spawnbaseline2);
|
|
else
|
|
MSG_WriteByte (&sv.signon, svc_spawnbaseline);
|
|
//johnfitz
|
|
|
|
MSG_WriteShort (&sv.signon,entnum);
|
|
|
|
//johnfitz -- PROTOCOL_FITZQUAKE
|
|
if (bits)
|
|
MSG_WriteByte (&sv.signon, bits);
|
|
|
|
if (bits & B_LARGEMODEL)
|
|
MSG_WriteShort (&sv.signon, svent->baseline.modelindex);
|
|
else
|
|
MSG_WriteByte (&sv.signon, svent->baseline.modelindex);
|
|
|
|
if (bits & B_LARGEFRAME)
|
|
MSG_WriteShort (&sv.signon, svent->baseline.frame);
|
|
else
|
|
MSG_WriteByte (&sv.signon, svent->baseline.frame);
|
|
//johnfitz
|
|
|
|
MSG_WriteByte (&sv.signon, svent->baseline.colormap);
|
|
MSG_WriteByte (&sv.signon, svent->baseline.skin);
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
MSG_WriteCoord(&sv.signon, svent->baseline.origin[i]);
|
|
MSG_WriteAngle(&sv.signon, svent->baseline.angles[i]);
|
|
}
|
|
|
|
//johnfitz -- PROTOCOL_FITZQUAKE
|
|
if (bits & B_ALPHA)
|
|
MSG_WriteByte (&sv.signon, svent->baseline.alpha);
|
|
//johnfitz
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
SV_SendReconnect
|
|
|
|
Tell all the clients that the server is changing levels
|
|
================
|
|
*/
|
|
void SV_SendReconnect (void)
|
|
{
|
|
byte data[128];
|
|
sizebuf_t msg;
|
|
|
|
msg.data = data;
|
|
msg.cursize = 0;
|
|
msg.maxsize = sizeof(data);
|
|
|
|
MSG_WriteChar (&msg, svc_stufftext);
|
|
MSG_WriteString (&msg, "reconnect\n");
|
|
NET_SendToAll (&msg, 5.0);
|
|
|
|
if (!isDedicated)
|
|
Cmd_ExecuteString ("reconnect\n", src_command);
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
SV_SaveSpawnparms
|
|
|
|
Grabs the current state of each client for saving across the
|
|
transition to another level
|
|
================
|
|
*/
|
|
void SV_SaveSpawnparms (void)
|
|
{
|
|
int i, j;
|
|
|
|
svs.serverflags = pr_global_struct->serverflags;
|
|
|
|
for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
|
|
{
|
|
if (!host_client->active)
|
|
continue;
|
|
|
|
// call the progs to get default spawn parms for the new client
|
|
pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
|
|
PR_ExecuteProgram (pr_global_struct->SetChangeParms);
|
|
for (j=0 ; j<NUM_SPAWN_PARMS ; j++)
|
|
host_client->spawn_parms[j] = (&pr_global_struct->parm1)[j];
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
SV_SpawnServer
|
|
|
|
This is called at the start of each level
|
|
================
|
|
*/
|
|
extern float scr_centertime_off;
|
|
void SV_SpawnServer (const char *server)
|
|
{
|
|
static char dummy[8] = { 0,0,0,0,0,0,0,0 };
|
|
edict_t *ent;
|
|
int i;
|
|
|
|
// let's not have any servers with no name
|
|
if (hostname.string[0] == 0)
|
|
Cvar_Set ("hostname", "UNNAMED");
|
|
scr_centertime_off = 0;
|
|
|
|
Con_DPrintf ("SpawnServer: %s\n",server);
|
|
svs.changelevel_issued = false; // now safe to issue another
|
|
|
|
//
|
|
// tell all connected clients that we are going to a new level
|
|
//
|
|
if (sv.active)
|
|
{
|
|
SV_SendReconnect ();
|
|
}
|
|
|
|
//
|
|
// make cvars consistant
|
|
//
|
|
if (coop.value)
|
|
Cvar_Set ("deathmatch", "0");
|
|
current_skill = (int)(skill.value + 0.5);
|
|
if (current_skill < 0)
|
|
current_skill = 0;
|
|
if (current_skill > 3)
|
|
current_skill = 3;
|
|
|
|
Cvar_SetValue ("skill", (float)current_skill);
|
|
|
|
//
|
|
// set up the new server
|
|
//
|
|
//memset (&sv, 0, sizeof(sv));
|
|
Host_ClearMemory ();
|
|
|
|
q_strlcpy (sv.name, server, sizeof(sv.name));
|
|
|
|
sv.protocol = sv_protocol; // johnfitz
|
|
|
|
// load progs to get entity field count
|
|
PR_LoadProgs ();
|
|
|
|
// allocate server memory
|
|
/* Host_ClearMemory() called above already cleared the whole sv structure */
|
|
sv.max_edicts = CLAMP (MIN_EDICTS,(int)max_edicts.value,MAX_EDICTS); //johnfitz -- max_edicts cvar
|
|
sv.edicts = (edict_t *) Hunk_AllocName (sv.max_edicts*pr_edict_size, "edicts");
|
|
|
|
sv.datagram.maxsize = sizeof(sv.datagram_buf);
|
|
sv.datagram.cursize = 0;
|
|
sv.datagram.data = sv.datagram_buf;
|
|
|
|
sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf);
|
|
sv.reliable_datagram.cursize = 0;
|
|
sv.reliable_datagram.data = sv.reliable_datagram_buf;
|
|
|
|
sv.signon.maxsize = sizeof(sv.signon_buf);
|
|
sv.signon.cursize = 0;
|
|
sv.signon.data = sv.signon_buf;
|
|
|
|
// leave slots at start for clients only
|
|
sv.num_edicts = svs.maxclients+1;
|
|
for (i=0 ; i<svs.maxclients ; i++)
|
|
{
|
|
ent = EDICT_NUM(i+1);
|
|
svs.clients[i].edict = ent;
|
|
}
|
|
|
|
sv.state = ss_loading;
|
|
sv.paused = false;
|
|
|
|
sv.time = 1.0;
|
|
|
|
q_strlcpy (sv.name, server, sizeof(sv.name));
|
|
q_snprintf (sv.modelname, sizeof(sv.modelname), "maps/%s.bsp", server);
|
|
sv.worldmodel = Mod_ForName (sv.modelname, false);
|
|
if (!sv.worldmodel)
|
|
{
|
|
Con_Printf ("Couldn't spawn server %s\n", sv.modelname);
|
|
sv.active = false;
|
|
return;
|
|
}
|
|
sv.models[1] = sv.worldmodel;
|
|
|
|
//
|
|
// clear world interaction links
|
|
//
|
|
SV_ClearWorld ();
|
|
|
|
sv.sound_precache[0] = dummy;
|
|
sv.model_precache[0] = dummy;
|
|
sv.model_precache[1] = sv.modelname;
|
|
for (i=1 ; i<sv.worldmodel->numsubmodels ; i++)
|
|
{
|
|
sv.model_precache[1+i] = localmodels[i];
|
|
sv.models[i+1] = Mod_ForName (localmodels[i], false);
|
|
}
|
|
|
|
//
|
|
// load the rest of the entities
|
|
//
|
|
ent = EDICT_NUM(0);
|
|
memset (&ent->v, 0, progs->entityfields * 4);
|
|
ent->free = false;
|
|
ent->v.model = PR_SetEngineString(sv.worldmodel->name);
|
|
ent->v.modelindex = 1; // world model
|
|
ent->v.solid = SOLID_BSP;
|
|
ent->v.movetype = MOVETYPE_PUSH;
|
|
|
|
if (coop.value)
|
|
pr_global_struct->coop = coop.value;
|
|
else
|
|
pr_global_struct->deathmatch = deathmatch.value;
|
|
|
|
pr_global_struct->mapname = PR_SetEngineString(sv.name);
|
|
|
|
// serverflags are for cross level information (sigils)
|
|
pr_global_struct->serverflags = svs.serverflags;
|
|
|
|
ED_LoadFromFile (sv.worldmodel->entities);
|
|
|
|
sv.active = true;
|
|
|
|
// all setup is completed, any further precache statements are errors
|
|
sv.state = ss_active;
|
|
|
|
// run two frames to allow everything to settle
|
|
host_frametime = 0.1;
|
|
SV_Physics ();
|
|
SV_Physics ();
|
|
|
|
// create a baseline for more efficient communications
|
|
SV_CreateBaseline ();
|
|
|
|
//johnfitz -- warn if signon buffer larger than standard server can handle
|
|
if (sv.signon.cursize > 8000-2) //max size that will fit into 8000-sized client->message buffer with 2 extra bytes on the end
|
|
Con_Warning ("%i byte signon buffer exceeds standard limit of 7998.\n", sv.signon.cursize);
|
|
//johnfitz
|
|
|
|
// send serverinfo to all connected clients
|
|
for (i=0,host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
|
|
if (host_client->active)
|
|
SV_SendServerinfo (host_client);
|
|
|
|
Con_DPrintf ("Server spawned.\n");
|
|
}
|
|
|