2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 // sv_main.c -- server main program
28 void VM_AutoSentStats_Clear (void);
29 void EntityFrameCSQC_ClearVersions (void);
30 void EntityFrameCSQC_InitClientVersions (int client, qboolean clear);
31 void VM_SV_WriteAutoSentStats (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats);
32 void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int numstates, const entity_state_t *states);
35 // select which protocol to host, this is fed to Protocol_EnumForName
36 cvar_t sv_protocolname = {0, "sv_protocolname", "DP7", "selects network protocol to host for (values include QUAKE, QUAKEDP, NEHAHRAMOVIE, DP1 and up)"};
37 cvar_t sv_ratelimitlocalplayer = {0, "sv_ratelimitlocalplayer", "0", "whether to apply rate limiting to the local player in a listen server (only useful for testing)"};
38 cvar_t sv_maxrate = {CVAR_SAVE | CVAR_NOTIFY, "sv_maxrate", "10000", "upper limit on client rate cvar, should reflect your network connection quality"};
39 cvar_t sv_allowdownloads = {0, "sv_allowdownloads", "1", "whether to allow clients to download files from the server (does not affect http downloads)"};
40 cvar_t sv_allowdownloads_inarchive = {0, "sv_allowdownloads_inarchive", "0", "whether to allow downloads from archives (pak/pk3)"};
41 cvar_t sv_allowdownloads_archive = {0, "sv_allowdownloads_archive", "0", "whether to allow downloads of archives (pak/pk3)"};
42 cvar_t sv_allowdownloads_config = {0, "sv_allowdownloads_config", "0", "whether to allow downloads of config files (cfg)"};
43 cvar_t sv_allowdownloads_dlcache = {0, "sv_allowdownloads_dlcache", "0", "whether to allow downloads of dlcache files (dlcache/)"};
45 extern cvar_t sv_random_seed;
47 static cvar_t sv_cullentities_pvs = {0, "sv_cullentities_pvs", "1", "fast but loose culling of hidden entities"}; // fast but loose
48 static cvar_t sv_cullentities_trace = {0, "sv_cullentities_trace", "0", "somewhat slow but very tight culling of hidden entities, minimizes network traffic and makes wallhack cheats useless"}; // tends to get false negatives, uses a timeout to keep entities visible a short time after becoming hidden
49 static cvar_t sv_cullentities_trace_samples = {0, "sv_cullentities_trace_samples", "1", "number of samples to test for entity culling"};
50 static cvar_t sv_cullentities_trace_samples_extra = {0, "sv_cullentities_trace_samples_extra", "2", "number of samples to test for entity culling when the entity affects its surroundings by e.g. dlight"};
51 static cvar_t sv_cullentities_trace_enlarge = {0, "sv_cullentities_trace_enlarge", "0", "box enlargement for entity culling"};
52 static cvar_t sv_cullentities_trace_delay = {0, "sv_cullentities_trace_delay", "1", "number of seconds until the entity gets actually culled"};
53 static cvar_t sv_cullentities_nevercullbmodels = {0, "sv_cullentities_nevercullbmodels", "0", "if enabled the clients are always notified of moving doors and lifts and other submodels of world (warning: eats a lot of network bandwidth on some levels!)"};
54 static cvar_t sv_cullentities_stats = {0, "sv_cullentities_stats", "0", "displays stats on network entities culled by various methods for each client"};
55 static cvar_t sv_entpatch = {0, "sv_entpatch", "1", "enables loading of .ent files to override entities in the bsp (for example Threewave CTF server pack contains .ent patch files enabling play of CTF on id1 maps)"};
57 cvar_t sv_gameplayfix_grenadebouncedownslopes = {0, "sv_gameplayfix_grenadebouncedownslopes", "1", "prevents MOVETYPE_BOUNCE (grenades) from getting stuck when fired down a downward sloping surface"};
58 cvar_t sv_gameplayfix_noairborncorpse = {0, "sv_gameplayfix_noairborncorpse", "1", "causes entities (corpses) sitting ontop of moving entities (players) to fall when the moving entity (player) is no longer supporting them"};
59 cvar_t sv_gameplayfix_stepdown = {0, "sv_gameplayfix_stepdown", "0", "attempts to step down stairs, not just up them (prevents the familiar thud..thud..thud.. when running down stairs and slopes)"};
60 cvar_t sv_gameplayfix_stepwhilejumping = {0, "sv_gameplayfix_stepwhilejumping", "1", "applies step-up onto a ledge even while airborn, useful if you would otherwise just-miss the floor when running across small areas with gaps (for instance running across the moving platforms in dm2, or jumping to the megahealth and red armor in dm2 rather than using the bridge)"};
61 cvar_t sv_gameplayfix_swiminbmodels = {0, "sv_gameplayfix_swiminbmodels", "1", "causes pointcontents (used to determine if you are in a liquid) to check bmodel entities as well as the world model, so you can swim around in (possibly moving) water bmodel entities"};
62 cvar_t sv_gameplayfix_setmodelrealbox = {0, "sv_gameplayfix_setmodelrealbox", "1", "fixes a bug in Quake that made setmodel always set the entity box to ('-16 -16 -16', '16 16 16') rather than properly checking the model box, breaks some poorly coded mods"};
63 cvar_t sv_gameplayfix_blowupfallenzombies = {0, "sv_gameplayfix_blowupfallenzombies", "1", "causes findradius to detect SOLID_NOT entities such as zombies and corpses on the floor, allowing splash damage to apply to them"};
64 cvar_t sv_gameplayfix_findradiusdistancetobox = {0, "sv_gameplayfix_findradiusdistancetobox", "1", "causes findradius to check the distance to the corner of a box rather than the center of the box, makes findradius detect bmodels such as very large doors that would otherwise be unaffected by splash damage"};
65 cvar_t sv_gameplayfix_qwplayerphysics = {0, "sv_gameplayfix_qwplayerphysics", "1", "changes water jumping to make it easier to get out of water, and prevents friction on landing when bunnyhopping"};
66 cvar_t sv_gameplayfix_upwardvelocityclearsongroundflag = {0, "sv_gameplayfix_upwardvelocityclearsongroundflag", "1", "prevents monsters, items, and most other objects from being stuck to the floor when pushed around by damage, and other situations in mods"};
67 cvar_t sv_gameplayfix_droptofloorstartsolid = {0, "sv_gameplayfix_droptofloorstartsolid", "1", "prevents items and monsters that start in a solid area from falling out of the level (makes droptofloor treat trace_startsolid as an acceptable outcome)"};
69 cvar_t sv_progs = {0, "sv_progs", "progs.dat", "selects which quakec progs.dat file to run" };
71 // TODO: move these cvars here
72 extern cvar_t sv_clmovement_enable;
73 extern cvar_t sv_clmovement_minping;
74 extern cvar_t sv_clmovement_minping_disabletime;
75 extern cvar_t sv_clmovement_waitforinput;
80 mempool_t *sv_mempool = NULL;
82 //============================================================================
84 extern void SV_Phys_Init (void);
85 static void SV_SaveEntFile_f(void);
86 static void SV_StartDownload_f(void);
87 static void SV_Download_f(void);
89 void SV_AreaStats_f(void)
91 World_PrintAreaStats(&sv.world, "server");
101 // init the csqc progs cvars, since they are updated/used by the server code
102 // TODO: fix this since this is a quick hack to make some of [515]'s broken code run ;) [9/13/2006 Black]
103 extern cvar_t csqc_progname; //[515]: csqc crc check and right csprogs name according to progs.dat
104 extern cvar_t csqc_progcrc;
105 extern cvar_t csqc_progsize;
106 Cvar_RegisterVariable (&csqc_progname);
107 Cvar_RegisterVariable (&csqc_progcrc);
108 Cvar_RegisterVariable (&csqc_progsize);
110 Cmd_AddCommand("sv_saveentfile", SV_SaveEntFile_f, "save map entities to .ent file (to allow external editing)");
111 Cmd_AddCommand("sv_areastats", SV_AreaStats_f, "prints statistics on entity culling during collision traces");
112 Cmd_AddCommand_WithClientCommand("sv_startdownload", NULL, SV_StartDownload_f, "begins sending a file to the client (network protocol use only)");
113 Cmd_AddCommand_WithClientCommand("download", NULL, SV_Download_f, "downloads a specified file from the server");
114 Cvar_RegisterVariable (&sv_maxvelocity);
115 Cvar_RegisterVariable (&sv_gravity);
116 Cvar_RegisterVariable (&sv_friction);
117 Cvar_RegisterVariable (&sv_waterfriction);
118 Cvar_RegisterVariable (&sv_edgefriction);
119 Cvar_RegisterVariable (&sv_stopspeed);
120 Cvar_RegisterVariable (&sv_maxspeed);
121 Cvar_RegisterVariable (&sv_maxairspeed);
122 Cvar_RegisterVariable (&sv_accelerate);
123 Cvar_RegisterVariable (&sv_airaccelerate);
124 Cvar_RegisterVariable (&sv_wateraccelerate);
125 Cvar_RegisterVariable (&sv_clmovement_enable);
126 Cvar_RegisterVariable (&sv_clmovement_minping);
127 Cvar_RegisterVariable (&sv_clmovement_minping_disabletime);
128 Cvar_RegisterVariable (&sv_clmovement_waitforinput);
129 Cvar_RegisterVariable (&sv_idealpitchscale);
130 Cvar_RegisterVariable (&sv_aim);
131 Cvar_RegisterVariable (&sv_nostep);
132 Cvar_RegisterVariable (&sv_cullentities_pvs);
133 Cvar_RegisterVariable (&sv_cullentities_trace);
134 Cvar_RegisterVariable (&sv_cullentities_trace_samples);
135 Cvar_RegisterVariable (&sv_cullentities_trace_samples_extra);
136 Cvar_RegisterVariable (&sv_cullentities_trace_enlarge);
137 Cvar_RegisterVariable (&sv_cullentities_trace_delay);
138 Cvar_RegisterVariable (&sv_cullentities_nevercullbmodels);
139 Cvar_RegisterVariable (&sv_cullentities_stats);
140 Cvar_RegisterVariable (&sv_entpatch);
141 Cvar_RegisterVariable (&sv_gameplayfix_grenadebouncedownslopes);
142 Cvar_RegisterVariable (&sv_gameplayfix_noairborncorpse);
143 Cvar_RegisterVariable (&sv_gameplayfix_stepdown);
144 Cvar_RegisterVariable (&sv_gameplayfix_stepwhilejumping);
145 Cvar_RegisterVariable (&sv_gameplayfix_swiminbmodels);
146 Cvar_RegisterVariable (&sv_gameplayfix_setmodelrealbox);
147 Cvar_RegisterVariable (&sv_gameplayfix_blowupfallenzombies);
148 Cvar_RegisterVariable (&sv_gameplayfix_findradiusdistancetobox);
149 Cvar_RegisterVariable (&sv_gameplayfix_qwplayerphysics);
150 Cvar_RegisterVariable (&sv_gameplayfix_upwardvelocityclearsongroundflag);
151 Cvar_RegisterVariable (&sv_gameplayfix_droptofloorstartsolid);
152 Cvar_RegisterVariable (&sv_protocolname);
153 Cvar_RegisterVariable (&sv_ratelimitlocalplayer);
154 Cvar_RegisterVariable (&sv_maxrate);
155 Cvar_RegisterVariable (&sv_allowdownloads);
156 Cvar_RegisterVariable (&sv_allowdownloads_inarchive);
157 Cvar_RegisterVariable (&sv_allowdownloads_archive);
158 Cvar_RegisterVariable (&sv_allowdownloads_config);
159 Cvar_RegisterVariable (&sv_allowdownloads_dlcache);
160 Cvar_RegisterVariable (&sv_progs);
165 sv_mempool = Mem_AllocPool("server", 0, NULL);
168 static void SV_SaveEntFile_f(void)
170 char basename[MAX_QPATH];
171 if (!sv.active || !sv.worldmodel)
173 Con_Print("Not running a server\n");
176 FS_StripExtension(sv.worldmodel->name, basename, sizeof(basename));
177 FS_WriteFile(va("%s.ent", basename), sv.worldmodel->brush.entities, (fs_offset_t)strlen(sv.worldmodel->brush.entities));
182 =============================================================================
186 =============================================================================
193 Make sure the event gets sent to all clients
196 void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count)
200 if (sv.datagram.cursize > MAX_PACKETFRAGMENT-18)
202 MSG_WriteByte (&sv.datagram, svc_particle);
203 MSG_WriteCoord (&sv.datagram, org[0], sv.protocol);
204 MSG_WriteCoord (&sv.datagram, org[1], sv.protocol);
205 MSG_WriteCoord (&sv.datagram, org[2], sv.protocol);
206 for (i=0 ; i<3 ; i++)
207 MSG_WriteChar (&sv.datagram, (int)bound(-128, dir[i]*16, 127));
208 MSG_WriteByte (&sv.datagram, count);
209 MSG_WriteByte (&sv.datagram, color);
216 Make sure the event gets sent to all clients
219 void SV_StartEffect (vec3_t org, int modelindex, int startframe, int framecount, int framerate)
221 if (modelindex >= 256 || startframe >= 256)
223 if (sv.datagram.cursize > MAX_PACKETFRAGMENT-19)
225 MSG_WriteByte (&sv.datagram, svc_effect2);
226 MSG_WriteCoord (&sv.datagram, org[0], sv.protocol);
227 MSG_WriteCoord (&sv.datagram, org[1], sv.protocol);
228 MSG_WriteCoord (&sv.datagram, org[2], sv.protocol);
229 MSG_WriteShort (&sv.datagram, modelindex);
230 MSG_WriteShort (&sv.datagram, startframe);
231 MSG_WriteByte (&sv.datagram, framecount);
232 MSG_WriteByte (&sv.datagram, framerate);
236 if (sv.datagram.cursize > MAX_PACKETFRAGMENT-17)
238 MSG_WriteByte (&sv.datagram, svc_effect);
239 MSG_WriteCoord (&sv.datagram, org[0], sv.protocol);
240 MSG_WriteCoord (&sv.datagram, org[1], sv.protocol);
241 MSG_WriteCoord (&sv.datagram, org[2], sv.protocol);
242 MSG_WriteByte (&sv.datagram, modelindex);
243 MSG_WriteByte (&sv.datagram, startframe);
244 MSG_WriteByte (&sv.datagram, framecount);
245 MSG_WriteByte (&sv.datagram, framerate);
253 Each entity can have eight independant sound sources, like voice,
256 Channel 0 is an auto-allocate channel, the others override anything
257 already running on that entity/channel pair.
259 An attenuation of 0 will play full volume everywhere in the level.
260 Larger attenuations will drop off. (max 4 attenuation)
264 void SV_StartSound (prvm_edict_t *entity, int channel, const char *sample, int volume, float attenuation)
266 int sound_num, field_mask, i, ent;
268 if (volume < 0 || volume > 255)
270 Con_Printf ("SV_StartSound: volume = %i\n", volume);
274 if (attenuation < 0 || attenuation > 4)
276 Con_Printf ("SV_StartSound: attenuation = %f\n", attenuation);
280 if (channel < 0 || channel > 7)
282 Con_Printf ("SV_StartSound: channel = %i\n", channel);
286 if (sv.datagram.cursize > MAX_PACKETFRAGMENT-21)
289 // find precache number for sound
290 sound_num = SV_SoundIndex(sample, 1);
294 ent = PRVM_NUM_FOR_EDICT(entity);
297 if (volume != DEFAULT_SOUND_PACKET_VOLUME)
298 field_mask |= SND_VOLUME;
299 if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)
300 field_mask |= SND_ATTENUATION;
302 field_mask |= SND_LARGEENTITY;
303 if (sound_num >= 256 || channel >= 8)
304 field_mask |= SND_LARGESOUND;
306 // directed messages go only to the entity they are targeted on
307 MSG_WriteByte (&sv.datagram, svc_sound);
308 MSG_WriteByte (&sv.datagram, field_mask);
309 if (field_mask & SND_VOLUME)
310 MSG_WriteByte (&sv.datagram, volume);
311 if (field_mask & SND_ATTENUATION)
312 MSG_WriteByte (&sv.datagram, (int)(attenuation*64));
313 if (field_mask & SND_LARGEENTITY)
315 MSG_WriteShort (&sv.datagram, ent);
316 MSG_WriteByte (&sv.datagram, channel);
319 MSG_WriteShort (&sv.datagram, (ent<<3) | channel);
320 if (field_mask & SND_LARGESOUND)
321 MSG_WriteShort (&sv.datagram, sound_num);
323 MSG_WriteByte (&sv.datagram, sound_num);
324 for (i = 0;i < 3;i++)
325 MSG_WriteCoord (&sv.datagram, entity->fields.server->origin[i]+0.5*(entity->fields.server->mins[i]+entity->fields.server->maxs[i]), sv.protocol);
329 ==============================================================================
333 ==============================================================================
340 Sends the first message from the server to a connected client.
341 This will be sent on the initial connection and upon each server load.
344 void SV_SendServerinfo (client_t *client)
349 // we know that this client has a netconnection and thus is not a bot
351 // edicts get reallocated on level changes, so we need to update it here
352 client->edict = PRVM_EDICT_NUM((client - svs.clients) + 1);
354 // clear cached stuff that depends on the level
355 client->weaponmodel[0] = 0;
356 client->weaponmodelindex = 0;
358 // LordHavoc: clear entityframe tracking
359 client->latestframenum = 0;
361 if (client->entitydatabase)
362 EntityFrame_FreeDatabase(client->entitydatabase);
363 if (client->entitydatabase4)
364 EntityFrame4_FreeDatabase(client->entitydatabase4);
365 if (client->entitydatabase5)
366 EntityFrame5_FreeDatabase(client->entitydatabase5);
368 if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE)
370 if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3)
371 client->entitydatabase = EntityFrame_AllocDatabase(sv_mempool);
372 else if (sv.protocol == PROTOCOL_DARKPLACES4)
373 client->entitydatabase4 = EntityFrame4_AllocDatabase(sv_mempool);
375 client->entitydatabase5 = EntityFrame5_AllocDatabase(sv_mempool);
378 SZ_Clear (&client->netconnection->message);
379 MSG_WriteByte (&client->netconnection->message, svc_print);
380 dpsnprintf (message, sizeof (message), "\nServer: %s build %s (progs %i crc)", gamename, buildstring, prog->filecrc);
381 MSG_WriteString (&client->netconnection->message,message);
383 //[515]: init csprogs according to version of svprogs, check the crc, etc.
384 if (sv.csqc_progname[0])
387 Con_DPrintf("sending csqc info to client (\"%s\" with size %i and crc %i)\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc);
388 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
389 MSG_WriteString (&client->netconnection->message, va("csqc_progname %s\n", sv.csqc_progname));
390 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
391 MSG_WriteString (&client->netconnection->message, va("csqc_progsize %i\n", sv.csqc_progsize));
392 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
393 MSG_WriteString (&client->netconnection->message, va("csqc_progcrc %i\n", sv.csqc_progcrc));
394 //[515]: init stufftext string (it is sent before svc_serverinfo)
395 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.SV_InitCmd);
398 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
399 MSG_WriteString (&client->netconnection->message, va("%s\n", PRVM_GetString(val->string)));
403 if (sv_allowdownloads.integer)
405 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
406 MSG_WriteString (&client->netconnection->message, "cl_serverextension_download 1");
409 // send at this time so it's guaranteed to get executed at the right time
413 host_client = client;
414 Curl_SendRequirements();
418 MSG_WriteByte (&client->netconnection->message, svc_serverinfo);
419 MSG_WriteLong (&client->netconnection->message, Protocol_NumberForEnum(sv.protocol));
420 MSG_WriteByte (&client->netconnection->message, svs.maxclients);
422 if (!coop.integer && deathmatch.integer)
423 MSG_WriteByte (&client->netconnection->message, GAME_DEATHMATCH);
425 MSG_WriteByte (&client->netconnection->message, GAME_COOP);
427 MSG_WriteString (&client->netconnection->message,PRVM_GetString(prog->edicts->fields.server->message));
429 for (i = 1;i < MAX_MODELS && sv.model_precache[i][0];i++)
430 MSG_WriteString (&client->netconnection->message, sv.model_precache[i]);
431 MSG_WriteByte (&client->netconnection->message, 0);
433 for (i = 1;i < MAX_SOUNDS && sv.sound_precache[i][0];i++)
434 MSG_WriteString (&client->netconnection->message, sv.sound_precache[i]);
435 MSG_WriteByte (&client->netconnection->message, 0);
438 MSG_WriteByte (&client->netconnection->message, svc_cdtrack);
439 MSG_WriteByte (&client->netconnection->message, (int)prog->edicts->fields.server->sounds);
440 MSG_WriteByte (&client->netconnection->message, (int)prog->edicts->fields.server->sounds);
443 MSG_WriteByte (&client->netconnection->message, svc_setview);
444 MSG_WriteShort (&client->netconnection->message, PRVM_NUM_FOR_EDICT(client->edict));
446 MSG_WriteByte (&client->netconnection->message, svc_signonnum);
447 MSG_WriteByte (&client->netconnection->message, 1);
449 client->spawned = false; // need prespawn, spawn, etc
451 // clear movement info until client enters the new level properly
452 memset(&client->cmd, 0, sizeof(client->cmd));
453 client->movesequence = 0;
454 #ifdef NUM_PING_TIMES
455 for (i = 0;i < NUM_PING_TIMES;i++)
456 client->ping_times[i] = 0;
457 client->num_pings = 0;
466 Initializes a client_t for a new net connection. This will only be called
467 once for a player each game, not once for each level change.
470 void SV_ConnectClient (int clientnum, netconn_t *netconnection)
474 float spawn_parms[NUM_SPAWN_PARMS];
476 client = svs.clients + clientnum;
478 if(netconnection)//[515]: bots don't play with csqc =)
479 EntityFrameCSQC_InitClientVersions(clientnum, false);
481 // set up the client_t
483 memcpy (spawn_parms, client->spawn_parms, sizeof(spawn_parms));
484 memset (client, 0, sizeof(*client));
485 client->active = true;
486 client->netconnection = netconnection;
488 Con_DPrintf("Client %s connected\n", client->netconnection ? client->netconnection->address : "botclient");
490 strlcpy(client->name, "unconnected", sizeof(client->name));
491 strlcpy(client->old_name, "unconnected", sizeof(client->old_name));
492 client->spawned = false;
493 client->edict = PRVM_EDICT_NUM(clientnum+1);
494 if (client->netconnection)
495 client->netconnection->message.allowoverflow = true; // we can catch it
496 // updated by receiving "rate" command from client
497 client->rate = NET_MINRATE;
498 // no limits for local player
499 if (client->netconnection && LHNETADDRESS_GetAddressType(&client->netconnection->peeraddress) == LHNETADDRESSTYPE_LOOP)
500 client->rate = 1000000000;
501 client->connecttime = realtime;
504 memcpy (client->spawn_parms, spawn_parms, sizeof(spawn_parms));
507 // call the progs to get default spawn parms for the new client
508 // set self to world to intentionally cause errors with broken SetNewParms code in some mods
509 prog->globals.server->self = 0;
510 PRVM_ExecuteProgram (prog->globals.server->SetNewParms, "QC function SetNewParms is missing");
511 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
512 client->spawn_parms[i] = (&prog->globals.server->parm1)[i];
514 // set up the entity for this client (including .colormap, .team, etc)
515 PRVM_ED_ClearEdict(client->edict);
518 // don't call SendServerinfo for a fresh botclient because its fields have
519 // not been set up by the qc yet
520 if (client->netconnection)
521 SV_SendServerinfo (client);
523 client->spawned = true;
528 ===============================================================================
532 ===============================================================================
541 void SV_ClearDatagram (void)
543 SZ_Clear (&sv.datagram);
547 =============================================================================
549 The PVS must include a small area around the client to allow head bobbing
550 or other small motion on the client side. Otherwise, a bob might cause an
551 entity that should be visible to not show up, especially when the bob
554 =============================================================================
557 int sv_writeentitiestoclient_pvsbytes;
558 unsigned char sv_writeentitiestoclient_pvs[MAX_MAP_LEAFS/8];
560 static int numsendentities;
561 static entity_state_t sendentities[MAX_EDICTS];
562 static entity_state_t *sendentitiesindex[MAX_EDICTS];
564 qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int e)
567 unsigned int modelindex, effects, flags, glowsize, lightstyle, lightpflags, light[4], specialvisibilityradius;
568 unsigned int customizeentityforclient;
570 vec3_t cullmins, cullmaxs;
574 // EF_NODRAW prevents sending for any reason except for your own
575 // client, so we must keep all clients in this superset
576 effects = (unsigned)ent->fields.server->effects;
578 // we can omit invisible entities with no effects that are not clients
579 // LordHavoc: this could kill tags attached to an invisible entity, I
580 // just hope we never have to support that case
581 i = (int)ent->fields.server->modelindex;
582 modelindex = (i >= 1 && i < MAX_MODELS && *PRVM_GetString(ent->fields.server->model)) ? i : 0;
585 i = (int)(PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_size)->_float * 0.25f);
586 glowsize = (unsigned char)bound(0, i, 255);
587 if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_trail)->_float)
588 flags |= RENDER_GLOWTRAIL;
590 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[0]*256;
591 light[0] = (unsigned short)bound(0, f, 65535);
592 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[1]*256;
593 light[1] = (unsigned short)bound(0, f, 65535);
594 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[2]*256;
595 light[2] = (unsigned short)bound(0, f, 65535);
596 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.light_lev)->_float;
597 light[3] = (unsigned short)bound(0, f, 65535);
598 lightstyle = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.style)->_float;
599 lightpflags = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.pflags)->_float;
601 if (gamemode == GAME_TENEBRAE)
603 // tenebrae's EF_FULLDYNAMIC conflicts with Q2's EF_NODRAW
607 lightpflags |= PFLAGS_FULLDYNAMIC;
609 // tenebrae's EF_GREEN conflicts with DP's EF_ADDITIVE
613 light[0] = (int)(0.2*256);
614 light[1] = (int)(1.0*256);
615 light[2] = (int)(0.2*256);
617 lightpflags |= PFLAGS_FULLDYNAMIC;
621 specialvisibilityradius = 0;
622 if (lightpflags & PFLAGS_FULLDYNAMIC)
623 specialvisibilityradius = max(specialvisibilityradius, light[3]);
625 specialvisibilityradius = max(specialvisibilityradius, glowsize * 4);
626 if (flags & RENDER_GLOWTRAIL)
627 specialvisibilityradius = max(specialvisibilityradius, 100);
628 if (effects & (EF_BRIGHTFIELD | EF_MUZZLEFLASH | EF_BRIGHTLIGHT | EF_DIMLIGHT | EF_RED | EF_BLUE | EF_FLAME | EF_STARDUST))
630 if (effects & EF_BRIGHTFIELD)
631 specialvisibilityradius = max(specialvisibilityradius, 80);
632 if (effects & EF_MUZZLEFLASH)
633 specialvisibilityradius = max(specialvisibilityradius, 100);
634 if (effects & EF_BRIGHTLIGHT)
635 specialvisibilityradius = max(specialvisibilityradius, 400);
636 if (effects & EF_DIMLIGHT)
637 specialvisibilityradius = max(specialvisibilityradius, 200);
638 if (effects & EF_RED)
639 specialvisibilityradius = max(specialvisibilityradius, 200);
640 if (effects & EF_BLUE)
641 specialvisibilityradius = max(specialvisibilityradius, 200);
642 if (effects & EF_FLAME)
643 specialvisibilityradius = max(specialvisibilityradius, 250);
644 if (effects & EF_STARDUST)
645 specialvisibilityradius = max(specialvisibilityradius, 100);
648 // early culling checks
649 // (final culling is done by SV_MarkWriteEntityStateToClient)
650 customizeentityforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.customizeentityforclient)->function;
651 if (!customizeentityforclient)
653 if (e > svs.maxclients && (!modelindex && !specialvisibilityradius))
655 // this 2 billion unit check is actually to detect NAN origins
656 // (we really don't want to send those)
657 if (VectorLength2(ent->fields.server->origin) > 2000000000.0*2000000000.0)
665 VectorCopy(ent->fields.server->origin, cs->origin);
666 VectorCopy(ent->fields.server->angles, cs->angles);
668 cs->effects = effects;
669 cs->colormap = (unsigned)ent->fields.server->colormap;
670 cs->modelindex = modelindex;
671 cs->skin = (unsigned)ent->fields.server->skin;
672 cs->frame = (unsigned)ent->fields.server->frame;
673 cs->viewmodelforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewmodelforclient)->edict;
674 cs->exteriormodelforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.exteriormodeltoclient)->edict;
675 cs->nodrawtoclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.nodrawtoclient)->edict;
676 cs->drawonlytoclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.drawonlytoclient)->edict;
677 cs->customizeentityforclient = customizeentityforclient;
678 cs->tagentity = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)->edict;
679 cs->tagindex = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_index)->_float;
680 cs->glowsize = glowsize;
682 // don't need to init cs->colormod because the defaultstate did that for us
683 //cs->colormod[0] = cs->colormod[1] = cs->colormod[2] = 32;
684 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.colormod);
685 if (val->vector[0] || val->vector[1] || val->vector[2])
687 i = (int)(val->vector[0] * 32.0f);cs->colormod[0] = bound(0, i, 255);
688 i = (int)(val->vector[1] * 32.0f);cs->colormod[1] = bound(0, i, 255);
689 i = (int)(val->vector[2] * 32.0f);cs->colormod[2] = bound(0, i, 255);
692 cs->modelindex = modelindex;
695 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.alpha)->_float * 255.0f);
699 cs->alpha = (unsigned char)bound(0, i, 255);
702 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderamt)->_float);
706 cs->alpha = (unsigned char)bound(0, i, 255);
710 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale)->_float * 16.0f);
714 cs->scale = (unsigned char)bound(0, i, 255);
718 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_color)->_float);
720 cs->glowcolor = (int)f;
722 if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.fullbright)->_float)
723 cs->effects |= EF_FULLBRIGHT;
725 if (ent->fields.server->movetype == MOVETYPE_STEP)
726 cs->flags |= RENDER_STEP;
727 if ((cs->effects & EF_LOWPRECISION) && cs->origin[0] >= -32768 && cs->origin[1] >= -32768 && cs->origin[2] >= -32768 && cs->origin[0] <= 32767 && cs->origin[1] <= 32767 && cs->origin[2] <= 32767)
728 cs->flags |= RENDER_LOWPRECISION;
729 if (ent->fields.server->colormap >= 1024)
730 cs->flags |= RENDER_COLORMAPPED;
731 if (cs->viewmodelforclient)
732 cs->flags |= RENDER_VIEWMODEL; // show relative to the view
734 cs->light[0] = light[0];
735 cs->light[1] = light[1];
736 cs->light[2] = light[2];
737 cs->light[3] = light[3];
738 cs->lightstyle = lightstyle;
739 cs->lightpflags = lightpflags;
741 cs->specialvisibilityradius = specialvisibilityradius;
743 // calculate the visible box of this entity (don't use the physics box
744 // as that is often smaller than a model, and would not count
745 // specialvisibilityradius)
746 if ((model = sv.models[modelindex]))
748 float scale = cs->scale * (1.0f / 16.0f);
749 if (cs->angles[0] || cs->angles[2]) // pitch and roll
751 VectorMA(cs->origin, scale, model->rotatedmins, cullmins);
752 VectorMA(cs->origin, scale, model->rotatedmaxs, cullmaxs);
754 else if (cs->angles[1])
756 VectorMA(cs->origin, scale, model->yawmins, cullmins);
757 VectorMA(cs->origin, scale, model->yawmaxs, cullmaxs);
761 VectorMA(cs->origin, scale, model->normalmins, cullmins);
762 VectorMA(cs->origin, scale, model->normalmaxs, cullmaxs);
767 // if there is no model (or it could not be loaded), use the physics box
768 VectorAdd(cs->origin, ent->fields.server->mins, cullmins);
769 VectorAdd(cs->origin, ent->fields.server->maxs, cullmaxs);
771 if (specialvisibilityradius)
773 cullmins[0] = min(cullmins[0], cs->origin[0] - specialvisibilityradius);
774 cullmins[1] = min(cullmins[1], cs->origin[1] - specialvisibilityradius);
775 cullmins[2] = min(cullmins[2], cs->origin[2] - specialvisibilityradius);
776 cullmaxs[0] = max(cullmaxs[0], cs->origin[0] + specialvisibilityradius);
777 cullmaxs[1] = max(cullmaxs[1], cs->origin[1] + specialvisibilityradius);
778 cullmaxs[2] = max(cullmaxs[2], cs->origin[2] + specialvisibilityradius);
780 if (!VectorCompare(cullmins, ent->priv.server->cullmins) || !VectorCompare(cullmaxs, ent->priv.server->cullmaxs))
782 VectorCopy(cullmins, ent->priv.server->cullmins);
783 VectorCopy(cullmaxs, ent->priv.server->cullmaxs);
784 ent->priv.server->pvs_numclusters = -1;
785 if (sv.worldmodel && sv.worldmodel->brush.FindBoxClusters)
787 i = sv.worldmodel->brush.FindBoxClusters(sv.worldmodel, cullmins, cullmaxs, MAX_ENTITYCLUSTERS, ent->priv.server->pvs_clusterlist);
788 if (i <= MAX_ENTITYCLUSTERS)
789 ent->priv.server->pvs_numclusters = i;
796 void SV_PrepareEntitiesForSending(void)
800 // send all entities that touch the pvs
802 sendentitiesindex[0] = NULL;
803 memset(sendentitiesindex, 0, prog->num_edicts * sizeof(entity_state_t *));
804 for (e = 1, ent = PRVM_NEXT_EDICT(prog->edicts);e < prog->num_edicts;e++, ent = PRVM_NEXT_EDICT(ent))
806 if (!ent->priv.server->free && SV_PrepareEntityForSending(ent, sendentities + numsendentities, e))
808 sendentitiesindex[e] = sendentities + numsendentities;
814 static int sententitiesmark = 0;
815 static int sententities[MAX_EDICTS];
816 static int sententitiesconsideration[MAX_EDICTS];
817 static int sv_writeentitiestoclient_culled_pvs;
818 static int sv_writeentitiestoclient_culled_trace;
819 static int sv_writeentitiestoclient_visibleentities;
820 static int sv_writeentitiestoclient_totalentities;
821 //static entity_frame_t sv_writeentitiestoclient_entityframe;
822 static int sv_writeentitiestoclient_clentnum;
823 static vec3_t sv_writeentitiestoclient_testeye;
824 static client_t *sv_writeentitiestoclient_client;
826 void SV_MarkWriteEntityStateToClient(entity_state_t *s)
831 if (sententitiesconsideration[s->number] == sententitiesmark)
833 sententitiesconsideration[s->number] = sententitiesmark;
834 sv_writeentitiestoclient_totalentities++;
836 if (s->customizeentityforclient)
838 prog->globals.server->self = s->number;
839 prog->globals.server->other = sv_writeentitiestoclient_clentnum;
840 PRVM_ExecuteProgram(s->customizeentityforclient, "customizeentityforclient: NULL function");
841 if(!PRVM_G_FLOAT(OFS_RETURN) || !SV_PrepareEntityForSending(PRVM_EDICT_NUM(s->number), s, s->number))
845 // never reject player
846 if (s->number != sv_writeentitiestoclient_clentnum)
848 // check various rejection conditions
849 if (s->nodrawtoclient == sv_writeentitiestoclient_clentnum)
851 if (s->drawonlytoclient && s->drawonlytoclient != sv_writeentitiestoclient_clentnum)
853 if (s->effects & EF_NODRAW)
855 // LordHavoc: only send entities with a model or important effects
856 if (!s->modelindex && s->specialvisibilityradius == 0)
859 isbmodel = (model = sv.models[s->modelindex]) != NULL && model->name[0] == '*';
860 // viewmodels don't have visibility checking
861 if (s->viewmodelforclient)
863 if (s->viewmodelforclient != sv_writeentitiestoclient_clentnum)
866 else if (s->tagentity)
868 // tag attached entities simply check their parent
869 if (!sendentitiesindex[s->tagentity])
871 SV_MarkWriteEntityStateToClient(sendentitiesindex[s->tagentity]);
872 if (sententities[s->tagentity] != sententitiesmark)
875 // always send world submodels in newer protocols because they don't
876 // generate much traffic (in old protocols they hog bandwidth)
877 // but only if sv_cullentities_alwayssendbmodels is on
878 else if (!(s->effects & EF_NODEPTHTEST) && (!isbmodel || !sv_cullentities_nevercullbmodels.integer || sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE))
880 // entity has survived every check so far, check if visible
881 ed = PRVM_EDICT_NUM(s->number);
883 // if not touching a visible leaf
884 if (sv_cullentities_pvs.integer && sv_writeentitiestoclient_pvsbytes)
886 if (ed->priv.server->pvs_numclusters < 0)
888 // entity too big for clusters list
889 if (sv.worldmodel && sv.worldmodel->brush.BoxTouchingPVS && !sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, sv_writeentitiestoclient_pvs, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
891 sv_writeentitiestoclient_culled_pvs++;
898 // check cached clusters list
899 for (i = 0;i < ed->priv.server->pvs_numclusters;i++)
900 if (CHECKPVSBIT(sv_writeentitiestoclient_pvs, ed->priv.server->pvs_clusterlist[i]))
902 if (i == ed->priv.server->pvs_numclusters)
904 sv_writeentitiestoclient_culled_pvs++;
910 // or not seen by random tracelines
911 if (sv_cullentities_trace.integer && !isbmodel)
913 if(Mod_CanSeeBox_Trace(s->specialvisibilityradius ? sv_cullentities_trace_samples_extra.integer : sv_cullentities_trace_samples.integer, sv_cullentities_trace_enlarge.value, sv.worldmodel, sv_writeentitiestoclient_testeye, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
914 sv_writeentitiestoclient_client->visibletime[s->number] = realtime + sv_cullentities_trace_delay.value;
915 if (realtime > sv_writeentitiestoclient_client->visibletime[s->number])
917 sv_writeentitiestoclient_culled_trace++;
924 // this just marks it for sending
925 // FIXME: it would be more efficient to send here, but the entity
926 // compressor isn't that flexible
927 sv_writeentitiestoclient_visibleentities++;
928 sententities[s->number] = sententitiesmark;
931 entity_state_t sendstates[MAX_EDICTS];
932 extern int csqc_clent;
934 void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg, int *stats)
936 int i, numsendstates;
939 // if there isn't enough space to accomplish anything, skip it
940 if (msg->cursize + 25 > msg->maxsize)
943 sv_writeentitiestoclient_client = client;
945 sv_writeentitiestoclient_culled_pvs = 0;
946 sv_writeentitiestoclient_culled_trace = 0;
947 sv_writeentitiestoclient_visibleentities = 0;
948 sv_writeentitiestoclient_totalentities = 0;
950 // find the client's PVS
951 // the real place being tested from
952 VectorAdd(clent->fields.server->origin, clent->fields.server->view_ofs, sv_writeentitiestoclient_testeye);
953 sv_writeentitiestoclient_pvsbytes = 0;
954 if (sv.worldmodel && sv.worldmodel->brush.FatPVS)
955 sv_writeentitiestoclient_pvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, sv_writeentitiestoclient_testeye, 8, sv_writeentitiestoclient_pvs, sizeof(sv_writeentitiestoclient_pvs));
957 csqc_clent = sv_writeentitiestoclient_clentnum = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
961 for (i = 0;i < numsendentities;i++)
962 SV_MarkWriteEntityStateToClient(sendentities + i);
965 for (i = 0;i < numsendentities;i++)
967 if (sententities[sendentities[i].number] == sententitiesmark)
969 s = &sendstates[numsendstates++];
970 *s = sendentities[i];
971 if (s->exteriormodelforclient && s->exteriormodelforclient == sv_writeentitiestoclient_clentnum)
972 s->flags |= RENDER_EXTERIORMODEL;
976 if (sv_cullentities_stats.integer)
977 Con_Printf("client \"%s\" entities: %d total, %d visible, %d culled by: %d pvs %d trace\n", client->name, sv_writeentitiestoclient_totalentities, sv_writeentitiestoclient_visibleentities, sv_writeentitiestoclient_culled_pvs + sv_writeentitiestoclient_culled_trace, sv_writeentitiestoclient_culled_pvs, sv_writeentitiestoclient_culled_trace);
979 EntityFrameCSQC_WriteFrame(msg, numsendstates, sendstates);
981 if (client->entitydatabase5)
982 EntityFrame5_WriteFrame(msg, client->entitydatabase5, numsendstates, sendstates, client - svs.clients + 1, stats, client->movesequence);
983 else if (client->entitydatabase4)
984 EntityFrame4_WriteFrame(msg, client->entitydatabase4, numsendstates, sendstates);
985 else if (client->entitydatabase)
986 EntityFrame_WriteFrame(msg, client->entitydatabase, numsendstates, sendstates, client - svs.clients + 1);
988 EntityFrameQuake_WriteFrame(msg, numsendstates, sendstates);
997 void SV_CleanupEnts (void)
1002 ent = PRVM_NEXT_EDICT(prog->edicts);
1003 for (e=1 ; e<prog->num_edicts ; e++, ent = PRVM_NEXT_EDICT(ent))
1004 ent->fields.server->effects = (int)ent->fields.server->effects & ~EF_MUZZLEFLASH;
1009 SV_WriteClientdataToMessage
1013 void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats)
1017 prvm_edict_t *other;
1025 // send a damage message
1027 if (ent->fields.server->dmg_take || ent->fields.server->dmg_save)
1029 other = PRVM_PROG_TO_EDICT(ent->fields.server->dmg_inflictor);
1030 MSG_WriteByte (msg, svc_damage);
1031 MSG_WriteByte (msg, (int)ent->fields.server->dmg_save);
1032 MSG_WriteByte (msg, (int)ent->fields.server->dmg_take);
1033 for (i=0 ; i<3 ; i++)
1034 MSG_WriteCoord (msg, other->fields.server->origin[i] + 0.5*(other->fields.server->mins[i] + other->fields.server->maxs[i]), sv.protocol);
1036 ent->fields.server->dmg_take = 0;
1037 ent->fields.server->dmg_save = 0;
1041 // send the current viewpos offset from the view entity
1043 SV_SetIdealPitch (); // how much to look up / down ideally
1045 // a fixangle might get lost in a dropped packet. Oh well.
1046 if ( ent->fields.server->fixangle )
1048 MSG_WriteByte (msg, svc_setangle);
1049 for (i=0 ; i < 3 ; i++)
1050 MSG_WriteAngle (msg, ent->fields.server->angles[i], sv.protocol);
1051 ent->fields.server->fixangle = 0;
1054 // stuff the sigil bits into the high bits of items for sbar, or else
1056 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.items2);
1057 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1058 items = (int)ent->fields.server->items | ((int)val->_float << 23);
1060 items = (int)ent->fields.server->items | ((int)prog->globals.server->serverflags << 28);
1062 VectorClear(punchvector);
1063 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.punchvector)))
1064 VectorCopy(val->vector, punchvector);
1066 // cache weapon model name and index in client struct to save time
1067 // (this search can be almost 1% of cpu time!)
1068 s = PRVM_GetString(ent->fields.server->weaponmodel);
1069 if (strcmp(s, client->weaponmodel))
1071 strlcpy(client->weaponmodel, s, sizeof(client->weaponmodel));
1072 client->weaponmodelindex = SV_ModelIndex(s, 1);
1076 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewzoom)))
1077 viewzoom = (int)(val->_float * 255.0f);
1083 if ((int)ent->fields.server->flags & FL_ONGROUND)
1084 bits |= SU_ONGROUND;
1085 if (ent->fields.server->waterlevel >= 2)
1087 if (ent->fields.server->idealpitch)
1088 bits |= SU_IDEALPITCH;
1090 for (i=0 ; i<3 ; i++)
1092 if (ent->fields.server->punchangle[i])
1093 bits |= (SU_PUNCH1<<i);
1094 if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE)
1096 bits |= (SU_PUNCHVEC1<<i);
1097 if (ent->fields.server->velocity[i])
1098 bits |= (SU_VELOCITY1<<i);
1101 memset(stats, 0, sizeof(int[MAX_CL_STATS]));
1102 stats[STAT_VIEWHEIGHT] = (int)ent->fields.server->view_ofs[2];
1103 stats[STAT_ITEMS] = items;
1104 stats[STAT_WEAPONFRAME] = (int)ent->fields.server->weaponframe;
1105 stats[STAT_ARMOR] = (int)ent->fields.server->armorvalue;
1106 stats[STAT_WEAPON] = client->weaponmodelindex;
1107 stats[STAT_HEALTH] = (int)ent->fields.server->health;
1108 stats[STAT_AMMO] = (int)ent->fields.server->currentammo;
1109 stats[STAT_SHELLS] = (int)ent->fields.server->ammo_shells;
1110 stats[STAT_NAILS] = (int)ent->fields.server->ammo_nails;
1111 stats[STAT_ROCKETS] = (int)ent->fields.server->ammo_rockets;
1112 stats[STAT_CELLS] = (int)ent->fields.server->ammo_cells;
1113 stats[STAT_ACTIVEWEAPON] = (int)ent->fields.server->weapon;
1114 stats[STAT_VIEWZOOM] = viewzoom;
1115 stats[STAT_TOTALSECRETS] = prog->globals.server->total_secrets;
1116 stats[STAT_TOTALMONSTERS] = prog->globals.server->total_monsters;
1117 // the QC bumps these itself by sending svc_'s, so we have to keep them
1118 // zero or they'll be corrected by the engine
1119 //stats[STAT_SECRETS] = prog->globals.server->found_secrets;
1120 //stats[STAT_MONSTERS] = prog->globals.server->killed_monsters;
1122 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5)
1124 if (stats[STAT_VIEWHEIGHT] != DEFAULT_VIEWHEIGHT) bits |= SU_VIEWHEIGHT;
1126 if (stats[STAT_WEAPONFRAME]) bits |= SU_WEAPONFRAME;
1127 if (stats[STAT_ARMOR]) bits |= SU_ARMOR;
1129 // FIXME: which protocols support this? does PROTOCOL_DARKPLACES3 support viewzoom?
1130 if (sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5)
1131 if (viewzoom != 255)
1132 bits |= SU_VIEWZOOM;
1137 if (bits >= 16777216)
1141 MSG_WriteByte (msg, svc_clientdata);
1142 MSG_WriteShort (msg, bits);
1143 if (bits & SU_EXTEND1)
1144 MSG_WriteByte(msg, bits >> 16);
1145 if (bits & SU_EXTEND2)
1146 MSG_WriteByte(msg, bits >> 24);
1148 if (bits & SU_VIEWHEIGHT)
1149 MSG_WriteChar (msg, stats[STAT_VIEWHEIGHT]);
1151 if (bits & SU_IDEALPITCH)
1152 MSG_WriteChar (msg, (int)ent->fields.server->idealpitch);
1154 for (i=0 ; i<3 ; i++)
1156 if (bits & (SU_PUNCH1<<i))
1158 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
1159 MSG_WriteChar(msg, (int)ent->fields.server->punchangle[i]);
1161 MSG_WriteAngle16i(msg, ent->fields.server->punchangle[i]);
1163 if (bits & (SU_PUNCHVEC1<<i))
1165 if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
1166 MSG_WriteCoord16i(msg, punchvector[i]);
1168 MSG_WriteCoord32f(msg, punchvector[i]);
1170 if (bits & (SU_VELOCITY1<<i))
1172 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
1173 MSG_WriteChar(msg, (int)(ent->fields.server->velocity[i] * (1.0f / 16.0f)));
1175 MSG_WriteCoord32f(msg, ent->fields.server->velocity[i]);
1179 if (bits & SU_ITEMS)
1180 MSG_WriteLong (msg, stats[STAT_ITEMS]);
1182 if (sv.protocol == PROTOCOL_DARKPLACES5)
1184 if (bits & SU_WEAPONFRAME)
1185 MSG_WriteShort (msg, stats[STAT_WEAPONFRAME]);
1186 if (bits & SU_ARMOR)
1187 MSG_WriteShort (msg, stats[STAT_ARMOR]);
1188 if (bits & SU_WEAPON)
1189 MSG_WriteShort (msg, stats[STAT_WEAPON]);
1190 MSG_WriteShort (msg, stats[STAT_HEALTH]);
1191 MSG_WriteShort (msg, stats[STAT_AMMO]);
1192 MSG_WriteShort (msg, stats[STAT_SHELLS]);
1193 MSG_WriteShort (msg, stats[STAT_NAILS]);
1194 MSG_WriteShort (msg, stats[STAT_ROCKETS]);
1195 MSG_WriteShort (msg, stats[STAT_CELLS]);
1196 MSG_WriteShort (msg, stats[STAT_ACTIVEWEAPON]);
1197 if (bits & SU_VIEWZOOM)
1198 MSG_WriteShort (msg, bound(0, stats[STAT_VIEWZOOM], 65535));
1200 else if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
1202 if (bits & SU_WEAPONFRAME)
1203 MSG_WriteByte (msg, stats[STAT_WEAPONFRAME]);
1204 if (bits & SU_ARMOR)
1205 MSG_WriteByte (msg, stats[STAT_ARMOR]);
1206 if (bits & SU_WEAPON)
1207 MSG_WriteByte (msg, stats[STAT_WEAPON]);
1208 MSG_WriteShort (msg, stats[STAT_HEALTH]);
1209 MSG_WriteByte (msg, stats[STAT_AMMO]);
1210 MSG_WriteByte (msg, stats[STAT_SHELLS]);
1211 MSG_WriteByte (msg, stats[STAT_NAILS]);
1212 MSG_WriteByte (msg, stats[STAT_ROCKETS]);
1213 MSG_WriteByte (msg, stats[STAT_CELLS]);
1214 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE || gamemode == GAME_NEXUIZ)
1216 for (i = 0;i < 32;i++)
1217 if (stats[STAT_WEAPON] & (1<<i))
1219 MSG_WriteByte (msg, i);
1222 MSG_WriteByte (msg, stats[STAT_WEAPON]);
1223 if (bits & SU_VIEWZOOM)
1225 if (sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
1226 MSG_WriteByte (msg, bound(0, stats[STAT_VIEWZOOM], 255));
1228 MSG_WriteShort (msg, bound(0, stats[STAT_VIEWZOOM], 65535));
1234 =======================
1235 SV_SendClientDatagram
1236 =======================
1238 static unsigned char sv_sendclientdatagram_buf[NET_MAXMESSAGE]; // FIXME?
1239 void SV_SendClientDatagram (client_t *client)
1241 int rate, maxrate, maxsize, maxsize2, downloadsize;
1243 int stats[MAX_CL_STATS];
1245 if (LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) == LHNETADDRESSTYPE_LOOP && !sv_ratelimitlocalplayer.integer)
1247 // for good singleplayer, send huge packets
1248 maxsize = sizeof(sv_sendclientdatagram_buf);
1249 maxsize2 = sizeof(sv_sendclientdatagram_buf);
1251 else if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
1253 // no rate limiting support on older protocols because dp protocols
1254 // 1-4 kick the client off if they overflow, and quake protocol shows
1255 // less than the full entity set if rate limited
1261 // PROTOCOL_DARKPLACES5 and later support packet size limiting of updates
1262 maxrate = max(NET_MINRATE, sv_maxrate.integer);
1263 if (sv_maxrate.integer != maxrate)
1264 Cvar_SetValueQuick(&sv_maxrate, maxrate);
1266 // this rate limiting does not understand sys_ticrate 0
1267 // (but no one should be running that on a server!)
1268 rate = bound(NET_MINRATE, client->rate, maxrate);
1269 rate = (int)(rate * sys_ticrate.value);
1270 maxsize = bound(50, rate, 1400);
1274 // while downloading, limit entity updates to half the packet
1275 // (any leftover space will be used for downloading)
1276 if (host_client->download_file)
1279 msg.data = sv_sendclientdatagram_buf;
1280 msg.maxsize = maxsize;
1283 if (host_client->spawned)
1285 MSG_WriteByte (&msg, svc_time);
1286 MSG_WriteFloat (&msg, sv.time);
1288 // add the client specific data to the datagram
1289 SV_WriteClientdataToMessage (client, client->edict, &msg, stats);
1290 VM_SV_WriteAutoSentStats (client, client->edict, &msg, stats);
1291 SV_WriteEntitiesToClient (client, client->edict, &msg, stats);
1293 // expand packet size to allow effects to go over the rate limit
1294 // (dropping them is FAR too ugly)
1295 msg.maxsize = maxsize2;
1297 // copy the server datagram if there is space
1298 // FIXME: put in delayed queue of effects to send
1299 if (sv.datagram.cursize > 0 && msg.cursize + sv.datagram.cursize <= msg.maxsize)
1300 SZ_Write (&msg, sv.datagram.data, sv.datagram.cursize);
1302 else if (realtime > client->keepalivetime)
1304 // the player isn't totally in the game yet
1305 // send small keepalive messages if too much time has passed
1306 msg.maxsize = maxsize2;
1307 client->keepalivetime = realtime + 5;
1308 MSG_WriteChar (&msg, svc_nop);
1311 msg.maxsize = maxsize2;
1313 // if a download is active, see if there is room to fit some download data
1315 downloadsize = maxsize * 2 - msg.cursize - 7;
1316 if (host_client->download_file && host_client->download_started && downloadsize > 0)
1318 fs_offset_t downloadstart;
1319 unsigned char data[1400];
1320 downloadstart = FS_Tell(host_client->download_file);
1321 downloadsize = min(downloadsize, (int)sizeof(data));
1322 downloadsize = FS_Read(host_client->download_file, data, downloadsize);
1323 // note this sends empty messages if at the end of the file, which is
1324 // necessary to keep the packet loss logic working
1325 // (the last blocks may be lost and need to be re-sent, and that will
1326 // only occur if the client acks the empty end messages, revealing
1327 // a gap in the download progress, causing the last blocks to be
1329 MSG_WriteChar (&msg, svc_downloaddata);
1330 MSG_WriteLong (&msg, downloadstart);
1331 MSG_WriteShort (&msg, downloadsize);
1332 if (downloadsize > 0)
1333 SZ_Write (&msg, data, downloadsize);
1336 // send the datagram
1337 NetConn_SendUnreliableMessage (client->netconnection, &msg, sv.protocol);
1341 =======================
1342 SV_UpdateToReliableMessages
1343 =======================
1345 void SV_UpdateToReliableMessages (void)
1354 // check for changes to be sent over the reliable streams
1355 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1357 // update the host_client fields we care about according to the entity fields
1358 host_client->edict = PRVM_EDICT_NUM(i+1);
1361 name = PRVM_GetString(host_client->edict->fields.server->netname);
1364 // always point the string back at host_client->name to keep it safe
1365 strlcpy (host_client->name, name, sizeof (host_client->name));
1366 host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
1367 if (strcmp(host_client->old_name, host_client->name))
1369 if (host_client->spawned)
1370 SV_BroadcastPrintf("%s changed name to %s\n", host_client->old_name, host_client->name);
1371 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1372 // send notification to all clients
1373 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1374 MSG_WriteByte (&sv.reliable_datagram, i);
1375 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1378 // DP_SV_CLIENTCOLORS
1379 // this is always found (since it's added by the progs loader)
1380 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
1381 host_client->colors = (int)val->_float;
1382 if (host_client->old_colors != host_client->colors)
1384 host_client->old_colors = host_client->colors;
1385 // send notification to all clients
1386 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1387 MSG_WriteByte (&sv.reliable_datagram, i);
1388 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1391 // NEXUIZ_PLAYERMODEL
1392 if( prog->fieldoffsets.playermodel >= 0 ) {
1393 model = PRVM_GetString(PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string);
1396 // always point the string back at host_client->name to keep it safe
1397 strlcpy (host_client->playermodel, model, sizeof (host_client->playermodel));
1398 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
1401 // NEXUIZ_PLAYERSKIN
1402 if( prog->fieldoffsets.playerskin >= 0 ) {
1403 skin = PRVM_GetString(PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string);
1406 // always point the string back at host_client->name to keep it safe
1407 strlcpy (host_client->playerskin, skin, sizeof (host_client->playerskin));
1408 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
1412 host_client->frags = (int)host_client->edict->fields.server->frags;
1413 if (host_client->old_frags != host_client->frags)
1415 host_client->old_frags = host_client->frags;
1416 // send notification to all clients
1417 MSG_WriteByte (&sv.reliable_datagram, svc_updatefrags);
1418 MSG_WriteByte (&sv.reliable_datagram, i);
1419 MSG_WriteShort (&sv.reliable_datagram, host_client->frags);
1423 for (j = 0, client = svs.clients;j < svs.maxclients;j++, client++)
1424 if (client->netconnection)
1425 SZ_Write (&client->netconnection->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize);
1427 SZ_Clear (&sv.reliable_datagram);
1432 =======================
1433 SV_SendClientMessages
1434 =======================
1436 void SV_SendClientMessages (void)
1438 int i, prepared = false;
1440 if (sv.protocol == PROTOCOL_QUAKEWORLD)
1441 Sys_Error("SV_SendClientMessages: no quakeworld support\n");
1443 // update frags, names, etc
1444 SV_UpdateToReliableMessages();
1446 // build individual updates
1447 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1449 if (!host_client->active)
1451 if (!host_client->netconnection)
1454 if (host_client->netconnection->message.overflowed)
1456 SV_DropClient (true); // if the message couldn't send, kick off
1463 // only prepare entities once per frame
1464 SV_PrepareEntitiesForSending();
1466 SV_SendClientDatagram (host_client);
1469 // clear muzzle flashes
1473 void SV_StartDownload_f(void)
1475 if (host_client->download_file)
1476 host_client->download_started = true;
1479 void SV_Download_f(void)
1481 const char *whichpack, *whichpack2, *extension;
1483 if (Cmd_Argc() != 2)
1485 SV_ClientPrintf("usage: download <filename>\n");
1489 if (FS_CheckNastyPath(Cmd_Argv(1), false))
1491 SV_ClientPrintf("Download rejected: nasty filename \"%s\"\n", Cmd_Argv(1));
1495 if (host_client->download_file)
1497 // at this point we'll assume the previous download should be aborted
1498 Con_DPrintf("Download of %s aborted by %s starting a new download\n", host_client->download_name, host_client->name);
1499 Host_ClientCommands("\nstopdownload\n");
1501 // close the file and reset variables
1502 FS_Close(host_client->download_file);
1503 host_client->download_file = NULL;
1504 host_client->download_name[0] = 0;
1505 host_client->download_expectedposition = 0;
1506 host_client->download_started = false;
1509 if (!sv_allowdownloads.integer)
1511 SV_ClientPrintf("Downloads are disabled on this server\n");
1512 Host_ClientCommands("\nstopdownload\n");
1516 strlcpy(host_client->download_name, Cmd_Argv(1), sizeof(host_client->download_name));
1517 extension = FS_FileExtension(host_client->download_name);
1519 // host_client is asking to download a specified file
1520 if (developer.integer >= 100)
1521 Con_Printf("Download request for %s by %s\n", host_client->download_name, host_client->name);
1523 if (!FS_FileExists(host_client->download_name))
1525 SV_ClientPrintf("Download rejected: server does not have the file \"%s\"\nYou may need to separately download or purchase the data archives for this game/mod to get this file\n", host_client->download_name);
1526 Host_ClientCommands("\nstopdownload\n");
1530 // check if the user is trying to download part of registered Quake(r)
1531 whichpack = FS_WhichPack(host_client->download_name);
1532 whichpack2 = FS_WhichPack("gfx/pop.lmp");
1533 if ((whichpack && whichpack2 && !strcasecmp(whichpack, whichpack2)) || FS_IsRegisteredQuakePack(host_client->download_name))
1535 SV_ClientPrintf("Download rejected: file \"%s\" is part of registered Quake(r)\nYou must purchase Quake(r) from id Software or a retailer to get this file\nPlease go to http://www.idsoftware.com/games/quake/quake/index.php?game_section=buy\n", host_client->download_name);
1536 Host_ClientCommands("\nstopdownload\n");
1540 // check if the server has forbidden archive downloads entirely
1541 if (!sv_allowdownloads_inarchive.integer)
1543 whichpack = FS_WhichPack(host_client->download_name);
1546 SV_ClientPrintf("Download rejected: file \"%s\" is in an archive (\"%s\")\nYou must separately download or purchase the data archives for this game/mod to get this file\n", host_client->download_name, whichpack);
1547 Host_ClientCommands("\nstopdownload\n");
1552 if (!sv_allowdownloads_config.integer)
1554 if (!strcasecmp(extension, "cfg"))
1556 SV_ClientPrintf("Download rejected: file \"%s\" is a .cfg file which is forbidden for security reasons\nYou must separately download or purchase the data archives for this game/mod to get this file\n", host_client->download_name);
1557 Host_ClientCommands("\nstopdownload\n");
1562 if (!sv_allowdownloads_dlcache.integer)
1564 if (!strncasecmp(host_client->download_name, "dlcache/", 8))
1566 SV_ClientPrintf("Download rejected: file \"%s\" is in the dlcache/ directory which is forbidden for security reasons\nYou must separately download or purchase the data archives for this game/mod to get this file\n", host_client->download_name);
1567 Host_ClientCommands("\nstopdownload\n");
1572 if (!sv_allowdownloads_archive.integer)
1574 if (!strcasecmp(extension, "pak") || !strcasecmp(extension, "pk3"))
1576 SV_ClientPrintf("Download rejected: file \"%s\" is an archive\nYou must separately download or purchase the data archives for this game/mod to get this file\n", host_client->download_name);
1577 Host_ClientCommands("\nstopdownload\n");
1582 host_client->download_file = FS_Open(host_client->download_name, "rb", true, false);
1583 if (!host_client->download_file)
1585 SV_ClientPrintf("Download rejected: server could not open the file \"%s\"\n", host_client->download_name);
1586 Host_ClientCommands("\nstopdownload\n");
1590 if (FS_FileSize(host_client->download_file) > 1<<30)
1592 SV_ClientPrintf("Download rejected: file \"%s\" is very large\n", host_client->download_name);
1593 Host_ClientCommands("\nstopdownload\n");
1594 FS_Close(host_client->download_file);
1595 host_client->download_file = NULL;
1599 Con_DPrintf("Downloading %s to %s\n", host_client->download_name, host_client->name);
1601 Host_ClientCommands("\ncl_downloadbegin %i %s\n", (int)FS_FileSize(host_client->download_file), host_client->download_name);
1603 host_client->download_expectedposition = 0;
1604 host_client->download_started = false;
1606 // the rest of the download process is handled in SV_SendClientDatagram
1607 // and other code dealing with svc_downloaddata and clc_ackdownloaddata
1609 // no svc_downloaddata messages will be sent until sv_startdownload is
1610 // sent by the client
1614 ==============================================================================
1618 ==============================================================================
1627 int SV_ModelIndex(const char *s, int precachemode)
1629 int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) ? 256 : MAX_MODELS);
1630 char filename[MAX_QPATH];
1634 //if (precachemode == 2)
1636 strlcpy(filename, s, sizeof(filename));
1637 for (i = 2;i < limit;i++)
1639 if (!sv.model_precache[i][0])
1643 if (sv.state != ss_loading && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5))
1645 Con_Printf("SV_ModelIndex(\"%s\"): precache_model can only be done in spawn functions\n", filename);
1648 if (precachemode == 1)
1649 Con_Printf("SV_ModelIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename);
1650 strlcpy(sv.model_precache[i], filename, sizeof(sv.model_precache[i]));
1651 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, false);
1652 if (sv.state != ss_loading)
1654 MSG_WriteByte(&sv.reliable_datagram, svc_precache);
1655 MSG_WriteShort(&sv.reliable_datagram, i);
1656 MSG_WriteString(&sv.reliable_datagram, filename);
1660 Con_Printf("SV_ModelIndex(\"%s\"): not precached\n", filename);
1663 if (!strcmp(sv.model_precache[i], filename))
1666 Con_Printf("SV_ModelIndex(\"%s\"): i (%i) == MAX_MODELS (%i)\n", filename, i, MAX_MODELS);
1676 int SV_SoundIndex(const char *s, int precachemode)
1678 int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) ? 256 : MAX_SOUNDS);
1679 char filename[MAX_QPATH];
1683 //if (precachemode == 2)
1685 strlcpy(filename, s, sizeof(filename));
1686 for (i = 1;i < limit;i++)
1688 if (!sv.sound_precache[i][0])
1692 if (sv.state != ss_loading && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5))
1694 Con_Printf("SV_SoundIndex(\"%s\"): precache_sound can only be done in spawn functions\n", filename);
1697 if (precachemode == 1)
1698 Con_Printf("SV_SoundIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename);
1699 strlcpy(sv.sound_precache[i], filename, sizeof(sv.sound_precache[i]));
1700 if (sv.state != ss_loading)
1702 MSG_WriteByte(&sv.reliable_datagram, svc_precache);
1703 MSG_WriteShort(&sv.reliable_datagram, i + 32768);
1704 MSG_WriteString(&sv.reliable_datagram, filename);
1708 Con_Printf("SV_SoundIndex(\"%s\"): not precached\n", filename);
1711 if (!strcmp(sv.sound_precache[i], filename))
1714 Con_Printf("SV_SoundIndex(\"%s\"): i (%i) == MAX_SOUNDS (%i)\n", filename, i, MAX_SOUNDS);
1718 // MUST match effectnameindex_t in client.h
1719 static const char *standardeffectnames[EFFECT_TOTAL] =
1727 "TE_SUPERSPIKEQUAD",
1743 "TE_TEI_BIGEXPLOSION",
1761 SV_ParticleEffectIndex
1765 int SV_ParticleEffectIndex(const char *name)
1767 int i, argc, linenumber, effectnameindex;
1768 fs_offset_t filesize;
1769 unsigned char *filedata;
1770 const char *text, *textstart, *textend;
1771 char argv[16][1024];
1772 if (!sv.particleeffectnamesloaded)
1774 sv.particleeffectnamesloaded = true;
1775 memset(sv.particleeffectname, 0, sizeof(sv.particleeffectname));
1776 for (i = 0;i < EFFECT_TOTAL;i++)
1777 strlcpy(sv.particleeffectname[i], standardeffectnames[i], sizeof(sv.particleeffectname[i]));
1778 filedata = FS_LoadFile("effectinfo.txt", tempmempool, true, &filesize);
1781 textstart = (const char *)filedata;
1782 textend = (const char *)filedata + filesize;
1784 for (linenumber = 1;;linenumber++)
1789 if (!COM_ParseToken(&text, true) || !strcmp(com_token, "\n"))
1793 strlcpy(argv[argc], com_token, sizeof(argv[argc]));
1797 if (com_token[0] == 0)
1798 break; // if the loop exited and it's not a \n, it's EOF
1801 if (!strcmp(argv[0], "effect"))
1805 for (effectnameindex = 1;effectnameindex < SV_MAX_PARTICLEEFFECTNAME;effectnameindex++)
1807 if (sv.particleeffectname[effectnameindex][0])
1809 if (!strcmp(sv.particleeffectname[effectnameindex], argv[1]))
1814 strlcpy(sv.particleeffectname[effectnameindex], argv[1], sizeof(sv.particleeffectname[effectnameindex]));
1818 // if we run out of names, abort
1819 if (effectnameindex == SV_MAX_PARTICLEEFFECTNAME)
1821 Con_Printf("effectinfo.txt:%i: too many effects!\n", linenumber);
1830 // search for the name
1831 for (effectnameindex = 1;effectnameindex < SV_MAX_PARTICLEEFFECTNAME && sv.particleeffectname[effectnameindex][0];effectnameindex++)
1832 if (!strcmp(sv.particleeffectname[effectnameindex], name))
1833 return effectnameindex;
1834 // return 0 if we couldn't find it
1844 void SV_CreateBaseline (void)
1846 int i, entnum, large;
1847 prvm_edict_t *svent;
1849 // LordHavoc: clear *all* states (note just active ones)
1850 for (entnum = 0;entnum < prog->max_edicts;entnum++)
1852 // get the current server version
1853 svent = PRVM_EDICT_NUM(entnum);
1855 // LordHavoc: always clear state values, whether the entity is in use or not
1856 svent->priv.server->baseline = defaultstate;
1858 if (svent->priv.server->free)
1860 if (entnum > svs.maxclients && !svent->fields.server->modelindex)
1863 // create entity baseline
1864 VectorCopy (svent->fields.server->origin, svent->priv.server->baseline.origin);
1865 VectorCopy (svent->fields.server->angles, svent->priv.server->baseline.angles);
1866 svent->priv.server->baseline.frame = (int)svent->fields.server->frame;
1867 svent->priv.server->baseline.skin = (int)svent->fields.server->skin;
1868 if (entnum > 0 && entnum <= svs.maxclients)
1870 svent->priv.server->baseline.colormap = entnum;
1871 svent->priv.server->baseline.modelindex = SV_ModelIndex("progs/player.mdl", 1);
1875 svent->priv.server->baseline.colormap = 0;
1876 svent->priv.server->baseline.modelindex = (int)svent->fields.server->modelindex;
1880 if (svent->priv.server->baseline.modelindex & 0xFF00 || svent->priv.server->baseline.frame & 0xFF00)
1883 // add to the message
1885 MSG_WriteByte (&sv.signon, svc_spawnbaseline2);
1887 MSG_WriteByte (&sv.signon, svc_spawnbaseline);
1888 MSG_WriteShort (&sv.signon, entnum);
1892 MSG_WriteShort (&sv.signon, svent->priv.server->baseline.modelindex);
1893 MSG_WriteShort (&sv.signon, svent->priv.server->baseline.frame);
1897 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.modelindex);
1898 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.frame);
1900 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.colormap);
1901 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.skin);
1902 for (i=0 ; i<3 ; i++)
1904 MSG_WriteCoord(&sv.signon, svent->priv.server->baseline.origin[i], sv.protocol);
1905 MSG_WriteAngle(&sv.signon, svent->priv.server->baseline.angles[i], sv.protocol);
1915 Grabs the current state of each client for saving across the
1916 transition to another level
1919 void SV_SaveSpawnparms (void)
1923 svs.serverflags = (int)prog->globals.server->serverflags;
1925 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1927 if (!host_client->active)
1930 // call the progs to get default spawn parms for the new client
1931 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1932 PRVM_ExecuteProgram (prog->globals.server->SetChangeParms, "QC function SetChangeParms is missing");
1933 for (j=0 ; j<NUM_SPAWN_PARMS ; j++)
1934 host_client->spawn_parms[j] = (&prog->globals.server->parm1)[j];
1938 void SV_IncreaseEdicts(void)
1942 int oldmax_edicts = prog->max_edicts;
1943 void *oldedictsengineprivate = prog->edictprivate;
1944 void *oldedictsfields = prog->edictsfields;
1945 void *oldmoved_edicts = sv.moved_edicts;
1947 if (prog->max_edicts >= MAX_EDICTS)
1950 // links don't survive the transition, so unlink everything
1951 for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
1953 if (!ent->priv.server->free)
1954 SV_UnlinkEdict(prog->edicts + i);
1955 memset(&ent->priv.server->areagrid, 0, sizeof(ent->priv.server->areagrid));
1957 World_Clear(&sv.world);
1959 prog->max_edicts = min(prog->max_edicts + 256, MAX_EDICTS);
1960 prog->edictprivate = PR_Alloc(prog->max_edicts * sizeof(edict_engineprivate_t));
1961 prog->edictsfields = PR_Alloc(prog->max_edicts * prog->edict_size);
1962 sv.moved_edicts = PR_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
1964 memcpy(prog->edictprivate, oldedictsengineprivate, oldmax_edicts * sizeof(edict_engineprivate_t));
1965 memcpy(prog->edictsfields, oldedictsfields, oldmax_edicts * prog->edict_size);
1967 for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
1969 ent->priv.vp = (unsigned char*) prog->edictprivate + i * prog->edictprivate_size;
1970 ent->fields.server = (void *)((unsigned char *)prog->edictsfields + i * prog->edict_size);
1971 // link every entity except world
1972 if (!ent->priv.server->free)
1973 SV_LinkEdict(ent, false);
1976 PR_Free(oldedictsengineprivate);
1977 PR_Free(oldedictsfields);
1978 PR_Free(oldmoved_edicts);
1985 This is called at the start of each level
1988 extern float scr_centertime_off;
1990 void SV_SpawnServer (const char *server)
1995 model_t *worldmodel;
1996 char modelname[sizeof(sv.modelname)];
1998 Con_DPrintf("SpawnServer: %s\n", server);
2000 if (cls.state != ca_dedicated)
2001 SCR_BeginLoadingPlaque();
2003 dpsnprintf (modelname, sizeof(modelname), "maps/%s.bsp", server);
2004 worldmodel = Mod_ForName(modelname, false, true, true);
2005 if (!worldmodel || !worldmodel->TraceBox)
2007 Con_Printf("Couldn't load map %s\n", modelname);
2011 // let's not have any servers with no name
2012 if (hostname.string[0] == 0)
2013 Cvar_Set ("hostname", "UNNAMED");
2014 scr_centertime_off = 0;
2016 svs.changelevel_issued = false; // now safe to issue another
2018 // make the map a required file for clients
2019 Curl_ClearRequirements();
2020 Curl_RequireFile(modelname);
2023 // tell all connected clients that we are going to a new level
2028 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
2030 if (client->netconnection)
2032 MSG_WriteByte(&client->netconnection->message, svc_stufftext);
2033 MSG_WriteString(&client->netconnection->message, "reconnect\n");
2040 NetConn_OpenServerPorts(true);
2044 // make cvars consistant
2047 Cvar_SetValue ("deathmatch", 0);
2048 // LordHavoc: it can be useful to have skills outside the range 0-3...
2049 //current_skill = bound(0, (int)(skill.value + 0.5), 3);
2050 //Cvar_SetValue ("skill", (float)current_skill);
2051 current_skill = (int)(skill.value + 0.5);
2054 // set up the new server
2056 memset (&sv, 0, sizeof(sv));
2057 // if running a local client, make sure it doesn't try to access the last
2058 // level's data which is no longer valiud
2061 if(*sv_random_seed.string)
2063 srand(sv_random_seed.integer);
2064 Con_Printf("NOTE: random seed is %d; use for debugging/benchmarking only!\nUnset sv_random_seed to get real random numbers again.\n", sv_random_seed.integer);
2071 strlcpy (sv.name, server, sizeof (sv.name));
2073 sv.protocol = Protocol_EnumForName(sv_protocolname.string);
2074 if (sv.protocol == PROTOCOL_UNKNOWN)
2077 Protocol_Names(buffer, sizeof(buffer));
2078 Con_Printf("Unknown sv_protocolname \"%s\", valid values are:\n%s\n", sv_protocolname.string, buffer);
2079 sv.protocol = PROTOCOL_QUAKE;
2084 // load progs to get entity field count
2085 //PR_LoadProgs ( sv_progs.string );
2087 // allocate server memory
2088 /*// start out with just enough room for clients and a reasonable estimate of entities
2089 prog->max_edicts = max(svs.maxclients + 1, 512);
2090 prog->max_edicts = min(prog->max_edicts, MAX_EDICTS);
2092 // prvm_edict_t structures (hidden from progs)
2093 prog->edicts = PR_Alloc(MAX_EDICTS * sizeof(prvm_edict_t));
2094 // engine private structures (hidden from progs)
2095 prog->edictprivate = PR_Alloc(prog->max_edicts * sizeof(edict_engineprivate_t));
2096 // progs fields, often accessed by server
2097 prog->edictsfields = PR_Alloc(prog->max_edicts * prog->edict_size);*/
2098 // used by PushMove to move back pushed entities
2099 sv.moved_edicts = (prvm_edict_t **)PRVM_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
2100 /*for (i = 0;i < prog->max_edicts;i++)
2102 ent = prog->edicts + i;
2103 ent->priv.vp = (unsigned char*) prog->edictprivate + i * prog->edictprivate_size;
2104 ent->fields.server = (void *)((unsigned char *)prog->edictsfields + i * prog->edict_size);
2107 // reset client csqc entity versions right away.
2108 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2109 EntityFrameCSQC_InitClientVersions(i, true);
2111 sv.datagram.maxsize = sizeof(sv.datagram_buf);
2112 sv.datagram.cursize = 0;
2113 sv.datagram.data = sv.datagram_buf;
2115 sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf);
2116 sv.reliable_datagram.cursize = 0;
2117 sv.reliable_datagram.data = sv.reliable_datagram_buf;
2119 sv.signon.maxsize = sizeof(sv.signon_buf);
2120 sv.signon.cursize = 0;
2121 sv.signon.data = sv.signon_buf;
2123 // leave slots at start for clients only
2124 //prog->num_edicts = svs.maxclients+1;
2126 sv.state = ss_loading;
2127 prog->allowworldwrites = true;
2130 prog->globals.server->time = sv.time = 1.0;
2133 worldmodel->used = true;
2135 strlcpy (sv.name, server, sizeof (sv.name));
2136 strlcpy(sv.modelname, modelname, sizeof(sv.modelname));
2137 sv.worldmodel = worldmodel;
2138 sv.models[1] = sv.worldmodel;
2141 // clear world interaction links
2143 VectorCopy(sv.worldmodel->normalmins, sv.world.areagrid_mins);
2144 VectorCopy(sv.worldmodel->normalmaxs, sv.world.areagrid_maxs);
2145 World_Clear(&sv.world);
2147 strlcpy(sv.sound_precache[0], "", sizeof(sv.sound_precache[0]));
2149 strlcpy(sv.model_precache[0], "", sizeof(sv.model_precache[0]));
2150 strlcpy(sv.model_precache[1], sv.modelname, sizeof(sv.model_precache[1]));
2151 for (i = 1;i < sv.worldmodel->brush.numsubmodels;i++)
2153 dpsnprintf(sv.model_precache[i+1], sizeof(sv.model_precache[i+1]), "*%i", i);
2154 sv.models[i+1] = Mod_ForName (sv.model_precache[i+1], false, false, false);
2158 // load the rest of the entities
2160 // AK possible hack since num_edicts is still 0
2161 ent = PRVM_EDICT_NUM(0);
2162 memset (ent->fields.server, 0, prog->progs->entityfields * 4);
2163 ent->priv.server->free = false;
2164 ent->fields.server->model = PRVM_SetEngineString(sv.modelname);
2165 ent->fields.server->modelindex = 1; // world model
2166 ent->fields.server->solid = SOLID_BSP;
2167 ent->fields.server->movetype = MOVETYPE_PUSH;
2168 VectorCopy(sv.worldmodel->normalmins, ent->fields.server->mins);
2169 VectorCopy(sv.worldmodel->normalmaxs, ent->fields.server->maxs);
2170 VectorCopy(sv.worldmodel->normalmins, ent->fields.server->absmin);
2171 VectorCopy(sv.worldmodel->normalmaxs, ent->fields.server->absmax);
2174 prog->globals.server->coop = coop.integer;
2176 prog->globals.server->deathmatch = deathmatch.integer;
2178 prog->globals.server->mapname = PRVM_SetEngineString(sv.name);
2180 // serverflags are for cross level information (sigils)
2181 prog->globals.server->serverflags = svs.serverflags;
2183 // we need to reset the spawned flag on all connected clients here so that
2184 // their thinks don't run during startup (before PutClientInServer)
2185 // we also need to set up the client entities now
2186 // and we need to set the ->edict pointers to point into the progs edicts
2187 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2189 host_client->spawned = false;
2190 host_client->edict = PRVM_EDICT_NUM(i + 1);
2191 PRVM_ED_ClearEdict(host_client->edict);
2194 // load replacement entity file if found
2195 if (sv_entpatch.integer && (entities = (char *)FS_LoadFile(va("maps/%s.ent", sv.name), tempmempool, true, NULL)))
2197 Con_Printf("Loaded maps/%s.ent\n", sv.name);
2198 PRVM_ED_LoadFromFile (entities);
2202 PRVM_ED_LoadFromFile (sv.worldmodel->brush.entities);
2205 // LordHavoc: clear world angles (to fix e3m3.bsp)
2206 VectorClear(prog->edicts->fields.server->angles);
2208 // all setup is completed, any further precache statements are errors
2209 sv.state = ss_active;
2210 prog->allowworldwrites = false;
2212 // run two frames to allow everything to settle
2213 for (i = 0;i < 2;i++)
2221 // create a baseline for more efficient communications
2222 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
2223 SV_CreateBaseline ();
2225 // send serverinfo to all connected clients, and set up botclients coming back from a level change
2226 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2228 if (!host_client->active)
2230 if (host_client->netconnection)
2231 SV_SendServerinfo(host_client);
2235 // if client is a botclient coming from a level change, we need to
2236 // set up client info that normally requires networking
2238 // copy spawn parms out of the client_t
2239 for (j=0 ; j< NUM_SPAWN_PARMS ; j++)
2240 (&prog->globals.server->parm1)[j] = host_client->spawn_parms[j];
2242 // call the spawn function
2243 host_client->clientconnectcalled = true;
2244 prog->globals.server->time = sv.time;
2245 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
2246 PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
2247 PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
2248 host_client->spawned = true;
2252 Con_DPrint("Server spawned.\n");
2253 NetConn_Heartbeat (2);
2258 /////////////////////////////////////////////////////
2261 void SV_VM_CB_BeginIncreaseEdicts(void)
2266 PRVM_Free( sv.moved_edicts );
2267 sv.moved_edicts = (prvm_edict_t **)PRVM_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
2269 // links don't survive the transition, so unlink everything
2270 for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
2272 if (!ent->priv.server->free)
2273 World_UnlinkEdict(prog->edicts + i);
2274 memset(&ent->priv.server->areagrid, 0, sizeof(ent->priv.server->areagrid));
2276 World_Clear(&sv.world);
2279 void SV_VM_CB_EndIncreaseEdicts(void)
2284 // link every entity except world
2285 for (i = 1, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
2286 if (!ent->priv.server->free)
2287 SV_LinkEdict(ent, false);
2290 void SV_VM_CB_InitEdict(prvm_edict_t *e)
2292 // LordHavoc: for consistency set these here
2293 int num = PRVM_NUM_FOR_EDICT(e) - 1;
2295 e->priv.server->move = false; // don't move on first frame
2297 if (num >= 0 && num < svs.maxclients)
2300 // set colormap and team on newly created player entity
2301 e->fields.server->colormap = num + 1;
2302 e->fields.server->team = (svs.clients[num].colors & 15) + 1;
2303 // set netname/clientcolors back to client values so that
2304 // DP_SV_CLIENTNAME and DP_SV_CLIENTCOLORS will not immediately
2306 e->fields.server->netname = PRVM_SetEngineString(svs.clients[num].name);
2307 if ((val = PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.clientcolors)))
2308 val->_float = svs.clients[num].colors;
2309 // NEXUIZ_PLAYERMODEL and NEXUIZ_PLAYERSKIN
2310 if( prog->fieldoffsets.playermodel >= 0 )
2311 PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(svs.clients[num].playermodel);
2312 if( prog->fieldoffsets.playerskin >= 0 )
2313 PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(svs.clients[num].playerskin);
2317 void SV_VM_CB_FreeEdict(prvm_edict_t *ed)
2319 World_UnlinkEdict(ed); // unlink from world bsp
2321 ed->fields.server->model = 0;
2322 ed->fields.server->takedamage = 0;
2323 ed->fields.server->modelindex = 0;
2324 ed->fields.server->colormap = 0;
2325 ed->fields.server->skin = 0;
2326 ed->fields.server->frame = 0;
2327 VectorClear(ed->fields.server->origin);
2328 VectorClear(ed->fields.server->angles);
2329 ed->fields.server->nextthink = -1;
2330 ed->fields.server->solid = 0;
2333 void SV_VM_CB_CountEdicts(void)
2337 int active, models, solid, step;
2339 active = models = solid = step = 0;
2340 for (i=0 ; i<prog->num_edicts ; i++)
2342 ent = PRVM_EDICT_NUM(i);
2343 if (ent->priv.server->free)
2346 if (ent->fields.server->solid)
2348 if (ent->fields.server->model)
2350 if (ent->fields.server->movetype == MOVETYPE_STEP)
2354 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
2355 Con_Printf("active :%3i\n", active);
2356 Con_Printf("view :%3i\n", models);
2357 Con_Printf("touch :%3i\n", solid);
2358 Con_Printf("step :%3i\n", step);
2361 qboolean SV_VM_CB_LoadEdict(prvm_edict_t *ent)
2363 // remove things from different skill levels or deathmatch
2364 if (gamemode != GAME_TRANSFUSION) //Transfusion does this in QC
2366 if (deathmatch.integer)
2368 if (((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_DEATHMATCH))
2373 else if ((current_skill <= 0 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_EASY ))
2374 || (current_skill == 1 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_MEDIUM))
2375 || (current_skill >= 2 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_HARD )))
2383 cvar_t pr_checkextension = {CVAR_READONLY, "pr_checkextension", "1", "indicates to QuakeC that the standard quakec extensions system is available (if 0, quakec should not attempt to use extensions)"};
2384 cvar_t nomonsters = {0, "nomonsters", "0", "unused cvar in quake, can be used by mods"};
2385 cvar_t gamecfg = {0, "gamecfg", "0", "unused cvar in quake, can be used by mods"};
2386 cvar_t scratch1 = {0, "scratch1", "0", "unused cvar in quake, can be used by mods"};
2387 cvar_t scratch2 = {0,"scratch2", "0", "unused cvar in quake, can be used by mods"};
2388 cvar_t scratch3 = {0, "scratch3", "0", "unused cvar in quake, can be used by mods"};
2389 cvar_t scratch4 = {0, "scratch4", "0", "unused cvar in quake, can be used by mods"};
2390 cvar_t savedgamecfg = {CVAR_SAVE, "savedgamecfg", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2391 cvar_t saved1 = {CVAR_SAVE, "saved1", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2392 cvar_t saved2 = {CVAR_SAVE, "saved2", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2393 cvar_t saved3 = {CVAR_SAVE, "saved3", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2394 cvar_t saved4 = {CVAR_SAVE, "saved4", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2395 cvar_t nehx00 = {0, "nehx00", "0", "nehahra data storage cvar (used in singleplayer)"};
2396 cvar_t nehx01 = {0, "nehx01", "0", "nehahra data storage cvar (used in singleplayer)"};
2397 cvar_t nehx02 = {0, "nehx02", "0", "nehahra data storage cvar (used in singleplayer)"};
2398 cvar_t nehx03 = {0, "nehx03", "0", "nehahra data storage cvar (used in singleplayer)"};
2399 cvar_t nehx04 = {0, "nehx04", "0", "nehahra data storage cvar (used in singleplayer)"};
2400 cvar_t nehx05 = {0, "nehx05", "0", "nehahra data storage cvar (used in singleplayer)"};
2401 cvar_t nehx06 = {0, "nehx06", "0", "nehahra data storage cvar (used in singleplayer)"};
2402 cvar_t nehx07 = {0, "nehx07", "0", "nehahra data storage cvar (used in singleplayer)"};
2403 cvar_t nehx08 = {0, "nehx08", "0", "nehahra data storage cvar (used in singleplayer)"};
2404 cvar_t nehx09 = {0, "nehx09", "0", "nehahra data storage cvar (used in singleplayer)"};
2405 cvar_t nehx10 = {0, "nehx10", "0", "nehahra data storage cvar (used in singleplayer)"};
2406 cvar_t nehx11 = {0, "nehx11", "0", "nehahra data storage cvar (used in singleplayer)"};
2407 cvar_t nehx12 = {0, "nehx12", "0", "nehahra data storage cvar (used in singleplayer)"};
2408 cvar_t nehx13 = {0, "nehx13", "0", "nehahra data storage cvar (used in singleplayer)"};
2409 cvar_t nehx14 = {0, "nehx14", "0", "nehahra data storage cvar (used in singleplayer)"};
2410 cvar_t nehx15 = {0, "nehx15", "0", "nehahra data storage cvar (used in singleplayer)"};
2411 cvar_t nehx16 = {0, "nehx16", "0", "nehahra data storage cvar (used in singleplayer)"};
2412 cvar_t nehx17 = {0, "nehx17", "0", "nehahra data storage cvar (used in singleplayer)"};
2413 cvar_t nehx18 = {0, "nehx18", "0", "nehahra data storage cvar (used in singleplayer)"};
2414 cvar_t nehx19 = {0, "nehx19", "0", "nehahra data storage cvar (used in singleplayer)"};
2415 cvar_t cutscene = {0, "cutscene", "1", "enables cutscenes in nehahra, can be used by other mods"};
2417 void SV_VM_Init(void)
2419 Cvar_RegisterVariable (&pr_checkextension);
2420 Cvar_RegisterVariable (&nomonsters);
2421 Cvar_RegisterVariable (&gamecfg);
2422 Cvar_RegisterVariable (&scratch1);
2423 Cvar_RegisterVariable (&scratch2);
2424 Cvar_RegisterVariable (&scratch3);
2425 Cvar_RegisterVariable (&scratch4);
2426 Cvar_RegisterVariable (&savedgamecfg);
2427 Cvar_RegisterVariable (&saved1);
2428 Cvar_RegisterVariable (&saved2);
2429 Cvar_RegisterVariable (&saved3);
2430 Cvar_RegisterVariable (&saved4);
2431 // LordHavoc: Nehahra uses these to pass data around cutscene demos
2432 if (gamemode == GAME_NEHAHRA)
2434 Cvar_RegisterVariable (&nehx00);
2435 Cvar_RegisterVariable (&nehx01);
2436 Cvar_RegisterVariable (&nehx02);
2437 Cvar_RegisterVariable (&nehx03);
2438 Cvar_RegisterVariable (&nehx04);
2439 Cvar_RegisterVariable (&nehx05);
2440 Cvar_RegisterVariable (&nehx06);
2441 Cvar_RegisterVariable (&nehx07);
2442 Cvar_RegisterVariable (&nehx08);
2443 Cvar_RegisterVariable (&nehx09);
2444 Cvar_RegisterVariable (&nehx10);
2445 Cvar_RegisterVariable (&nehx11);
2446 Cvar_RegisterVariable (&nehx12);
2447 Cvar_RegisterVariable (&nehx13);
2448 Cvar_RegisterVariable (&nehx14);
2449 Cvar_RegisterVariable (&nehx15);
2450 Cvar_RegisterVariable (&nehx16);
2451 Cvar_RegisterVariable (&nehx17);
2452 Cvar_RegisterVariable (&nehx18);
2453 Cvar_RegisterVariable (&nehx19);
2455 Cvar_RegisterVariable (&cutscene); // for Nehahra but useful to other mods as well
2458 #define REQFIELDS (sizeof(reqfields) / sizeof(prvm_required_field_t))
2460 prvm_required_field_t reqfields[] =
2462 {ev_entity, "cursor_trace_ent"},
2463 {ev_entity, "drawonlytoclient"},
2464 {ev_entity, "exteriormodeltoclient"},
2465 {ev_entity, "nodrawtoclient"},
2466 {ev_entity, "tag_entity"},
2467 {ev_entity, "viewmodelforclient"},
2468 {ev_float, "alpha"},
2469 {ev_float, "ammo_cells1"},
2470 {ev_float, "ammo_lava_nails"},
2471 {ev_float, "ammo_multi_rockets"},
2472 {ev_float, "ammo_nails1"},
2473 {ev_float, "ammo_plasma"},
2474 {ev_float, "ammo_rockets1"},
2475 {ev_float, "ammo_shells1"},
2476 {ev_float, "button3"},
2477 {ev_float, "button4"},
2478 {ev_float, "button5"},
2479 {ev_float, "button6"},
2480 {ev_float, "button7"},
2481 {ev_float, "button8"},
2482 {ev_float, "button9"},
2483 {ev_float, "button10"},
2484 {ev_float, "button11"},
2485 {ev_float, "button12"},
2486 {ev_float, "button13"},
2487 {ev_float, "button14"},
2488 {ev_float, "button15"},
2489 {ev_float, "button16"},
2490 {ev_float, "buttonchat"},
2491 {ev_float, "buttonuse"},
2492 {ev_float, "clientcolors"},
2493 {ev_float, "cursor_active"},
2494 {ev_float, "fullbright"},
2495 {ev_float, "glow_color"},
2496 {ev_float, "glow_size"},
2497 {ev_float, "glow_trail"},
2498 {ev_float, "gravity"},
2499 {ev_float, "idealpitch"},
2500 {ev_float, "items2"},
2501 {ev_float, "light_lev"},
2502 {ev_float, "pflags"},
2504 {ev_float, "pitch_speed"},
2505 {ev_float, "pmodel"},
2506 {ev_float, "renderamt"}, // HalfLife support
2507 {ev_float, "rendermode"}, // HalfLife support
2508 {ev_float, "scale"},
2509 {ev_float, "style"},
2510 {ev_float, "tag_index"},
2511 {ev_float, "Version"},
2512 {ev_float, "viewzoom"},
2513 {ev_vector, "color"},
2514 {ev_vector, "colormod"},
2515 {ev_vector, "cursor_screen"},
2516 {ev_vector, "cursor_trace_endpos"},
2517 {ev_vector, "cursor_trace_start"},
2518 {ev_vector, "movement"},
2519 {ev_vector, "punchvector"},
2520 {ev_string, "playermodel"},
2521 {ev_string, "playerskin"},
2522 {ev_function, "SendEntity"},
2523 {ev_function, "customizeentityforclient"},
2524 // DRESK - Support for Entity Contents Transition Event
2525 {ev_function, "contentstransition"},
2528 void SV_VM_Setup(void)
2530 extern cvar_t csqc_progname; //[515]: csqc crc check and right csprogs name according to progs.dat
2531 extern cvar_t csqc_progcrc;
2532 extern cvar_t csqc_progsize;
2533 size_t csprogsdatasize;
2535 PRVM_InitProg( PRVM_SERVERPROG );
2537 // allocate the mempools
2538 // TODO: move the magic numbers/constants into #defines [9/13/2006 Black]
2539 prog->progs_mempool = Mem_AllocPool("Server Progs", 0, NULL);
2540 prog->builtins = vm_sv_builtins;
2541 prog->numbuiltins = vm_sv_numbuiltins;
2542 prog->headercrc = PROGHEADER_CRC;
2543 prog->max_edicts = 512;
2544 prog->limit_edicts = MAX_EDICTS;
2545 prog->reserved_edicts = svs.maxclients;
2546 prog->edictprivate_size = sizeof(edict_engineprivate_t);
2547 prog->name = "server";
2548 prog->extensionstring = vm_sv_extensions;
2549 prog->loadintoworld = true;
2551 prog->begin_increase_edicts = SV_VM_CB_BeginIncreaseEdicts;
2552 prog->end_increase_edicts = SV_VM_CB_EndIncreaseEdicts;
2553 prog->init_edict = SV_VM_CB_InitEdict;
2554 prog->free_edict = SV_VM_CB_FreeEdict;
2555 prog->count_edicts = SV_VM_CB_CountEdicts;
2556 prog->load_edict = SV_VM_CB_LoadEdict;
2557 prog->init_cmd = VM_SV_Cmd_Init;
2558 prog->reset_cmd = VM_SV_Cmd_Reset;
2559 prog->error_cmd = Host_Error;
2561 // TODO: add a requiredfuncs list (ask LH if this is necessary at all)
2562 PRVM_LoadProgs( sv_progs.string, 0, NULL, REQFIELDS, reqfields, 0, NULL );
2564 // some mods compiled with scrambling compilers lack certain critical
2565 // global names and field names such as "self" and "time" and "nextthink"
2566 // so we have to set these offsets manually, matching the entvars_t
2567 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, angles);
2568 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, chain);
2569 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, classname);
2570 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, frame);
2571 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, groundentity);
2572 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ideal_yaw);
2573 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, nextthink);
2574 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, think);
2575 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, yaw_speed);
2576 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, self);
2577 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, time);
2578 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_forward);
2579 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_right);
2580 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_up);
2581 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_allsolid);
2582 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_startsolid);
2583 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_fraction);
2584 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inwater);
2585 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inopen);
2586 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_endpos);
2587 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_normal);
2588 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_dist);
2589 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_ent);
2590 // OP_STATE is always supported on server (due to entvars_t)
2591 prog->flag |= PRVM_OP_STATE;
2593 VM_AutoSentStats_Clear();//[515]: csqc
2594 EntityFrameCSQC_ClearVersions();//[515]: csqc
2598 // see if there is a csprogs.dat installed, and if so, set the csqc_progcrc accordingly, this will be sent to connecting clients to tell them to only load a matching csprogs.dat file
2599 sv.csqc_progname[0] = 0;
2600 sv.csqc_progcrc = FS_CRCFile(csqc_progname.string, &csprogsdatasize);
2601 sv.csqc_progsize = csprogsdatasize;
2602 if (sv.csqc_progsize > 0)
2604 strlcpy(sv.csqc_progname, csqc_progname.string, sizeof(sv.csqc_progname));
2605 Con_DPrintf("server detected csqc progs file \"%s\" with size %i and crc %i\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc);
2609 void SV_VM_Begin(void)
2612 PRVM_SetProg( PRVM_SERVERPROG );
2614 prog->globals.server->time = (float) sv.time;
2617 void SV_VM_End(void)