newtree/source/sv_send.c

853 lines
20 KiB
C
Raw Normal View History

/*
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$
*/
2000-05-10 11:29:38 +00:00
2000-05-17 10:03:19 +00:00
#ifdef HAVE_CONFIG_H
# include "config.h"
2000-05-17 10:03:19 +00:00
#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>
Enabled time stamped messages for server. To turn it on, set sv_timestamps. To configure how timestamps are formatted, use the sv_timefmt Cvar. It's a formatted string, with the following special tokens (taken from the strftime() manual page): %a The abbreviated weekday name according to the cur­ rent locale. %A The full weekday name according to the current locale. %b The abbreviated month name according to the current locale. %B The full month name according to the current locale. %c The preferred date and time representation for the current locale. %C The century number (year/100) as a 2-digit integer. (SU) %C The century number (the year divided by 100 and truncated to an integer). %d The day of the month as a decimal number (range 01 to 31). %D Equivalent to %m/%d/%y. (Yecch - for Americans only. Americans should note that in other coun­ tries %d/%m/%y is rather common. This means that in international context this format is ambiguous and should not be used.) (SU) %e Like %d, the day of the month as a decimal number, but a leading zero is replaced by a space. (SU) %E Modifier: use alternative format, see below. (SU) %G The ISO 8601 year with century as a decimal number. The 4-digit year corresponding to the ISO week num­ ber (see %V). This has the same format and value as %y, except that if the ISO week number belongs to the previous or next year, that year is used instead. (TZ) %g Like %G, but without century, i.e., with a 2-digit year (00-99). (TZ) %h Equivalent to %b. (SU) %H The hour as a decimal number using a 24-hour clock (range 00 to 23). %I The hour as a decimal number using a 12-hour clock (range 01 to 12). %j The day of the year as a decimal number (range 001 to 366). %k The hour (24-hour clock) as a decimal number (range 0 to 23); single digits are preceded by a blank. (See also %H.) (TZ) %l The hour (12-hour clock) as a decimal number (range 1 to 12); single digits are preceded by a blank. (See also %I.) (TZ) %m The month as a decimal number (range 01 to 12). %M The minute as a decimal number (range 00 to 59). %n A newline character. (SU) %O Modifier: use alternative format, see below. (SU) %p Either `AM' or `PM' according to the given time value, or the corresponding strings for the current locale. Noon is treated as `pm' and midnight as `am'. %P Like %p but in lowercase: `am' or `pm' or a corre­ sponding string for the current locale. (GNU) %r The time in a.m. or p.m. notation. In the POSIX locale this is equivalent to `%I:%M:%S %p'. (SU) %R The time in 24-hour notation (%H:%M). (SU) For a version including the seconds, see %T below. %s The number of seconds since the Epoch, i.e., since 1970-01-01 00:00:00 UTC. (TZ) %S The second as a decimal number (range 00 to 61). %t A tab character. (SU) %T The time in 24-hour notation (%H:%M:%S). (SU) %u The day of the week as a decimal, range 1 to 7, Monday being 1. See also %w. (SU) %U The week number of the current year as a decimal number, range 00 to 53, starting with the first Sunday as the first day of week 01. See also %V and %W. %V The ISO 8601:1988 week number of the current year as a decimal number, range 01 to 53, where week 1 is the first week that has at least 4 days in the current year, and with Monday as the first day of the week. See also %U and %W. (SU) %w The day of the week as a decimal, range 0 to 6, Sunday being 0. See also %u. %W The week number of the current year as a decimal number, range 00 to 53, starting with the first Monday as the first day of week 01. %x The preferred date representation for the current locale without the time. %X The preferred time representation for the current locale without the date. %y The year as a decimal number without a century (range 00 to 99). %Y The year as a decimal number including the century. %z The time-zone as hour offset from GMT. Required to emit RFC822-conformant dates (using "%a, %d %b %Y %H:%M:%S %z"). (GNU) %Z The time zone or name or abbreviation. %+ The date and time in date(1) format. (TZ) %% A literal `%' character. Some of these may not work on some systems.
2000-08-02 07:13:12 +00:00
#include <time.h>
2000-05-10 11:29:38 +00:00
#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];
2000-05-10 11:29:38 +00:00
redirect_t sv_redirected;
2000-05-10 11:29:38 +00:00
extern cvar_t *sv_phs;
extern cvar_t *sv_timestamps;
extern cvar_t *sv_timefmt;
2000-05-10 11:29:38 +00:00
/*
==================
SV_FlushRedirect
==================
*/
void
SV_FlushRedirect (void)
2000-05-10 11:29:38 +00:00
{
char send[8000 + 6];
2000-05-10 11:29:38 +00:00
if (sv_redirected == RD_PACKET) {
2000-05-10 11:29:38 +00:00
send[0] = 0xff;
send[1] = 0xff;
send[2] = 0xff;
send[3] = 0xff;
send[4] = A2C_PRINT;
memcpy (send + 5, outputbuf, strlen (outputbuf) + 1);
2000-05-10 11:29:38 +00:00
NET_SendPacket (strlen (send) + 1, send, net_from);
} else if (sv_redirected == RD_CLIENT) {
ClientReliableWrite_Begin (host_client, svc_print,
strlen (outputbuf) + 3);
2000-05-10 11:29:38 +00:00
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)
2000-05-10 11:29:38 +00:00
{
sv_redirected = rd;
outputbuf[0] = 0;
}
void
SV_EndRedirect (void)
2000-05-10 11:29:38 +00:00
{
SV_FlushRedirect ();
sv_redirected = RD_NONE;
}
/*
================
Con_Printf
Handles cursor positioning, line wrapping, etc
================
*/
#define MAXPRINTMSG 4096
void
Con_Printf (char *fmt, ...)
2000-05-10 11:29:38 +00:00
{
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);
2000-05-10 11:29:38 +00:00
va_end (argptr);
if (sv_redirected) { // Add to redirected message
2000-10-02 04:24:08 +00:00
if (strlen (msg) + strlen (outputbuf) > sizeof (outputbuf) - 1)
SV_FlushRedirect ();
strncat (outputbuf, msg, sizeof (outputbuf) - strlen (outputbuf));
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;
2000-08-02 15:16:36 +00:00
if (timestamps) {
mytime = time (NULL);
local = localtime (&mytime);
strftime (msg3, sizeof (msg3), sv_timefmt->string, local);
Enabled time stamped messages for server. To turn it on, set sv_timestamps. To configure how timestamps are formatted, use the sv_timefmt Cvar. It's a formatted string, with the following special tokens (taken from the strftime() manual page): %a The abbreviated weekday name according to the cur­ rent locale. %A The full weekday name according to the current locale. %b The abbreviated month name according to the current locale. %B The full month name according to the current locale. %c The preferred date and time representation for the current locale. %C The century number (year/100) as a 2-digit integer. (SU) %C The century number (the year divided by 100 and truncated to an integer). %d The day of the month as a decimal number (range 01 to 31). %D Equivalent to %m/%d/%y. (Yecch - for Americans only. Americans should note that in other coun­ tries %d/%m/%y is rather common. This means that in international context this format is ambiguous and should not be used.) (SU) %e Like %d, the day of the month as a decimal number, but a leading zero is replaced by a space. (SU) %E Modifier: use alternative format, see below. (SU) %G The ISO 8601 year with century as a decimal number. The 4-digit year corresponding to the ISO week num­ ber (see %V). This has the same format and value as %y, except that if the ISO week number belongs to the previous or next year, that year is used instead. (TZ) %g Like %G, but without century, i.e., with a 2-digit year (00-99). (TZ) %h Equivalent to %b. (SU) %H The hour as a decimal number using a 24-hour clock (range 00 to 23). %I The hour as a decimal number using a 12-hour clock (range 01 to 12). %j The day of the year as a decimal number (range 001 to 366). %k The hour (24-hour clock) as a decimal number (range 0 to 23); single digits are preceded by a blank. (See also %H.) (TZ) %l The hour (12-hour clock) as a decimal number (range 1 to 12); single digits are preceded by a blank. (See also %I.) (TZ) %m The month as a decimal number (range 01 to 12). %M The minute as a decimal number (range 00 to 59). %n A newline character. (SU) %O Modifier: use alternative format, see below. (SU) %p Either `AM' or `PM' according to the given time value, or the corresponding strings for the current locale. Noon is treated as `pm' and midnight as `am'. %P Like %p but in lowercase: `am' or `pm' or a corre­ sponding string for the current locale. (GNU) %r The time in a.m. or p.m. notation. In the POSIX locale this is equivalent to `%I:%M:%S %p'. (SU) %R The time in 24-hour notation (%H:%M). (SU) For a version including the seconds, see %T below. %s The number of seconds since the Epoch, i.e., since 1970-01-01 00:00:00 UTC. (TZ) %S The second as a decimal number (range 00 to 61). %t A tab character. (SU) %T The time in 24-hour notation (%H:%M:%S). (SU) %u The day of the week as a decimal, range 1 to 7, Monday being 1. See also %w. (SU) %U The week number of the current year as a decimal number, range 00 to 53, starting with the first Sunday as the first day of week 01. See also %V and %W. %V The ISO 8601:1988 week number of the current year as a decimal number, range 01 to 53, where week 1 is the first week that has at least 4 days in the current year, and with Monday as the first day of the week. See also %U and %W. (SU) %w The day of the week as a decimal, range 0 to 6, Sunday being 0. See also %u. %W The week number of the current year as a decimal number, range 00 to 53, starting with the first Monday as the first day of week 01. %x The preferred date representation for the current locale without the time. %X The preferred time representation for the current locale without the date. %y The year as a decimal number without a century (range 00 to 99). %Y The year as a decimal number including the century. %z The time-zone as hour offset from GMT. Required to emit RFC822-conformant dates (using "%a, %d %b %Y %H:%M:%S %z"). (GNU) %Z The time zone or name or abbreviation. %+ The date and time in date(1) format. (TZ) %% A literal `%' character. Some of these may not work on some systems.
2000-08-02 07:13:12 +00:00
snprintf (msg2, sizeof (msg2), "%s%s", msg3, msg);
} else {
snprintf (msg2, sizeof (msg2), "%s", msg);
}
Enabled time stamped messages for server. To turn it on, set sv_timestamps. To configure how timestamps are formatted, use the sv_timefmt Cvar. It's a formatted string, with the following special tokens (taken from the strftime() manual page): %a The abbreviated weekday name according to the cur­ rent locale. %A The full weekday name according to the current locale. %b The abbreviated month name according to the current locale. %B The full month name according to the current locale. %c The preferred date and time representation for the current locale. %C The century number (year/100) as a 2-digit integer. (SU) %C The century number (the year divided by 100 and truncated to an integer). %d The day of the month as a decimal number (range 01 to 31). %D Equivalent to %m/%d/%y. (Yecch - for Americans only. Americans should note that in other coun­ tries %d/%m/%y is rather common. This means that in international context this format is ambiguous and should not be used.) (SU) %e Like %d, the day of the month as a decimal number, but a leading zero is replaced by a space. (SU) %E Modifier: use alternative format, see below. (SU) %G The ISO 8601 year with century as a decimal number. The 4-digit year corresponding to the ISO week num­ ber (see %V). This has the same format and value as %y, except that if the ISO week number belongs to the previous or next year, that year is used instead. (TZ) %g Like %G, but without century, i.e., with a 2-digit year (00-99). (TZ) %h Equivalent to %b. (SU) %H The hour as a decimal number using a 24-hour clock (range 00 to 23). %I The hour as a decimal number using a 12-hour clock (range 01 to 12). %j The day of the year as a decimal number (range 001 to 366). %k The hour (24-hour clock) as a decimal number (range 0 to 23); single digits are preceded by a blank. (See also %H.) (TZ) %l The hour (12-hour clock) as a decimal number (range 1 to 12); single digits are preceded by a blank. (See also %I.) (TZ) %m The month as a decimal number (range 01 to 12). %M The minute as a decimal number (range 00 to 59). %n A newline character. (SU) %O Modifier: use alternative format, see below. (SU) %p Either `AM' or `PM' according to the given time value, or the corresponding strings for the current locale. Noon is treated as `pm' and midnight as `am'. %P Like %p but in lowercase: `am' or `pm' or a corre­ sponding string for the current locale. (GNU) %r The time in a.m. or p.m. notation. In the POSIX locale this is equivalent to `%I:%M:%S %p'. (SU) %R The time in 24-hour notation (%H:%M). (SU) For a version including the seconds, see %T below. %s The number of seconds since the Epoch, i.e., since 1970-01-01 00:00:00 UTC. (TZ) %S The second as a decimal number (range 00 to 61). %t A tab character. (SU) %T The time in 24-hour notation (%H:%M:%S). (SU) %u The day of the week as a decimal, range 1 to 7, Monday being 1. See also %w. (SU) %U The week number of the current year as a decimal number, range 00 to 53, starting with the first Sunday as the first day of week 01. See also %V and %W. %V The ISO 8601:1988 week number of the current year as a decimal number, range 01 to 53, where week 1 is the first week that has at least 4 days in the current year, and with Monday as the first day of the week. See also %U and %W. (SU) %w The day of the week as a decimal, range 0 to 6, Sunday being 0. See also %u. %W The week number of the current year as a decimal number, range 00 to 53, starting with the first Monday as the first day of week 01. %x The preferred date representation for the current locale without the time. %X The preferred time representation for the current locale without the date. %y The year as a decimal number without a century (range 00 to 99). %Y The year as a decimal number including the century. %z The time-zone as hour offset from GMT. Required to emit RFC822-conformant dates (using "%a, %d %b %Y %H:%M:%S %z"). (GNU) %Z The time zone or name or abbreviation. %+ The date and time in date(1) format. (TZ) %% A literal `%' character. Some of these may not work on some systems.
2000-08-02 07:13:12 +00:00
Sys_Printf ("%s", msg2); // also echo to debugging console
if (sv_logfile)
Qprintf (sv_logfile, "%s", msg2);
2000-05-10 11:29:38 +00:00
}
}
/*
================
Con_DPrintf
A Con_Printf that only shows up if the "developer" cvar is set
================
*/
void
Con_DPrintf (char *fmt, ...)
2000-05-10 11:29:38 +00:00
{
va_list argptr;
char msg[MAXPRINTMSG];
2000-05-10 11:29:38 +00:00
if (!developer->int_val)
2000-05-10 11:29:38 +00:00
return;
va_start (argptr, fmt);
2000-10-02 04:24:08 +00:00
vsnprintf (msg, sizeof (msg), fmt, argptr);
2000-05-10 11:29:38 +00:00
va_end (argptr);
2000-05-10 11:29:38 +00:00
Con_Printf ("%s", msg);
}
/*
=============================================================================
EVENT MESSAGES
=============================================================================
*/
static void
SV_PrintToClient (client_t *cl, int level, char *string)
2000-05-10 11:29:38 +00:00
{
ClientReliableWrite_Begin (cl, svc_print, strlen (string) + 3);
2000-05-10 11:29:38 +00:00
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, ...)
2000-05-10 11:29:38 +00:00
{
va_list argptr;
char string[1024];
2000-05-10 11:29:38 +00:00
if (level < cl->messagelevel)
return;
va_start (argptr, fmt);
vsnprintf (string, sizeof (string), fmt, argptr);
2000-05-10 11:29:38 +00:00
va_end (argptr);
SV_PrintToClient (cl, level, string);
2000-05-10 11:29:38 +00:00
}
/*
=================
SV_BroadcastPrintf
Sends text to all active clients
=================
*/
void
SV_BroadcastPrintf (int level, char *fmt, ...)
2000-05-10 11:29:38 +00:00
{
va_list argptr;
char string[1024];
client_t *cl;
int i;
2000-05-10 11:29:38 +00:00
va_start (argptr, fmt);
vsnprintf (string, sizeof (string), fmt, argptr);
2000-05-10 11:29:38 +00:00
va_end (argptr);
Sys_Printf ("%s", string); // print to the console
for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) {
2000-05-10 11:29:38 +00:00
if (level < cl->messagelevel)
continue;
if (!cl->state)
continue;
SV_PrintToClient (cl, level, string);
2000-05-10 11:29:38 +00:00
}
}
/*
=================
SV_BroadcastCommand
Sends text to all active clients
=================
*/
void
SV_BroadcastCommand (char *fmt, ...)
2000-05-10 11:29:38 +00:00
{
va_list argptr;
char string[1024];
2000-05-10 11:29:38 +00:00
if (!sv.state)
return;
va_start (argptr, fmt);
vsnprintf (string, sizeof (string), fmt, argptr);
2000-05-10 11:29:38 +00:00
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)
2000-05-10 11:29:38 +00:00
{
client_t *client;
byte *mask;
mleaf_t *leaf;
int leafnum;
int j;
qboolean reliable;
2000-05-10 11:29:38 +00:00
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);
2000-05-10 11:29:38 +00:00
}
// send the data to all relevent clients
for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) {
2000-05-10 11:29:38 +00:00
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)
2000-05-10 11:29:38 +00:00
goto inrange;
}
leaf = Mod_PointInLeaf (client->edict->v.origin, sv.worldmodel);
if (leaf) {
2000-05-10 11:29:38 +00:00
// -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");
2000-05-10 11:29:38 +00:00
continue;
}
}
inrange:
2000-05-10 11:29:38 +00:00
if (reliable) {
ClientReliableCheckBlock (client, sv.multicast.cursize);
ClientReliableWrite_SZ (client, sv.multicast.data,
sv.multicast.cursize);
2000-05-10 11:29:38 +00:00
} else
SZ_Write (&client->datagram, sv.multicast.data,
sv.multicast.cursize);
2000-05-10 11:29:38 +00:00
}
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;
2000-05-10 11:29:38 +00:00
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);
2000-05-10 11:29:38 +00:00
if ((channel & 8) || !sv_phs->int_val) // no PHS flag
2000-05-10 11:29:38 +00:00
{
if (channel & 8)
reliable = true; // sounds that break the phs are
// reliable
2000-05-10 11:29:38 +00:00
use_phs = false;
channel &= 7;
} else
2000-05-10 11:29:38 +00:00
use_phs = true;
// if (channel == CHAN_BODY || channel == CHAN_VOICE)
// reliable = true;
2000-05-10 11:29:38 +00:00
channel = (ent << 3) | channel;
2000-05-10 11:29:38 +00:00
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 {
2000-05-10 11:29:38 +00:00
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);
2000-05-10 11:29:38 +00:00
MSG_WriteByte (&sv.multicast, sound_num);
for (i = 0; i < 3; i++)
2000-05-10 11:29:38 +00:00
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);
}
2000-05-10 11:29:38 +00:00
/*
===============================================================================
FRAME UPDATES
===============================================================================
*/
int sv_nailmodel, sv_supernailmodel, sv_playermodel;
2000-05-10 11:29:38 +00:00
void
SV_FindModelNumbers (void)
2000-05-10 11:29:38 +00:00
{
int i;
2000-05-10 11:29:38 +00:00
sv_nailmodel = -1;
sv_supernailmodel = -1;
sv_playermodel = -1;
for (i = 0; i < MAX_MODELS; i++) {
2000-05-10 11:29:38 +00:00
if (!sv.model_precache[i])
break;
if (!strcmp (sv.model_precache[i], "progs/spike.mdl"))
2000-05-10 11:29:38 +00:00
sv_nailmodel = i;
if (!strcmp (sv.model_precache[i], "progs/s_spike.mdl"))
2000-05-10 11:29:38 +00:00
sv_supernailmodel = i;
if (!strcmp (sv.model_precache[i], "progs/player.mdl"))
2000-05-10 11:29:38 +00:00
sv_playermodel = i;
}
}
/*
==================
SV_WriteClientdataToMessage
==================
*/
void
SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg)
2000-05-10 11:29:38 +00:00
{
int i;
edict_t *other;
edict_t *ent;
2000-05-10 11:29:38 +00:00
ent = client->edict;
// send the chokecount for r_netgraph
if (client->chokecount) {
2000-05-10 11:29:38 +00:00
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);
2000-05-10 11:29:38 +00:00
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]));
2000-05-10 11:29:38 +00:00
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) {
2000-05-10 11:29:38 +00:00
MSG_WriteByte (msg, svc_setangle);
for (i = 0; i < 3; i++)
MSG_WriteAngle (msg, ent->v.angles[i]);
2000-05-10 11:29:38 +00:00
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)
2000-05-10 11:29:38 +00:00
{
edict_t *ent;
int stats[MAX_CL_STATS];
int i;
2000-05-10 11:29:38 +00:00
ent = client->edict;
memset (stats, 0, sizeof (stats));
2000-05-10 11:29:38 +00:00
// 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));
2000-05-10 11:29:38 +00:00
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);
2000-05-10 11:29:38 +00:00
// 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);
2000-05-13 22:51:05 +00:00
for (i = 0; i < MAX_CL_STATS; i++)
if (stats[i] != client->stats[i]) {
2000-05-10 11:29:38 +00:00
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]);
2000-05-10 11:29:38 +00:00
}
}
}
/*
=======================
SV_SendClientDatagram
=======================
*/
qboolean
SV_SendClientDatagram (client_t *client)
2000-05-10 11:29:38 +00:00
{
byte buf[MAX_DATAGRAM];
sizebuf_t msg;
2000-05-10 11:29:38 +00:00
msg.data = buf;
msg.maxsize = sizeof (buf);
2000-05-10 11:29:38 +00:00
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) {
2000-05-10 11:29:38 +00:00
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)
2000-05-10 11:29:38 +00:00
{
int i, j;
client_t *client;
eval_t *val;
edict_t *ent;
2000-05-10 11:29:38 +00:00
// 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++) {
2000-05-10 11:29:38 +00:00
if (host_client->state != cs_spawned)
continue;
if (host_client->sendinfo) {
2000-05-10 11:29:38 +00:00
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++) {
2000-05-10 11:29:38 +00:00
if (client->state < cs_connected)
continue;
ClientReliableWrite_Begin (client, svc_updatefrags, 4);
ClientReliableWrite_Byte (client, i);
ClientReliableWrite_Short (client, host_client->edict->v.frags);
2000-05-10 11:29:38 +00:00
}
host_client->old_frags = host_client->edict->v.frags;
}
// maxspeed/entgravity changes
ent = host_client->edict;
val = GetEdictFieldValue (ent, "gravity");
2000-05-10 11:29:38 +00:00
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);
2000-05-10 11:29:38 +00:00
}
val = GetEdictFieldValue (ent, "maxspeed");
2000-05-10 11:29:38 +00:00
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);
2000-05-10 11:29:38 +00:00
}
}
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++) {
2000-05-10 11:29:38 +00:00
if (client->state < cs_connected)
continue; // reliables go to all connected or
// spawned
2000-05-10 11:29:38 +00:00
ClientReliableCheckBlock (client, sv.reliable_datagram.cursize);
ClientReliableWrite_SZ (client, sv.reliable_datagram.data,
sv.reliable_datagram.cursize);
2000-05-10 11:29:38 +00:00
if (client->state != cs_spawned)
continue; // datagrams only go to spawned
SZ_Write (&client->datagram, sv.datagram.data, sv.datagram.cursize);
2000-05-10 11:29:38 +00:00
}
SZ_Clear (&sv.reliable_datagram);
SZ_Clear (&sv.datagram);
}
2000-05-19 23:51:56 +00:00
#if defined(_WIN32) && !defined(__GNUC__)
2000-05-10 11:29:38 +00:00
#pragma optimize( "", off )
#endif
/*
=======================
SV_SendClientMessages
=======================
*/
void
SV_SendClientMessages (void)
2000-05-10 11:29:38 +00:00
{
int i, j;
client_t *c;
2000-05-10 11:29:38 +00:00
// update frags, names, etc
SV_UpdateToReliableMessages ();
// build individual updates
for (i = 0, c = svs.clients; i < MAX_CLIENTS; i++, c++) {
2000-05-10 11:29:38 +00:00
if (!c->state)
continue;
if (c->drop) {
SV_DropClient (c);
2000-05-10 11:29:38 +00:00
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]);
2000-05-10 11:29:38 +00:00
// it'll fit
SZ_Write (&c->netchan.message, c->backbuf_data[0],
c->backbuf_size[0]);
// move along, move along
2000-05-10 11:29:38 +00:00
for (j = 1; j < c->num_backbuf; j++) {
memcpy (c->backbuf_data[j - 1], c->backbuf_data[j],
c->backbuf_size[j]);
2000-05-10 11:29:38 +00:00
c->backbuf_size[j - 1] = c->backbuf_size[j];
}
c->num_backbuf--;
if (c->num_backbuf) {
memset (&c->backbuf, 0, sizeof (c->backbuf));
2000-05-10 11:29:38 +00:00
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]);
2000-05-10 11:29:38 +00:00
}
}
}
// if the reliable message overflowed,
// drop the client
if (c->netchan.message.overflowed) {
2000-05-10 11:29:38 +00:00
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);
2000-05-10 11:29:38 +00:00
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)) {
2000-05-10 11:29:38 +00:00
c->chokecount++;
continue; // bandwidth choke
2000-05-10 11:29:38 +00:00
}
if (c->state == cs_spawned)
SV_SendClientDatagram (c);
else
Netchan_Transmit (&c->netchan, 0, NULL); // just update
// reliable
2000-05-10 11:29:38 +00:00
}
}
2000-05-19 23:51:56 +00:00
#if defined(_WIN32) && !defined(__GNUC__)
2000-05-10 11:29:38 +00:00
#pragma optimize( "", on )
#endif
/*
=======================
SV_SendMessagesToAll
FIXME: does this sequence right?
=======================
*/
void
SV_SendMessagesToAll (void)
2000-05-10 11:29:38 +00:00
{
int i;
client_t *c;
2000-05-10 11:29:38 +00:00
for (i = 0, c = svs.clients; i < MAX_CLIENTS; i++, c++)
if (c->state) // FIXME: should this only send to
// active?
2000-05-10 11:29:38 +00:00
c->send_message = true;
2000-05-10 11:29:38 +00:00
SV_SendClientMessages ();
}