diff options
| author | Travis Bradshaw <travis.bradshaw@idsoftware.com> | 2012-01-31 14:32:09 -0600 |
|---|---|---|
| committer | Travis Bradshaw <travis.bradshaw@idsoftware.com> | 2012-01-31 14:32:09 -0600 |
| commit | 0023db327bc1db00068284b70e1db45857aeee35 (patch) | |
| tree | 01dd8366d20bd674741cfe8ceb069d82a2e33973 /QW/server/sv_send.c | |
| download | quake-pum-0023db327bc1db00068284b70e1db45857aeee35.tar.gz | |
The Quake sources as originally release under the GPL license on December 21, 1999
Diffstat (limited to 'QW/server/sv_send.c')
| -rw-r--r-- | QW/server/sv_send.c | 806 |
1 files changed, 806 insertions, 0 deletions
diff --git a/QW/server/sv_send.c b/QW/server/sv_send.c new file mode 100644 index 0000000..6d0172b --- /dev/null +++ b/QW/server/sv_send.c @@ -0,0 +1,806 @@ +/* +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 the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// sv_main.c -- server main program + +#include "qwsvdef.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; + +/* +================== +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 +// FIXME: make a buffer size safe vsprintf? +void Con_Printf (char *fmt, ...) +{ + va_list argptr; + char msg[MAXPRINTMSG]; + + va_start (argptr,fmt); + vsprintf (msg,fmt,argptr); + va_end (argptr); + + // add to redirected message + if (sv_redirected) + { + if (strlen (msg) + strlen(outputbuf) > sizeof(outputbuf) - 1) + SV_FlushRedirect (); + strcat (outputbuf, msg); + return; + } + + Sys_Printf ("%s", msg); // also echo to debugging console + if (sv_logfile) + fprintf (sv_logfile, "%s", msg); +} + +/* +================ +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.value) + return; + + va_start (argptr,fmt); + vsprintf (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); + vsprintf (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); + vsprintf (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); + vsprintf (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.value) // 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); + + 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); +} + +#ifdef _WIN32 +#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 + + } +} + +#ifdef _WIN32 +#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 (); +} + |
