quakeforge/qw/source/sv_send.c
Bill Currie 6d5ffa9f8e [build] Move to non-recursive make
There's still some cleanup to do, but everything seems to be working
nicely: `make -j` works, `make distcheck` passes. There is probably
plenty of bitrot in the package directories (RPM, debian), though.

The vc project files have been removed since those versions are way out
of date and quakeforge is pretty much dependent on gcc now anyway.

Most of the old Makefile.am files  are now Makemodule.am.  This should
allow for new Makefile.am files that allow local building (to be added
on an as-needed bases).  The current remaining Makefile.am files are for
standalone sub-projects.a

The installable bins are currently built in the top-level build
directory. This may change if the clutter gets to be too much.

While this does make a noticeable difference in build times, the main
reason for the switch was to take care of the growing dependency issues:
now it's possible to build tools for code generation (eg, using qfcc and
ruamoko programs for code-gen).
2020-06-25 11:35:37 +09:00

933 lines
23 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
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdarg.h>
#include <stdlib.h>
#include <time.h>
#include "QF/cvar.h"
#include "QF/console.h"
#include "QF/dstring.h"
#include "QF/msg.h"
#include "QF/sound.h" // FIXME: DEFAULT_SOUND_PACKET_*
#include "QF/sys.h"
#include "compat.h"
#include "qw/bothdefs.h"
#include "qw/include/server.h"
#include "qw/include/sv_progs.h"
#include "qw/include/sv_recorder.h"
#define CHAN_AUTO 0
#define CHAN_WEAPON 1
#define CHAN_VOICE 2
#define CHAN_ITEM 3
#define CHAN_BODY 4
/* SV_Printf redirection */
dstring_t outputbuf = {&dstring_default_mem};
int con_printf_no_log;
redirect_t sv_redirected;
static void
SV_FlushRedirect (void)
{
char send[8000 + 6];
size_t count;
int bytes;
const char *p;
if (!outputbuf.size)
return;
count = strlen (outputbuf.str);
if (sv_redirected == RD_PACKET) {
send[0] = 0xff;
send[1] = 0xff;
send[2] = 0xff;
send[3] = 0xff;
send[4] = A2C_PRINT;
p = outputbuf.str;
while (count) {
bytes = min (count, sizeof (send) - 5);
memcpy (send + 5, p, bytes);
Netchan_SendPacket (bytes + 5, send, net_from);
p += bytes;
count -= bytes;
}
} else if (sv_redirected == RD_CLIENT || sv_redirected > RD_MOD) {
client_t *cl;
if (sv_redirected > RD_MOD) {
cl = svs.clients + sv_redirected - RD_MOD - 1;
if (cl->state != cs_spawned) //FIXME record to mvd?
count = 0;
} else {
cl = host_client;
}
p = outputbuf.str;
while (count) {
// +/- 3 for svc_print, PRINT_HIGH and nul byte
// min of 4 because we don't want to send an effectively empty
// message
bytes = MSG_ReliableCheckSize (&cl->backbuf, count + 3, 4) - 3;
// if writing another packet would overflow the client, just drop
// the rest of the data. getting rudely disconnected would be much
// more annoying than losing the tail end of the output
if (bytes <= 0)
break;
MSG_ReliableWrite_Begin (&cl->backbuf, svc_print, bytes + 3);
MSG_ReliableWrite_Byte (&cl->backbuf, PRINT_HIGH);
MSG_ReliableWrite_SZ (&cl->backbuf, p, bytes);
MSG_ReliableWrite_Byte (&cl->backbuf, 0);
p += bytes;
count -= bytes;
}
}
// RD_MOD doesn't do anything :)
// clear it
dstring_clear (&outputbuf);
}
/*
SV_BeginRedirect
Send SV_Printf data to the remote client
instead of the console
*/
void
SV_BeginRedirect (redirect_t rd)
{
sv_redirected = rd;
dstring_clear (&outputbuf);
}
void
SV_EndRedirect (void)
{
SV_FlushRedirect ();
sv_redirected = RD_NONE;
}
#define MAXPRINTMSG 4096
static int
find_userid (const char *name)
{
int i;
for (i = 0; i < MAX_CLIENTS; i++) {
if (!svs.clients[i].state)
continue;
if (!strcmp (svs.clients[i].name, name)) {
return svs.clients[i].userid;
}
}
return 0;
}
/*
SV_Printf
Handles cursor positioning, line wrapping, etc
*/
// FIXME: the msg variables need to be renamed/cleaned up
void
SV_Print (const char *fmt, va_list args)
{
static dstring_t *premsg;
static dstring_t *msg;
static dstring_t *msg2;
static int pending = 0; // partial line being printed
char msg3[MAXPRINTMSG];
time_t mytime = 0;
struct tm *local = NULL;
qboolean timestamps = false;
char *in;
if (!premsg) {
premsg = dstring_newstr ();
msg = dstring_new ();
msg2 = dstring_new ();
}
dstring_clearstr (msg);
dvsprintf (premsg, fmt, args);
in = premsg->str;
if (!*premsg->str)
return;
// expand FFnickFF to nick <userid>
do {
char *beg = strchr (in, 0xFF);
if (beg) {
char *name = beg + 1;
char *end = strchr (name, 0xFF);
if (!end) {
end = beg + strlen (name);
}
*end = 0;
dstring_appendsubstr (msg, in, beg - in);
dasprintf (msg, "%s <%d>", name, find_userid (name));
in = end + 1;
} else {
dstring_appendstr (msg, in);
break;
}
} while (*in);
if (sv_redirected) { // Add to redirected message
dstring_appendstr (&outputbuf, msg->str);
}
if (*msg->str && !con_printf_no_log) {
// We want to output to console and maybe logfile
if (sv_timestamps && sv_timefmt && sv_timefmt->string
&& sv_timestamps->int_val && !pending)
timestamps = true;
if (timestamps) {
mytime = time (NULL);
local = localtime (&mytime);
strftime (msg3, sizeof (msg3), sv_timefmt->string, local);
dsprintf (msg2, "%s%s", msg3, msg->str);
} else {
dsprintf (msg2, "%s", msg->str);
}
if (msg2->str[strlen (msg2->str) - 1] != '\n') {
pending = 1;
} else {
pending = 0;
}
Con_Printf ("%s", msg2->str); // also echo to debugging console
}
}
/* EVENT MESSAGES */
static void
SV_PrintToClient (client_t *cl, int level, const char *string)
{
static char *buffer;
const char *a;
unsigned char *b;
int size;
static int buffer_size;
size = strlen (string) + 1;
if (size > buffer_size) {
buffer_size = (size + 1023) & ~1023; // 1k multiples
if (buffer)
free (buffer);
buffer = malloc (buffer_size);
if (!buffer)
Sys_Error ("SV_PrintToClient: could not allocate %d bytes",
buffer_size);
}
a = string;
b = (byte *) buffer;
// strip 0xFFs
while ((*b = *a++))
if (*b != 0xFF)
b++;
MSG_ReliableWrite_Begin (&cl->backbuf, svc_print, strlen (buffer) + 3);
MSG_ReliableWrite_Byte (&cl->backbuf, level);
MSG_ReliableWrite_String (&cl->backbuf, buffer);
}
/*
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 (const vec3_t origin, int to)
{
byte *mask;
client_t *client;
int leafnum, j;
mleaf_t *leaf;
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;
Sys_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, SVvector (client->edict, origin), delta);
if (VectorLength (delta) <= 1024)
goto inrange;
}
leaf = Mod_PointInLeaf (SVvector (client->edict, 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)))) {
// SV_Printf ("supressed multicast\n");
continue;
}
}
inrange:
if (reliable) {
MSG_ReliableCheckBlock (&client->backbuf, sv.multicast.cursize);
MSG_ReliableWrite_SZ (&client->backbuf, sv.multicast.data,
sv.multicast.cursize);
} else
SZ_Write (&client->datagram, sv.multicast.data,
sv.multicast.cursize);
}
if (sv.recorders) {
if (reliable) {
sizebuf_t *dbuf = SVR_WriteBegin (dem_all, 0,
sv.multicast.cursize);
SZ_Write (dbuf, sv.multicast.data, sv.multicast.cursize);
} else
SZ_Write (SVR_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
already 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 ent, sound_num, i;
qboolean use_phs;
qboolean reliable = false;
vec3_t origin;
if (volume < 0 || volume > 255)
Sys_Error ("SV_StartSound: volume = %i", volume);
if (attenuation < 0 || attenuation > 4)
Sys_Error ("SV_StartSound: attenuation = %f", attenuation);
if (channel < 0 || channel > 15)
Sys_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]) {
SV_Printf ("SV_StartSound: %s not precacheed\n", sample);
return;
}
ent = NUM_FOR_EDICT (&sv_pr_state, 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;
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 (SVfloat (entity, solid) == SOLID_BSP) {
for (i = 0; i < 3; i++)
origin[i] = SVvector (entity, origin)[i] + 0.5 *
(SVvector (entity, mins)[i] + SVvector (entity, maxs)[i]);
} else {
VectorCopy (SVvector (entity, 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);
MSG_WriteCoordV (&sv.multicast, origin);
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;
}
}
void
SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg)
{
edict_t *ent, *other;
int i, clnum;
sizebuf_t *dbuf;
ent = client->edict;
clnum = NUM_FOR_EDICT (&sv_pr_state, ent) - 1;
// 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 (SVfloat (ent, dmg_take) || SVfloat (ent, dmg_save)) {
other = PROG_TO_EDICT (&sv_pr_state, SVentity (ent, dmg_inflictor));
MSG_WriteByte (msg, svc_damage);
MSG_WriteByte (msg, SVfloat (ent, dmg_save));
MSG_WriteByte (msg, SVfloat (ent, dmg_take));
for (i = 0; i < 3; i++)
MSG_WriteCoord (msg, SVvector (other, origin)[i] + 0.5 *
(SVvector (other, mins)[i] +
SVvector (other, maxs)[i]));
SVfloat (ent, dmg_take) = 0;
SVfloat (ent, dmg_save) = 0;
}
// add this to server demo
if (sv.recorders && msg->cursize) {
dbuf = SVR_WriteBegin (dem_single, clnum, msg->cursize);
SZ_Write (dbuf, msg->data, msg->cursize);
}
// a fixangle might get lost in a dropped packet. Oh well.
if (SVfloat (ent, fixangle)) {
vec_t *angles = SVvector (ent, angles);
MSG_WriteByte (msg, svc_setangle);
MSG_WriteAngleV (msg, angles);
SVfloat (ent, fixangle) = 0;
if (sv.recorders) {
dbuf = SVR_Datagram ();
MSG_WriteByte (dbuf, svc_setangle);
MSG_WriteByte (dbuf, clnum);
MSG_WriteAngleV (dbuf, angles);
}
}
}
void
SV_GetStats (edict_t *ent, int spectator, int stats[])
{
memset (stats, 0, sizeof (int) * MAX_CL_STATS);
stats[STAT_HEALTH] = SVfloat (ent, health);
stats[STAT_WEAPON] = SV_ModelIndex (PR_GetString (&sv_pr_state,
SVstring (ent, weaponmodel)));
stats[STAT_AMMO] = SVfloat (ent, currentammo);
stats[STAT_ARMOR] = SVfloat (ent, armorvalue);
stats[STAT_SHELLS] = SVfloat (ent, ammo_shells);
stats[STAT_NAILS] = SVfloat (ent, ammo_nails);
stats[STAT_ROCKETS] = SVfloat (ent, ammo_rockets);
stats[STAT_CELLS] = SVfloat (ent, ammo_cells);
if (!spectator)
stats[STAT_ACTIVEWEAPON] = SVfloat (ent, weapon);
// stuff the sigil bits into the high bits of items for sbar
stats[STAT_ITEMS] = ((int) SVfloat (ent, items)
| ((int) *sv_globals.serverflags << 28));
// Extensions to the QW 2.40 protocol for Mega2k --KB
stats[STAT_VIEWHEIGHT] = (int) SVvector (ent, view_ofs)[2];
// FIXME: this should become a * key! --KB
if (SVfloat (ent, movetype) == MOVETYPE_FLY
&& !atoi (Info_ValueForKey (svs.info, "playerfly")))
SVfloat (ent, movetype) = MOVETYPE_WALK;
stats[STAT_FLYMODE] = (SVfloat (ent, movetype) == MOVETYPE_FLY);
}
/*
SV_UpdateClientStats
Performs a delta update of the stats array. This should be performed only
when a reliable message can be delivered this frame.
*/
static void
SV_UpdateClientStats (client_t *client)
{
edict_t *ent;
int i;
int stats[MAX_CL_STATS];
ent = client->edict;
// 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;
SV_GetStats (ent, client->spectator, stats);
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) {
MSG_ReliableWrite_Begin (&client->backbuf, svc_updatestat, 3);
MSG_ReliableWrite_Byte (&client->backbuf, i);
MSG_ReliableWrite_Byte (&client->backbuf, stats[i]);
} else {
MSG_ReliableWrite_Begin (&client->backbuf,
svc_updatestatlong, 6);
MSG_ReliableWrite_Byte (&client->backbuf, i);
MSG_ReliableWrite_Long (&client->backbuf, stats[i]);
}
}
}
static 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);
if (client->state == cs_server)
return true;
// send over all the objects that are in the PVS
// this will include clients, a packetentities, and
// possibly a nails update
SV_WriteEntitiesToClient (&client->delta, &msg);
// copy the accumulated multicast datagram
// for this client out to the message
if (client->datagram.overflowed)
SV_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) {
SV_Printf ("WARNING: msg overflowed for %s\n", client->name);
SZ_Clear (&msg);
}
// send the datagram
Netchan_Transmit (&client->netchan, msg.cursize, msg.data);
return true;
}
static void
SV_UpdateToReliableMessages (void)
{
client_t *client;
edict_t *ent;
int i, j;
sizebuf_t *dbuf;
// 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 && host_client->state != cs_server)
continue;
if (host_client->sendinfo) {
host_client->sendinfo = false;
SV_FullClientUpdate (host_client, &sv.reliable_datagram);
}
if (host_client->old_frags != (int) SVfloat (host_client->edict, frags)) {
for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) {
if (client->state < cs_connected)
continue;
MSG_ReliableWrite_Begin (&client->backbuf, svc_updatefrags, 4);
MSG_ReliableWrite_Byte (&client->backbuf, i);
MSG_ReliableWrite_Short (&client->backbuf,
SVfloat (host_client->edict, frags));
}
if (sv.recorders) {
dbuf = SVR_WriteBegin (dem_all, 0, 4);
MSG_WriteByte (dbuf, svc_updatefrags);
MSG_WriteByte (dbuf, i);
MSG_WriteShort (dbuf, SVfloat (host_client->edict, frags));
}
host_client->old_frags = SVfloat (host_client->edict, frags);
}
// maxspeed/entgravity changes
ent = host_client->edict;
if (sv_fields.gravity != -1
&& host_client->entgravity != SVfloat (ent, gravity)) {
host_client->entgravity = SVfloat (ent, gravity);
if (host_client->state != cs_server) {
MSG_ReliableWrite_Begin (&host_client->backbuf,
svc_entgravity, 5);
MSG_ReliableWrite_Float (&host_client->backbuf,
host_client->entgravity);
}
if (sv.recorders) {
dbuf = SVR_WriteBegin (dem_single, i, 5);
MSG_WriteByte (dbuf, svc_entgravity);
MSG_WriteFloat (dbuf, host_client->entgravity);
}
}
if (sv_fields.maxspeed != -1
&& host_client->maxspeed != SVfloat (ent, maxspeed)) {
host_client->maxspeed = SVfloat (ent, maxspeed);
if (host_client->state != cs_server) {
MSG_ReliableWrite_Begin (&host_client->backbuf,
svc_maxspeed, 5);
MSG_ReliableWrite_Float (&host_client->backbuf,
host_client->maxspeed);
}
if (sv.recorders) {
dbuf = SVR_WriteBegin (dem_single, i, 5);
MSG_WriteByte (dbuf, svc_maxspeed);
MSG_WriteFloat (dbuf, 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
MSG_ReliableCheckBlock (&client->backbuf,
sv.reliable_datagram.cursize);
MSG_ReliableWrite_SZ (&client->backbuf, sv.reliable_datagram.data,
sv.reliable_datagram.cursize);
if (client->state != cs_spawned)
continue; // datagrams go to only spawned
SZ_Write (&client->datagram, sv.datagram.data, sv.datagram.cursize);
}
if (sv.recorders && sv.reliable_datagram.cursize) {
dbuf = SVR_WriteBegin (dem_all, 0, sv.reliable_datagram.cursize);
SZ_Write (dbuf, sv.reliable_datagram.data,
sv.reliable_datagram.cursize);
}
if (sv.recorders)
SZ_Write (SVR_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
void
SV_SendClientMessages (void)
{
client_t *c;
int i;
// update frags, names, etc
SV_UpdateToReliableMessages ();
// build individual updates
for (i = 0, c = svs.clients; i < MAX_CLIENTS; i++, c++) {
if (c->state != cs_server) {
if (c->state < cs_zombie)
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->backbuf.num_backbuf)
MSG_Reliable_Send (&c->backbuf);
// 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);
SV_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
}
// send messages only 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 || c->state == cs_server)
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)
{
client_t *c;
int i;
for (i = 0, c = svs.clients; i < MAX_CLIENTS; i++, c++)
if (c->state < cs_zombie) // FIXME: should this send to only active?
c->send_message = true;
SV_SendClientMessages ();
}
void
SV_Printf (const char *fmt, ...)
{
va_list argptr;
va_start (argptr, fmt);
SV_Print (fmt, argptr);
va_end (argptr);
}
/*
SV_ClientPrintf
Sends text across to be displayed if the level passes
*/
void
SV_ClientPrintf (int recorder, client_t *cl, int level, const char *fmt, ...)
{
char string[1024];
va_list argptr;
if (level < cl->messagelevel)
return;
va_start (argptr, fmt);
vsnprintf (string, sizeof (string), fmt, argptr);
va_end (argptr);
if (recorder && sv.recorders) {
sizebuf_t *dbuf = SVR_WriteBegin (dem_single, cl - svs.clients,
strlen (string) + 3);
MSG_WriteByte (dbuf, svc_print);
MSG_WriteByte (dbuf, level);
MSG_WriteString (dbuf, string);
}
SV_PrintToClient (cl, level, string);
}
/*
SV_BroadcastPrintf
Sends text to all active clients
*/
void
SV_BroadcastPrintf (int level, const char *fmt, ...)
{
char string[1024];
client_t *cl;
int i;
va_list argptr;
va_start (argptr, fmt);
vsnprintf (string, sizeof (string), fmt, argptr);
va_end (argptr);
SV_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 < cs_zombie) //FIXME record to mvd
continue;
SV_PrintToClient (cl, level, string);
}
if (sv.recorders) {
sizebuf_t *dbuf = SVR_WriteBegin (dem_all, cl - svs.clients,
strlen (string) + 3);
MSG_WriteByte (dbuf, svc_print);
MSG_WriteByte (dbuf, level);
MSG_WriteString (dbuf, string);
}
}
/*
SV_BroadcastCommand
Sends text to all active clients
*/
void
SV_BroadcastCommand (const char *fmt, ...)
{
char string[1024];
va_list argptr;
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);
}