home *** CD-ROM | disk | FTP | other *** search
- /*
- 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 ();
- }
-
-