mirror of
https://git.code.sf.net/p/quake/newtree
synced 2024-11-29 23:32:16 +00:00
6f40c97643
sv_minqfversion: Set to the minimum allowable QuakeForge version you want to allow on a server. QF clients prior to current CVS _will_not_work_ with this cvar, as the QF version was not reported previously. sv_maxrate: Change maximum rate algorithm; if sv_maxrate is nonzero, it is the maximum rate allowed by the server. If it is unset, the maximum is 10000 like it used to be. The rate is silently capped, so it won't kick. Cvar fixed: sv_timekick and friends; Now kicks on too little time passed as well. Functionality removed: "Last message repeated X times". It causes too much trouble.
858 lines
20 KiB
C
858 lines
20 KiB
C
/*
|
|
sv_send.c
|
|
|
|
(description)
|
|
|
|
Copyright (C) 1996-1997 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:
|
|
|
|
Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA
|
|
|
|
$Id$
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
#include "server.h"
|
|
#include "crc.h"
|
|
#include "msg.h"
|
|
#include "world.h"
|
|
#include "commdef.h"
|
|
#include "cmd.h"
|
|
#include "sys.h"
|
|
#include "pmove.h"
|
|
#include "bothdefs.h"
|
|
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
|
|
#define CHAN_AUTO 0
|
|
#define CHAN_WEAPON 1
|
|
#define CHAN_VOICE 2
|
|
#define CHAN_ITEM 3
|
|
#define CHAN_BODY 4
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
Con_Printf redirection
|
|
|
|
=============================================================================
|
|
*/
|
|
|
|
char outputbuf[8000];
|
|
|
|
redirect_t sv_redirected;
|
|
|
|
extern cvar_t *sv_phs;
|
|
extern cvar_t *sv_timestamps;
|
|
extern cvar_t *sv_timefmt;
|
|
|
|
/*
|
|
==================
|
|
SV_FlushRedirect
|
|
==================
|
|
*/
|
|
void SV_FlushRedirect (void)
|
|
{
|
|
char send[8000+6];
|
|
|
|
if (sv_redirected == RD_PACKET)
|
|
{
|
|
send[0] = 0xff;
|
|
send[1] = 0xff;
|
|
send[2] = 0xff;
|
|
send[3] = 0xff;
|
|
send[4] = A2C_PRINT;
|
|
memcpy (send+5, outputbuf, strlen(outputbuf)+1);
|
|
|
|
NET_SendPacket (strlen(send)+1, send, net_from);
|
|
}
|
|
else if (sv_redirected == RD_CLIENT)
|
|
{
|
|
ClientReliableWrite_Begin (host_client, svc_print, strlen(outputbuf)+3);
|
|
ClientReliableWrite_Byte (host_client, PRINT_HIGH);
|
|
ClientReliableWrite_String (host_client, outputbuf);
|
|
}
|
|
|
|
// clear it
|
|
outputbuf[0] = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
SV_BeginRedirect
|
|
|
|
Send Con_Printf data to the remote client
|
|
instead of the console
|
|
==================
|
|
*/
|
|
void SV_BeginRedirect (redirect_t rd)
|
|
{
|
|
sv_redirected = rd;
|
|
outputbuf[0] = 0;
|
|
}
|
|
|
|
void SV_EndRedirect (void)
|
|
{
|
|
SV_FlushRedirect ();
|
|
sv_redirected = RD_NONE;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
Con_Printf
|
|
|
|
Handles cursor positioning, line wrapping, etc
|
|
================
|
|
*/
|
|
#define MAXPRINTMSG 4096
|
|
|
|
void Con_Printf (char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
char msg[MAXPRINTMSG];
|
|
char msg2[MAXPRINTMSG];
|
|
char msg3[MAXPRINTMSG];
|
|
|
|
time_t mytime = 0;
|
|
struct tm *local = NULL;
|
|
qboolean timestamps = false;
|
|
|
|
va_start (argptr, fmt);
|
|
vsnprintf (msg, sizeof (msg), fmt, argptr);
|
|
va_end (argptr);
|
|
|
|
if (sv_redirected) { // Add to redirected message
|
|
if (strlen (msg) + strlen (outputbuf) > sizeof (outputbuf) - 1)
|
|
SV_FlushRedirect ();
|
|
strcat (outputbuf, msg);
|
|
return;
|
|
} else { // We want to output to console and maybe logfile
|
|
if (sv_timestamps && sv_timefmt && sv_timefmt->string && sv_timestamps->int_val)
|
|
timestamps = true;
|
|
|
|
if (timestamps) {
|
|
mytime = time (NULL);
|
|
local = localtime (&mytime);
|
|
strftime (msg3, sizeof (msg3), sv_timefmt->string, local);
|
|
|
|
snprintf (msg2, sizeof (msg2), "%s%s", msg3, msg);
|
|
} else {
|
|
snprintf (msg2, sizeof (msg2), "%s", msg);
|
|
}
|
|
|
|
Sys_Printf ("%s", msg2); // also echo to debugging console
|
|
if (sv_logfile)
|
|
Qprintf (sv_logfile, "%s", msg2);
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
Con_DPrintf
|
|
|
|
A Con_Printf that only shows up if the "developer" cvar is set
|
|
================
|
|
*/
|
|
void Con_DPrintf (char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
char msg[MAXPRINTMSG];
|
|
|
|
if (!developer->int_val)
|
|
return;
|
|
|
|
va_start (argptr, fmt);
|
|
vsnprintf (msg, sizeof (msg), fmt, argptr);
|
|
va_end (argptr);
|
|
|
|
Con_Printf ("%s", msg);
|
|
}
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
EVENT MESSAGES
|
|
|
|
=============================================================================
|
|
*/
|
|
|
|
static void SV_PrintToClient(client_t *cl, int level, char *string)
|
|
{
|
|
ClientReliableWrite_Begin (cl, svc_print, strlen(string)+3);
|
|
ClientReliableWrite_Byte (cl, level);
|
|
ClientReliableWrite_String (cl, string);
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
SV_ClientPrintf
|
|
|
|
Sends text across to be displayed if the level passes
|
|
=================
|
|
*/
|
|
void SV_ClientPrintf (client_t *cl, int level, char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
if (level < cl->messagelevel)
|
|
return;
|
|
|
|
va_start (argptr, fmt);
|
|
vsnprintf (string, sizeof(string), fmt, argptr);
|
|
va_end (argptr);
|
|
|
|
SV_PrintToClient(cl, level, string);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
SV_BroadcastPrintf
|
|
|
|
Sends text to all active clients
|
|
=================
|
|
*/
|
|
void SV_BroadcastPrintf (int level, char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
client_t *cl;
|
|
int i;
|
|
|
|
va_start (argptr, fmt);
|
|
vsnprintf (string, sizeof(string), fmt, argptr);
|
|
va_end (argptr);
|
|
|
|
Sys_Printf ("%s", string); // print to the console
|
|
|
|
for (i=0, cl = svs.clients ; i<MAX_CLIENTS ; i++, cl++)
|
|
{
|
|
if (level < cl->messagelevel)
|
|
continue;
|
|
if (!cl->state)
|
|
continue;
|
|
|
|
SV_PrintToClient(cl, level, string);
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
SV_BroadcastCommand
|
|
|
|
Sends text to all active clients
|
|
=================
|
|
*/
|
|
void SV_BroadcastCommand (char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
if (!sv.state)
|
|
return;
|
|
va_start (argptr, fmt);
|
|
vsnprintf (string, sizeof(string), fmt, argptr);
|
|
va_end (argptr);
|
|
|
|
MSG_WriteByte (&sv.reliable_datagram, svc_stufftext);
|
|
MSG_WriteString (&sv.reliable_datagram, string);
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
SV_Multicast
|
|
|
|
Sends the contents of sv.multicast to a subset of the clients,
|
|
then clears sv.multicast.
|
|
|
|
MULTICAST_ALL same as broadcast
|
|
MULTICAST_PVS send to clients potentially visible from org
|
|
MULTICAST_PHS send to clients potentially hearable from org
|
|
=================
|
|
*/
|
|
void SV_Multicast (vec3_t origin, int to)
|
|
{
|
|
client_t *client;
|
|
byte *mask;
|
|
mleaf_t *leaf;
|
|
int leafnum;
|
|
int j;
|
|
qboolean reliable;
|
|
|
|
leaf = Mod_PointInLeaf (origin, sv.worldmodel);
|
|
if (!leaf)
|
|
leafnum = 0;
|
|
else
|
|
leafnum = leaf - sv.worldmodel->leafs;
|
|
|
|
reliable = false;
|
|
|
|
switch (to)
|
|
{
|
|
case MULTICAST_ALL_R:
|
|
reliable = true; // intentional fallthrough
|
|
case MULTICAST_ALL:
|
|
mask = sv.pvs; // leaf 0 is everything;
|
|
break;
|
|
|
|
case MULTICAST_PHS_R:
|
|
reliable = true; // intentional fallthrough
|
|
case MULTICAST_PHS:
|
|
mask = sv.phs + leafnum * 4*((sv.worldmodel->numleafs+31)>>5);
|
|
break;
|
|
|
|
case MULTICAST_PVS_R:
|
|
reliable = true; // intentional fallthrough
|
|
case MULTICAST_PVS:
|
|
mask = sv.pvs + leafnum * 4*((sv.worldmodel->numleafs+31)>>5);
|
|
break;
|
|
|
|
default:
|
|
mask = NULL;
|
|
SV_Error ("SV_Multicast: bad to:%i", to);
|
|
}
|
|
|
|
// send the data to all relevent clients
|
|
for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++)
|
|
{
|
|
if (client->state != cs_spawned)
|
|
continue;
|
|
|
|
if (to == MULTICAST_PHS_R || to == MULTICAST_PHS) {
|
|
vec3_t delta;
|
|
VectorSubtract(origin, client->edict->v.origin, delta);
|
|
if (Length(delta) <= 1024)
|
|
goto inrange;
|
|
}
|
|
|
|
leaf = Mod_PointInLeaf (client->edict->v.origin, sv.worldmodel);
|
|
if (leaf)
|
|
{
|
|
// -1 is because pvs rows are 1 based, not 0 based like leafs
|
|
leafnum = leaf - sv.worldmodel->leafs - 1;
|
|
if ( !(mask[leafnum>>3] & (1<<(leafnum&7)) ) )
|
|
{
|
|
// Con_Printf ("supressed multicast\n");
|
|
continue;
|
|
}
|
|
}
|
|
|
|
inrange:
|
|
if (reliable) {
|
|
ClientReliableCheckBlock(client, sv.multicast.cursize);
|
|
ClientReliableWrite_SZ(client, sv.multicast.data, sv.multicast.cursize);
|
|
} else
|
|
SZ_Write (&client->datagram, sv.multicast.data, sv.multicast.cursize);
|
|
}
|
|
|
|
SZ_Clear (&sv.multicast);
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
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, char *sample, int volume,
|
|
float attenuation)
|
|
{
|
|
int sound_num;
|
|
int field_mask;
|
|
int i;
|
|
int ent;
|
|
vec3_t origin;
|
|
qboolean use_phs;
|
|
qboolean reliable = false;
|
|
|
|
if (volume < 0 || volume > 255)
|
|
SV_Error ("SV_StartSound: volume = %i", volume);
|
|
|
|
if (attenuation < 0 || attenuation > 4)
|
|
SV_Error ("SV_StartSound: attenuation = %f", attenuation);
|
|
|
|
if (channel < 0 || channel > 15)
|
|
SV_Error ("SV_StartSound: channel = %i", channel);
|
|
|
|
// 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);
|
|
|
|
if ((channel & 8) || !sv_phs->int_val) // no PHS flag
|
|
{
|
|
if (channel & 8)
|
|
reliable = true; // sounds that break the phs are reliable
|
|
use_phs = false;
|
|
channel &= 7;
|
|
}
|
|
else
|
|
use_phs = true;
|
|
|
|
// if (channel == CHAN_BODY || channel == CHAN_VOICE)
|
|
// reliable = true;
|
|
|
|
channel = (ent<<3) | channel;
|
|
|
|
field_mask = 0;
|
|
if (volume != DEFAULT_SOUND_PACKET_VOLUME)
|
|
channel |= SND_VOLUME;
|
|
if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)
|
|
channel |= SND_ATTENUATION;
|
|
|
|
// use the entity origin unless it is a bmodel
|
|
if (entity->v.solid == SOLID_BSP)
|
|
{
|
|
for (i=0 ; i<3 ; i++)
|
|
origin[i] = entity->v.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i]);
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (entity->v.origin, origin);
|
|
}
|
|
|
|
MSG_WriteByte (&sv.multicast, svc_sound);
|
|
MSG_WriteShort (&sv.multicast, channel);
|
|
if (channel & SND_VOLUME)
|
|
MSG_WriteByte (&sv.multicast, volume);
|
|
if (channel & SND_ATTENUATION)
|
|
MSG_WriteByte (&sv.multicast, attenuation*64);
|
|
MSG_WriteByte (&sv.multicast, sound_num);
|
|
for (i=0 ; i<3 ; i++)
|
|
MSG_WriteCoord (&sv.multicast, origin[i]);
|
|
|
|
if (use_phs)
|
|
SV_Multicast (origin, reliable ? MULTICAST_PHS_R : MULTICAST_PHS);
|
|
else
|
|
SV_Multicast (origin, reliable ? MULTICAST_ALL_R : MULTICAST_ALL);
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
FRAME UPDATES
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
int sv_nailmodel, sv_supernailmodel, sv_playermodel;
|
|
|
|
void SV_FindModelNumbers (void)
|
|
{
|
|
int i;
|
|
|
|
sv_nailmodel = -1;
|
|
sv_supernailmodel = -1;
|
|
sv_playermodel = -1;
|
|
|
|
for (i=0 ; i<MAX_MODELS ; i++)
|
|
{
|
|
if (!sv.model_precache[i])
|
|
break;
|
|
if (!strcmp(sv.model_precache[i],"progs/spike.mdl"))
|
|
sv_nailmodel = i;
|
|
if (!strcmp(sv.model_precache[i],"progs/s_spike.mdl"))
|
|
sv_supernailmodel = i;
|
|
if (!strcmp(sv.model_precache[i],"progs/player.mdl"))
|
|
sv_playermodel = i;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
SV_WriteClientdataToMessage
|
|
|
|
==================
|
|
*/
|
|
void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg)
|
|
{
|
|
int i;
|
|
edict_t *other;
|
|
edict_t *ent;
|
|
|
|
ent = client->edict;
|
|
|
|
// send the chokecount for r_netgraph
|
|
if (client->chokecount)
|
|
{
|
|
MSG_WriteByte (msg, svc_chokecount);
|
|
MSG_WriteByte (msg, client->chokecount);
|
|
client->chokecount = 0;
|
|
}
|
|
|
|
// send a damage message if the player got hit this frame
|
|
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;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=======================
|
|
SV_UpdateClientStats
|
|
|
|
Performs a delta update of the stats array. This should only be performed
|
|
when a reliable message can be delivered this frame.
|
|
=======================
|
|
*/
|
|
void SV_UpdateClientStats (client_t *client)
|
|
{
|
|
edict_t *ent;
|
|
int stats[MAX_CL_STATS];
|
|
int i;
|
|
|
|
ent = client->edict;
|
|
memset (stats, 0, sizeof(stats));
|
|
|
|
// if we are a spectator and we are tracking a player, we get his stats
|
|
// so our status bar reflects his
|
|
if (client->spectator && client->spec_track > 0)
|
|
ent = svs.clients[client->spec_track - 1].edict;
|
|
|
|
stats[STAT_HEALTH] = ent->v.health;
|
|
stats[STAT_WEAPON] = SV_ModelIndex(PR_GetString(ent->v.weaponmodel));
|
|
stats[STAT_AMMO] = ent->v.currentammo;
|
|
stats[STAT_ARMOR] = ent->v.armorvalue;
|
|
stats[STAT_SHELLS] = ent->v.ammo_shells;
|
|
stats[STAT_NAILS] = ent->v.ammo_nails;
|
|
stats[STAT_ROCKETS] = ent->v.ammo_rockets;
|
|
stats[STAT_CELLS] = ent->v.ammo_cells;
|
|
if (!client->spectator)
|
|
stats[STAT_ACTIVEWEAPON] = ent->v.weapon;
|
|
// stuff the sigil bits into the high bits of items for sbar
|
|
stats[STAT_ITEMS] = (int)ent->v.items | ((int)pr_global_struct->serverflags << 28);
|
|
|
|
// Extensions to the QW 2.40 protocol for Mega2k --KB
|
|
stats[STAT_VIEWHEIGHT] = (int)ent->v.view_ofs[2];
|
|
|
|
// FIXME: this should become a * key! --KB
|
|
if (ent->v.movetype == MOVETYPE_FLY && !atoi(Info_ValueForKey
|
|
(svs.info, "playerfly")))
|
|
ent->v.movetype = MOVETYPE_WALK;
|
|
|
|
stats[STAT_FLYMODE] = (ent->v.movetype == MOVETYPE_FLY);
|
|
|
|
|
|
for (i=0 ; i<MAX_CL_STATS ; i++)
|
|
if (stats[i] != client->stats[i])
|
|
{
|
|
client->stats[i] = stats[i];
|
|
if (stats[i] >=0 && stats[i] <= 255)
|
|
{
|
|
ClientReliableWrite_Begin(client, svc_updatestat, 3);
|
|
ClientReliableWrite_Byte(client, i);
|
|
ClientReliableWrite_Byte(client, stats[i]);
|
|
}
|
|
else
|
|
{
|
|
ClientReliableWrite_Begin(client, svc_updatestatlong, 6);
|
|
ClientReliableWrite_Byte(client, i);
|
|
ClientReliableWrite_Long(client, stats[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=======================
|
|
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;
|
|
msg.allowoverflow = true;
|
|
msg.overflowed = false;
|
|
|
|
// add the client specific data to the datagram
|
|
SV_WriteClientdataToMessage (client, &msg);
|
|
|
|
// send over all the objects that are in the PVS
|
|
// this will include clients, a packetentities, and
|
|
// possibly a nails update
|
|
SV_WriteEntitiesToClient (client, &msg);
|
|
|
|
// copy the accumulated multicast datagram
|
|
// for this client out to the message
|
|
if (client->datagram.overflowed)
|
|
Con_Printf ("WARNING: datagram overflowed for %s\n", client->name);
|
|
else
|
|
SZ_Write (&msg, client->datagram.data, client->datagram.cursize);
|
|
SZ_Clear (&client->datagram);
|
|
|
|
// send deltas over reliable stream
|
|
if (Netchan_CanReliable (&client->netchan))
|
|
SV_UpdateClientStats (client);
|
|
|
|
if (msg.overflowed)
|
|
{
|
|
Con_Printf ("WARNING: msg overflowed for %s\n", client->name);
|
|
SZ_Clear (&msg);
|
|
}
|
|
|
|
// send the datagram
|
|
Netchan_Transmit (&client->netchan, msg.cursize, buf);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=======================
|
|
SV_UpdateToReliableMessages
|
|
=======================
|
|
*/
|
|
void SV_UpdateToReliableMessages (void)
|
|
{
|
|
int i, j;
|
|
client_t *client;
|
|
eval_t *val;
|
|
edict_t *ent;
|
|
|
|
// check for changes to be sent over the reliable streams to all clients
|
|
for (i=0, host_client = svs.clients ; i<MAX_CLIENTS ; i++, host_client++)
|
|
{
|
|
if (host_client->state != cs_spawned)
|
|
continue;
|
|
if (host_client->sendinfo)
|
|
{
|
|
host_client->sendinfo = false;
|
|
SV_FullClientUpdate (host_client, &sv.reliable_datagram);
|
|
}
|
|
if (host_client->old_frags != host_client->edict->v.frags)
|
|
{
|
|
for (j=0, client = svs.clients ; j<MAX_CLIENTS ; j++, client++)
|
|
{
|
|
if (client->state < cs_connected)
|
|
continue;
|
|
ClientReliableWrite_Begin(client, svc_updatefrags, 4);
|
|
ClientReliableWrite_Byte(client, i);
|
|
ClientReliableWrite_Short(client, host_client->edict->v.frags);
|
|
}
|
|
|
|
host_client->old_frags = host_client->edict->v.frags;
|
|
}
|
|
|
|
// maxspeed/entgravity changes
|
|
ent = host_client->edict;
|
|
|
|
val = GetEdictFieldValue(ent, "gravity");
|
|
if (val && host_client->entgravity != val->_float) {
|
|
host_client->entgravity = val->_float;
|
|
ClientReliableWrite_Begin(host_client, svc_entgravity, 5);
|
|
ClientReliableWrite_Float(host_client, host_client->entgravity);
|
|
}
|
|
val = GetEdictFieldValue(ent, "maxspeed");
|
|
if (val && host_client->maxspeed != val->_float) {
|
|
host_client->maxspeed = val->_float;
|
|
ClientReliableWrite_Begin(host_client, svc_maxspeed, 5);
|
|
ClientReliableWrite_Float(host_client, host_client->maxspeed);
|
|
}
|
|
|
|
}
|
|
|
|
if (sv.datagram.overflowed)
|
|
SZ_Clear (&sv.datagram);
|
|
|
|
// append the broadcast messages to each client messages
|
|
for (j=0, client = svs.clients ; j<MAX_CLIENTS ; j++, client++)
|
|
{
|
|
if (client->state < cs_connected)
|
|
continue; // reliables go to all connected or spawned
|
|
|
|
ClientReliableCheckBlock(client, sv.reliable_datagram.cursize);
|
|
ClientReliableWrite_SZ(client, sv.reliable_datagram.data, sv.reliable_datagram.cursize);
|
|
|
|
if (client->state != cs_spawned)
|
|
continue; // datagrams only go to spawned
|
|
SZ_Write (&client->datagram
|
|
, sv.datagram.data
|
|
, sv.datagram.cursize);
|
|
}
|
|
|
|
SZ_Clear (&sv.reliable_datagram);
|
|
SZ_Clear (&sv.datagram);
|
|
}
|
|
|
|
#if defined(_WIN32) && !defined(__GNUC__)
|
|
#pragma optimize( "", off )
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
=======================
|
|
SV_SendClientMessages
|
|
=======================
|
|
*/
|
|
void SV_SendClientMessages (void)
|
|
{
|
|
int i, j;
|
|
client_t *c;
|
|
|
|
// update frags, names, etc
|
|
SV_UpdateToReliableMessages ();
|
|
|
|
// build individual updates
|
|
for (i=0, c = svs.clients ; i<MAX_CLIENTS ; i++, c++)
|
|
{
|
|
if (!c->state)
|
|
continue;
|
|
|
|
if (c->drop) {
|
|
SV_DropClient(c);
|
|
c->drop = false;
|
|
continue;
|
|
}
|
|
|
|
// check to see if we have a backbuf to stick in the reliable
|
|
if (c->num_backbuf) {
|
|
// will it fit?
|
|
if (c->netchan.message.cursize + c->backbuf_size[0] <
|
|
c->netchan.message.maxsize) {
|
|
|
|
Con_DPrintf("%s: backbuf %d bytes\n",
|
|
c->name, c->backbuf_size[0]);
|
|
|
|
// it'll fit
|
|
SZ_Write(&c->netchan.message, c->backbuf_data[0],
|
|
c->backbuf_size[0]);
|
|
|
|
//move along, move along
|
|
for (j = 1; j < c->num_backbuf; j++) {
|
|
memcpy(c->backbuf_data[j - 1], c->backbuf_data[j],
|
|
c->backbuf_size[j]);
|
|
c->backbuf_size[j - 1] = c->backbuf_size[j];
|
|
}
|
|
|
|
c->num_backbuf--;
|
|
if (c->num_backbuf) {
|
|
memset(&c->backbuf, 0, sizeof(c->backbuf));
|
|
c->backbuf.data = c->backbuf_data[c->num_backbuf - 1];
|
|
c->backbuf.cursize = c->backbuf_size[c->num_backbuf - 1];
|
|
c->backbuf.maxsize = sizeof(c->backbuf_data[c->num_backbuf - 1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// if the reliable message overflowed,
|
|
// drop the client
|
|
if (c->netchan.message.overflowed)
|
|
{
|
|
SZ_Clear (&c->netchan.message);
|
|
SZ_Clear (&c->datagram);
|
|
SV_BroadcastPrintf (PRINT_HIGH, "%s overflowed\n", c->name);
|
|
Con_Printf ("WARNING: reliable overflow for %s\n",c->name);
|
|
SV_DropClient (c);
|
|
c->send_message = true;
|
|
c->netchan.cleartime = 0; // don't choke this message
|
|
}
|
|
|
|
// only send messages if the client has sent one
|
|
// and the bandwidth is not choked
|
|
if (!c->send_message)
|
|
continue;
|
|
c->send_message = false; // try putting this after choke?
|
|
if (!sv.paused && !Netchan_CanPacket (&c->netchan))
|
|
{
|
|
c->chokecount++;
|
|
continue; // bandwidth choke
|
|
}
|
|
|
|
if (c->state == cs_spawned)
|
|
SV_SendClientDatagram (c);
|
|
else
|
|
Netchan_Transmit (&c->netchan, 0, NULL); // just update reliable
|
|
|
|
}
|
|
}
|
|
|
|
#if defined(_WIN32) && !defined(__GNUC__)
|
|
#pragma optimize( "", on )
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
=======================
|
|
SV_SendMessagesToAll
|
|
|
|
FIXME: does this sequence right?
|
|
=======================
|
|
*/
|
|
void SV_SendMessagesToAll (void)
|
|
{
|
|
int i;
|
|
client_t *c;
|
|
|
|
for (i=0, c = svs.clients ; i<MAX_CLIENTS ; i++, c++)
|
|
if (c->state) // FIXME: should this only send to active?
|
|
c->send_message = true;
|
|
|
|
SV_SendClientMessages ();
|
|
}
|