From a8f828a066773a59fdf646299359d35ae0e845af Mon Sep 17 00:00:00 2001 From: wyatt8740 Date: Mon, 6 Jan 2014 12:02:16 -0500 Subject: Removed accidental upload of copyrighted map files --- WinQuake/sv_main.c | 2400 ++++++++++++++++++++++++++-------------------------- 1 file changed, 1200 insertions(+), 1200 deletions(-) (limited to 'WinQuake/sv_main.c') diff --git a/WinQuake/sv_main.c b/WinQuake/sv_main.c index deda690..ae38b18 100644 --- a/WinQuake/sv_main.c +++ b/WinQuake/sv_main.c @@ -1,1200 +1,1200 @@ -/* -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 "quakedef.h" - -server_t sv; -server_static_t svs; - -char localmodels[MAX_MODELS][5]; // inline model names for precache - -//============================================================================ - -/* -=============== -SV_Init -=============== -*/ -void SV_Init (void) -{ - int i; - extern cvar_t sv_maxvelocity; - extern cvar_t sv_gravity; - extern cvar_t sv_nostep; - extern cvar_t sv_friction; - extern cvar_t sv_edgefriction; - extern cvar_t sv_stopspeed; - extern cvar_t sv_maxspeed; - extern cvar_t sv_accelerate; - extern cvar_t sv_idealpitchscale; - extern cvar_t sv_aim; - - Cvar_RegisterVariable (&sv_maxvelocity); - Cvar_RegisterVariable (&sv_gravity); - Cvar_RegisterVariable (&sv_friction); - Cvar_RegisterVariable (&sv_edgefriction); - Cvar_RegisterVariable (&sv_stopspeed); - Cvar_RegisterVariable (&sv_maxspeed); - Cvar_RegisterVariable (&sv_accelerate); - Cvar_RegisterVariable (&sv_idealpitchscale); - Cvar_RegisterVariable (&sv_aim); - Cvar_RegisterVariable (&sv_nostep); - - for (i=0 ; i MAX_DATAGRAM-16) - return; - MSG_WriteByte (&sv.datagram, svc_particle); - MSG_WriteCoord (&sv.datagram, org[0]); - MSG_WriteCoord (&sv.datagram, org[1]); - MSG_WriteCoord (&sv.datagram, org[2]); - for (i=0 ; i<3 ; i++) - { - v = dir[i]*16; - if (v > 127) - v = 127; - else if (v < -128) - v = -128; - MSG_WriteChar (&sv.datagram, v); - } - MSG_WriteByte (&sv.datagram, count); - MSG_WriteByte (&sv.datagram, color); -} - -/* -================== -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; - - 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 > 7) - Sys_Error ("SV_StartSound: channel = %i", channel); - - if (sv.datagram.cursize > MAX_DATAGRAM-16) - return; - -// find precache number for sound - for (sound_num=1 ; sound_numv.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i])); -} - -/* -============================================================================== - -CLIENT SPAWNING - -============================================================================== -*/ - -/* -================ -SV_SendServerinfo - -Sends the first message from the server to a connected client. -This will be sent on the initial connection and upon each server load. -================ -*/ -void SV_SendServerinfo (client_t *client) -{ - char **s; - char message[2048]; - - MSG_WriteByte (&client->message, svc_print); - sprintf (message, "%c\nVERSION %4.2f SERVER (%i CRC)", 2, VERSION, pr_crc); - MSG_WriteString (&client->message,message); - - MSG_WriteByte (&client->message, svc_serverinfo); - MSG_WriteLong (&client->message, PROTOCOL_VERSION); - MSG_WriteByte (&client->message, svs.maxclients); - - if (!coop.value && deathmatch.value) - MSG_WriteByte (&client->message, GAME_DEATHMATCH); - else - MSG_WriteByte (&client->message, GAME_COOP); - - sprintf (message, pr_strings+sv.edicts->v.message); - - MSG_WriteString (&client->message,message); - - for (s = sv.model_precache+1 ; *s ; s++) - MSG_WriteString (&client->message, *s); - MSG_WriteByte (&client->message, 0); - - for (s = sv.sound_precache+1 ; *s ; s++) - MSG_WriteString (&client->message, *s); - MSG_WriteByte (&client->message, 0); - -// send music - MSG_WriteByte (&client->message, svc_cdtrack); - MSG_WriteByte (&client->message, sv.edicts->v.sounds); - MSG_WriteByte (&client->message, sv.edicts->v.sounds); - -// set view - MSG_WriteByte (&client->message, svc_setview); - MSG_WriteShort (&client->message, NUM_FOR_EDICT(client->edict)); - - MSG_WriteByte (&client->message, svc_signonnum); - MSG_WriteByte (&client->message, 1); - - client->sendsignon = true; - client->spawned = false; // need prespawn, spawn, etc -} - -/* -================ -SV_ConnectClient - -Initializes a client_t for a new net connection. This will only be called -once for a player each game, not once for each level change. -================ -*/ -void SV_ConnectClient (int clientnum) -{ - edict_t *ent; - client_t *client; - int edictnum; - struct qsocket_s *netconnection; - int i; - float spawn_parms[NUM_SPAWN_PARMS]; - - client = svs.clients + clientnum; - - Con_DPrintf ("Client %s connected\n", client->netconnection->address); - - edictnum = clientnum+1; - - ent = EDICT_NUM(edictnum); - -// set up the client_t - netconnection = client->netconnection; - - if (sv.loadgame) - memcpy (spawn_parms, client->spawn_parms, sizeof(spawn_parms)); - memset (client, 0, sizeof(*client)); - client->netconnection = netconnection; - - strcpy (client->name, "unconnected"); - client->active = true; - client->spawned = false; - client->edict = ent; - client->message.data = client->msgbuf; - client->message.maxsize = sizeof(client->msgbuf); - client->message.allowoverflow = true; // we can catch it - -#ifdef IDGODS - client->privileged = IsID(&client->netconnection->addr); -#else - client->privileged = false; -#endif - - if (sv.loadgame) - memcpy (client->spawn_parms, spawn_parms, sizeof(spawn_parms)); - else - { - // call the progs to get default spawn parms for the new client - PR_ExecuteProgram (pr_global_struct->SetNewParms); - for (i=0 ; ispawn_parms[i] = (&pr_global_struct->parm1)[i]; - } - - SV_SendServerinfo (client); -} - - -/* -=================== -SV_CheckForNewClients - -=================== -*/ -void SV_CheckForNewClients (void) -{ - struct qsocket_s *ret; - int i; - -// -// check for new connections -// - while (1) - { - ret = NET_CheckNewConnections (); - if (!ret) - break; - - // - // init a new client structure - // - for (i=0 ; icontents < 0) - { - if (node->contents != CONTENTS_SOLID) - { - pvs = Mod_LeafPVS ( (mleaf_t *)node, sv.worldmodel); - for (i=0 ; iplane; - d = DotProduct (org, plane->normal) - plane->dist; - if (d > 8) - node = node->children[0]; - else if (d < -8) - node = node->children[1]; - else - { // go down both - SV_AddToFatPVS (org, node->children[0]); - node = node->children[1]; - } - } -} - -/* -============= -SV_FatPVS - -Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the -given point. -============= -*/ -byte *SV_FatPVS (vec3_t org) -{ - fatbytes = (sv.worldmodel->numleafs+31)>>3; - Q_memset (fatpvs, 0, fatbytes); - SV_AddToFatPVS (org, sv.worldmodel->nodes); - return fatpvs; -} - -//============================================================================= - - -/* -============= -SV_WriteEntitiesToClient - -============= -*/ -void SV_WriteEntitiesToClient (edict_t *clent, sizebuf_t *msg) -{ - int e, i; - int bits; - byte *pvs; - vec3_t org; - float miss; - edict_t *ent; - -// find the client's PVS - VectorAdd (clent->v.origin, clent->v.view_ofs, org); - pvs = SV_FatPVS (org); - -// send over all entities (excpet the client) that touch the pvs - ent = NEXT_EDICT(sv.edicts); - for (e=1 ; ev.effects == EF_NODRAW) - continue; -#endif - -// ignore if not touching a PV leaf - if (ent != clent) // clent is ALLWAYS sent - { -// ignore ents without visible models - if (!ent->v.modelindex || !pr_strings[ent->v.model]) - continue; - - for (i=0 ; i < ent->num_leafs ; i++) - if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) )) - break; - - if (i == ent->num_leafs) - continue; // not visible - } - - if (msg->maxsize - msg->cursize < 16) - { - Con_Printf ("packet overflow\n"); - return; - } - -// send an update - bits = 0; - - for (i=0 ; i<3 ; i++) - { - miss = ent->v.origin[i] - ent->baseline.origin[i]; - if ( miss < -0.1 || miss > 0.1 ) - bits |= U_ORIGIN1<v.angles[0] != ent->baseline.angles[0] ) - bits |= U_ANGLE1; - - if ( ent->v.angles[1] != ent->baseline.angles[1] ) - bits |= U_ANGLE2; - - if ( ent->v.angles[2] != ent->baseline.angles[2] ) - bits |= U_ANGLE3; - - if (ent->v.movetype == MOVETYPE_STEP) - bits |= U_NOLERP; // don't mess up the step animation - - if (ent->baseline.colormap != ent->v.colormap) - bits |= U_COLORMAP; - - if (ent->baseline.skin != ent->v.skin) - bits |= U_SKIN; - - if (ent->baseline.frame != ent->v.frame) - bits |= U_FRAME; - - if (ent->baseline.effects != ent->v.effects) - bits |= U_EFFECTS; - - if (ent->baseline.modelindex != ent->v.modelindex) - bits |= U_MODEL; - - if (e >= 256) - bits |= U_LONGENTITY; - - if (bits >= 256) - bits |= U_MOREBITS; - - // - // write the message - // - MSG_WriteByte (msg,bits | U_SIGNAL); - - if (bits & U_MOREBITS) - MSG_WriteByte (msg, bits>>8); - if (bits & U_LONGENTITY) - MSG_WriteShort (msg,e); - else - MSG_WriteByte (msg,e); - - if (bits & U_MODEL) - MSG_WriteByte (msg, ent->v.modelindex); - if (bits & U_FRAME) - MSG_WriteByte (msg, ent->v.frame); - if (bits & U_COLORMAP) - MSG_WriteByte (msg, ent->v.colormap); - if (bits & U_SKIN) - MSG_WriteByte (msg, ent->v.skin); - if (bits & U_EFFECTS) - MSG_WriteByte (msg, ent->v.effects); - if (bits & U_ORIGIN1) - MSG_WriteCoord (msg, ent->v.origin[0]); - if (bits & U_ANGLE1) - MSG_WriteAngle(msg, ent->v.angles[0]); - if (bits & U_ORIGIN2) - MSG_WriteCoord (msg, ent->v.origin[1]); - if (bits & U_ANGLE2) - MSG_WriteAngle(msg, ent->v.angles[1]); - if (bits & U_ORIGIN3) - MSG_WriteCoord (msg, ent->v.origin[2]); - if (bits & U_ANGLE3) - MSG_WriteAngle(msg, ent->v.angles[2]); - } -} - -/* -============= -SV_CleanupEnts - -============= -*/ -void SV_CleanupEnts (void) -{ - int e; - edict_t *ent; - - ent = NEXT_EDICT(sv.edicts); - for (e=1 ; ev.effects = (int)ent->v.effects & ~EF_MUZZLEFLASH; - } - -} - -/* -================== -SV_WriteClientdataToMessage - -================== -*/ -void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg) -{ - int bits; - int i; - edict_t *other; - int items; -#ifndef QUAKE2 - eval_t *val; -#endif - -// -// send a damage message -// - 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; - } - -// -// send the current viewpos offset from the view entity -// - SV_SetIdealPitch (); // how much to look up / down ideally - -// 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; - } - - bits = 0; - - if (ent->v.view_ofs[2] != DEFAULT_VIEWHEIGHT) - bits |= SU_VIEWHEIGHT; - - if (ent->v.idealpitch) - bits |= SU_IDEALPITCH; - -// stuff the sigil bits into the high bits of items for sbar, or else -// mix in items2 -#ifdef QUAKE2 - items = (int)ent->v.items | ((int)ent->v.items2 << 23); -#else - val = GetEdictFieldValue(ent, "items2"); - - if (val) - items = (int)ent->v.items | ((int)val->_float << 23); - else - items = (int)ent->v.items | ((int)pr_global_struct->serverflags << 28); -#endif - - bits |= SU_ITEMS; - - if ( (int)ent->v.flags & FL_ONGROUND) - bits |= SU_ONGROUND; - - if ( ent->v.waterlevel >= 2) - bits |= SU_INWATER; - - for (i=0 ; i<3 ; i++) - { - if (ent->v.punchangle[i]) - bits |= (SU_PUNCH1<v.velocity[i]) - bits |= (SU_VELOCITY1<v.weaponframe) - bits |= SU_WEAPONFRAME; - - if (ent->v.armorvalue) - bits |= SU_ARMOR; - -// if (ent->v.weapon) - bits |= SU_WEAPON; - -// send the data - - MSG_WriteByte (msg, svc_clientdata); - MSG_WriteShort (msg, bits); - - if (bits & SU_VIEWHEIGHT) - MSG_WriteChar (msg, ent->v.view_ofs[2]); - - if (bits & SU_IDEALPITCH) - MSG_WriteChar (msg, ent->v.idealpitch); - - for (i=0 ; i<3 ; i++) - { - if (bits & (SU_PUNCH1<v.punchangle[i]); - if (bits & (SU_VELOCITY1<v.velocity[i]/16); - } - -// [always sent] if (bits & SU_ITEMS) - MSG_WriteLong (msg, items); - - if (bits & SU_WEAPONFRAME) - MSG_WriteByte (msg, ent->v.weaponframe); - if (bits & SU_ARMOR) - MSG_WriteByte (msg, ent->v.armorvalue); - if (bits & SU_WEAPON) - MSG_WriteByte (msg, SV_ModelIndex(pr_strings+ent->v.weaponmodel)); - - MSG_WriteShort (msg, ent->v.health); - MSG_WriteByte (msg, ent->v.currentammo); - MSG_WriteByte (msg, ent->v.ammo_shells); - MSG_WriteByte (msg, ent->v.ammo_nails); - MSG_WriteByte (msg, ent->v.ammo_rockets); - MSG_WriteByte (msg, ent->v.ammo_cells); - - if (standard_quake) - { - MSG_WriteByte (msg, ent->v.weapon); - } - else - { - for(i=0;i<32;i++) - { - if ( ((int)ent->v.weapon) & (1<edict, &msg); - - SV_WriteEntitiesToClient (client->edict, &msg); - -// copy the server datagram if there is space - if (msg.cursize + sv.datagram.cursize < msg.maxsize) - SZ_Write (&msg, sv.datagram.data, sv.datagram.cursize); - -// send the datagram - if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1) - { - SV_DropClient (true);// if the message couldn't send, kick off - return false; - } - - return true; -} - -/* -======================= -SV_UpdateToReliableMessages -======================= -*/ -void SV_UpdateToReliableMessages (void) -{ - int i, j; - client_t *client; - -// check for changes to be sent over the reliable streams - for (i=0, host_client = svs.clients ; iold_frags != host_client->edict->v.frags) - { - for (j=0, client = svs.clients ; jactive) - continue; - MSG_WriteByte (&client->message, svc_updatefrags); - MSG_WriteByte (&client->message, i); - MSG_WriteShort (&client->message, host_client->edict->v.frags); - } - - host_client->old_frags = host_client->edict->v.frags; - } - } - - for (j=0, client = svs.clients ; jactive) - continue; - SZ_Write (&client->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize); - } - - SZ_Clear (&sv.reliable_datagram); -} - - -/* -======================= -SV_SendNop - -Send a nop message without trashing or sending the accumulated client -message buffer -======================= -*/ -void SV_SendNop (client_t *client) -{ - sizebuf_t msg; - byte buf[4]; - - msg.data = buf; - msg.maxsize = sizeof(buf); - msg.cursize = 0; - - MSG_WriteChar (&msg, svc_nop); - - if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1) - SV_DropClient (true); // if the message couldn't send, kick off - client->last_message = realtime; -} - -/* -======================= -SV_SendClientMessages -======================= -*/ -void SV_SendClientMessages (void) -{ - int i; - -// update frags, names, etc - SV_UpdateToReliableMessages (); - -// build individual updates - for (i=0, host_client = svs.clients ; iactive) - continue; - - if (host_client->spawned) - { - if (!SV_SendClientDatagram (host_client)) - continue; - } - else - { - // the player isn't totally in the game yet - // send small keepalive messages if too much time has passed - // send a full message when the next signon stage has been requested - // some other message data (name changes, etc) may accumulate - // between signon stages - if (!host_client->sendsignon) - { - if (realtime - host_client->last_message > 5) - SV_SendNop (host_client); - continue; // don't send out non-signon messages - } - } - - // check for an overflowed message. Should only happen - // on a very fucked up connection that backs up a lot, then - // changes level - if (host_client->message.overflowed) - { - SV_DropClient (true); - host_client->message.overflowed = false; - continue; - } - - if (host_client->message.cursize || host_client->dropasap) - { - if (!NET_CanSendMessage (host_client->netconnection)) - { -// I_Printf ("can't write\n"); - continue; - } - - if (host_client->dropasap) - SV_DropClient (false); // went to another level - else - { - if (NET_SendMessage (host_client->netconnection - , &host_client->message) == -1) - SV_DropClient (true); // if the message couldn't send, kick off - SZ_Clear (&host_client->message); - host_client->last_message = realtime; - host_client->sendsignon = false; - } - } - } - - -// clear muzzle flashes - SV_CleanupEnts (); -} - - -/* -============================================================================== - -SERVER SPAWNING - -============================================================================== -*/ - -/* -================ -SV_ModelIndex - -================ -*/ -int SV_ModelIndex (char *name) -{ - int i; - - if (!name || !name[0]) - return 0; - - for (i=0 ; ifree) - continue; - if (entnum > svs.maxclients && !svent->v.modelindex) - continue; - - // - // create entity baseline - // - VectorCopy (svent->v.origin, svent->baseline.origin); - VectorCopy (svent->v.angles, svent->baseline.angles); - svent->baseline.frame = svent->v.frame; - svent->baseline.skin = svent->v.skin; - if (entnum > 0 && entnum <= svs.maxclients) - { - svent->baseline.colormap = entnum; - svent->baseline.modelindex = SV_ModelIndex("progs/player.mdl"); - } - else - { - svent->baseline.colormap = 0; - svent->baseline.modelindex = - SV_ModelIndex(pr_strings + svent->v.model); - } - - // - // add to the message - // - MSG_WriteByte (&sv.signon,svc_spawnbaseline); - MSG_WriteShort (&sv.signon,entnum); - - MSG_WriteByte (&sv.signon, svent->baseline.modelindex); - MSG_WriteByte (&sv.signon, svent->baseline.frame); - MSG_WriteByte (&sv.signon, svent->baseline.colormap); - MSG_WriteByte (&sv.signon, svent->baseline.skin); - for (i=0 ; i<3 ; i++) - { - MSG_WriteCoord(&sv.signon, svent->baseline.origin[i]); - MSG_WriteAngle(&sv.signon, svent->baseline.angles[i]); - } - } -} - - -/* -================ -SV_SendReconnect - -Tell all the clients that the server is changing levels -================ -*/ -void SV_SendReconnect (void) -{ - char data[128]; - sizebuf_t msg; - - msg.data = data; - msg.cursize = 0; - msg.maxsize = sizeof(data); - - MSG_WriteChar (&msg, svc_stufftext); - MSG_WriteString (&msg, "reconnect\n"); - NET_SendToAll (&msg, 5); - - if (cls.state != ca_dedicated) -#ifdef QUAKE2 - Cbuf_InsertText ("reconnect\n"); -#else - Cmd_ExecuteString ("reconnect\n", src_command); -#endif -} - - -/* -================ -SV_SaveSpawnparms - -Grabs the current state of each client for saving across the -transition to another level -================ -*/ -void SV_SaveSpawnparms (void) -{ - int i, j; - - svs.serverflags = pr_global_struct->serverflags; - - for (i=0, host_client = svs.clients ; iactive) - continue; - - // call the progs to get default spawn parms for the new client - pr_global_struct->self = EDICT_TO_PROG(host_client->edict); - PR_ExecuteProgram (pr_global_struct->SetChangeParms); - for (j=0 ; jspawn_parms[j] = (&pr_global_struct->parm1)[j]; - } -} - - -/* -================ -SV_SpawnServer - -This is called at the start of each level -================ -*/ -extern float scr_centertime_off; - -#ifdef QUAKE2 -void SV_SpawnServer (char *server, char *startspot) -#else -void SV_SpawnServer (char *server) -#endif -{ - edict_t *ent; - int i; - - // let's not have any servers with no name - if (hostname.string[0] == 0) - Cvar_Set ("hostname", "UNNAMED"); - scr_centertime_off = 0; - - Con_DPrintf ("SpawnServer: %s\n",server); - svs.changelevel_issued = false; // now safe to issue another - -// -// tell all connected clients that we are going to a new level -// - if (sv.active) - { - SV_SendReconnect (); - } - -// -// make cvars consistant -// - if (coop.value) - Cvar_SetValue ("deathmatch", 0); - current_skill = (int)(skill.value + 0.5); - if (current_skill < 0) - current_skill = 0; - if (current_skill > 3) - current_skill = 3; - - Cvar_SetValue ("skill", (float)current_skill); - -// -// set up the new server -// - Host_ClearMemory (); - - memset (&sv, 0, sizeof(sv)); - - strcpy (sv.name, server); -#ifdef QUAKE2 - if (startspot) - strcpy(sv.startspot, startspot); -#endif - -// load progs to get entity field count - PR_LoadProgs (); - -// allocate server memory - sv.max_edicts = MAX_EDICTS; - - sv.edicts = Hunk_AllocName (sv.max_edicts*pr_edict_size, "edicts"); - - sv.datagram.maxsize = sizeof(sv.datagram_buf); - sv.datagram.cursize = 0; - sv.datagram.data = sv.datagram_buf; - - sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf); - sv.reliable_datagram.cursize = 0; - sv.reliable_datagram.data = sv.reliable_datagram_buf; - - sv.signon.maxsize = sizeof(sv.signon_buf); - sv.signon.cursize = 0; - sv.signon.data = sv.signon_buf; - -// leave slots at start for clients only - sv.num_edicts = svs.maxclients+1; - for (i=0 ; inumsubmodels ; i++) - { - sv.model_precache[1+i] = localmodels[i]; - sv.models[i+1] = Mod_ForName (localmodels[i], false); - } - -// -// load the rest of the entities -// - ent = EDICT_NUM(0); - memset (&ent->v, 0, progs->entityfields * 4); - ent->free = false; - ent->v.model = sv.worldmodel->name - pr_strings; - ent->v.modelindex = 1; // world model - ent->v.solid = SOLID_BSP; - ent->v.movetype = MOVETYPE_PUSH; - - if (coop.value) - pr_global_struct->coop = coop.value; - else - pr_global_struct->deathmatch = deathmatch.value; - - pr_global_struct->mapname = sv.name - pr_strings; -#ifdef QUAKE2 - pr_global_struct->startspot = sv.startspot - pr_strings; -#endif - -// serverflags are for cross level information (sigils) - pr_global_struct->serverflags = svs.serverflags; - - ED_LoadFromFile (sv.worldmodel->entities); - - sv.active = true; - -// all setup is completed, any further precache statements are errors - sv.state = ss_active; - -// run two frames to allow everything to settle - host_frametime = 0.1; - SV_Physics (); - SV_Physics (); - -// create a baseline for more efficient communications - SV_CreateBaseline (); - -// send serverinfo to all connected clients - for (i=0,host_client = svs.clients ; iactive) - SV_SendServerinfo (host_client); - - Con_DPrintf ("Server spawned.\n"); -} - +/* +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 "quakedef.h" + +server_t sv; +server_static_t svs; + +char localmodels[MAX_MODELS][5]; // inline model names for precache + +//============================================================================ + +/* +=============== +SV_Init +=============== +*/ +void SV_Init (void) +{ + int i; + extern cvar_t sv_maxvelocity; + extern cvar_t sv_gravity; + extern cvar_t sv_nostep; + extern cvar_t sv_friction; + extern cvar_t sv_edgefriction; + extern cvar_t sv_stopspeed; + extern cvar_t sv_maxspeed; + extern cvar_t sv_accelerate; + extern cvar_t sv_idealpitchscale; + extern cvar_t sv_aim; + + Cvar_RegisterVariable (&sv_maxvelocity); + Cvar_RegisterVariable (&sv_gravity); + Cvar_RegisterVariable (&sv_friction); + Cvar_RegisterVariable (&sv_edgefriction); + Cvar_RegisterVariable (&sv_stopspeed); + Cvar_RegisterVariable (&sv_maxspeed); + Cvar_RegisterVariable (&sv_accelerate); + Cvar_RegisterVariable (&sv_idealpitchscale); + Cvar_RegisterVariable (&sv_aim); + Cvar_RegisterVariable (&sv_nostep); + + for (i=0 ; i MAX_DATAGRAM-16) + return; + MSG_WriteByte (&sv.datagram, svc_particle); + MSG_WriteCoord (&sv.datagram, org[0]); + MSG_WriteCoord (&sv.datagram, org[1]); + MSG_WriteCoord (&sv.datagram, org[2]); + for (i=0 ; i<3 ; i++) + { + v = dir[i]*16; + if (v > 127) + v = 127; + else if (v < -128) + v = -128; + MSG_WriteChar (&sv.datagram, v); + } + MSG_WriteByte (&sv.datagram, count); + MSG_WriteByte (&sv.datagram, color); +} + +/* +================== +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; + + 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 > 7) + Sys_Error ("SV_StartSound: channel = %i", channel); + + if (sv.datagram.cursize > MAX_DATAGRAM-16) + return; + +// find precache number for sound + for (sound_num=1 ; sound_numv.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i])); +} + +/* +============================================================================== + +CLIENT SPAWNING + +============================================================================== +*/ + +/* +================ +SV_SendServerinfo + +Sends the first message from the server to a connected client. +This will be sent on the initial connection and upon each server load. +================ +*/ +void SV_SendServerinfo (client_t *client) +{ + char **s; + char message[2048]; + + MSG_WriteByte (&client->message, svc_print); + sprintf (message, "%c\nVERSION %4.2f SERVER (%i CRC)", 2, VERSION, pr_crc); + MSG_WriteString (&client->message,message); + + MSG_WriteByte (&client->message, svc_serverinfo); + MSG_WriteLong (&client->message, PROTOCOL_VERSION); + MSG_WriteByte (&client->message, svs.maxclients); + + if (!coop.value && deathmatch.value) + MSG_WriteByte (&client->message, GAME_DEATHMATCH); + else + MSG_WriteByte (&client->message, GAME_COOP); + + sprintf (message, pr_strings+sv.edicts->v.message); + + MSG_WriteString (&client->message,message); + + for (s = sv.model_precache+1 ; *s ; s++) + MSG_WriteString (&client->message, *s); + MSG_WriteByte (&client->message, 0); + + for (s = sv.sound_precache+1 ; *s ; s++) + MSG_WriteString (&client->message, *s); + MSG_WriteByte (&client->message, 0); + +// send music + MSG_WriteByte (&client->message, svc_cdtrack); + MSG_WriteByte (&client->message, sv.edicts->v.sounds); + MSG_WriteByte (&client->message, sv.edicts->v.sounds); + +// set view + MSG_WriteByte (&client->message, svc_setview); + MSG_WriteShort (&client->message, NUM_FOR_EDICT(client->edict)); + + MSG_WriteByte (&client->message, svc_signonnum); + MSG_WriteByte (&client->message, 1); + + client->sendsignon = true; + client->spawned = false; // need prespawn, spawn, etc +} + +/* +================ +SV_ConnectClient + +Initializes a client_t for a new net connection. This will only be called +once for a player each game, not once for each level change. +================ +*/ +void SV_ConnectClient (int clientnum) +{ + edict_t *ent; + client_t *client; + int edictnum; + struct qsocket_s *netconnection; + int i; + float spawn_parms[NUM_SPAWN_PARMS]; + + client = svs.clients + clientnum; + + Con_DPrintf ("Client %s connected\n", client->netconnection->address); + + edictnum = clientnum+1; + + ent = EDICT_NUM(edictnum); + +// set up the client_t + netconnection = client->netconnection; + + if (sv.loadgame) + memcpy (spawn_parms, client->spawn_parms, sizeof(spawn_parms)); + memset (client, 0, sizeof(*client)); + client->netconnection = netconnection; + + strcpy (client->name, "unconnected"); + client->active = true; + client->spawned = false; + client->edict = ent; + client->message.data = client->msgbuf; + client->message.maxsize = sizeof(client->msgbuf); + client->message.allowoverflow = true; // we can catch it + +#ifdef IDGODS + client->privileged = IsID(&client->netconnection->addr); +#else + client->privileged = false; +#endif + + if (sv.loadgame) + memcpy (client->spawn_parms, spawn_parms, sizeof(spawn_parms)); + else + { + // call the progs to get default spawn parms for the new client + PR_ExecuteProgram (pr_global_struct->SetNewParms); + for (i=0 ; ispawn_parms[i] = (&pr_global_struct->parm1)[i]; + } + + SV_SendServerinfo (client); +} + + +/* +=================== +SV_CheckForNewClients + +=================== +*/ +void SV_CheckForNewClients (void) +{ + struct qsocket_s *ret; + int i; + +// +// check for new connections +// + while (1) + { + ret = NET_CheckNewConnections (); + if (!ret) + break; + + // + // init a new client structure + // + for (i=0 ; icontents < 0) + { + if (node->contents != CONTENTS_SOLID) + { + pvs = Mod_LeafPVS ( (mleaf_t *)node, sv.worldmodel); + for (i=0 ; iplane; + d = DotProduct (org, plane->normal) - plane->dist; + if (d > 8) + node = node->children[0]; + else if (d < -8) + node = node->children[1]; + else + { // go down both + SV_AddToFatPVS (org, node->children[0]); + node = node->children[1]; + } + } +} + +/* +============= +SV_FatPVS + +Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the +given point. +============= +*/ +byte *SV_FatPVS (vec3_t org) +{ + fatbytes = (sv.worldmodel->numleafs+31)>>3; + Q_memset (fatpvs, 0, fatbytes); + SV_AddToFatPVS (org, sv.worldmodel->nodes); + return fatpvs; +} + +//============================================================================= + + +/* +============= +SV_WriteEntitiesToClient + +============= +*/ +void SV_WriteEntitiesToClient (edict_t *clent, sizebuf_t *msg) +{ + int e, i; + int bits; + byte *pvs; + vec3_t org; + float miss; + edict_t *ent; + +// find the client's PVS + VectorAdd (clent->v.origin, clent->v.view_ofs, org); + pvs = SV_FatPVS (org); + +// send over all entities (excpet the client) that touch the pvs + ent = NEXT_EDICT(sv.edicts); + for (e=1 ; ev.effects == EF_NODRAW) + continue; +#endif + +// ignore if not touching a PV leaf + if (ent != clent) // clent is ALLWAYS sent + { +// ignore ents without visible models + if (!ent->v.modelindex || !pr_strings[ent->v.model]) + continue; + + for (i=0 ; i < ent->num_leafs ; i++) + if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) )) + break; + + if (i == ent->num_leafs) + continue; // not visible + } + + if (msg->maxsize - msg->cursize < 16) + { + Con_Printf ("packet overflow\n"); + return; + } + +// send an update + bits = 0; + + for (i=0 ; i<3 ; i++) + { + miss = ent->v.origin[i] - ent->baseline.origin[i]; + if ( miss < -0.1 || miss > 0.1 ) + bits |= U_ORIGIN1<v.angles[0] != ent->baseline.angles[0] ) + bits |= U_ANGLE1; + + if ( ent->v.angles[1] != ent->baseline.angles[1] ) + bits |= U_ANGLE2; + + if ( ent->v.angles[2] != ent->baseline.angles[2] ) + bits |= U_ANGLE3; + + if (ent->v.movetype == MOVETYPE_STEP) + bits |= U_NOLERP; // don't mess up the step animation + + if (ent->baseline.colormap != ent->v.colormap) + bits |= U_COLORMAP; + + if (ent->baseline.skin != ent->v.skin) + bits |= U_SKIN; + + if (ent->baseline.frame != ent->v.frame) + bits |= U_FRAME; + + if (ent->baseline.effects != ent->v.effects) + bits |= U_EFFECTS; + + if (ent->baseline.modelindex != ent->v.modelindex) + bits |= U_MODEL; + + if (e >= 256) + bits |= U_LONGENTITY; + + if (bits >= 256) + bits |= U_MOREBITS; + + // + // write the message + // + MSG_WriteByte (msg,bits | U_SIGNAL); + + if (bits & U_MOREBITS) + MSG_WriteByte (msg, bits>>8); + if (bits & U_LONGENTITY) + MSG_WriteShort (msg,e); + else + MSG_WriteByte (msg,e); + + if (bits & U_MODEL) + MSG_WriteByte (msg, ent->v.modelindex); + if (bits & U_FRAME) + MSG_WriteByte (msg, ent->v.frame); + if (bits & U_COLORMAP) + MSG_WriteByte (msg, ent->v.colormap); + if (bits & U_SKIN) + MSG_WriteByte (msg, ent->v.skin); + if (bits & U_EFFECTS) + MSG_WriteByte (msg, ent->v.effects); + if (bits & U_ORIGIN1) + MSG_WriteCoord (msg, ent->v.origin[0]); + if (bits & U_ANGLE1) + MSG_WriteAngle(msg, ent->v.angles[0]); + if (bits & U_ORIGIN2) + MSG_WriteCoord (msg, ent->v.origin[1]); + if (bits & U_ANGLE2) + MSG_WriteAngle(msg, ent->v.angles[1]); + if (bits & U_ORIGIN3) + MSG_WriteCoord (msg, ent->v.origin[2]); + if (bits & U_ANGLE3) + MSG_WriteAngle(msg, ent->v.angles[2]); + } +} + +/* +============= +SV_CleanupEnts + +============= +*/ +void SV_CleanupEnts (void) +{ + int e; + edict_t *ent; + + ent = NEXT_EDICT(sv.edicts); + for (e=1 ; ev.effects = (int)ent->v.effects & ~EF_MUZZLEFLASH; + } + +} + +/* +================== +SV_WriteClientdataToMessage + +================== +*/ +void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg) +{ + int bits; + int i; + edict_t *other; + int items; +#ifndef QUAKE2 + eval_t *val; +#endif + +// +// send a damage message +// + 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; + } + +// +// send the current viewpos offset from the view entity +// + SV_SetIdealPitch (); // how much to look up / down ideally + +// 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; + } + + bits = 0; + + if (ent->v.view_ofs[2] != DEFAULT_VIEWHEIGHT) + bits |= SU_VIEWHEIGHT; + + if (ent->v.idealpitch) + bits |= SU_IDEALPITCH; + +// stuff the sigil bits into the high bits of items for sbar, or else +// mix in items2 +#ifdef QUAKE2 + items = (int)ent->v.items | ((int)ent->v.items2 << 23); +#else + val = GetEdictFieldValue(ent, "items2"); + + if (val) + items = (int)ent->v.items | ((int)val->_float << 23); + else + items = (int)ent->v.items | ((int)pr_global_struct->serverflags << 28); +#endif + + bits |= SU_ITEMS; + + if ( (int)ent->v.flags & FL_ONGROUND) + bits |= SU_ONGROUND; + + if ( ent->v.waterlevel >= 2) + bits |= SU_INWATER; + + for (i=0 ; i<3 ; i++) + { + if (ent->v.punchangle[i]) + bits |= (SU_PUNCH1<v.velocity[i]) + bits |= (SU_VELOCITY1<v.weaponframe) + bits |= SU_WEAPONFRAME; + + if (ent->v.armorvalue) + bits |= SU_ARMOR; + +// if (ent->v.weapon) + bits |= SU_WEAPON; + +// send the data + + MSG_WriteByte (msg, svc_clientdata); + MSG_WriteShort (msg, bits); + + if (bits & SU_VIEWHEIGHT) + MSG_WriteChar (msg, ent->v.view_ofs[2]); + + if (bits & SU_IDEALPITCH) + MSG_WriteChar (msg, ent->v.idealpitch); + + for (i=0 ; i<3 ; i++) + { + if (bits & (SU_PUNCH1<v.punchangle[i]); + if (bits & (SU_VELOCITY1<v.velocity[i]/16); + } + +// [always sent] if (bits & SU_ITEMS) + MSG_WriteLong (msg, items); + + if (bits & SU_WEAPONFRAME) + MSG_WriteByte (msg, ent->v.weaponframe); + if (bits & SU_ARMOR) + MSG_WriteByte (msg, ent->v.armorvalue); + if (bits & SU_WEAPON) + MSG_WriteByte (msg, SV_ModelIndex(pr_strings+ent->v.weaponmodel)); + + MSG_WriteShort (msg, ent->v.health); + MSG_WriteByte (msg, ent->v.currentammo); + MSG_WriteByte (msg, ent->v.ammo_shells); + MSG_WriteByte (msg, ent->v.ammo_nails); + MSG_WriteByte (msg, ent->v.ammo_rockets); + MSG_WriteByte (msg, ent->v.ammo_cells); + + if (standard_quake) + { + MSG_WriteByte (msg, ent->v.weapon); + } + else + { + for(i=0;i<32;i++) + { + if ( ((int)ent->v.weapon) & (1<edict, &msg); + + SV_WriteEntitiesToClient (client->edict, &msg); + +// copy the server datagram if there is space + if (msg.cursize + sv.datagram.cursize < msg.maxsize) + SZ_Write (&msg, sv.datagram.data, sv.datagram.cursize); + +// send the datagram + if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1) + { + SV_DropClient (true);// if the message couldn't send, kick off + return false; + } + + return true; +} + +/* +======================= +SV_UpdateToReliableMessages +======================= +*/ +void SV_UpdateToReliableMessages (void) +{ + int i, j; + client_t *client; + +// check for changes to be sent over the reliable streams + for (i=0, host_client = svs.clients ; iold_frags != host_client->edict->v.frags) + { + for (j=0, client = svs.clients ; jactive) + continue; + MSG_WriteByte (&client->message, svc_updatefrags); + MSG_WriteByte (&client->message, i); + MSG_WriteShort (&client->message, host_client->edict->v.frags); + } + + host_client->old_frags = host_client->edict->v.frags; + } + } + + for (j=0, client = svs.clients ; jactive) + continue; + SZ_Write (&client->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize); + } + + SZ_Clear (&sv.reliable_datagram); +} + + +/* +======================= +SV_SendNop + +Send a nop message without trashing or sending the accumulated client +message buffer +======================= +*/ +void SV_SendNop (client_t *client) +{ + sizebuf_t msg; + byte buf[4]; + + msg.data = buf; + msg.maxsize = sizeof(buf); + msg.cursize = 0; + + MSG_WriteChar (&msg, svc_nop); + + if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1) + SV_DropClient (true); // if the message couldn't send, kick off + client->last_message = realtime; +} + +/* +======================= +SV_SendClientMessages +======================= +*/ +void SV_SendClientMessages (void) +{ + int i; + +// update frags, names, etc + SV_UpdateToReliableMessages (); + +// build individual updates + for (i=0, host_client = svs.clients ; iactive) + continue; + + if (host_client->spawned) + { + if (!SV_SendClientDatagram (host_client)) + continue; + } + else + { + // the player isn't totally in the game yet + // send small keepalive messages if too much time has passed + // send a full message when the next signon stage has been requested + // some other message data (name changes, etc) may accumulate + // between signon stages + if (!host_client->sendsignon) + { + if (realtime - host_client->last_message > 5) + SV_SendNop (host_client); + continue; // don't send out non-signon messages + } + } + + // check for an overflowed message. Should only happen + // on a very fucked up connection that backs up a lot, then + // changes level + if (host_client->message.overflowed) + { + SV_DropClient (true); + host_client->message.overflowed = false; + continue; + } + + if (host_client->message.cursize || host_client->dropasap) + { + if (!NET_CanSendMessage (host_client->netconnection)) + { +// I_Printf ("can't write\n"); + continue; + } + + if (host_client->dropasap) + SV_DropClient (false); // went to another level + else + { + if (NET_SendMessage (host_client->netconnection + , &host_client->message) == -1) + SV_DropClient (true); // if the message couldn't send, kick off + SZ_Clear (&host_client->message); + host_client->last_message = realtime; + host_client->sendsignon = false; + } + } + } + + +// clear muzzle flashes + SV_CleanupEnts (); +} + + +/* +============================================================================== + +SERVER SPAWNING + +============================================================================== +*/ + +/* +================ +SV_ModelIndex + +================ +*/ +int SV_ModelIndex (char *name) +{ + int i; + + if (!name || !name[0]) + return 0; + + for (i=0 ; ifree) + continue; + if (entnum > svs.maxclients && !svent->v.modelindex) + continue; + + // + // create entity baseline + // + VectorCopy (svent->v.origin, svent->baseline.origin); + VectorCopy (svent->v.angles, svent->baseline.angles); + svent->baseline.frame = svent->v.frame; + svent->baseline.skin = svent->v.skin; + if (entnum > 0 && entnum <= svs.maxclients) + { + svent->baseline.colormap = entnum; + svent->baseline.modelindex = SV_ModelIndex("progs/player.mdl"); + } + else + { + svent->baseline.colormap = 0; + svent->baseline.modelindex = + SV_ModelIndex(pr_strings + svent->v.model); + } + + // + // add to the message + // + MSG_WriteByte (&sv.signon,svc_spawnbaseline); + MSG_WriteShort (&sv.signon,entnum); + + MSG_WriteByte (&sv.signon, svent->baseline.modelindex); + MSG_WriteByte (&sv.signon, svent->baseline.frame); + MSG_WriteByte (&sv.signon, svent->baseline.colormap); + MSG_WriteByte (&sv.signon, svent->baseline.skin); + for (i=0 ; i<3 ; i++) + { + MSG_WriteCoord(&sv.signon, svent->baseline.origin[i]); + MSG_WriteAngle(&sv.signon, svent->baseline.angles[i]); + } + } +} + + +/* +================ +SV_SendReconnect + +Tell all the clients that the server is changing levels +================ +*/ +void SV_SendReconnect (void) +{ + char data[128]; + sizebuf_t msg; + + msg.data = data; + msg.cursize = 0; + msg.maxsize = sizeof(data); + + MSG_WriteChar (&msg, svc_stufftext); + MSG_WriteString (&msg, "reconnect\n"); + NET_SendToAll (&msg, 5); + + if (cls.state != ca_dedicated) +#ifdef QUAKE2 + Cbuf_InsertText ("reconnect\n"); +#else + Cmd_ExecuteString ("reconnect\n", src_command); +#endif +} + + +/* +================ +SV_SaveSpawnparms + +Grabs the current state of each client for saving across the +transition to another level +================ +*/ +void SV_SaveSpawnparms (void) +{ + int i, j; + + svs.serverflags = pr_global_struct->serverflags; + + for (i=0, host_client = svs.clients ; iactive) + continue; + + // call the progs to get default spawn parms for the new client + pr_global_struct->self = EDICT_TO_PROG(host_client->edict); + PR_ExecuteProgram (pr_global_struct->SetChangeParms); + for (j=0 ; jspawn_parms[j] = (&pr_global_struct->parm1)[j]; + } +} + + +/* +================ +SV_SpawnServer + +This is called at the start of each level +================ +*/ +extern float scr_centertime_off; + +#ifdef QUAKE2 +void SV_SpawnServer (char *server, char *startspot) +#else +void SV_SpawnServer (char *server) +#endif +{ + edict_t *ent; + int i; + + // let's not have any servers with no name + if (hostname.string[0] == 0) + Cvar_Set ("hostname", "UNNAMED"); + scr_centertime_off = 0; + + Con_DPrintf ("SpawnServer: %s\n",server); + svs.changelevel_issued = false; // now safe to issue another + +// +// tell all connected clients that we are going to a new level +// + if (sv.active) + { + SV_SendReconnect (); + } + +// +// make cvars consistant +// + if (coop.value) + Cvar_SetValue ("deathmatch", 0); + current_skill = (int)(skill.value + 0.5); + if (current_skill < 0) + current_skill = 0; + if (current_skill > 3) + current_skill = 3; + + Cvar_SetValue ("skill", (float)current_skill); + +// +// set up the new server +// + Host_ClearMemory (); + + memset (&sv, 0, sizeof(sv)); + + strcpy (sv.name, server); +#ifdef QUAKE2 + if (startspot) + strcpy(sv.startspot, startspot); +#endif + +// load progs to get entity field count + PR_LoadProgs (); + +// allocate server memory + sv.max_edicts = MAX_EDICTS; + + sv.edicts = Hunk_AllocName (sv.max_edicts*pr_edict_size, "edicts"); + + sv.datagram.maxsize = sizeof(sv.datagram_buf); + sv.datagram.cursize = 0; + sv.datagram.data = sv.datagram_buf; + + sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf); + sv.reliable_datagram.cursize = 0; + sv.reliable_datagram.data = sv.reliable_datagram_buf; + + sv.signon.maxsize = sizeof(sv.signon_buf); + sv.signon.cursize = 0; + sv.signon.data = sv.signon_buf; + +// leave slots at start for clients only + sv.num_edicts = svs.maxclients+1; + for (i=0 ; inumsubmodels ; i++) + { + sv.model_precache[1+i] = localmodels[i]; + sv.models[i+1] = Mod_ForName (localmodels[i], false); + } + +// +// load the rest of the entities +// + ent = EDICT_NUM(0); + memset (&ent->v, 0, progs->entityfields * 4); + ent->free = false; + ent->v.model = sv.worldmodel->name - pr_strings; + ent->v.modelindex = 1; // world model + ent->v.solid = SOLID_BSP; + ent->v.movetype = MOVETYPE_PUSH; + + if (coop.value) + pr_global_struct->coop = coop.value; + else + pr_global_struct->deathmatch = deathmatch.value; + + pr_global_struct->mapname = sv.name - pr_strings; +#ifdef QUAKE2 + pr_global_struct->startspot = sv.startspot - pr_strings; +#endif + +// serverflags are for cross level information (sigils) + pr_global_struct->serverflags = svs.serverflags; + + ED_LoadFromFile (sv.worldmodel->entities); + + sv.active = true; + +// all setup is completed, any further precache statements are errors + sv.state = ss_active; + +// run two frames to allow everything to settle + host_frametime = 0.1; + SV_Physics (); + SV_Physics (); + +// create a baseline for more efficient communications + SV_CreateBaseline (); + +// send serverinfo to all connected clients + for (i=0,host_client = svs.clients ; iactive) + SV_SendServerinfo (host_client); + + Con_DPrintf ("Server spawned.\n"); +} + -- cgit v1.2.3