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", "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
456 Initializes a client_t for a new net connection. This will only be called
457 once for a player each game, not once for each level change.
460 void SV_ConnectClient (int clientnum, netconn_t *netconnection)
464 float spawn_parms[NUM_SPAWN_PARMS];
466 client = svs.clients + clientnum;
468 if(netconnection)//[515]: bots don't play with csqc =)
469 EntityFrameCSQC_InitClientVersions(clientnum, false);
471 // set up the client_t
473 memcpy (spawn_parms, client->spawn_parms, sizeof(spawn_parms));
474 memset (client, 0, sizeof(*client));
475 client->active = true;
476 client->netconnection = netconnection;
478 Con_DPrintf("Client %s connected\n", client->netconnection ? client->netconnection->address : "botclient");
480 strlcpy(client->name, "unconnected", sizeof(client->name));
481 strlcpy(client->old_name, "unconnected", sizeof(client->old_name));
482 client->spawned = false;
483 client->edict = PRVM_EDICT_NUM(clientnum+1);
484 if (client->netconnection)
485 client->netconnection->message.allowoverflow = true; // we can catch it
486 // updated by receiving "rate" command from client
487 client->rate = NET_MINRATE;
488 // no limits for local player
489 if (client->netconnection && LHNETADDRESS_GetAddressType(&client->netconnection->peeraddress) == LHNETADDRESSTYPE_LOOP)
490 client->rate = 1000000000;
491 client->connecttime = realtime;
494 memcpy (client->spawn_parms, spawn_parms, sizeof(spawn_parms));
497 // call the progs to get default spawn parms for the new client
498 // set self to world to intentionally cause errors with broken SetNewParms code in some mods
499 prog->globals.server->self = 0;
500 PRVM_ExecuteProgram (prog->globals.server->SetNewParms, "QC function SetNewParms is missing");
501 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
502 client->spawn_parms[i] = (&prog->globals.server->parm1)[i];
504 // set up the entity for this client (including .colormap, .team, etc)
505 PRVM_ED_ClearEdict(client->edict);
508 // don't call SendServerinfo for a fresh botclient because its fields have
509 // not been set up by the qc yet
510 if (client->netconnection)
511 SV_SendServerinfo (client);
513 client->spawned = true;
518 ===============================================================================
522 ===============================================================================
531 void SV_ClearDatagram (void)
533 SZ_Clear (&sv.datagram);
537 =============================================================================
539 The PVS must include a small area around the client to allow head bobbing
540 or other small motion on the client side. Otherwise, a bob might cause an
541 entity that should be visible to not show up, especially when the bob
544 =============================================================================
547 int sv_writeentitiestoclient_pvsbytes;
548 unsigned char sv_writeentitiestoclient_pvs[MAX_MAP_LEAFS/8];
550 static int numsendentities;
551 static entity_state_t sendentities[MAX_EDICTS];
552 static entity_state_t *sendentitiesindex[MAX_EDICTS];
554 qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int e)
557 unsigned int modelindex, effects, flags, glowsize, lightstyle, lightpflags, light[4], specialvisibilityradius;
558 unsigned int customizeentityforclient;
560 vec3_t cullmins, cullmaxs;
564 // EF_NODRAW prevents sending for any reason except for your own
565 // client, so we must keep all clients in this superset
566 effects = (unsigned)ent->fields.server->effects;
568 // we can omit invisible entities with no effects that are not clients
569 // LordHavoc: this could kill tags attached to an invisible entity, I
570 // just hope we never have to support that case
571 i = (int)ent->fields.server->modelindex;
572 modelindex = (i >= 1 && i < MAX_MODELS && *PRVM_GetString(ent->fields.server->model)) ? i : 0;
575 i = (int)(PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_size)->_float * 0.25f);
576 glowsize = (unsigned char)bound(0, i, 255);
577 if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_trail)->_float)
578 flags |= RENDER_GLOWTRAIL;
580 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[0]*256;
581 light[0] = (unsigned short)bound(0, f, 65535);
582 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[1]*256;
583 light[1] = (unsigned short)bound(0, f, 65535);
584 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[2]*256;
585 light[2] = (unsigned short)bound(0, f, 65535);
586 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.light_lev)->_float;
587 light[3] = (unsigned short)bound(0, f, 65535);
588 lightstyle = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.style)->_float;
589 lightpflags = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.pflags)->_float;
591 if (gamemode == GAME_TENEBRAE)
593 // tenebrae's EF_FULLDYNAMIC conflicts with Q2's EF_NODRAW
597 lightpflags |= PFLAGS_FULLDYNAMIC;
599 // tenebrae's EF_GREEN conflicts with DP's EF_ADDITIVE
603 light[0] = (int)(0.2*256);
604 light[1] = (int)(1.0*256);
605 light[2] = (int)(0.2*256);
607 lightpflags |= PFLAGS_FULLDYNAMIC;
611 specialvisibilityradius = 0;
612 if (lightpflags & PFLAGS_FULLDYNAMIC)
613 specialvisibilityradius = max(specialvisibilityradius, light[3]);
615 specialvisibilityradius = max(specialvisibilityradius, glowsize * 4);
616 if (flags & RENDER_GLOWTRAIL)
617 specialvisibilityradius = max(specialvisibilityradius, 100);
618 if (effects & (EF_BRIGHTFIELD | EF_MUZZLEFLASH | EF_BRIGHTLIGHT | EF_DIMLIGHT | EF_RED | EF_BLUE | EF_FLAME | EF_STARDUST))
620 if (effects & EF_BRIGHTFIELD)
621 specialvisibilityradius = max(specialvisibilityradius, 80);
622 if (effects & EF_MUZZLEFLASH)
623 specialvisibilityradius = max(specialvisibilityradius, 100);
624 if (effects & EF_BRIGHTLIGHT)
625 specialvisibilityradius = max(specialvisibilityradius, 400);
626 if (effects & EF_DIMLIGHT)
627 specialvisibilityradius = max(specialvisibilityradius, 200);
628 if (effects & EF_RED)
629 specialvisibilityradius = max(specialvisibilityradius, 200);
630 if (effects & EF_BLUE)
631 specialvisibilityradius = max(specialvisibilityradius, 200);
632 if (effects & EF_FLAME)
633 specialvisibilityradius = max(specialvisibilityradius, 250);
634 if (effects & EF_STARDUST)
635 specialvisibilityradius = max(specialvisibilityradius, 100);
638 // early culling checks
639 // (final culling is done by SV_MarkWriteEntityStateToClient)
640 customizeentityforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.customizeentityforclient)->function;
641 if (!customizeentityforclient)
643 if (e > svs.maxclients && (!modelindex && !specialvisibilityradius))
645 // this 2 billion unit check is actually to detect NAN origins
646 // (we really don't want to send those)
647 if (VectorLength2(ent->fields.server->origin) > 2000000000.0*2000000000.0)
655 VectorCopy(ent->fields.server->origin, cs->origin);
656 VectorCopy(ent->fields.server->angles, cs->angles);
658 cs->effects = effects;
659 cs->colormap = (unsigned)ent->fields.server->colormap;
660 cs->modelindex = modelindex;
661 cs->skin = (unsigned)ent->fields.server->skin;
662 cs->frame = (unsigned)ent->fields.server->frame;
663 cs->viewmodelforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewmodelforclient)->edict;
664 cs->exteriormodelforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.exteriormodeltoclient)->edict;
665 cs->nodrawtoclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.nodrawtoclient)->edict;
666 cs->drawonlytoclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.drawonlytoclient)->edict;
667 cs->customizeentityforclient = customizeentityforclient;
668 cs->tagentity = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)->edict;
669 cs->tagindex = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_index)->_float;
670 cs->glowsize = glowsize;
672 // don't need to init cs->colormod because the defaultstate did that for us
673 //cs->colormod[0] = cs->colormod[1] = cs->colormod[2] = 32;
674 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.colormod);
675 if (val->vector[0] || val->vector[1] || val->vector[2])
677 i = (int)(val->vector[0] * 32.0f);cs->colormod[0] = bound(0, i, 255);
678 i = (int)(val->vector[1] * 32.0f);cs->colormod[1] = bound(0, i, 255);
679 i = (int)(val->vector[2] * 32.0f);cs->colormod[2] = bound(0, i, 255);
682 cs->modelindex = modelindex;
685 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.alpha)->_float * 255.0f);
689 cs->alpha = (unsigned char)bound(0, i, 255);
692 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderamt)->_float);
696 cs->alpha = (unsigned char)bound(0, i, 255);
700 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale)->_float * 16.0f);
704 cs->scale = (unsigned char)bound(0, i, 255);
708 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_color)->_float);
710 cs->glowcolor = (int)f;
712 if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.fullbright)->_float)
713 cs->effects |= EF_FULLBRIGHT;
715 if (ent->fields.server->movetype == MOVETYPE_STEP)
716 cs->flags |= RENDER_STEP;
717 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)
718 cs->flags |= RENDER_LOWPRECISION;
719 if (ent->fields.server->colormap >= 1024)
720 cs->flags |= RENDER_COLORMAPPED;
721 if (cs->viewmodelforclient)
722 cs->flags |= RENDER_VIEWMODEL; // show relative to the view
724 cs->light[0] = light[0];
725 cs->light[1] = light[1];
726 cs->light[2] = light[2];
727 cs->light[3] = light[3];
728 cs->lightstyle = lightstyle;
729 cs->lightpflags = lightpflags;
731 cs->specialvisibilityradius = specialvisibilityradius;
733 // calculate the visible box of this entity (don't use the physics box
734 // as that is often smaller than a model, and would not count
735 // specialvisibilityradius)
736 if ((model = sv.models[modelindex]))
738 float scale = cs->scale * (1.0f / 16.0f);
739 if (cs->angles[0] || cs->angles[2]) // pitch and roll
741 VectorMA(cs->origin, scale, model->rotatedmins, cullmins);
742 VectorMA(cs->origin, scale, model->rotatedmaxs, cullmaxs);
744 else if (cs->angles[1])
746 VectorMA(cs->origin, scale, model->yawmins, cullmins);
747 VectorMA(cs->origin, scale, model->yawmaxs, cullmaxs);
751 VectorMA(cs->origin, scale, model->normalmins, cullmins);
752 VectorMA(cs->origin, scale, model->normalmaxs, cullmaxs);
757 // if there is no model (or it could not be loaded), use the physics box
758 VectorAdd(cs->origin, ent->fields.server->mins, cullmins);
759 VectorAdd(cs->origin, ent->fields.server->maxs, cullmaxs);
761 if (specialvisibilityradius)
763 cullmins[0] = min(cullmins[0], cs->origin[0] - specialvisibilityradius);
764 cullmins[1] = min(cullmins[1], cs->origin[1] - specialvisibilityradius);
765 cullmins[2] = min(cullmins[2], cs->origin[2] - specialvisibilityradius);
766 cullmaxs[0] = max(cullmaxs[0], cs->origin[0] + specialvisibilityradius);
767 cullmaxs[1] = max(cullmaxs[1], cs->origin[1] + specialvisibilityradius);
768 cullmaxs[2] = max(cullmaxs[2], cs->origin[2] + specialvisibilityradius);
770 if (!VectorCompare(cullmins, ent->priv.server->cullmins) || !VectorCompare(cullmaxs, ent->priv.server->cullmaxs))
772 VectorCopy(cullmins, ent->priv.server->cullmins);
773 VectorCopy(cullmaxs, ent->priv.server->cullmaxs);
774 ent->priv.server->pvs_numclusters = -1;
775 if (sv.worldmodel && sv.worldmodel->brush.FindBoxClusters)
777 i = sv.worldmodel->brush.FindBoxClusters(sv.worldmodel, cullmins, cullmaxs, MAX_ENTITYCLUSTERS, ent->priv.server->pvs_clusterlist);
778 if (i <= MAX_ENTITYCLUSTERS)
779 ent->priv.server->pvs_numclusters = i;
786 void SV_PrepareEntitiesForSending(void)
790 // send all entities that touch the pvs
792 sendentitiesindex[0] = NULL;
793 memset(sendentitiesindex, 0, prog->num_edicts * sizeof(entity_state_t *));
794 for (e = 1, ent = PRVM_NEXT_EDICT(prog->edicts);e < prog->num_edicts;e++, ent = PRVM_NEXT_EDICT(ent))
796 if (!ent->priv.server->free && SV_PrepareEntityForSending(ent, sendentities + numsendentities, e))
798 sendentitiesindex[e] = sendentities + numsendentities;
804 static int sententitiesmark = 0;
805 static int sententities[MAX_EDICTS];
806 static int sententitiesconsideration[MAX_EDICTS];
807 static int sv_writeentitiestoclient_culled_pvs;
808 static int sv_writeentitiestoclient_culled_trace;
809 static int sv_writeentitiestoclient_visibleentities;
810 static int sv_writeentitiestoclient_totalentities;
811 //static entity_frame_t sv_writeentitiestoclient_entityframe;
812 static int sv_writeentitiestoclient_clentnum;
813 static vec3_t sv_writeentitiestoclient_testeye;
814 static client_t *sv_writeentitiestoclient_client;
816 void SV_MarkWriteEntityStateToClient(entity_state_t *s)
821 if (sententitiesconsideration[s->number] == sententitiesmark)
823 sententitiesconsideration[s->number] = sententitiesmark;
824 sv_writeentitiestoclient_totalentities++;
826 if (s->customizeentityforclient)
828 prog->globals.server->self = s->number;
829 prog->globals.server->other = sv_writeentitiestoclient_clentnum;
830 PRVM_ExecuteProgram(s->customizeentityforclient, "customizeentityforclient: NULL function");
831 if(!PRVM_G_FLOAT(OFS_RETURN) || !SV_PrepareEntityForSending(PRVM_EDICT_NUM(s->number), s, s->number))
835 // never reject player
836 if (s->number != sv_writeentitiestoclient_clentnum)
838 // check various rejection conditions
839 if (s->nodrawtoclient == sv_writeentitiestoclient_clentnum)
841 if (s->drawonlytoclient && s->drawonlytoclient != sv_writeentitiestoclient_clentnum)
843 if (s->effects & EF_NODRAW)
845 // LordHavoc: only send entities with a model or important effects
846 if (!s->modelindex && s->specialvisibilityradius == 0)
849 isbmodel = (model = sv.models[s->modelindex]) != NULL && model->name[0] == '*';
850 // viewmodels don't have visibility checking
851 if (s->viewmodelforclient)
853 if (s->viewmodelforclient != sv_writeentitiestoclient_clentnum)
856 else if (s->tagentity)
858 // tag attached entities simply check their parent
859 if (!sendentitiesindex[s->tagentity])
861 SV_MarkWriteEntityStateToClient(sendentitiesindex[s->tagentity]);
862 if (sententities[s->tagentity] != sententitiesmark)
865 // always send world submodels in newer protocols because they don't
866 // generate much traffic (in old protocols they hog bandwidth)
867 // but only if sv_cullentities_alwayssendbmodels is on
868 else if (!(s->effects & EF_NODEPTHTEST) && (!isbmodel || !sv_cullentities_nevercullbmodels.integer || sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE))
870 // entity has survived every check so far, check if visible
871 ed = PRVM_EDICT_NUM(s->number);
873 // if not touching a visible leaf
874 if (sv_cullentities_pvs.integer && sv_writeentitiestoclient_pvsbytes)
876 if (ed->priv.server->pvs_numclusters < 0)
878 // entity too big for clusters list
879 if (sv.worldmodel && sv.worldmodel->brush.BoxTouchingPVS && !sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, sv_writeentitiestoclient_pvs, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
881 sv_writeentitiestoclient_culled_pvs++;
888 // check cached clusters list
889 for (i = 0;i < ed->priv.server->pvs_numclusters;i++)
890 if (CHECKPVSBIT(sv_writeentitiestoclient_pvs, ed->priv.server->pvs_clusterlist[i]))
892 if (i == ed->priv.server->pvs_numclusters)
894 sv_writeentitiestoclient_culled_pvs++;
900 // or not seen by random tracelines
901 if (sv_cullentities_trace.integer && !isbmodel)
903 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))
904 sv_writeentitiestoclient_client->visibletime[s->number] = realtime + sv_cullentities_trace_delay.value;
905 if (realtime > sv_writeentitiestoclient_client->visibletime[s->number])
907 sv_writeentitiestoclient_culled_trace++;
914 // this just marks it for sending
915 // FIXME: it would be more efficient to send here, but the entity
916 // compressor isn't that flexible
917 sv_writeentitiestoclient_visibleentities++;
918 sententities[s->number] = sententitiesmark;
921 entity_state_t sendstates[MAX_EDICTS];
922 extern int csqc_clent;
924 void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg, int *stats)
926 int i, numsendstates;
929 // if there isn't enough space to accomplish anything, skip it
930 if (msg->cursize + 25 > msg->maxsize)
933 sv_writeentitiestoclient_client = client;
935 sv_writeentitiestoclient_culled_pvs = 0;
936 sv_writeentitiestoclient_culled_trace = 0;
937 sv_writeentitiestoclient_visibleentities = 0;
938 sv_writeentitiestoclient_totalentities = 0;
940 // find the client's PVS
941 // the real place being tested from
942 VectorAdd(clent->fields.server->origin, clent->fields.server->view_ofs, sv_writeentitiestoclient_testeye);
943 sv_writeentitiestoclient_pvsbytes = 0;
944 if (sv.worldmodel && sv.worldmodel->brush.FatPVS)
945 sv_writeentitiestoclient_pvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, sv_writeentitiestoclient_testeye, 8, sv_writeentitiestoclient_pvs, sizeof(sv_writeentitiestoclient_pvs));
947 csqc_clent = sv_writeentitiestoclient_clentnum = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
951 for (i = 0;i < numsendentities;i++)
952 SV_MarkWriteEntityStateToClient(sendentities + i);
955 for (i = 0;i < numsendentities;i++)
957 if (sententities[sendentities[i].number] == sententitiesmark)
959 s = &sendstates[numsendstates++];
960 *s = sendentities[i];
961 if (s->exteriormodelforclient && s->exteriormodelforclient == sv_writeentitiestoclient_clentnum)
962 s->flags |= RENDER_EXTERIORMODEL;
966 if (sv_cullentities_stats.integer)
967 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);
969 EntityFrameCSQC_WriteFrame(msg, numsendstates, sendstates);
971 if (client->entitydatabase5)
972 EntityFrame5_WriteFrame(msg, client->entitydatabase5, numsendstates, sendstates, client - svs.clients + 1, stats, client->movesequence);
973 else if (client->entitydatabase4)
974 EntityFrame4_WriteFrame(msg, client->entitydatabase4, numsendstates, sendstates);
975 else if (client->entitydatabase)
976 EntityFrame_WriteFrame(msg, client->entitydatabase, numsendstates, sendstates, client - svs.clients + 1);
978 EntityFrameQuake_WriteFrame(msg, numsendstates, sendstates);
987 void SV_CleanupEnts (void)
992 ent = PRVM_NEXT_EDICT(prog->edicts);
993 for (e=1 ; e<prog->num_edicts ; e++, ent = PRVM_NEXT_EDICT(ent))
994 ent->fields.server->effects = (int)ent->fields.server->effects & ~EF_MUZZLEFLASH;
999 SV_WriteClientdataToMessage
1003 void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats)
1007 prvm_edict_t *other;
1015 // send a damage message
1017 if (ent->fields.server->dmg_take || ent->fields.server->dmg_save)
1019 other = PRVM_PROG_TO_EDICT(ent->fields.server->dmg_inflictor);
1020 MSG_WriteByte (msg, svc_damage);
1021 MSG_WriteByte (msg, (int)ent->fields.server->dmg_save);
1022 MSG_WriteByte (msg, (int)ent->fields.server->dmg_take);
1023 for (i=0 ; i<3 ; i++)
1024 MSG_WriteCoord (msg, other->fields.server->origin[i] + 0.5*(other->fields.server->mins[i] + other->fields.server->maxs[i]), sv.protocol);
1026 ent->fields.server->dmg_take = 0;
1027 ent->fields.server->dmg_save = 0;
1031 // send the current viewpos offset from the view entity
1033 SV_SetIdealPitch (); // how much to look up / down ideally
1035 // a fixangle might get lost in a dropped packet. Oh well.
1036 if ( ent->fields.server->fixangle )
1038 MSG_WriteByte (msg, svc_setangle);
1039 for (i=0 ; i < 3 ; i++)
1040 MSG_WriteAngle (msg, ent->fields.server->angles[i], sv.protocol);
1041 ent->fields.server->fixangle = 0;
1044 // stuff the sigil bits into the high bits of items for sbar, or else
1046 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.items2);
1047 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1048 items = (int)ent->fields.server->items | ((int)val->_float << 23);
1050 items = (int)ent->fields.server->items | ((int)prog->globals.server->serverflags << 28);
1052 VectorClear(punchvector);
1053 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.punchvector)))
1054 VectorCopy(val->vector, punchvector);
1056 // cache weapon model name and index in client struct to save time
1057 // (this search can be almost 1% of cpu time!)
1058 s = PRVM_GetString(ent->fields.server->weaponmodel);
1059 if (strcmp(s, client->weaponmodel))
1061 strlcpy(client->weaponmodel, s, sizeof(client->weaponmodel));
1062 client->weaponmodelindex = SV_ModelIndex(s, 1);
1066 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewzoom)))
1067 viewzoom = (int)(val->_float * 255.0f);
1073 if ((int)ent->fields.server->flags & FL_ONGROUND)
1074 bits |= SU_ONGROUND;
1075 if (ent->fields.server->waterlevel >= 2)
1077 if (ent->fields.server->idealpitch)
1078 bits |= SU_IDEALPITCH;
1080 for (i=0 ; i<3 ; i++)
1082 if (ent->fields.server->punchangle[i])
1083 bits |= (SU_PUNCH1<<i);
1084 if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE)
1086 bits |= (SU_PUNCHVEC1<<i);
1087 if (ent->fields.server->velocity[i])
1088 bits |= (SU_VELOCITY1<<i);
1091 memset(stats, 0, sizeof(int[MAX_CL_STATS]));
1092 stats[STAT_VIEWHEIGHT] = (int)ent->fields.server->view_ofs[2];
1093 stats[STAT_ITEMS] = items;
1094 stats[STAT_WEAPONFRAME] = (int)ent->fields.server->weaponframe;
1095 stats[STAT_ARMOR] = (int)ent->fields.server->armorvalue;
1096 stats[STAT_WEAPON] = client->weaponmodelindex;
1097 stats[STAT_HEALTH] = (int)ent->fields.server->health;
1098 stats[STAT_AMMO] = (int)ent->fields.server->currentammo;
1099 stats[STAT_SHELLS] = (int)ent->fields.server->ammo_shells;
1100 stats[STAT_NAILS] = (int)ent->fields.server->ammo_nails;
1101 stats[STAT_ROCKETS] = (int)ent->fields.server->ammo_rockets;
1102 stats[STAT_CELLS] = (int)ent->fields.server->ammo_cells;
1103 stats[STAT_ACTIVEWEAPON] = (int)ent->fields.server->weapon;
1104 stats[STAT_VIEWZOOM] = viewzoom;
1105 stats[STAT_TOTALSECRETS] = prog->globals.server->total_secrets;
1106 stats[STAT_TOTALMONSTERS] = prog->globals.server->total_monsters;
1107 // the QC bumps these itself by sending svc_'s, so we have to keep them
1108 // zero or they'll be corrected by the engine
1109 //stats[STAT_SECRETS] = prog->globals.server->found_secrets;
1110 //stats[STAT_MONSTERS] = prog->globals.server->killed_monsters;
1112 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)
1114 if (stats[STAT_VIEWHEIGHT] != DEFAULT_VIEWHEIGHT) bits |= SU_VIEWHEIGHT;
1116 if (stats[STAT_WEAPONFRAME]) bits |= SU_WEAPONFRAME;
1117 if (stats[STAT_ARMOR]) bits |= SU_ARMOR;
1119 // FIXME: which protocols support this? does PROTOCOL_DARKPLACES3 support viewzoom?
1120 if (sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5)
1121 if (viewzoom != 255)
1122 bits |= SU_VIEWZOOM;
1127 if (bits >= 16777216)
1131 MSG_WriteByte (msg, svc_clientdata);
1132 MSG_WriteShort (msg, bits);
1133 if (bits & SU_EXTEND1)
1134 MSG_WriteByte(msg, bits >> 16);
1135 if (bits & SU_EXTEND2)
1136 MSG_WriteByte(msg, bits >> 24);
1138 if (bits & SU_VIEWHEIGHT)
1139 MSG_WriteChar (msg, stats[STAT_VIEWHEIGHT]);
1141 if (bits & SU_IDEALPITCH)
1142 MSG_WriteChar (msg, (int)ent->fields.server->idealpitch);
1144 for (i=0 ; i<3 ; i++)
1146 if (bits & (SU_PUNCH1<<i))
1148 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
1149 MSG_WriteChar(msg, (int)ent->fields.server->punchangle[i]);
1151 MSG_WriteAngle16i(msg, ent->fields.server->punchangle[i]);
1153 if (bits & (SU_PUNCHVEC1<<i))
1155 if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
1156 MSG_WriteCoord16i(msg, punchvector[i]);
1158 MSG_WriteCoord32f(msg, punchvector[i]);
1160 if (bits & (SU_VELOCITY1<<i))
1162 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
1163 MSG_WriteChar(msg, (int)(ent->fields.server->velocity[i] * (1.0f / 16.0f)));
1165 MSG_WriteCoord32f(msg, ent->fields.server->velocity[i]);
1169 if (bits & SU_ITEMS)
1170 MSG_WriteLong (msg, stats[STAT_ITEMS]);
1172 if (sv.protocol == PROTOCOL_DARKPLACES5)
1174 if (bits & SU_WEAPONFRAME)
1175 MSG_WriteShort (msg, stats[STAT_WEAPONFRAME]);
1176 if (bits & SU_ARMOR)
1177 MSG_WriteShort (msg, stats[STAT_ARMOR]);
1178 if (bits & SU_WEAPON)
1179 MSG_WriteShort (msg, stats[STAT_WEAPON]);
1180 MSG_WriteShort (msg, stats[STAT_HEALTH]);
1181 MSG_WriteShort (msg, stats[STAT_AMMO]);
1182 MSG_WriteShort (msg, stats[STAT_SHELLS]);
1183 MSG_WriteShort (msg, stats[STAT_NAILS]);
1184 MSG_WriteShort (msg, stats[STAT_ROCKETS]);
1185 MSG_WriteShort (msg, stats[STAT_CELLS]);
1186 MSG_WriteShort (msg, stats[STAT_ACTIVEWEAPON]);
1187 if (bits & SU_VIEWZOOM)
1188 MSG_WriteShort (msg, bound(0, stats[STAT_VIEWZOOM], 65535));
1190 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)
1192 if (bits & SU_WEAPONFRAME)
1193 MSG_WriteByte (msg, stats[STAT_WEAPONFRAME]);
1194 if (bits & SU_ARMOR)
1195 MSG_WriteByte (msg, stats[STAT_ARMOR]);
1196 if (bits & SU_WEAPON)
1197 MSG_WriteByte (msg, stats[STAT_WEAPON]);
1198 MSG_WriteShort (msg, stats[STAT_HEALTH]);
1199 MSG_WriteByte (msg, stats[STAT_AMMO]);
1200 MSG_WriteByte (msg, stats[STAT_SHELLS]);
1201 MSG_WriteByte (msg, stats[STAT_NAILS]);
1202 MSG_WriteByte (msg, stats[STAT_ROCKETS]);
1203 MSG_WriteByte (msg, stats[STAT_CELLS]);
1204 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE || gamemode == GAME_NEXUIZ)
1206 for (i = 0;i < 32;i++)
1207 if (stats[STAT_WEAPON] & (1<<i))
1209 MSG_WriteByte (msg, i);
1212 MSG_WriteByte (msg, stats[STAT_WEAPON]);
1213 if (bits & SU_VIEWZOOM)
1215 if (sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
1216 MSG_WriteByte (msg, bound(0, stats[STAT_VIEWZOOM], 255));
1218 MSG_WriteShort (msg, bound(0, stats[STAT_VIEWZOOM], 65535));
1224 =======================
1225 SV_SendClientDatagram
1226 =======================
1228 static unsigned char sv_sendclientdatagram_buf[NET_MAXMESSAGE]; // FIXME?
1229 void SV_SendClientDatagram (client_t *client)
1231 int rate, maxrate, maxsize, maxsize2, downloadsize;
1233 int stats[MAX_CL_STATS];
1235 if (LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) == LHNETADDRESSTYPE_LOOP && !sv_ratelimitlocalplayer.integer)
1237 // for good singleplayer, send huge packets
1238 maxsize = sizeof(sv_sendclientdatagram_buf);
1239 maxsize2 = sizeof(sv_sendclientdatagram_buf);
1241 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)
1243 // no rate limiting support on older protocols because dp protocols
1244 // 1-4 kick the client off if they overflow, and quake protocol shows
1245 // less than the full entity set if rate limited
1251 // PROTOCOL_DARKPLACES5 and later support packet size limiting of updates
1252 maxrate = max(NET_MINRATE, sv_maxrate.integer);
1253 if (sv_maxrate.integer != maxrate)
1254 Cvar_SetValueQuick(&sv_maxrate, maxrate);
1256 // this rate limiting does not understand sys_ticrate 0
1257 // (but no one should be running that on a server!)
1258 rate = bound(NET_MINRATE, client->rate, maxrate);
1259 rate = (int)(rate * sys_ticrate.value);
1260 maxsize = bound(50, rate, 1400);
1264 // while downloading, limit entity updates to half the packet
1265 // (any leftover space will be used for downloading)
1266 if (host_client->download_file)
1269 msg.data = sv_sendclientdatagram_buf;
1270 msg.maxsize = maxsize;
1273 if (host_client->spawned)
1275 MSG_WriteByte (&msg, svc_time);
1276 MSG_WriteFloat (&msg, sv.time);
1278 // add the client specific data to the datagram
1279 SV_WriteClientdataToMessage (client, client->edict, &msg, stats);
1280 VM_SV_WriteAutoSentStats (client, client->edict, &msg, stats);
1281 SV_WriteEntitiesToClient (client, client->edict, &msg, stats);
1283 // expand packet size to allow effects to go over the rate limit
1284 // (dropping them is FAR too ugly)
1285 msg.maxsize = maxsize2;
1287 // copy the server datagram if there is space
1288 // FIXME: put in delayed queue of effects to send
1289 if (sv.datagram.cursize > 0 && msg.cursize + sv.datagram.cursize <= msg.maxsize)
1290 SZ_Write (&msg, sv.datagram.data, sv.datagram.cursize);
1292 else if (realtime > client->keepalivetime)
1294 // the player isn't totally in the game yet
1295 // send small keepalive messages if too much time has passed
1296 msg.maxsize = maxsize2;
1297 client->keepalivetime = realtime + 5;
1298 MSG_WriteChar (&msg, svc_nop);
1301 msg.maxsize = maxsize2;
1303 // if a download is active, see if there is room to fit some download data
1305 downloadsize = maxsize * 2 - msg.cursize - 7;
1306 if (host_client->download_file && host_client->download_started && downloadsize > 0)
1308 fs_offset_t downloadstart;
1309 unsigned char data[1400];
1310 downloadstart = FS_Tell(host_client->download_file);
1311 downloadsize = min(downloadsize, (int)sizeof(data));
1312 downloadsize = FS_Read(host_client->download_file, data, downloadsize);
1313 // note this sends empty messages if at the end of the file, which is
1314 // necessary to keep the packet loss logic working
1315 // (the last blocks may be lost and need to be re-sent, and that will
1316 // only occur if the client acks the empty end messages, revealing
1317 // a gap in the download progress, causing the last blocks to be
1319 MSG_WriteChar (&msg, svc_downloaddata);
1320 MSG_WriteLong (&msg, downloadstart);
1321 MSG_WriteShort (&msg, downloadsize);
1322 if (downloadsize > 0)
1323 SZ_Write (&msg, data, downloadsize);
1326 // send the datagram
1327 NetConn_SendUnreliableMessage (client->netconnection, &msg, sv.protocol);
1331 =======================
1332 SV_UpdateToReliableMessages
1333 =======================
1335 void SV_UpdateToReliableMessages (void)
1344 // check for changes to be sent over the reliable streams
1345 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1347 // update the host_client fields we care about according to the entity fields
1348 host_client->edict = PRVM_EDICT_NUM(i+1);
1351 name = PRVM_GetString(host_client->edict->fields.server->netname);
1354 // always point the string back at host_client->name to keep it safe
1355 strlcpy (host_client->name, name, sizeof (host_client->name));
1356 host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
1357 if (strcmp(host_client->old_name, host_client->name))
1359 if (host_client->spawned)
1360 SV_BroadcastPrintf("%s changed name to %s\n", host_client->old_name, host_client->name);
1361 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1362 // send notification to all clients
1363 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1364 MSG_WriteByte (&sv.reliable_datagram, i);
1365 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1368 // DP_SV_CLIENTCOLORS
1369 // this is always found (since it's added by the progs loader)
1370 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
1371 host_client->colors = (int)val->_float;
1372 if (host_client->old_colors != host_client->colors)
1374 host_client->old_colors = host_client->colors;
1375 // send notification to all clients
1376 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1377 MSG_WriteByte (&sv.reliable_datagram, i);
1378 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1381 // NEXUIZ_PLAYERMODEL
1382 if( prog->fieldoffsets.playermodel >= 0 ) {
1383 model = PRVM_GetString(PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string);
1386 // always point the string back at host_client->name to keep it safe
1387 strlcpy (host_client->playermodel, model, sizeof (host_client->playermodel));
1388 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
1391 // NEXUIZ_PLAYERSKIN
1392 if( prog->fieldoffsets.playerskin >= 0 ) {
1393 skin = PRVM_GetString(PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string);
1396 // always point the string back at host_client->name to keep it safe
1397 strlcpy (host_client->playerskin, skin, sizeof (host_client->playerskin));
1398 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
1402 host_client->frags = (int)host_client->edict->fields.server->frags;
1403 if (host_client->old_frags != host_client->frags)
1405 host_client->old_frags = host_client->frags;
1406 // send notification to all clients
1407 MSG_WriteByte (&sv.reliable_datagram, svc_updatefrags);
1408 MSG_WriteByte (&sv.reliable_datagram, i);
1409 MSG_WriteShort (&sv.reliable_datagram, host_client->frags);
1413 for (j = 0, client = svs.clients;j < svs.maxclients;j++, client++)
1414 if (client->netconnection)
1415 SZ_Write (&client->netconnection->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize);
1417 SZ_Clear (&sv.reliable_datagram);
1422 =======================
1423 SV_SendClientMessages
1424 =======================
1426 void SV_SendClientMessages (void)
1428 int i, prepared = false;
1430 if (sv.protocol == PROTOCOL_QUAKEWORLD)
1431 Sys_Error("SV_SendClientMessages: no quakeworld support\n");
1433 // update frags, names, etc
1434 SV_UpdateToReliableMessages();
1436 // build individual updates
1437 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1439 if (!host_client->active)
1441 if (!host_client->netconnection)
1444 if (host_client->netconnection->message.overflowed)
1446 SV_DropClient (true); // if the message couldn't send, kick off
1453 // only prepare entities once per frame
1454 SV_PrepareEntitiesForSending();
1456 SV_SendClientDatagram (host_client);
1459 // clear muzzle flashes
1463 void SV_StartDownload_f(void)
1465 if (host_client->download_file)
1466 host_client->download_started = true;
1469 void SV_Download_f(void)
1471 const char *whichpack, *whichpack2, *extension;
1473 if (Cmd_Argc() != 2)
1475 SV_ClientPrintf("usage: download <filename>\n");
1479 if (FS_CheckNastyPath(Cmd_Argv(1), false))
1481 SV_ClientPrintf("Download rejected: nasty filename \"%s\"\n", Cmd_Argv(1));
1485 if (host_client->download_file)
1487 // at this point we'll assume the previous download should be aborted
1488 Con_DPrintf("Download of %s aborted by %s starting a new download\n", host_client->download_name, host_client->name);
1489 Host_ClientCommands("\nstopdownload\n");
1491 // close the file and reset variables
1492 FS_Close(host_client->download_file);
1493 host_client->download_file = NULL;
1494 host_client->download_name[0] = 0;
1495 host_client->download_expectedposition = 0;
1496 host_client->download_started = false;
1499 if (!sv_allowdownloads.integer)
1501 SV_ClientPrintf("Downloads are disabled on this server\n");
1502 Host_ClientCommands("\nstopdownload\n");
1506 strlcpy(host_client->download_name, Cmd_Argv(1), sizeof(host_client->download_name));
1507 extension = FS_FileExtension(host_client->download_name);
1509 // host_client is asking to download a specified file
1510 if (developer.integer >= 100)
1511 Con_Printf("Download request for %s by %s\n", host_client->download_name, host_client->name);
1513 if (!FS_FileExists(host_client->download_name))
1515 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);
1516 Host_ClientCommands("\nstopdownload\n");
1520 // check if the user is trying to download part of registered Quake(r)
1521 whichpack = FS_WhichPack(host_client->download_name);
1522 whichpack2 = FS_WhichPack("gfx/pop.lmp");
1523 if ((whichpack && whichpack2 && !strcasecmp(whichpack, whichpack2)) || FS_IsRegisteredQuakePack(host_client->download_name))
1525 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);
1526 Host_ClientCommands("\nstopdownload\n");
1530 // check if the server has forbidden archive downloads entirely
1531 if (!sv_allowdownloads_inarchive.integer)
1533 whichpack = FS_WhichPack(host_client->download_name);
1536 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);
1537 Host_ClientCommands("\nstopdownload\n");
1542 if (!sv_allowdownloads_config.integer)
1544 if (!strcasecmp(extension, "cfg"))
1546 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);
1547 Host_ClientCommands("\nstopdownload\n");
1552 if (!sv_allowdownloads_dlcache.integer)
1554 if (!strncasecmp(host_client->download_name, "dlcache/", 8))
1556 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);
1557 Host_ClientCommands("\nstopdownload\n");
1562 if (!sv_allowdownloads_archive.integer)
1564 if (!strcasecmp(extension, "pak") || !strcasecmp(extension, "pk3"))
1566 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);
1567 Host_ClientCommands("\nstopdownload\n");
1572 host_client->download_file = FS_Open(host_client->download_name, "rb", true, false);
1573 if (!host_client->download_file)
1575 SV_ClientPrintf("Download rejected: server could not open the file \"%s\"\n", host_client->download_name);
1576 Host_ClientCommands("\nstopdownload\n");
1580 if (FS_FileSize(host_client->download_file) > 1<<30)
1582 SV_ClientPrintf("Download rejected: file \"%s\" is very large\n", host_client->download_name);
1583 Host_ClientCommands("\nstopdownload\n");
1584 FS_Close(host_client->download_file);
1585 host_client->download_file = NULL;
1589 Con_DPrintf("Downloading %s to %s\n", host_client->download_name, host_client->name);
1591 Host_ClientCommands("\ncl_downloadbegin %i %s\n", (int)FS_FileSize(host_client->download_file), host_client->download_name);
1593 host_client->download_expectedposition = 0;
1594 host_client->download_started = false;
1596 // the rest of the download process is handled in SV_SendClientDatagram
1597 // and other code dealing with svc_downloaddata and clc_ackdownloaddata
1599 // no svc_downloaddata messages will be sent until sv_startdownload is
1600 // sent by the client
1604 ==============================================================================
1608 ==============================================================================
1617 int SV_ModelIndex(const char *s, int precachemode)
1619 int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) ? 256 : MAX_MODELS);
1620 char filename[MAX_QPATH];
1624 //if (precachemode == 2)
1626 strlcpy(filename, s, sizeof(filename));
1627 for (i = 2;i < limit;i++)
1629 if (!sv.model_precache[i][0])
1633 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))
1635 Con_Printf("SV_ModelIndex(\"%s\"): precache_model can only be done in spawn functions\n", filename);
1638 if (precachemode == 1)
1639 Con_Printf("SV_ModelIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename);
1640 strlcpy(sv.model_precache[i], filename, sizeof(sv.model_precache[i]));
1641 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, false);
1642 if (sv.state != ss_loading)
1644 MSG_WriteByte(&sv.reliable_datagram, svc_precache);
1645 MSG_WriteShort(&sv.reliable_datagram, i);
1646 MSG_WriteString(&sv.reliable_datagram, filename);
1650 Con_Printf("SV_ModelIndex(\"%s\"): not precached\n", filename);
1653 if (!strcmp(sv.model_precache[i], filename))
1656 Con_Printf("SV_ModelIndex(\"%s\"): i (%i) == MAX_MODELS (%i)\n", filename, i, MAX_MODELS);
1666 int SV_SoundIndex(const char *s, int precachemode)
1668 int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) ? 256 : MAX_SOUNDS);
1669 char filename[MAX_QPATH];
1673 //if (precachemode == 2)
1675 strlcpy(filename, s, sizeof(filename));
1676 for (i = 1;i < limit;i++)
1678 if (!sv.sound_precache[i][0])
1682 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))
1684 Con_Printf("SV_SoundIndex(\"%s\"): precache_sound can only be done in spawn functions\n", filename);
1687 if (precachemode == 1)
1688 Con_Printf("SV_SoundIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename);
1689 strlcpy(sv.sound_precache[i], filename, sizeof(sv.sound_precache[i]));
1690 if (sv.state != ss_loading)
1692 MSG_WriteByte(&sv.reliable_datagram, svc_precache);
1693 MSG_WriteShort(&sv.reliable_datagram, i + 32768);
1694 MSG_WriteString(&sv.reliable_datagram, filename);
1698 Con_Printf("SV_SoundIndex(\"%s\"): not precached\n", filename);
1701 if (!strcmp(sv.sound_precache[i], filename))
1704 Con_Printf("SV_SoundIndex(\"%s\"): i (%i) == MAX_SOUNDS (%i)\n", filename, i, MAX_SOUNDS);
1708 // MUST match effectnameindex_t in client.h
1709 static const char *standardeffectnames[EFFECT_TOTAL] =
1717 "TE_SUPERSPIKEQUAD",
1733 "TE_TEI_BIGEXPLOSION",
1751 SV_ParticleEffectIndex
1755 int SV_ParticleEffectIndex(const char *name)
1757 int i, argc, linenumber, effectnameindex;
1758 fs_offset_t filesize;
1759 unsigned char *filedata;
1760 const char *text, *textstart, *textend;
1761 char argv[16][1024];
1762 if (!sv.particleeffectnamesloaded)
1764 sv.particleeffectnamesloaded = true;
1765 memset(sv.particleeffectname, 0, sizeof(sv.particleeffectname));
1766 for (i = 0;i < EFFECT_TOTAL;i++)
1767 strlcpy(sv.particleeffectname[i], standardeffectnames[i], sizeof(sv.particleeffectname[i]));
1768 filedata = FS_LoadFile("effectinfo.txt", tempmempool, true, &filesize);
1771 textstart = (const char *)filedata;
1772 textend = (const char *)filedata + filesize;
1774 for (linenumber = 1;;linenumber++)
1779 if (!COM_ParseToken(&text, true) || !strcmp(com_token, "\n"))
1783 strlcpy(argv[argc], com_token, sizeof(argv[argc]));
1787 if (com_token[0] == 0)
1788 break; // if the loop exited and it's not a \n, it's EOF
1791 if (!strcmp(argv[0], "effect"))
1795 for (effectnameindex = 1;effectnameindex < SV_MAX_PARTICLEEFFECTNAME;effectnameindex++)
1797 if (sv.particleeffectname[effectnameindex][0])
1799 if (!strcmp(sv.particleeffectname[effectnameindex], argv[1]))
1804 strlcpy(sv.particleeffectname[effectnameindex], argv[1], sizeof(sv.particleeffectname[effectnameindex]));
1808 // if we run out of names, abort
1809 if (effectnameindex == SV_MAX_PARTICLEEFFECTNAME)
1811 Con_Printf("effectinfo.txt:%i: too many effects!\n", linenumber);
1820 // search for the name
1821 for (effectnameindex = 1;effectnameindex < SV_MAX_PARTICLEEFFECTNAME && sv.particleeffectname[effectnameindex][0];effectnameindex++)
1822 if (!strcmp(sv.particleeffectname[effectnameindex], name))
1823 return effectnameindex;
1824 // return 0 if we couldn't find it
1834 void SV_CreateBaseline (void)
1836 int i, entnum, large;
1837 prvm_edict_t *svent;
1839 // LordHavoc: clear *all* states (note just active ones)
1840 for (entnum = 0;entnum < prog->max_edicts;entnum++)
1842 // get the current server version
1843 svent = PRVM_EDICT_NUM(entnum);
1845 // LordHavoc: always clear state values, whether the entity is in use or not
1846 svent->priv.server->baseline = defaultstate;
1848 if (svent->priv.server->free)
1850 if (entnum > svs.maxclients && !svent->fields.server->modelindex)
1853 // create entity baseline
1854 VectorCopy (svent->fields.server->origin, svent->priv.server->baseline.origin);
1855 VectorCopy (svent->fields.server->angles, svent->priv.server->baseline.angles);
1856 svent->priv.server->baseline.frame = (int)svent->fields.server->frame;
1857 svent->priv.server->baseline.skin = (int)svent->fields.server->skin;
1858 if (entnum > 0 && entnum <= svs.maxclients)
1860 svent->priv.server->baseline.colormap = entnum;
1861 svent->priv.server->baseline.modelindex = SV_ModelIndex("progs/player.mdl", 1);
1865 svent->priv.server->baseline.colormap = 0;
1866 svent->priv.server->baseline.modelindex = (int)svent->fields.server->modelindex;
1870 if (svent->priv.server->baseline.modelindex & 0xFF00 || svent->priv.server->baseline.frame & 0xFF00)
1873 // add to the message
1875 MSG_WriteByte (&sv.signon, svc_spawnbaseline2);
1877 MSG_WriteByte (&sv.signon, svc_spawnbaseline);
1878 MSG_WriteShort (&sv.signon, entnum);
1882 MSG_WriteShort (&sv.signon, svent->priv.server->baseline.modelindex);
1883 MSG_WriteShort (&sv.signon, svent->priv.server->baseline.frame);
1887 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.modelindex);
1888 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.frame);
1890 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.colormap);
1891 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.skin);
1892 for (i=0 ; i<3 ; i++)
1894 MSG_WriteCoord(&sv.signon, svent->priv.server->baseline.origin[i], sv.protocol);
1895 MSG_WriteAngle(&sv.signon, svent->priv.server->baseline.angles[i], sv.protocol);
1905 Grabs the current state of each client for saving across the
1906 transition to another level
1909 void SV_SaveSpawnparms (void)
1913 svs.serverflags = (int)prog->globals.server->serverflags;
1915 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1917 if (!host_client->active)
1920 // call the progs to get default spawn parms for the new client
1921 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1922 PRVM_ExecuteProgram (prog->globals.server->SetChangeParms, "QC function SetChangeParms is missing");
1923 for (j=0 ; j<NUM_SPAWN_PARMS ; j++)
1924 host_client->spawn_parms[j] = (&prog->globals.server->parm1)[j];
1928 void SV_IncreaseEdicts(void)
1932 int oldmax_edicts = prog->max_edicts;
1933 void *oldedictsengineprivate = prog->edictprivate;
1934 void *oldedictsfields = prog->edictsfields;
1935 void *oldmoved_edicts = sv.moved_edicts;
1937 if (prog->max_edicts >= MAX_EDICTS)
1940 // links don't survive the transition, so unlink everything
1941 for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
1943 if (!ent->priv.server->free)
1944 SV_UnlinkEdict(prog->edicts + i);
1945 memset(&ent->priv.server->areagrid, 0, sizeof(ent->priv.server->areagrid));
1947 World_Clear(&sv.world);
1949 prog->max_edicts = min(prog->max_edicts + 256, MAX_EDICTS);
1950 prog->edictprivate = PR_Alloc(prog->max_edicts * sizeof(edict_engineprivate_t));
1951 prog->edictsfields = PR_Alloc(prog->max_edicts * prog->edict_size);
1952 sv.moved_edicts = PR_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
1954 memcpy(prog->edictprivate, oldedictsengineprivate, oldmax_edicts * sizeof(edict_engineprivate_t));
1955 memcpy(prog->edictsfields, oldedictsfields, oldmax_edicts * prog->edict_size);
1957 for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
1959 ent->priv.vp = (unsigned char*) prog->edictprivate + i * prog->edictprivate_size;
1960 ent->fields.server = (void *)((unsigned char *)prog->edictsfields + i * prog->edict_size);
1961 // link every entity except world
1962 if (!ent->priv.server->free)
1963 SV_LinkEdict(ent, false);
1966 PR_Free(oldedictsengineprivate);
1967 PR_Free(oldedictsfields);
1968 PR_Free(oldmoved_edicts);
1975 This is called at the start of each level
1978 extern float scr_centertime_off;
1980 void SV_SpawnServer (const char *server)
1985 model_t *worldmodel;
1986 char modelname[sizeof(sv.modelname)];
1988 Con_DPrintf("SpawnServer: %s\n", server);
1990 if (cls.state != ca_dedicated)
1991 SCR_BeginLoadingPlaque();
1993 dpsnprintf (modelname, sizeof(modelname), "maps/%s.bsp", server);
1994 worldmodel = Mod_ForName(modelname, false, true, true);
1995 if (!worldmodel || !worldmodel->TraceBox)
1997 Con_Printf("Couldn't load map %s\n", modelname);
2001 // let's not have any servers with no name
2002 if (hostname.string[0] == 0)
2003 Cvar_Set ("hostname", "UNNAMED");
2004 scr_centertime_off = 0;
2006 svs.changelevel_issued = false; // now safe to issue another
2008 // make the map a required file for clients
2009 Curl_ClearRequirements();
2010 Curl_RequireFile(modelname);
2013 // tell all connected clients that we are going to a new level
2018 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
2020 if (client->netconnection)
2022 MSG_WriteByte(&client->netconnection->message, svc_stufftext);
2023 MSG_WriteString(&client->netconnection->message, "reconnect\n");
2030 NetConn_OpenServerPorts(true);
2034 // make cvars consistant
2037 Cvar_SetValue ("deathmatch", 0);
2038 // LordHavoc: it can be useful to have skills outside the range 0-3...
2039 //current_skill = bound(0, (int)(skill.value + 0.5), 3);
2040 //Cvar_SetValue ("skill", (float)current_skill);
2041 current_skill = (int)(skill.value + 0.5);
2044 // set up the new server
2046 memset (&sv, 0, sizeof(sv));
2047 // if running a local client, make sure it doesn't try to access the last
2048 // level's data which is no longer valiud
2051 if(*sv_random_seed.string)
2053 srand(sv_random_seed.integer);
2054 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);
2061 strlcpy (sv.name, server, sizeof (sv.name));
2063 sv.protocol = Protocol_EnumForName(sv_protocolname.string);
2064 if (sv.protocol == PROTOCOL_UNKNOWN)
2067 Protocol_Names(buffer, sizeof(buffer));
2068 Con_Printf("Unknown sv_protocolname \"%s\", valid values are:\n%s\n", sv_protocolname.string, buffer);
2069 sv.protocol = PROTOCOL_QUAKE;
2074 // load progs to get entity field count
2075 //PR_LoadProgs ( sv_progs.string );
2077 // allocate server memory
2078 /*// start out with just enough room for clients and a reasonable estimate of entities
2079 prog->max_edicts = max(svs.maxclients + 1, 512);
2080 prog->max_edicts = min(prog->max_edicts, MAX_EDICTS);
2082 // prvm_edict_t structures (hidden from progs)
2083 prog->edicts = PR_Alloc(MAX_EDICTS * sizeof(prvm_edict_t));
2084 // engine private structures (hidden from progs)
2085 prog->edictprivate = PR_Alloc(prog->max_edicts * sizeof(edict_engineprivate_t));
2086 // progs fields, often accessed by server
2087 prog->edictsfields = PR_Alloc(prog->max_edicts * prog->edict_size);*/
2088 // used by PushMove to move back pushed entities
2089 sv.moved_edicts = (prvm_edict_t **)PRVM_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
2090 /*for (i = 0;i < prog->max_edicts;i++)
2092 ent = prog->edicts + i;
2093 ent->priv.vp = (unsigned char*) prog->edictprivate + i * prog->edictprivate_size;
2094 ent->fields.server = (void *)((unsigned char *)prog->edictsfields + i * prog->edict_size);
2097 // reset client csqc entity versions right away.
2098 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2099 EntityFrameCSQC_InitClientVersions(i, true);
2101 sv.datagram.maxsize = sizeof(sv.datagram_buf);
2102 sv.datagram.cursize = 0;
2103 sv.datagram.data = sv.datagram_buf;
2105 sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf);
2106 sv.reliable_datagram.cursize = 0;
2107 sv.reliable_datagram.data = sv.reliable_datagram_buf;
2109 sv.signon.maxsize = sizeof(sv.signon_buf);
2110 sv.signon.cursize = 0;
2111 sv.signon.data = sv.signon_buf;
2113 // leave slots at start for clients only
2114 //prog->num_edicts = svs.maxclients+1;
2116 sv.state = ss_loading;
2117 prog->allowworldwrites = true;
2120 prog->globals.server->time = sv.time = 1.0;
2123 worldmodel->used = true;
2125 strlcpy (sv.name, server, sizeof (sv.name));
2126 strlcpy(sv.modelname, modelname, sizeof(sv.modelname));
2127 sv.worldmodel = worldmodel;
2128 sv.models[1] = sv.worldmodel;
2131 // clear world interaction links
2133 VectorCopy(sv.worldmodel->normalmins, sv.world.areagrid_mins);
2134 VectorCopy(sv.worldmodel->normalmaxs, sv.world.areagrid_maxs);
2135 World_Clear(&sv.world);
2137 strlcpy(sv.sound_precache[0], "", sizeof(sv.sound_precache[0]));
2139 strlcpy(sv.model_precache[0], "", sizeof(sv.model_precache[0]));
2140 strlcpy(sv.model_precache[1], sv.modelname, sizeof(sv.model_precache[1]));
2141 for (i = 1;i < sv.worldmodel->brush.numsubmodels;i++)
2143 dpsnprintf(sv.model_precache[i+1], sizeof(sv.model_precache[i+1]), "*%i", i);
2144 sv.models[i+1] = Mod_ForName (sv.model_precache[i+1], false, false, false);
2148 // load the rest of the entities
2150 // AK possible hack since num_edicts is still 0
2151 ent = PRVM_EDICT_NUM(0);
2152 memset (ent->fields.server, 0, prog->progs->entityfields * 4);
2153 ent->priv.server->free = false;
2154 ent->fields.server->model = PRVM_SetEngineString(sv.modelname);
2155 ent->fields.server->modelindex = 1; // world model
2156 ent->fields.server->solid = SOLID_BSP;
2157 ent->fields.server->movetype = MOVETYPE_PUSH;
2158 VectorCopy(sv.worldmodel->normalmins, ent->fields.server->mins);
2159 VectorCopy(sv.worldmodel->normalmaxs, ent->fields.server->maxs);
2160 VectorCopy(sv.worldmodel->normalmins, ent->fields.server->absmin);
2161 VectorCopy(sv.worldmodel->normalmaxs, ent->fields.server->absmax);
2164 prog->globals.server->coop = coop.integer;
2166 prog->globals.server->deathmatch = deathmatch.integer;
2168 prog->globals.server->mapname = PRVM_SetEngineString(sv.name);
2170 // serverflags are for cross level information (sigils)
2171 prog->globals.server->serverflags = svs.serverflags;
2173 // we need to reset the spawned flag on all connected clients here so that
2174 // their thinks don't run during startup (before PutClientInServer)
2175 // we also need to set up the client entities now
2176 // and we need to set the ->edict pointers to point into the progs edicts
2177 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2179 host_client->spawned = false;
2180 host_client->edict = PRVM_EDICT_NUM(i + 1);
2181 PRVM_ED_ClearEdict(host_client->edict);
2184 // load replacement entity file if found
2185 if (sv_entpatch.integer && (entities = (char *)FS_LoadFile(va("maps/%s.ent", sv.name), tempmempool, true, NULL)))
2187 Con_Printf("Loaded maps/%s.ent\n", sv.name);
2188 PRVM_ED_LoadFromFile (entities);
2192 PRVM_ED_LoadFromFile (sv.worldmodel->brush.entities);
2195 // LordHavoc: clear world angles (to fix e3m3.bsp)
2196 VectorClear(prog->edicts->fields.server->angles);
2198 // all setup is completed, any further precache statements are errors
2199 sv.state = ss_active;
2200 prog->allowworldwrites = false;
2202 // run two frames to allow everything to settle
2203 for (i = 0;i < 2;i++)
2211 // create a baseline for more efficient communications
2212 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
2213 SV_CreateBaseline ();
2215 // send serverinfo to all connected clients, and set up botclients coming back from a level change
2216 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2218 if (!host_client->active)
2220 if (host_client->netconnection)
2221 SV_SendServerinfo(host_client);
2225 // if client is a botclient coming from a level change, we need to
2226 // set up client info that normally requires networking
2228 // copy spawn parms out of the client_t
2229 for (j=0 ; j< NUM_SPAWN_PARMS ; j++)
2230 (&prog->globals.server->parm1)[j] = host_client->spawn_parms[j];
2232 // call the spawn function
2233 host_client->clientconnectcalled = true;
2234 prog->globals.server->time = sv.time;
2235 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
2236 PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
2237 PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
2238 host_client->spawned = true;
2242 Con_DPrint("Server spawned.\n");
2243 NetConn_Heartbeat (2);
2248 /////////////////////////////////////////////////////
2251 void SV_VM_CB_BeginIncreaseEdicts(void)
2256 PRVM_Free( sv.moved_edicts );
2257 sv.moved_edicts = (prvm_edict_t **)PRVM_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
2259 // links don't survive the transition, so unlink everything
2260 for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
2262 if (!ent->priv.server->free)
2263 World_UnlinkEdict(prog->edicts + i);
2264 memset(&ent->priv.server->areagrid, 0, sizeof(ent->priv.server->areagrid));
2266 World_Clear(&sv.world);
2269 void SV_VM_CB_EndIncreaseEdicts(void)
2274 // link every entity except world
2275 for (i = 1, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
2276 if (!ent->priv.server->free)
2277 SV_LinkEdict(ent, false);
2280 void SV_VM_CB_InitEdict(prvm_edict_t *e)
2282 // LordHavoc: for consistency set these here
2283 int num = PRVM_NUM_FOR_EDICT(e) - 1;
2285 e->priv.server->move = false; // don't move on first frame
2287 if (num >= 0 && num < svs.maxclients)
2290 // set colormap and team on newly created player entity
2291 e->fields.server->colormap = num + 1;
2292 e->fields.server->team = (svs.clients[num].colors & 15) + 1;
2293 // set netname/clientcolors back to client values so that
2294 // DP_SV_CLIENTNAME and DP_SV_CLIENTCOLORS will not immediately
2296 e->fields.server->netname = PRVM_SetEngineString(svs.clients[num].name);
2297 if ((val = PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.clientcolors)))
2298 val->_float = svs.clients[num].colors;
2299 // NEXUIZ_PLAYERMODEL and NEXUIZ_PLAYERSKIN
2300 if( prog->fieldoffsets.playermodel >= 0 )
2301 PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(svs.clients[num].playermodel);
2302 if( prog->fieldoffsets.playerskin >= 0 )
2303 PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(svs.clients[num].playerskin);
2307 void SV_VM_CB_FreeEdict(prvm_edict_t *ed)
2309 World_UnlinkEdict(ed); // unlink from world bsp
2311 ed->fields.server->model = 0;
2312 ed->fields.server->takedamage = 0;
2313 ed->fields.server->modelindex = 0;
2314 ed->fields.server->colormap = 0;
2315 ed->fields.server->skin = 0;
2316 ed->fields.server->frame = 0;
2317 VectorClear(ed->fields.server->origin);
2318 VectorClear(ed->fields.server->angles);
2319 ed->fields.server->nextthink = -1;
2320 ed->fields.server->solid = 0;
2323 void SV_VM_CB_CountEdicts(void)
2327 int active, models, solid, step;
2329 active = models = solid = step = 0;
2330 for (i=0 ; i<prog->num_edicts ; i++)
2332 ent = PRVM_EDICT_NUM(i);
2333 if (ent->priv.server->free)
2336 if (ent->fields.server->solid)
2338 if (ent->fields.server->model)
2340 if (ent->fields.server->movetype == MOVETYPE_STEP)
2344 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
2345 Con_Printf("active :%3i\n", active);
2346 Con_Printf("view :%3i\n", models);
2347 Con_Printf("touch :%3i\n", solid);
2348 Con_Printf("step :%3i\n", step);
2351 qboolean SV_VM_CB_LoadEdict(prvm_edict_t *ent)
2353 // remove things from different skill levels or deathmatch
2354 if (gamemode != GAME_TRANSFUSION) //Transfusion does this in QC
2356 if (deathmatch.integer)
2358 if (((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_DEATHMATCH))
2363 else if ((current_skill <= 0 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_EASY ))
2364 || (current_skill == 1 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_MEDIUM))
2365 || (current_skill >= 2 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_HARD )))
2373 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)"};
2374 cvar_t nomonsters = {0, "nomonsters", "0", "unused cvar in quake, can be used by mods"};
2375 cvar_t gamecfg = {0, "gamecfg", "0", "unused cvar in quake, can be used by mods"};
2376 cvar_t scratch1 = {0, "scratch1", "0", "unused cvar in quake, can be used by mods"};
2377 cvar_t scratch2 = {0,"scratch2", "0", "unused cvar in quake, can be used by mods"};
2378 cvar_t scratch3 = {0, "scratch3", "0", "unused cvar in quake, can be used by mods"};
2379 cvar_t scratch4 = {0, "scratch4", "0", "unused cvar in quake, can be used by mods"};
2380 cvar_t savedgamecfg = {CVAR_SAVE, "savedgamecfg", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2381 cvar_t saved1 = {CVAR_SAVE, "saved1", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2382 cvar_t saved2 = {CVAR_SAVE, "saved2", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2383 cvar_t saved3 = {CVAR_SAVE, "saved3", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2384 cvar_t saved4 = {CVAR_SAVE, "saved4", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2385 cvar_t nehx00 = {0, "nehx00", "0", "nehahra data storage cvar (used in singleplayer)"};
2386 cvar_t nehx01 = {0, "nehx01", "0", "nehahra data storage cvar (used in singleplayer)"};
2387 cvar_t nehx02 = {0, "nehx02", "0", "nehahra data storage cvar (used in singleplayer)"};
2388 cvar_t nehx03 = {0, "nehx03", "0", "nehahra data storage cvar (used in singleplayer)"};
2389 cvar_t nehx04 = {0, "nehx04", "0", "nehahra data storage cvar (used in singleplayer)"};
2390 cvar_t nehx05 = {0, "nehx05", "0", "nehahra data storage cvar (used in singleplayer)"};
2391 cvar_t nehx06 = {0, "nehx06", "0", "nehahra data storage cvar (used in singleplayer)"};
2392 cvar_t nehx07 = {0, "nehx07", "0", "nehahra data storage cvar (used in singleplayer)"};
2393 cvar_t nehx08 = {0, "nehx08", "0", "nehahra data storage cvar (used in singleplayer)"};
2394 cvar_t nehx09 = {0, "nehx09", "0", "nehahra data storage cvar (used in singleplayer)"};
2395 cvar_t nehx10 = {0, "nehx10", "0", "nehahra data storage cvar (used in singleplayer)"};
2396 cvar_t nehx11 = {0, "nehx11", "0", "nehahra data storage cvar (used in singleplayer)"};
2397 cvar_t nehx12 = {0, "nehx12", "0", "nehahra data storage cvar (used in singleplayer)"};
2398 cvar_t nehx13 = {0, "nehx13", "0", "nehahra data storage cvar (used in singleplayer)"};
2399 cvar_t nehx14 = {0, "nehx14", "0", "nehahra data storage cvar (used in singleplayer)"};
2400 cvar_t nehx15 = {0, "nehx15", "0", "nehahra data storage cvar (used in singleplayer)"};
2401 cvar_t nehx16 = {0, "nehx16", "0", "nehahra data storage cvar (used in singleplayer)"};
2402 cvar_t nehx17 = {0, "nehx17", "0", "nehahra data storage cvar (used in singleplayer)"};
2403 cvar_t nehx18 = {0, "nehx18", "0", "nehahra data storage cvar (used in singleplayer)"};
2404 cvar_t nehx19 = {0, "nehx19", "0", "nehahra data storage cvar (used in singleplayer)"};
2405 cvar_t cutscene = {0, "cutscene", "1", "enables cutscenes in nehahra, can be used by other mods"};
2407 void SV_VM_Init(void)
2409 Cvar_RegisterVariable (&pr_checkextension);
2410 Cvar_RegisterVariable (&nomonsters);
2411 Cvar_RegisterVariable (&gamecfg);
2412 Cvar_RegisterVariable (&scratch1);
2413 Cvar_RegisterVariable (&scratch2);
2414 Cvar_RegisterVariable (&scratch3);
2415 Cvar_RegisterVariable (&scratch4);
2416 Cvar_RegisterVariable (&savedgamecfg);
2417 Cvar_RegisterVariable (&saved1);
2418 Cvar_RegisterVariable (&saved2);
2419 Cvar_RegisterVariable (&saved3);
2420 Cvar_RegisterVariable (&saved4);
2421 // LordHavoc: Nehahra uses these to pass data around cutscene demos
2422 if (gamemode == GAME_NEHAHRA)
2424 Cvar_RegisterVariable (&nehx00);
2425 Cvar_RegisterVariable (&nehx01);
2426 Cvar_RegisterVariable (&nehx02);
2427 Cvar_RegisterVariable (&nehx03);
2428 Cvar_RegisterVariable (&nehx04);
2429 Cvar_RegisterVariable (&nehx05);
2430 Cvar_RegisterVariable (&nehx06);
2431 Cvar_RegisterVariable (&nehx07);
2432 Cvar_RegisterVariable (&nehx08);
2433 Cvar_RegisterVariable (&nehx09);
2434 Cvar_RegisterVariable (&nehx10);
2435 Cvar_RegisterVariable (&nehx11);
2436 Cvar_RegisterVariable (&nehx12);
2437 Cvar_RegisterVariable (&nehx13);
2438 Cvar_RegisterVariable (&nehx14);
2439 Cvar_RegisterVariable (&nehx15);
2440 Cvar_RegisterVariable (&nehx16);
2441 Cvar_RegisterVariable (&nehx17);
2442 Cvar_RegisterVariable (&nehx18);
2443 Cvar_RegisterVariable (&nehx19);
2445 Cvar_RegisterVariable (&cutscene); // for Nehahra but useful to other mods as well
2448 #define REQFIELDS (sizeof(reqfields) / sizeof(prvm_required_field_t))
2450 prvm_required_field_t reqfields[] =
2452 {ev_entity, "cursor_trace_ent"},
2453 {ev_entity, "drawonlytoclient"},
2454 {ev_entity, "exteriormodeltoclient"},
2455 {ev_entity, "nodrawtoclient"},
2456 {ev_entity, "tag_entity"},
2457 {ev_entity, "viewmodelforclient"},
2458 {ev_float, "alpha"},
2459 {ev_float, "ammo_cells1"},
2460 {ev_float, "ammo_lava_nails"},
2461 {ev_float, "ammo_multi_rockets"},
2462 {ev_float, "ammo_nails1"},
2463 {ev_float, "ammo_plasma"},
2464 {ev_float, "ammo_rockets1"},
2465 {ev_float, "ammo_shells1"},
2466 {ev_float, "button3"},
2467 {ev_float, "button4"},
2468 {ev_float, "button5"},
2469 {ev_float, "button6"},
2470 {ev_float, "button7"},
2471 {ev_float, "button8"},
2472 {ev_float, "button9"},
2473 {ev_float, "button10"},
2474 {ev_float, "button11"},
2475 {ev_float, "button12"},
2476 {ev_float, "button13"},
2477 {ev_float, "button14"},
2478 {ev_float, "button15"},
2479 {ev_float, "button16"},
2480 {ev_float, "buttonchat"},
2481 {ev_float, "buttonuse"},
2482 {ev_float, "clientcolors"},
2483 {ev_float, "cursor_active"},
2484 {ev_float, "fullbright"},
2485 {ev_float, "glow_color"},
2486 {ev_float, "glow_size"},
2487 {ev_float, "glow_trail"},
2488 {ev_float, "gravity"},
2489 {ev_float, "idealpitch"},
2490 {ev_float, "items2"},
2491 {ev_float, "light_lev"},
2492 {ev_float, "pflags"},
2494 {ev_float, "pitch_speed"},
2495 {ev_float, "pmodel"},
2496 {ev_float, "renderamt"}, // HalfLife support
2497 {ev_float, "rendermode"}, // HalfLife support
2498 {ev_float, "scale"},
2499 {ev_float, "style"},
2500 {ev_float, "tag_index"},
2501 {ev_float, "Version"},
2502 {ev_float, "viewzoom"},
2503 {ev_vector, "color"},
2504 {ev_vector, "colormod"},
2505 {ev_vector, "cursor_screen"},
2506 {ev_vector, "cursor_trace_endpos"},
2507 {ev_vector, "cursor_trace_start"},
2508 {ev_vector, "movement"},
2509 {ev_vector, "punchvector"},
2510 {ev_string, "playermodel"},
2511 {ev_string, "playerskin"},
2512 {ev_function, "SendEntity"},
2513 {ev_function, "customizeentityforclient"},
2514 // DRESK - Support for Entity Contents Transition Event
2515 {ev_function, "contentstransition"},
2518 void SV_VM_Setup(void)
2520 extern cvar_t csqc_progname; //[515]: csqc crc check and right csprogs name according to progs.dat
2521 extern cvar_t csqc_progcrc;
2522 extern cvar_t csqc_progsize;
2523 size_t csprogsdatasize;
2525 PRVM_InitProg( PRVM_SERVERPROG );
2527 // allocate the mempools
2528 // TODO: move the magic numbers/constants into #defines [9/13/2006 Black]
2529 prog->progs_mempool = Mem_AllocPool("Server Progs", 0, NULL);
2530 prog->builtins = vm_sv_builtins;
2531 prog->numbuiltins = vm_sv_numbuiltins;
2532 prog->headercrc = PROGHEADER_CRC;
2533 prog->max_edicts = 512;
2534 prog->limit_edicts = MAX_EDICTS;
2535 prog->reserved_edicts = svs.maxclients;
2536 prog->edictprivate_size = sizeof(edict_engineprivate_t);
2537 prog->name = "server";
2538 prog->extensionstring = vm_sv_extensions;
2539 prog->loadintoworld = true;
2541 prog->begin_increase_edicts = SV_VM_CB_BeginIncreaseEdicts;
2542 prog->end_increase_edicts = SV_VM_CB_EndIncreaseEdicts;
2543 prog->init_edict = SV_VM_CB_InitEdict;
2544 prog->free_edict = SV_VM_CB_FreeEdict;
2545 prog->count_edicts = SV_VM_CB_CountEdicts;
2546 prog->load_edict = SV_VM_CB_LoadEdict;
2547 prog->init_cmd = VM_SV_Cmd_Init;
2548 prog->reset_cmd = VM_SV_Cmd_Reset;
2549 prog->error_cmd = Host_Error;
2551 // TODO: add a requiredfuncs list (ask LH if this is necessary at all)
2552 PRVM_LoadProgs( sv_progs.string, 0, NULL, REQFIELDS, reqfields, 0, NULL );
2554 // some mods compiled with scrambling compilers lack certain critical
2555 // global names and field names such as "self" and "time" and "nextthink"
2556 // so we have to set these offsets manually, matching the entvars_t
2557 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, angles);
2558 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, chain);
2559 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, classname);
2560 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, frame);
2561 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, groundentity);
2562 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ideal_yaw);
2563 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, nextthink);
2564 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, think);
2565 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, yaw_speed);
2566 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, self);
2567 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, time);
2568 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_forward);
2569 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_right);
2570 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_up);
2571 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_allsolid);
2572 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_startsolid);
2573 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_fraction);
2574 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inwater);
2575 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inopen);
2576 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_endpos);
2577 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_normal);
2578 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_dist);
2579 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_ent);
2580 // OP_STATE is always supported on server (due to entvars_t)
2581 prog->flag |= PRVM_OP_STATE;
2583 VM_AutoSentStats_Clear();//[515]: csqc
2584 EntityFrameCSQC_ClearVersions();//[515]: csqc
2588 // 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
2589 sv.csqc_progname[0] = 0;
2590 sv.csqc_progcrc = FS_CRCFile(csqc_progname.string, &csprogsdatasize);
2591 sv.csqc_progsize = csprogsdatasize;
2592 if (sv.csqc_progsize > 0)
2594 strlcpy(sv.csqc_progname, csqc_progname.string, sizeof(sv.csqc_progname));
2595 Con_DPrintf("server detected csqc progs file \"%s\" with size %i and crc %i\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc);
2599 void SV_VM_Begin(void)
2602 PRVM_SetProg( PRVM_SERVERPROG );
2604 prog->globals.server->time = (float) sv.time;
2607 void SV_VM_End(void)