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 MSG_WriteByte (&client->netconnection->message, svc_serverinfo);
410 MSG_WriteLong (&client->netconnection->message, Protocol_NumberForEnum(sv.protocol));
411 MSG_WriteByte (&client->netconnection->message, svs.maxclients);
413 if (!coop.integer && deathmatch.integer)
414 MSG_WriteByte (&client->netconnection->message, GAME_DEATHMATCH);
416 MSG_WriteByte (&client->netconnection->message, GAME_COOP);
418 MSG_WriteString (&client->netconnection->message,PRVM_GetString(prog->edicts->fields.server->message));
420 for (i = 1;i < MAX_MODELS && sv.model_precache[i][0];i++)
421 MSG_WriteString (&client->netconnection->message, sv.model_precache[i]);
422 MSG_WriteByte (&client->netconnection->message, 0);
424 for (i = 1;i < MAX_SOUNDS && sv.sound_precache[i][0];i++)
425 MSG_WriteString (&client->netconnection->message, sv.sound_precache[i]);
426 MSG_WriteByte (&client->netconnection->message, 0);
429 MSG_WriteByte (&client->netconnection->message, svc_cdtrack);
430 MSG_WriteByte (&client->netconnection->message, (int)prog->edicts->fields.server->sounds);
431 MSG_WriteByte (&client->netconnection->message, (int)prog->edicts->fields.server->sounds);
434 MSG_WriteByte (&client->netconnection->message, svc_setview);
435 MSG_WriteShort (&client->netconnection->message, PRVM_NUM_FOR_EDICT(client->edict));
437 MSG_WriteByte (&client->netconnection->message, svc_signonnum);
438 MSG_WriteByte (&client->netconnection->message, 1);
443 host_client = client;
444 Curl_SendRequirements();
448 client->spawned = false; // need prespawn, spawn, etc
455 Initializes a client_t for a new net connection. This will only be called
456 once for a player each game, not once for each level change.
459 void SV_ConnectClient (int clientnum, netconn_t *netconnection)
463 float spawn_parms[NUM_SPAWN_PARMS];
465 client = svs.clients + clientnum;
467 if(netconnection)//[515]: bots don't play with csqc =)
468 EntityFrameCSQC_InitClientVersions(clientnum, false);
470 // set up the client_t
472 memcpy (spawn_parms, client->spawn_parms, sizeof(spawn_parms));
473 memset (client, 0, sizeof(*client));
474 client->active = true;
475 client->netconnection = netconnection;
477 Con_DPrintf("Client %s connected\n", client->netconnection ? client->netconnection->address : "botclient");
479 strlcpy(client->name, "unconnected", sizeof(client->name));
480 strlcpy(client->old_name, "unconnected", sizeof(client->old_name));
481 client->spawned = false;
482 client->edict = PRVM_EDICT_NUM(clientnum+1);
483 if (client->netconnection)
484 client->netconnection->message.allowoverflow = true; // we can catch it
485 // updated by receiving "rate" command from client
486 client->rate = NET_MINRATE;
487 // no limits for local player
488 if (client->netconnection && LHNETADDRESS_GetAddressType(&client->netconnection->peeraddress) == LHNETADDRESSTYPE_LOOP)
489 client->rate = 1000000000;
490 client->connecttime = realtime;
493 memcpy (client->spawn_parms, spawn_parms, sizeof(spawn_parms));
496 // call the progs to get default spawn parms for the new client
497 // set self to world to intentionally cause errors with broken SetNewParms code in some mods
498 prog->globals.server->self = 0;
499 PRVM_ExecuteProgram (prog->globals.server->SetNewParms, "QC function SetNewParms is missing");
500 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
501 client->spawn_parms[i] = (&prog->globals.server->parm1)[i];
503 // set up the entity for this client (including .colormap, .team, etc)
504 PRVM_ED_ClearEdict(client->edict);
507 // don't call SendServerinfo for a fresh botclient because its fields have
508 // not been set up by the qc yet
509 if (client->netconnection)
510 SV_SendServerinfo (client);
512 client->spawned = true;
517 ===============================================================================
521 ===============================================================================
530 void SV_ClearDatagram (void)
532 SZ_Clear (&sv.datagram);
536 =============================================================================
538 The PVS must include a small area around the client to allow head bobbing
539 or other small motion on the client side. Otherwise, a bob might cause an
540 entity that should be visible to not show up, especially when the bob
543 =============================================================================
546 int sv_writeentitiestoclient_pvsbytes;
547 unsigned char sv_writeentitiestoclient_pvs[MAX_MAP_LEAFS/8];
549 static int numsendentities;
550 static entity_state_t sendentities[MAX_EDICTS];
551 static entity_state_t *sendentitiesindex[MAX_EDICTS];
553 qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int e)
556 unsigned int modelindex, effects, flags, glowsize, lightstyle, lightpflags, light[4], specialvisibilityradius;
557 unsigned int customizeentityforclient;
559 vec3_t cullmins, cullmaxs;
563 // EF_NODRAW prevents sending for any reason except for your own
564 // client, so we must keep all clients in this superset
565 effects = (unsigned)ent->fields.server->effects;
567 // we can omit invisible entities with no effects that are not clients
568 // LordHavoc: this could kill tags attached to an invisible entity, I
569 // just hope we never have to support that case
570 i = (int)ent->fields.server->modelindex;
571 modelindex = (i >= 1 && i < MAX_MODELS && *PRVM_GetString(ent->fields.server->model)) ? i : 0;
574 i = (int)(PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_size)->_float * 0.25f);
575 glowsize = (unsigned char)bound(0, i, 255);
576 if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_trail)->_float)
577 flags |= RENDER_GLOWTRAIL;
579 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[0]*256;
580 light[0] = (unsigned short)bound(0, f, 65535);
581 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[1]*256;
582 light[1] = (unsigned short)bound(0, f, 65535);
583 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[2]*256;
584 light[2] = (unsigned short)bound(0, f, 65535);
585 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.light_lev)->_float;
586 light[3] = (unsigned short)bound(0, f, 65535);
587 lightstyle = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.style)->_float;
588 lightpflags = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.pflags)->_float;
590 if (gamemode == GAME_TENEBRAE)
592 // tenebrae's EF_FULLDYNAMIC conflicts with Q2's EF_NODRAW
596 lightpflags |= PFLAGS_FULLDYNAMIC;
598 // tenebrae's EF_GREEN conflicts with DP's EF_ADDITIVE
602 light[0] = (int)(0.2*256);
603 light[1] = (int)(1.0*256);
604 light[2] = (int)(0.2*256);
606 lightpflags |= PFLAGS_FULLDYNAMIC;
610 specialvisibilityradius = 0;
611 if (lightpflags & PFLAGS_FULLDYNAMIC)
612 specialvisibilityradius = max(specialvisibilityradius, light[3]);
614 specialvisibilityradius = max(specialvisibilityradius, glowsize * 4);
615 if (flags & RENDER_GLOWTRAIL)
616 specialvisibilityradius = max(specialvisibilityradius, 100);
617 if (effects & (EF_BRIGHTFIELD | EF_MUZZLEFLASH | EF_BRIGHTLIGHT | EF_DIMLIGHT | EF_RED | EF_BLUE | EF_FLAME | EF_STARDUST))
619 if (effects & EF_BRIGHTFIELD)
620 specialvisibilityradius = max(specialvisibilityradius, 80);
621 if (effects & EF_MUZZLEFLASH)
622 specialvisibilityradius = max(specialvisibilityradius, 100);
623 if (effects & EF_BRIGHTLIGHT)
624 specialvisibilityradius = max(specialvisibilityradius, 400);
625 if (effects & EF_DIMLIGHT)
626 specialvisibilityradius = max(specialvisibilityradius, 200);
627 if (effects & EF_RED)
628 specialvisibilityradius = max(specialvisibilityradius, 200);
629 if (effects & EF_BLUE)
630 specialvisibilityradius = max(specialvisibilityradius, 200);
631 if (effects & EF_FLAME)
632 specialvisibilityradius = max(specialvisibilityradius, 250);
633 if (effects & EF_STARDUST)
634 specialvisibilityradius = max(specialvisibilityradius, 100);
637 // early culling checks
638 // (final culling is done by SV_MarkWriteEntityStateToClient)
639 customizeentityforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.customizeentityforclient)->function;
640 if (!customizeentityforclient)
642 if (e > svs.maxclients && (!modelindex && !specialvisibilityradius))
644 // this 2 billion unit check is actually to detect NAN origins
645 // (we really don't want to send those)
646 if (VectorLength2(ent->fields.server->origin) > 2000000000.0*2000000000.0)
654 VectorCopy(ent->fields.server->origin, cs->origin);
655 VectorCopy(ent->fields.server->angles, cs->angles);
657 cs->effects = effects;
658 cs->colormap = (unsigned)ent->fields.server->colormap;
659 cs->modelindex = modelindex;
660 cs->skin = (unsigned)ent->fields.server->skin;
661 cs->frame = (unsigned)ent->fields.server->frame;
662 cs->viewmodelforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewmodelforclient)->edict;
663 cs->exteriormodelforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.exteriormodeltoclient)->edict;
664 cs->nodrawtoclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.nodrawtoclient)->edict;
665 cs->drawonlytoclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.drawonlytoclient)->edict;
666 cs->customizeentityforclient = customizeentityforclient;
667 cs->tagentity = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)->edict;
668 cs->tagindex = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_index)->_float;
669 cs->glowsize = glowsize;
671 // don't need to init cs->colormod because the defaultstate did that for us
672 //cs->colormod[0] = cs->colormod[1] = cs->colormod[2] = 32;
673 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.colormod);
674 if (val->vector[0] || val->vector[1] || val->vector[2])
676 i = (int)(val->vector[0] * 32.0f);cs->colormod[0] = bound(0, i, 255);
677 i = (int)(val->vector[1] * 32.0f);cs->colormod[1] = bound(0, i, 255);
678 i = (int)(val->vector[2] * 32.0f);cs->colormod[2] = bound(0, i, 255);
681 cs->modelindex = modelindex;
684 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.alpha)->_float * 255.0f);
688 cs->alpha = (unsigned char)bound(0, i, 255);
691 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderamt)->_float);
695 cs->alpha = (unsigned char)bound(0, i, 255);
699 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale)->_float * 16.0f);
703 cs->scale = (unsigned char)bound(0, i, 255);
707 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_color)->_float);
709 cs->glowcolor = (int)f;
711 if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.fullbright)->_float)
712 cs->effects |= EF_FULLBRIGHT;
714 if (ent->fields.server->movetype == MOVETYPE_STEP)
715 cs->flags |= RENDER_STEP;
716 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)
717 cs->flags |= RENDER_LOWPRECISION;
718 if (ent->fields.server->colormap >= 1024)
719 cs->flags |= RENDER_COLORMAPPED;
720 if (cs->viewmodelforclient)
721 cs->flags |= RENDER_VIEWMODEL; // show relative to the view
723 cs->light[0] = light[0];
724 cs->light[1] = light[1];
725 cs->light[2] = light[2];
726 cs->light[3] = light[3];
727 cs->lightstyle = lightstyle;
728 cs->lightpflags = lightpflags;
730 cs->specialvisibilityradius = specialvisibilityradius;
732 // calculate the visible box of this entity (don't use the physics box
733 // as that is often smaller than a model, and would not count
734 // specialvisibilityradius)
735 if ((model = sv.models[modelindex]))
737 float scale = cs->scale * (1.0f / 16.0f);
738 if (cs->angles[0] || cs->angles[2]) // pitch and roll
740 VectorMA(cs->origin, scale, model->rotatedmins, cullmins);
741 VectorMA(cs->origin, scale, model->rotatedmaxs, cullmaxs);
743 else if (cs->angles[1])
745 VectorMA(cs->origin, scale, model->yawmins, cullmins);
746 VectorMA(cs->origin, scale, model->yawmaxs, cullmaxs);
750 VectorMA(cs->origin, scale, model->normalmins, cullmins);
751 VectorMA(cs->origin, scale, model->normalmaxs, cullmaxs);
756 // if there is no model (or it could not be loaded), use the physics box
757 VectorAdd(cs->origin, ent->fields.server->mins, cullmins);
758 VectorAdd(cs->origin, ent->fields.server->maxs, cullmaxs);
760 if (specialvisibilityradius)
762 cullmins[0] = min(cullmins[0], cs->origin[0] - specialvisibilityradius);
763 cullmins[1] = min(cullmins[1], cs->origin[1] - specialvisibilityradius);
764 cullmins[2] = min(cullmins[2], cs->origin[2] - specialvisibilityradius);
765 cullmaxs[0] = max(cullmaxs[0], cs->origin[0] + specialvisibilityradius);
766 cullmaxs[1] = max(cullmaxs[1], cs->origin[1] + specialvisibilityradius);
767 cullmaxs[2] = max(cullmaxs[2], cs->origin[2] + specialvisibilityradius);
769 if (!VectorCompare(cullmins, ent->priv.server->cullmins) || !VectorCompare(cullmaxs, ent->priv.server->cullmaxs))
771 VectorCopy(cullmins, ent->priv.server->cullmins);
772 VectorCopy(cullmaxs, ent->priv.server->cullmaxs);
773 ent->priv.server->pvs_numclusters = -1;
774 if (sv.worldmodel && sv.worldmodel->brush.FindBoxClusters)
776 i = sv.worldmodel->brush.FindBoxClusters(sv.worldmodel, cullmins, cullmaxs, MAX_ENTITYCLUSTERS, ent->priv.server->pvs_clusterlist);
777 if (i <= MAX_ENTITYCLUSTERS)
778 ent->priv.server->pvs_numclusters = i;
785 void SV_PrepareEntitiesForSending(void)
789 // send all entities that touch the pvs
791 sendentitiesindex[0] = NULL;
792 memset(sendentitiesindex, 0, prog->num_edicts * sizeof(entity_state_t *));
793 for (e = 1, ent = PRVM_NEXT_EDICT(prog->edicts);e < prog->num_edicts;e++, ent = PRVM_NEXT_EDICT(ent))
795 if (!ent->priv.server->free && SV_PrepareEntityForSending(ent, sendentities + numsendentities, e))
797 sendentitiesindex[e] = sendentities + numsendentities;
803 static int sententitiesmark = 0;
804 static int sententities[MAX_EDICTS];
805 static int sententitiesconsideration[MAX_EDICTS];
806 static int sv_writeentitiestoclient_culled_pvs;
807 static int sv_writeentitiestoclient_culled_trace;
808 static int sv_writeentitiestoclient_visibleentities;
809 static int sv_writeentitiestoclient_totalentities;
810 //static entity_frame_t sv_writeentitiestoclient_entityframe;
811 static int sv_writeentitiestoclient_clentnum;
812 static vec3_t sv_writeentitiestoclient_testeye;
813 static client_t *sv_writeentitiestoclient_client;
815 void SV_MarkWriteEntityStateToClient(entity_state_t *s)
820 if (sententitiesconsideration[s->number] == sententitiesmark)
822 sententitiesconsideration[s->number] = sententitiesmark;
823 sv_writeentitiestoclient_totalentities++;
825 if (s->customizeentityforclient)
827 prog->globals.server->self = s->number;
828 prog->globals.server->other = sv_writeentitiestoclient_clentnum;
829 PRVM_ExecuteProgram(s->customizeentityforclient, "customizeentityforclient: NULL function");
830 if(!PRVM_G_FLOAT(OFS_RETURN) || !SV_PrepareEntityForSending(PRVM_EDICT_NUM(s->number), s, s->number))
834 // never reject player
835 if (s->number != sv_writeentitiestoclient_clentnum)
837 // check various rejection conditions
838 if (s->nodrawtoclient == sv_writeentitiestoclient_clentnum)
840 if (s->drawonlytoclient && s->drawonlytoclient != sv_writeentitiestoclient_clentnum)
842 if (s->effects & EF_NODRAW)
844 // LordHavoc: only send entities with a model or important effects
845 if (!s->modelindex && s->specialvisibilityradius == 0)
848 isbmodel = (model = sv.models[s->modelindex]) != NULL && model->name[0] == '*';
849 // viewmodels don't have visibility checking
850 if (s->viewmodelforclient)
852 if (s->viewmodelforclient != sv_writeentitiestoclient_clentnum)
855 else if (s->tagentity)
857 // tag attached entities simply check their parent
858 if (!sendentitiesindex[s->tagentity])
860 SV_MarkWriteEntityStateToClient(sendentitiesindex[s->tagentity]);
861 if (sententities[s->tagentity] != sententitiesmark)
864 // always send world submodels in newer protocols because they don't
865 // generate much traffic (in old protocols they hog bandwidth)
866 // but only if sv_cullentities_alwayssendbmodels is on
867 else if (!(s->effects & EF_NODEPTHTEST) && (!isbmodel || !sv_cullentities_nevercullbmodels.integer || sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE))
869 // entity has survived every check so far, check if visible
870 ed = PRVM_EDICT_NUM(s->number);
872 // if not touching a visible leaf
873 if (sv_cullentities_pvs.integer && sv_writeentitiestoclient_pvsbytes)
875 if (ed->priv.server->pvs_numclusters < 0)
877 // entity too big for clusters list
878 if (sv.worldmodel && sv.worldmodel->brush.BoxTouchingPVS && !sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, sv_writeentitiestoclient_pvs, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
880 sv_writeentitiestoclient_culled_pvs++;
887 // check cached clusters list
888 for (i = 0;i < ed->priv.server->pvs_numclusters;i++)
889 if (CHECKPVSBIT(sv_writeentitiestoclient_pvs, ed->priv.server->pvs_clusterlist[i]))
891 if (i == ed->priv.server->pvs_numclusters)
893 sv_writeentitiestoclient_culled_pvs++;
899 // or not seen by random tracelines
900 if (sv_cullentities_trace.integer && !isbmodel)
902 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))
903 sv_writeentitiestoclient_client->visibletime[s->number] = realtime + sv_cullentities_trace_delay.value;
904 if (realtime > sv_writeentitiestoclient_client->visibletime[s->number])
906 sv_writeentitiestoclient_culled_trace++;
913 // this just marks it for sending
914 // FIXME: it would be more efficient to send here, but the entity
915 // compressor isn't that flexible
916 sv_writeentitiestoclient_visibleentities++;
917 sententities[s->number] = sententitiesmark;
920 entity_state_t sendstates[MAX_EDICTS];
921 extern int csqc_clent;
923 void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg, int *stats)
925 int i, numsendstates;
928 // if there isn't enough space to accomplish anything, skip it
929 if (msg->cursize + 25 > msg->maxsize)
932 sv_writeentitiestoclient_client = client;
934 sv_writeentitiestoclient_culled_pvs = 0;
935 sv_writeentitiestoclient_culled_trace = 0;
936 sv_writeentitiestoclient_visibleentities = 0;
937 sv_writeentitiestoclient_totalentities = 0;
939 // find the client's PVS
940 // the real place being tested from
941 VectorAdd(clent->fields.server->origin, clent->fields.server->view_ofs, sv_writeentitiestoclient_testeye);
942 sv_writeentitiestoclient_pvsbytes = 0;
943 if (sv.worldmodel && sv.worldmodel->brush.FatPVS)
944 sv_writeentitiestoclient_pvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, sv_writeentitiestoclient_testeye, 8, sv_writeentitiestoclient_pvs, sizeof(sv_writeentitiestoclient_pvs));
946 csqc_clent = sv_writeentitiestoclient_clentnum = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
950 for (i = 0;i < numsendentities;i++)
951 SV_MarkWriteEntityStateToClient(sendentities + i);
954 for (i = 0;i < numsendentities;i++)
956 if (sententities[sendentities[i].number] == sententitiesmark)
958 s = &sendstates[numsendstates++];
959 *s = sendentities[i];
960 if (s->exteriormodelforclient && s->exteriormodelforclient == sv_writeentitiestoclient_clentnum)
961 s->flags |= RENDER_EXTERIORMODEL;
965 if (sv_cullentities_stats.integer)
966 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);
968 EntityFrameCSQC_WriteFrame(msg, numsendstates, sendstates);
970 if (client->entitydatabase5)
971 EntityFrame5_WriteFrame(msg, client->entitydatabase5, numsendstates, sendstates, client - svs.clients + 1, stats, client->movesequence);
972 else if (client->entitydatabase4)
973 EntityFrame4_WriteFrame(msg, client->entitydatabase4, numsendstates, sendstates);
974 else if (client->entitydatabase)
975 EntityFrame_WriteFrame(msg, client->entitydatabase, numsendstates, sendstates, client - svs.clients + 1);
977 EntityFrameQuake_WriteFrame(msg, numsendstates, sendstates);
986 void SV_CleanupEnts (void)
991 ent = PRVM_NEXT_EDICT(prog->edicts);
992 for (e=1 ; e<prog->num_edicts ; e++, ent = PRVM_NEXT_EDICT(ent))
993 ent->fields.server->effects = (int)ent->fields.server->effects & ~EF_MUZZLEFLASH;
998 SV_WriteClientdataToMessage
1002 void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats)
1006 prvm_edict_t *other;
1014 // send a damage message
1016 if (ent->fields.server->dmg_take || ent->fields.server->dmg_save)
1018 other = PRVM_PROG_TO_EDICT(ent->fields.server->dmg_inflictor);
1019 MSG_WriteByte (msg, svc_damage);
1020 MSG_WriteByte (msg, (int)ent->fields.server->dmg_save);
1021 MSG_WriteByte (msg, (int)ent->fields.server->dmg_take);
1022 for (i=0 ; i<3 ; i++)
1023 MSG_WriteCoord (msg, other->fields.server->origin[i] + 0.5*(other->fields.server->mins[i] + other->fields.server->maxs[i]), sv.protocol);
1025 ent->fields.server->dmg_take = 0;
1026 ent->fields.server->dmg_save = 0;
1030 // send the current viewpos offset from the view entity
1032 SV_SetIdealPitch (); // how much to look up / down ideally
1034 // a fixangle might get lost in a dropped packet. Oh well.
1035 if ( ent->fields.server->fixangle )
1037 MSG_WriteByte (msg, svc_setangle);
1038 for (i=0 ; i < 3 ; i++)
1039 MSG_WriteAngle (msg, ent->fields.server->angles[i], sv.protocol);
1040 ent->fields.server->fixangle = 0;
1043 // stuff the sigil bits into the high bits of items for sbar, or else
1045 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.items2);
1046 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1047 items = (int)ent->fields.server->items | ((int)val->_float << 23);
1049 items = (int)ent->fields.server->items | ((int)prog->globals.server->serverflags << 28);
1051 VectorClear(punchvector);
1052 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.punchvector)))
1053 VectorCopy(val->vector, punchvector);
1055 // cache weapon model name and index in client struct to save time
1056 // (this search can be almost 1% of cpu time!)
1057 s = PRVM_GetString(ent->fields.server->weaponmodel);
1058 if (strcmp(s, client->weaponmodel))
1060 strlcpy(client->weaponmodel, s, sizeof(client->weaponmodel));
1061 client->weaponmodelindex = SV_ModelIndex(s, 1);
1065 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewzoom)))
1066 viewzoom = (int)(val->_float * 255.0f);
1072 if ((int)ent->fields.server->flags & FL_ONGROUND)
1073 bits |= SU_ONGROUND;
1074 if (ent->fields.server->waterlevel >= 2)
1076 if (ent->fields.server->idealpitch)
1077 bits |= SU_IDEALPITCH;
1079 for (i=0 ; i<3 ; i++)
1081 if (ent->fields.server->punchangle[i])
1082 bits |= (SU_PUNCH1<<i);
1083 if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE)
1085 bits |= (SU_PUNCHVEC1<<i);
1086 if (ent->fields.server->velocity[i])
1087 bits |= (SU_VELOCITY1<<i);
1090 memset(stats, 0, sizeof(int[MAX_CL_STATS]));
1091 stats[STAT_VIEWHEIGHT] = (int)ent->fields.server->view_ofs[2];
1092 stats[STAT_ITEMS] = items;
1093 stats[STAT_WEAPONFRAME] = (int)ent->fields.server->weaponframe;
1094 stats[STAT_ARMOR] = (int)ent->fields.server->armorvalue;
1095 stats[STAT_WEAPON] = client->weaponmodelindex;
1096 stats[STAT_HEALTH] = (int)ent->fields.server->health;
1097 stats[STAT_AMMO] = (int)ent->fields.server->currentammo;
1098 stats[STAT_SHELLS] = (int)ent->fields.server->ammo_shells;
1099 stats[STAT_NAILS] = (int)ent->fields.server->ammo_nails;
1100 stats[STAT_ROCKETS] = (int)ent->fields.server->ammo_rockets;
1101 stats[STAT_CELLS] = (int)ent->fields.server->ammo_cells;
1102 stats[STAT_ACTIVEWEAPON] = (int)ent->fields.server->weapon;
1103 stats[STAT_VIEWZOOM] = viewzoom;
1104 stats[STAT_TOTALSECRETS] = prog->globals.server->total_secrets;
1105 stats[STAT_TOTALMONSTERS] = prog->globals.server->total_monsters;
1106 // the QC bumps these itself by sending svc_'s, so we have to keep them
1107 // zero or they'll be corrected by the engine
1108 //stats[STAT_SECRETS] = prog->globals.server->found_secrets;
1109 //stats[STAT_MONSTERS] = prog->globals.server->killed_monsters;
1111 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)
1113 if (stats[STAT_VIEWHEIGHT] != DEFAULT_VIEWHEIGHT) bits |= SU_VIEWHEIGHT;
1115 if (stats[STAT_WEAPONFRAME]) bits |= SU_WEAPONFRAME;
1116 if (stats[STAT_ARMOR]) bits |= SU_ARMOR;
1118 // FIXME: which protocols support this? does PROTOCOL_DARKPLACES3 support viewzoom?
1119 if (sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5)
1120 if (viewzoom != 255)
1121 bits |= SU_VIEWZOOM;
1126 if (bits >= 16777216)
1130 MSG_WriteByte (msg, svc_clientdata);
1131 MSG_WriteShort (msg, bits);
1132 if (bits & SU_EXTEND1)
1133 MSG_WriteByte(msg, bits >> 16);
1134 if (bits & SU_EXTEND2)
1135 MSG_WriteByte(msg, bits >> 24);
1137 if (bits & SU_VIEWHEIGHT)
1138 MSG_WriteChar (msg, stats[STAT_VIEWHEIGHT]);
1140 if (bits & SU_IDEALPITCH)
1141 MSG_WriteChar (msg, (int)ent->fields.server->idealpitch);
1143 for (i=0 ; i<3 ; i++)
1145 if (bits & (SU_PUNCH1<<i))
1147 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
1148 MSG_WriteChar(msg, (int)ent->fields.server->punchangle[i]);
1150 MSG_WriteAngle16i(msg, ent->fields.server->punchangle[i]);
1152 if (bits & (SU_PUNCHVEC1<<i))
1154 if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
1155 MSG_WriteCoord16i(msg, punchvector[i]);
1157 MSG_WriteCoord32f(msg, punchvector[i]);
1159 if (bits & (SU_VELOCITY1<<i))
1161 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
1162 MSG_WriteChar(msg, (int)(ent->fields.server->velocity[i] * (1.0f / 16.0f)));
1164 MSG_WriteCoord32f(msg, ent->fields.server->velocity[i]);
1168 if (bits & SU_ITEMS)
1169 MSG_WriteLong (msg, stats[STAT_ITEMS]);
1171 if (sv.protocol == PROTOCOL_DARKPLACES5)
1173 if (bits & SU_WEAPONFRAME)
1174 MSG_WriteShort (msg, stats[STAT_WEAPONFRAME]);
1175 if (bits & SU_ARMOR)
1176 MSG_WriteShort (msg, stats[STAT_ARMOR]);
1177 if (bits & SU_WEAPON)
1178 MSG_WriteShort (msg, stats[STAT_WEAPON]);
1179 MSG_WriteShort (msg, stats[STAT_HEALTH]);
1180 MSG_WriteShort (msg, stats[STAT_AMMO]);
1181 MSG_WriteShort (msg, stats[STAT_SHELLS]);
1182 MSG_WriteShort (msg, stats[STAT_NAILS]);
1183 MSG_WriteShort (msg, stats[STAT_ROCKETS]);
1184 MSG_WriteShort (msg, stats[STAT_CELLS]);
1185 MSG_WriteShort (msg, stats[STAT_ACTIVEWEAPON]);
1186 if (bits & SU_VIEWZOOM)
1187 MSG_WriteShort (msg, bound(0, stats[STAT_VIEWZOOM], 65535));
1189 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)
1191 if (bits & SU_WEAPONFRAME)
1192 MSG_WriteByte (msg, stats[STAT_WEAPONFRAME]);
1193 if (bits & SU_ARMOR)
1194 MSG_WriteByte (msg, stats[STAT_ARMOR]);
1195 if (bits & SU_WEAPON)
1196 MSG_WriteByte (msg, stats[STAT_WEAPON]);
1197 MSG_WriteShort (msg, stats[STAT_HEALTH]);
1198 MSG_WriteByte (msg, stats[STAT_AMMO]);
1199 MSG_WriteByte (msg, stats[STAT_SHELLS]);
1200 MSG_WriteByte (msg, stats[STAT_NAILS]);
1201 MSG_WriteByte (msg, stats[STAT_ROCKETS]);
1202 MSG_WriteByte (msg, stats[STAT_CELLS]);
1203 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE || gamemode == GAME_NEXUIZ)
1205 for (i = 0;i < 32;i++)
1206 if (stats[STAT_WEAPON] & (1<<i))
1208 MSG_WriteByte (msg, i);
1211 MSG_WriteByte (msg, stats[STAT_WEAPON]);
1212 if (bits & SU_VIEWZOOM)
1214 if (sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
1215 MSG_WriteByte (msg, bound(0, stats[STAT_VIEWZOOM], 255));
1217 MSG_WriteShort (msg, bound(0, stats[STAT_VIEWZOOM], 65535));
1223 =======================
1224 SV_SendClientDatagram
1225 =======================
1227 static unsigned char sv_sendclientdatagram_buf[NET_MAXMESSAGE]; // FIXME?
1228 void SV_SendClientDatagram (client_t *client)
1230 int rate, maxrate, maxsize, maxsize2, downloadsize;
1232 int stats[MAX_CL_STATS];
1234 if (LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) == LHNETADDRESSTYPE_LOOP && !sv_ratelimitlocalplayer.integer)
1236 // for good singleplayer, send huge packets
1237 maxsize = sizeof(sv_sendclientdatagram_buf);
1238 maxsize2 = sizeof(sv_sendclientdatagram_buf);
1240 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)
1242 // no rate limiting support on older protocols because dp protocols
1243 // 1-4 kick the client off if they overflow, and quake protocol shows
1244 // less than the full entity set if rate limited
1250 // PROTOCOL_DARKPLACES5 and later support packet size limiting of updates
1251 maxrate = max(NET_MINRATE, sv_maxrate.integer);
1252 if (sv_maxrate.integer != maxrate)
1253 Cvar_SetValueQuick(&sv_maxrate, maxrate);
1255 // this rate limiting does not understand sys_ticrate 0
1256 // (but no one should be running that on a server!)
1257 rate = bound(NET_MINRATE, client->rate, maxrate);
1258 rate = (int)(rate * sys_ticrate.value);
1259 maxsize = bound(50, rate, 1400);
1263 // while downloading, limit entity updates to half the packet
1264 // (any leftover space will be used for downloading)
1265 if (host_client->download_file)
1268 msg.data = sv_sendclientdatagram_buf;
1269 msg.maxsize = maxsize;
1272 if (host_client->spawned)
1274 MSG_WriteByte (&msg, svc_time);
1275 MSG_WriteFloat (&msg, sv.time);
1277 // add the client specific data to the datagram
1278 SV_WriteClientdataToMessage (client, client->edict, &msg, stats);
1279 VM_SV_WriteAutoSentStats (client, client->edict, &msg, stats);
1280 SV_WriteEntitiesToClient (client, client->edict, &msg, stats);
1282 // expand packet size to allow effects to go over the rate limit
1283 // (dropping them is FAR too ugly)
1284 msg.maxsize = maxsize2;
1286 // copy the server datagram if there is space
1287 // FIXME: put in delayed queue of effects to send
1288 if (sv.datagram.cursize > 0 && msg.cursize + sv.datagram.cursize <= msg.maxsize)
1289 SZ_Write (&msg, sv.datagram.data, sv.datagram.cursize);
1291 else if (realtime > client->keepalivetime)
1293 // the player isn't totally in the game yet
1294 // send small keepalive messages if too much time has passed
1295 msg.maxsize = maxsize2;
1296 client->keepalivetime = realtime + 5;
1297 MSG_WriteChar (&msg, svc_nop);
1300 msg.maxsize = maxsize2;
1302 // if a download is active, see if there is room to fit some download data
1304 downloadsize = maxsize * 2 - msg.cursize - 7;
1305 if (host_client->download_file && host_client->download_started && downloadsize > 0)
1307 fs_offset_t downloadstart;
1308 unsigned char data[1400];
1309 downloadstart = FS_Tell(host_client->download_file);
1310 downloadsize = min(downloadsize, (int)sizeof(data));
1311 downloadsize = FS_Read(host_client->download_file, data, downloadsize);
1312 // note this sends empty messages if at the end of the file, which is
1313 // necessary to keep the packet loss logic working
1314 // (the last blocks may be lost and need to be re-sent, and that will
1315 // only occur if the client acks the empty end messages, revealing
1316 // a gap in the download progress, causing the last blocks to be
1318 MSG_WriteChar (&msg, svc_downloaddata);
1319 MSG_WriteLong (&msg, downloadstart);
1320 MSG_WriteShort (&msg, downloadsize);
1321 if (downloadsize > 0)
1322 SZ_Write (&msg, data, downloadsize);
1325 // send the datagram
1326 NetConn_SendUnreliableMessage (client->netconnection, &msg, sv.protocol);
1330 =======================
1331 SV_UpdateToReliableMessages
1332 =======================
1334 void SV_UpdateToReliableMessages (void)
1343 // check for changes to be sent over the reliable streams
1344 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1346 // update the host_client fields we care about according to the entity fields
1347 host_client->edict = PRVM_EDICT_NUM(i+1);
1350 name = PRVM_GetString(host_client->edict->fields.server->netname);
1353 // always point the string back at host_client->name to keep it safe
1354 strlcpy (host_client->name, name, sizeof (host_client->name));
1355 host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
1356 if (strcmp(host_client->old_name, host_client->name))
1358 if (host_client->spawned)
1359 SV_BroadcastPrintf("%s changed name to %s\n", host_client->old_name, host_client->name);
1360 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1361 // send notification to all clients
1362 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1363 MSG_WriteByte (&sv.reliable_datagram, i);
1364 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1367 // DP_SV_CLIENTCOLORS
1368 // this is always found (since it's added by the progs loader)
1369 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
1370 host_client->colors = (int)val->_float;
1371 if (host_client->old_colors != host_client->colors)
1373 host_client->old_colors = host_client->colors;
1374 // send notification to all clients
1375 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1376 MSG_WriteByte (&sv.reliable_datagram, i);
1377 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1380 // NEXUIZ_PLAYERMODEL
1381 if( prog->fieldoffsets.playermodel >= 0 ) {
1382 model = PRVM_GetString(PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string);
1385 // always point the string back at host_client->name to keep it safe
1386 strlcpy (host_client->playermodel, model, sizeof (host_client->playermodel));
1387 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
1390 // NEXUIZ_PLAYERSKIN
1391 if( prog->fieldoffsets.playerskin >= 0 ) {
1392 skin = PRVM_GetString(PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string);
1395 // always point the string back at host_client->name to keep it safe
1396 strlcpy (host_client->playerskin, skin, sizeof (host_client->playerskin));
1397 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
1401 host_client->frags = (int)host_client->edict->fields.server->frags;
1402 if (host_client->old_frags != host_client->frags)
1404 host_client->old_frags = host_client->frags;
1405 // send notification to all clients
1406 MSG_WriteByte (&sv.reliable_datagram, svc_updatefrags);
1407 MSG_WriteByte (&sv.reliable_datagram, i);
1408 MSG_WriteShort (&sv.reliable_datagram, host_client->frags);
1412 for (j = 0, client = svs.clients;j < svs.maxclients;j++, client++)
1413 if (client->netconnection)
1414 SZ_Write (&client->netconnection->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize);
1416 SZ_Clear (&sv.reliable_datagram);
1421 =======================
1422 SV_SendClientMessages
1423 =======================
1425 void SV_SendClientMessages (void)
1427 int i, prepared = false;
1429 if (sv.protocol == PROTOCOL_QUAKEWORLD)
1430 Sys_Error("SV_SendClientMessages: no quakeworld support\n");
1432 // update frags, names, etc
1433 SV_UpdateToReliableMessages();
1435 // build individual updates
1436 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1438 if (!host_client->active)
1440 if (!host_client->netconnection)
1443 if (host_client->netconnection->message.overflowed)
1445 SV_DropClient (true); // if the message couldn't send, kick off
1452 // only prepare entities once per frame
1453 SV_PrepareEntitiesForSending();
1455 SV_SendClientDatagram (host_client);
1458 // clear muzzle flashes
1462 void SV_StartDownload_f(void)
1464 if (host_client->download_file)
1465 host_client->download_started = true;
1468 void SV_Download_f(void)
1470 const char *whichpack, *whichpack2, *extension;
1472 if (Cmd_Argc() != 2)
1474 SV_ClientPrintf("usage: download <filename>\n");
1478 if (FS_CheckNastyPath(Cmd_Argv(1), false))
1480 SV_ClientPrintf("Download rejected: nasty filename \"%s\"\n", Cmd_Argv(1));
1484 if (host_client->download_file)
1486 // at this point we'll assume the previous download should be aborted
1487 Con_DPrintf("Download of %s aborted by %s starting a new download\n", host_client->download_name, host_client->name);
1488 Host_ClientCommands("\nstopdownload\n");
1490 // close the file and reset variables
1491 FS_Close(host_client->download_file);
1492 host_client->download_file = NULL;
1493 host_client->download_name[0] = 0;
1494 host_client->download_expectedposition = 0;
1495 host_client->download_started = false;
1498 if (!sv_allowdownloads.integer)
1500 SV_ClientPrintf("Downloads are disabled on this server\n");
1501 Host_ClientCommands("\nstopdownload\n");
1505 strlcpy(host_client->download_name, Cmd_Argv(1), sizeof(host_client->download_name));
1506 extension = FS_FileExtension(host_client->download_name);
1508 // host_client is asking to download a specified file
1509 if (developer.integer >= 100)
1510 Con_Printf("Download request for %s by %s\n", host_client->download_name, host_client->name);
1512 if (!FS_FileExists(host_client->download_name))
1514 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);
1515 Host_ClientCommands("\nstopdownload\n");
1519 // check if the user is trying to download part of registered Quake(r)
1520 whichpack = FS_WhichPack(host_client->download_name);
1521 whichpack2 = FS_WhichPack("gfx/pop.lmp");
1522 if ((whichpack && whichpack2 && !strcasecmp(whichpack, whichpack2)) || FS_IsRegisteredQuakePack(host_client->download_name))
1524 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);
1525 Host_ClientCommands("\nstopdownload\n");
1529 // check if the server has forbidden archive downloads entirely
1530 if (!sv_allowdownloads_inarchive.integer)
1532 whichpack = FS_WhichPack(host_client->download_name);
1535 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);
1536 Host_ClientCommands("\nstopdownload\n");
1541 if (!sv_allowdownloads_config.integer)
1543 if (!strcasecmp(extension, "cfg"))
1545 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);
1546 Host_ClientCommands("\nstopdownload\n");
1551 if (!sv_allowdownloads_dlcache.integer)
1553 if (!strncasecmp(host_client->download_name, "dlcache/", 8))
1555 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);
1556 Host_ClientCommands("\nstopdownload\n");
1561 if (!sv_allowdownloads_archive.integer)
1563 if (!strcasecmp(extension, "pak") || !strcasecmp(extension, "pk3"))
1565 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);
1566 Host_ClientCommands("\nstopdownload\n");
1571 host_client->download_file = FS_Open(host_client->download_name, "rb", true, false);
1572 if (!host_client->download_file)
1574 SV_ClientPrintf("Download rejected: server could not open the file \"%s\"\n", host_client->download_name);
1575 Host_ClientCommands("\nstopdownload\n");
1579 if (FS_FileSize(host_client->download_file) > 1<<30)
1581 SV_ClientPrintf("Download rejected: file \"%s\" is very large\n", host_client->download_name);
1582 Host_ClientCommands("\nstopdownload\n");
1583 FS_Close(host_client->download_file);
1584 host_client->download_file = NULL;
1588 Con_DPrintf("Downloading %s to %s\n", host_client->download_name, host_client->name);
1590 Host_ClientCommands("\ncl_downloadbegin %i %s\n", (int)FS_FileSize(host_client->download_file), host_client->download_name);
1592 host_client->download_expectedposition = 0;
1593 host_client->download_started = false;
1595 // the rest of the download process is handled in SV_SendClientDatagram
1596 // and other code dealing with svc_downloaddata and clc_ackdownloaddata
1598 // no svc_downloaddata messages will be sent until sv_startdownload is
1599 // sent by the client
1603 ==============================================================================
1607 ==============================================================================
1616 int SV_ModelIndex(const char *s, int precachemode)
1618 int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) ? 256 : MAX_MODELS);
1619 char filename[MAX_QPATH];
1623 //if (precachemode == 2)
1625 strlcpy(filename, s, sizeof(filename));
1626 for (i = 2;i < limit;i++)
1628 if (!sv.model_precache[i][0])
1632 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))
1634 Con_Printf("SV_ModelIndex(\"%s\"): precache_model can only be done in spawn functions\n", filename);
1637 if (precachemode == 1)
1638 Con_Printf("SV_ModelIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename);
1639 strlcpy(sv.model_precache[i], filename, sizeof(sv.model_precache[i]));
1640 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, false);
1641 if (sv.state != ss_loading)
1643 MSG_WriteByte(&sv.reliable_datagram, svc_precache);
1644 MSG_WriteShort(&sv.reliable_datagram, i);
1645 MSG_WriteString(&sv.reliable_datagram, filename);
1649 Con_Printf("SV_ModelIndex(\"%s\"): not precached\n", filename);
1652 if (!strcmp(sv.model_precache[i], filename))
1655 Con_Printf("SV_ModelIndex(\"%s\"): i (%i) == MAX_MODELS (%i)\n", filename, i, MAX_MODELS);
1665 int SV_SoundIndex(const char *s, int precachemode)
1667 int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) ? 256 : MAX_SOUNDS);
1668 char filename[MAX_QPATH];
1672 //if (precachemode == 2)
1674 strlcpy(filename, s, sizeof(filename));
1675 for (i = 1;i < limit;i++)
1677 if (!sv.sound_precache[i][0])
1681 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))
1683 Con_Printf("SV_SoundIndex(\"%s\"): precache_sound can only be done in spawn functions\n", filename);
1686 if (precachemode == 1)
1687 Con_Printf("SV_SoundIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename);
1688 strlcpy(sv.sound_precache[i], filename, sizeof(sv.sound_precache[i]));
1689 if (sv.state != ss_loading)
1691 MSG_WriteByte(&sv.reliable_datagram, svc_precache);
1692 MSG_WriteShort(&sv.reliable_datagram, i + 32768);
1693 MSG_WriteString(&sv.reliable_datagram, filename);
1697 Con_Printf("SV_SoundIndex(\"%s\"): not precached\n", filename);
1700 if (!strcmp(sv.sound_precache[i], filename))
1703 Con_Printf("SV_SoundIndex(\"%s\"): i (%i) == MAX_SOUNDS (%i)\n", filename, i, MAX_SOUNDS);
1707 // MUST match effectnameindex_t in client.h
1708 static const char *standardeffectnames[EFFECT_TOTAL] =
1716 "TE_SUPERSPIKEQUAD",
1732 "TE_TEI_BIGEXPLOSION",
1750 SV_ParticleEffectIndex
1754 int SV_ParticleEffectIndex(const char *name)
1756 int i, argc, linenumber, effectnameindex;
1757 fs_offset_t filesize;
1758 unsigned char *filedata;
1759 const char *text, *textstart, *textend;
1760 char argv[16][1024];
1761 if (!sv.particleeffectnamesloaded)
1763 sv.particleeffectnamesloaded = true;
1764 memset(sv.particleeffectname, 0, sizeof(sv.particleeffectname));
1765 for (i = 0;i < EFFECT_TOTAL;i++)
1766 strlcpy(sv.particleeffectname[i], standardeffectnames[i], sizeof(sv.particleeffectname[i]));
1767 filedata = FS_LoadFile("effectinfo.txt", tempmempool, true, &filesize);
1770 textstart = (const char *)filedata;
1771 textend = (const char *)filedata + filesize;
1773 for (linenumber = 1;;linenumber++)
1778 if (!COM_ParseToken(&text, true) || !strcmp(com_token, "\n"))
1782 strlcpy(argv[argc], com_token, sizeof(argv[argc]));
1786 if (com_token[0] == 0)
1787 break; // if the loop exited and it's not a \n, it's EOF
1790 if (!strcmp(argv[0], "effect"))
1794 for (effectnameindex = 1;effectnameindex < SV_MAX_PARTICLEEFFECTNAME;effectnameindex++)
1796 if (sv.particleeffectname[effectnameindex][0])
1798 if (!strcmp(sv.particleeffectname[effectnameindex], argv[1]))
1803 strlcpy(sv.particleeffectname[effectnameindex], argv[1], sizeof(sv.particleeffectname[effectnameindex]));
1807 // if we run out of names, abort
1808 if (effectnameindex == SV_MAX_PARTICLEEFFECTNAME)
1810 Con_Printf("effectinfo.txt:%i: too many effects!\n", linenumber);
1819 // search for the name
1820 for (effectnameindex = 1;effectnameindex < SV_MAX_PARTICLEEFFECTNAME && sv.particleeffectname[effectnameindex][0];effectnameindex++)
1821 if (!strcmp(sv.particleeffectname[effectnameindex], name))
1822 return effectnameindex;
1823 // return 0 if we couldn't find it
1833 void SV_CreateBaseline (void)
1835 int i, entnum, large;
1836 prvm_edict_t *svent;
1838 // LordHavoc: clear *all* states (note just active ones)
1839 for (entnum = 0;entnum < prog->max_edicts;entnum++)
1841 // get the current server version
1842 svent = PRVM_EDICT_NUM(entnum);
1844 // LordHavoc: always clear state values, whether the entity is in use or not
1845 svent->priv.server->baseline = defaultstate;
1847 if (svent->priv.server->free)
1849 if (entnum > svs.maxclients && !svent->fields.server->modelindex)
1852 // create entity baseline
1853 VectorCopy (svent->fields.server->origin, svent->priv.server->baseline.origin);
1854 VectorCopy (svent->fields.server->angles, svent->priv.server->baseline.angles);
1855 svent->priv.server->baseline.frame = (int)svent->fields.server->frame;
1856 svent->priv.server->baseline.skin = (int)svent->fields.server->skin;
1857 if (entnum > 0 && entnum <= svs.maxclients)
1859 svent->priv.server->baseline.colormap = entnum;
1860 svent->priv.server->baseline.modelindex = SV_ModelIndex("progs/player.mdl", 1);
1864 svent->priv.server->baseline.colormap = 0;
1865 svent->priv.server->baseline.modelindex = (int)svent->fields.server->modelindex;
1869 if (svent->priv.server->baseline.modelindex & 0xFF00 || svent->priv.server->baseline.frame & 0xFF00)
1872 // add to the message
1874 MSG_WriteByte (&sv.signon, svc_spawnbaseline2);
1876 MSG_WriteByte (&sv.signon, svc_spawnbaseline);
1877 MSG_WriteShort (&sv.signon, entnum);
1881 MSG_WriteShort (&sv.signon, svent->priv.server->baseline.modelindex);
1882 MSG_WriteShort (&sv.signon, svent->priv.server->baseline.frame);
1886 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.modelindex);
1887 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.frame);
1889 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.colormap);
1890 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.skin);
1891 for (i=0 ; i<3 ; i++)
1893 MSG_WriteCoord(&sv.signon, svent->priv.server->baseline.origin[i], sv.protocol);
1894 MSG_WriteAngle(&sv.signon, svent->priv.server->baseline.angles[i], sv.protocol);
1904 Grabs the current state of each client for saving across the
1905 transition to another level
1908 void SV_SaveSpawnparms (void)
1912 svs.serverflags = (int)prog->globals.server->serverflags;
1914 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1916 if (!host_client->active)
1919 // call the progs to get default spawn parms for the new client
1920 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1921 PRVM_ExecuteProgram (prog->globals.server->SetChangeParms, "QC function SetChangeParms is missing");
1922 for (j=0 ; j<NUM_SPAWN_PARMS ; j++)
1923 host_client->spawn_parms[j] = (&prog->globals.server->parm1)[j];
1927 void SV_IncreaseEdicts(void)
1931 int oldmax_edicts = prog->max_edicts;
1932 void *oldedictsengineprivate = prog->edictprivate;
1933 void *oldedictsfields = prog->edictsfields;
1934 void *oldmoved_edicts = sv.moved_edicts;
1936 if (prog->max_edicts >= MAX_EDICTS)
1939 // links don't survive the transition, so unlink everything
1940 for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
1942 if (!ent->priv.server->free)
1943 SV_UnlinkEdict(prog->edicts + i);
1944 memset(&ent->priv.server->areagrid, 0, sizeof(ent->priv.server->areagrid));
1946 World_Clear(&sv.world);
1948 prog->max_edicts = min(prog->max_edicts + 256, MAX_EDICTS);
1949 prog->edictprivate = PR_Alloc(prog->max_edicts * sizeof(edict_engineprivate_t));
1950 prog->edictsfields = PR_Alloc(prog->max_edicts * prog->edict_size);
1951 sv.moved_edicts = PR_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
1953 memcpy(prog->edictprivate, oldedictsengineprivate, oldmax_edicts * sizeof(edict_engineprivate_t));
1954 memcpy(prog->edictsfields, oldedictsfields, oldmax_edicts * prog->edict_size);
1956 for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
1958 ent->priv.vp = (unsigned char*) prog->edictprivate + i * prog->edictprivate_size;
1959 ent->fields.server = (void *)((unsigned char *)prog->edictsfields + i * prog->edict_size);
1960 // link every entity except world
1961 if (!ent->priv.server->free)
1962 SV_LinkEdict(ent, false);
1965 PR_Free(oldedictsengineprivate);
1966 PR_Free(oldedictsfields);
1967 PR_Free(oldmoved_edicts);
1974 This is called at the start of each level
1977 extern float scr_centertime_off;
1979 void SV_SpawnServer (const char *server)
1984 model_t *worldmodel;
1985 char modelname[sizeof(sv.modelname)];
1987 Con_DPrintf("SpawnServer: %s\n", server);
1989 if (cls.state != ca_dedicated)
1990 SCR_BeginLoadingPlaque();
1992 dpsnprintf (modelname, sizeof(modelname), "maps/%s.bsp", server);
1993 worldmodel = Mod_ForName(modelname, false, true, true);
1994 if (!worldmodel || !worldmodel->TraceBox)
1996 Con_Printf("Couldn't load map %s\n", modelname);
2000 // let's not have any servers with no name
2001 if (hostname.string[0] == 0)
2002 Cvar_Set ("hostname", "UNNAMED");
2003 scr_centertime_off = 0;
2005 svs.changelevel_issued = false; // now safe to issue another
2007 // make the map a required file for clients
2008 Curl_ClearRequirements();
2009 Curl_RequireFile(modelname);
2012 // tell all connected clients that we are going to a new level
2017 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
2019 if (client->netconnection)
2021 MSG_WriteByte(&client->netconnection->message, svc_stufftext);
2022 MSG_WriteString(&client->netconnection->message, "reconnect\n");
2029 NetConn_OpenServerPorts(true);
2033 // make cvars consistant
2036 Cvar_SetValue ("deathmatch", 0);
2037 // LordHavoc: it can be useful to have skills outside the range 0-3...
2038 //current_skill = bound(0, (int)(skill.value + 0.5), 3);
2039 //Cvar_SetValue ("skill", (float)current_skill);
2040 current_skill = (int)(skill.value + 0.5);
2043 // set up the new server
2045 memset (&sv, 0, sizeof(sv));
2046 // if running a local client, make sure it doesn't try to access the last
2047 // level's data which is no longer valiud
2050 if(*sv_random_seed.string)
2052 srand(sv_random_seed.integer);
2053 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);
2060 strlcpy (sv.name, server, sizeof (sv.name));
2062 sv.protocol = Protocol_EnumForName(sv_protocolname.string);
2063 if (sv.protocol == PROTOCOL_UNKNOWN)
2066 Protocol_Names(buffer, sizeof(buffer));
2067 Con_Printf("Unknown sv_protocolname \"%s\", valid values are:\n%s\n", sv_protocolname.string, buffer);
2068 sv.protocol = PROTOCOL_QUAKE;
2073 // load progs to get entity field count
2074 //PR_LoadProgs ( sv_progs.string );
2076 // allocate server memory
2077 /*// start out with just enough room for clients and a reasonable estimate of entities
2078 prog->max_edicts = max(svs.maxclients + 1, 512);
2079 prog->max_edicts = min(prog->max_edicts, MAX_EDICTS);
2081 // prvm_edict_t structures (hidden from progs)
2082 prog->edicts = PR_Alloc(MAX_EDICTS * sizeof(prvm_edict_t));
2083 // engine private structures (hidden from progs)
2084 prog->edictprivate = PR_Alloc(prog->max_edicts * sizeof(edict_engineprivate_t));
2085 // progs fields, often accessed by server
2086 prog->edictsfields = PR_Alloc(prog->max_edicts * prog->edict_size);*/
2087 // used by PushMove to move back pushed entities
2088 sv.moved_edicts = (prvm_edict_t **)PRVM_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
2089 /*for (i = 0;i < prog->max_edicts;i++)
2091 ent = prog->edicts + i;
2092 ent->priv.vp = (unsigned char*) prog->edictprivate + i * prog->edictprivate_size;
2093 ent->fields.server = (void *)((unsigned char *)prog->edictsfields + i * prog->edict_size);
2096 // reset client csqc entity versions right away.
2097 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2098 EntityFrameCSQC_InitClientVersions(i, true);
2100 sv.datagram.maxsize = sizeof(sv.datagram_buf);
2101 sv.datagram.cursize = 0;
2102 sv.datagram.data = sv.datagram_buf;
2104 sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf);
2105 sv.reliable_datagram.cursize = 0;
2106 sv.reliable_datagram.data = sv.reliable_datagram_buf;
2108 sv.signon.maxsize = sizeof(sv.signon_buf);
2109 sv.signon.cursize = 0;
2110 sv.signon.data = sv.signon_buf;
2112 // leave slots at start for clients only
2113 //prog->num_edicts = svs.maxclients+1;
2115 sv.state = ss_loading;
2116 prog->allowworldwrites = true;
2119 prog->globals.server->time = sv.time = 1.0;
2122 worldmodel->used = true;
2124 strlcpy (sv.name, server, sizeof (sv.name));
2125 strlcpy(sv.modelname, modelname, sizeof(sv.modelname));
2126 sv.worldmodel = worldmodel;
2127 sv.models[1] = sv.worldmodel;
2130 // clear world interaction links
2132 VectorCopy(sv.worldmodel->normalmins, sv.world.areagrid_mins);
2133 VectorCopy(sv.worldmodel->normalmaxs, sv.world.areagrid_maxs);
2134 World_Clear(&sv.world);
2136 strlcpy(sv.sound_precache[0], "", sizeof(sv.sound_precache[0]));
2138 strlcpy(sv.model_precache[0], "", sizeof(sv.model_precache[0]));
2139 strlcpy(sv.model_precache[1], sv.modelname, sizeof(sv.model_precache[1]));
2140 for (i = 1;i < sv.worldmodel->brush.numsubmodels;i++)
2142 dpsnprintf(sv.model_precache[i+1], sizeof(sv.model_precache[i+1]), "*%i", i);
2143 sv.models[i+1] = Mod_ForName (sv.model_precache[i+1], false, false, false);
2147 // load the rest of the entities
2149 // AK possible hack since num_edicts is still 0
2150 ent = PRVM_EDICT_NUM(0);
2151 memset (ent->fields.server, 0, prog->progs->entityfields * 4);
2152 ent->priv.server->free = false;
2153 ent->fields.server->model = PRVM_SetEngineString(sv.modelname);
2154 ent->fields.server->modelindex = 1; // world model
2155 ent->fields.server->solid = SOLID_BSP;
2156 ent->fields.server->movetype = MOVETYPE_PUSH;
2157 VectorCopy(sv.worldmodel->normalmins, ent->fields.server->mins);
2158 VectorCopy(sv.worldmodel->normalmaxs, ent->fields.server->maxs);
2159 VectorCopy(sv.worldmodel->normalmins, ent->fields.server->absmin);
2160 VectorCopy(sv.worldmodel->normalmaxs, ent->fields.server->absmax);
2163 prog->globals.server->coop = coop.integer;
2165 prog->globals.server->deathmatch = deathmatch.integer;
2167 prog->globals.server->mapname = PRVM_SetEngineString(sv.name);
2169 // serverflags are for cross level information (sigils)
2170 prog->globals.server->serverflags = svs.serverflags;
2172 // we need to reset the spawned flag on all connected clients here so that
2173 // their thinks don't run during startup (before PutClientInServer)
2174 // we also need to set up the client entities now
2175 // and we need to set the ->edict pointers to point into the progs edicts
2176 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2178 host_client->spawned = false;
2179 host_client->edict = PRVM_EDICT_NUM(i + 1);
2180 PRVM_ED_ClearEdict(host_client->edict);
2183 // load replacement entity file if found
2184 if (sv_entpatch.integer && (entities = (char *)FS_LoadFile(va("maps/%s.ent", sv.name), tempmempool, true, NULL)))
2186 Con_Printf("Loaded maps/%s.ent\n", sv.name);
2187 PRVM_ED_LoadFromFile (entities);
2191 PRVM_ED_LoadFromFile (sv.worldmodel->brush.entities);
2194 // LordHavoc: clear world angles (to fix e3m3.bsp)
2195 VectorClear(prog->edicts->fields.server->angles);
2197 // all setup is completed, any further precache statements are errors
2198 sv.state = ss_active;
2199 prog->allowworldwrites = false;
2201 // run two frames to allow everything to settle
2202 for (i = 0;i < 2;i++)
2210 // create a baseline for more efficient communications
2211 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
2212 SV_CreateBaseline ();
2214 // send serverinfo to all connected clients, and set up botclients coming back from a level change
2215 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2217 if (!host_client->active)
2219 if (host_client->netconnection)
2220 SV_SendServerinfo(host_client);
2224 // if client is a botclient coming from a level change, we need to
2225 // set up client info that normally requires networking
2227 // copy spawn parms out of the client_t
2228 for (j=0 ; j< NUM_SPAWN_PARMS ; j++)
2229 (&prog->globals.server->parm1)[j] = host_client->spawn_parms[j];
2231 // call the spawn function
2232 host_client->clientconnectcalled = true;
2233 prog->globals.server->time = sv.time;
2234 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
2235 PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
2236 PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
2237 host_client->spawned = true;
2241 Con_DPrint("Server spawned.\n");
2242 NetConn_Heartbeat (2);
2247 /////////////////////////////////////////////////////
2250 void SV_VM_CB_BeginIncreaseEdicts(void)
2255 PRVM_Free( sv.moved_edicts );
2256 sv.moved_edicts = (prvm_edict_t **)PRVM_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
2258 // links don't survive the transition, so unlink everything
2259 for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
2261 if (!ent->priv.server->free)
2262 World_UnlinkEdict(prog->edicts + i);
2263 memset(&ent->priv.server->areagrid, 0, sizeof(ent->priv.server->areagrid));
2265 World_Clear(&sv.world);
2268 void SV_VM_CB_EndIncreaseEdicts(void)
2273 // link every entity except world
2274 for (i = 1, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
2275 if (!ent->priv.server->free)
2276 SV_LinkEdict(ent, false);
2279 void SV_VM_CB_InitEdict(prvm_edict_t *e)
2281 // LordHavoc: for consistency set these here
2282 int num = PRVM_NUM_FOR_EDICT(e) - 1;
2284 e->priv.server->move = false; // don't move on first frame
2286 if (num >= 0 && num < svs.maxclients)
2289 // set colormap and team on newly created player entity
2290 e->fields.server->colormap = num + 1;
2291 e->fields.server->team = (svs.clients[num].colors & 15) + 1;
2292 // set netname/clientcolors back to client values so that
2293 // DP_SV_CLIENTNAME and DP_SV_CLIENTCOLORS will not immediately
2295 e->fields.server->netname = PRVM_SetEngineString(svs.clients[num].name);
2296 if ((val = PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.clientcolors)))
2297 val->_float = svs.clients[num].colors;
2298 // NEXUIZ_PLAYERMODEL and NEXUIZ_PLAYERSKIN
2299 if( prog->fieldoffsets.playermodel >= 0 )
2300 PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(svs.clients[num].playermodel);
2301 if( prog->fieldoffsets.playerskin >= 0 )
2302 PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(svs.clients[num].playerskin);
2306 void SV_VM_CB_FreeEdict(prvm_edict_t *ed)
2308 World_UnlinkEdict(ed); // unlink from world bsp
2310 ed->fields.server->model = 0;
2311 ed->fields.server->takedamage = 0;
2312 ed->fields.server->modelindex = 0;
2313 ed->fields.server->colormap = 0;
2314 ed->fields.server->skin = 0;
2315 ed->fields.server->frame = 0;
2316 VectorClear(ed->fields.server->origin);
2317 VectorClear(ed->fields.server->angles);
2318 ed->fields.server->nextthink = -1;
2319 ed->fields.server->solid = 0;
2322 void SV_VM_CB_CountEdicts(void)
2326 int active, models, solid, step;
2328 active = models = solid = step = 0;
2329 for (i=0 ; i<prog->num_edicts ; i++)
2331 ent = PRVM_EDICT_NUM(i);
2332 if (ent->priv.server->free)
2335 if (ent->fields.server->solid)
2337 if (ent->fields.server->model)
2339 if (ent->fields.server->movetype == MOVETYPE_STEP)
2343 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
2344 Con_Printf("active :%3i\n", active);
2345 Con_Printf("view :%3i\n", models);
2346 Con_Printf("touch :%3i\n", solid);
2347 Con_Printf("step :%3i\n", step);
2350 qboolean SV_VM_CB_LoadEdict(prvm_edict_t *ent)
2352 // remove things from different skill levels or deathmatch
2353 if (gamemode != GAME_TRANSFUSION) //Transfusion does this in QC
2355 if (deathmatch.integer)
2357 if (((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_DEATHMATCH))
2362 else if ((current_skill <= 0 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_EASY ))
2363 || (current_skill == 1 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_MEDIUM))
2364 || (current_skill >= 2 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_HARD )))
2372 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)"};
2373 cvar_t nomonsters = {0, "nomonsters", "0", "unused cvar in quake, can be used by mods"};
2374 cvar_t gamecfg = {0, "gamecfg", "0", "unused cvar in quake, can be used by mods"};
2375 cvar_t scratch1 = {0, "scratch1", "0", "unused cvar in quake, can be used by mods"};
2376 cvar_t scratch2 = {0,"scratch2", "0", "unused cvar in quake, can be used by mods"};
2377 cvar_t scratch3 = {0, "scratch3", "0", "unused cvar in quake, can be used by mods"};
2378 cvar_t scratch4 = {0, "scratch4", "0", "unused cvar in quake, can be used by mods"};
2379 cvar_t savedgamecfg = {CVAR_SAVE, "savedgamecfg", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2380 cvar_t saved1 = {CVAR_SAVE, "saved1", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2381 cvar_t saved2 = {CVAR_SAVE, "saved2", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2382 cvar_t saved3 = {CVAR_SAVE, "saved3", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2383 cvar_t saved4 = {CVAR_SAVE, "saved4", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2384 cvar_t nehx00 = {0, "nehx00", "0", "nehahra data storage cvar (used in singleplayer)"};
2385 cvar_t nehx01 = {0, "nehx01", "0", "nehahra data storage cvar (used in singleplayer)"};
2386 cvar_t nehx02 = {0, "nehx02", "0", "nehahra data storage cvar (used in singleplayer)"};
2387 cvar_t nehx03 = {0, "nehx03", "0", "nehahra data storage cvar (used in singleplayer)"};
2388 cvar_t nehx04 = {0, "nehx04", "0", "nehahra data storage cvar (used in singleplayer)"};
2389 cvar_t nehx05 = {0, "nehx05", "0", "nehahra data storage cvar (used in singleplayer)"};
2390 cvar_t nehx06 = {0, "nehx06", "0", "nehahra data storage cvar (used in singleplayer)"};
2391 cvar_t nehx07 = {0, "nehx07", "0", "nehahra data storage cvar (used in singleplayer)"};
2392 cvar_t nehx08 = {0, "nehx08", "0", "nehahra data storage cvar (used in singleplayer)"};
2393 cvar_t nehx09 = {0, "nehx09", "0", "nehahra data storage cvar (used in singleplayer)"};
2394 cvar_t nehx10 = {0, "nehx10", "0", "nehahra data storage cvar (used in singleplayer)"};
2395 cvar_t nehx11 = {0, "nehx11", "0", "nehahra data storage cvar (used in singleplayer)"};
2396 cvar_t nehx12 = {0, "nehx12", "0", "nehahra data storage cvar (used in singleplayer)"};
2397 cvar_t nehx13 = {0, "nehx13", "0", "nehahra data storage cvar (used in singleplayer)"};
2398 cvar_t nehx14 = {0, "nehx14", "0", "nehahra data storage cvar (used in singleplayer)"};
2399 cvar_t nehx15 = {0, "nehx15", "0", "nehahra data storage cvar (used in singleplayer)"};
2400 cvar_t nehx16 = {0, "nehx16", "0", "nehahra data storage cvar (used in singleplayer)"};
2401 cvar_t nehx17 = {0, "nehx17", "0", "nehahra data storage cvar (used in singleplayer)"};
2402 cvar_t nehx18 = {0, "nehx18", "0", "nehahra data storage cvar (used in singleplayer)"};
2403 cvar_t nehx19 = {0, "nehx19", "0", "nehahra data storage cvar (used in singleplayer)"};
2404 cvar_t cutscene = {0, "cutscene", "1", "enables cutscenes in nehahra, can be used by other mods"};
2406 void SV_VM_Init(void)
2408 Cvar_RegisterVariable (&pr_checkextension);
2409 Cvar_RegisterVariable (&nomonsters);
2410 Cvar_RegisterVariable (&gamecfg);
2411 Cvar_RegisterVariable (&scratch1);
2412 Cvar_RegisterVariable (&scratch2);
2413 Cvar_RegisterVariable (&scratch3);
2414 Cvar_RegisterVariable (&scratch4);
2415 Cvar_RegisterVariable (&savedgamecfg);
2416 Cvar_RegisterVariable (&saved1);
2417 Cvar_RegisterVariable (&saved2);
2418 Cvar_RegisterVariable (&saved3);
2419 Cvar_RegisterVariable (&saved4);
2420 // LordHavoc: Nehahra uses these to pass data around cutscene demos
2421 if (gamemode == GAME_NEHAHRA)
2423 Cvar_RegisterVariable (&nehx00);
2424 Cvar_RegisterVariable (&nehx01);
2425 Cvar_RegisterVariable (&nehx02);
2426 Cvar_RegisterVariable (&nehx03);
2427 Cvar_RegisterVariable (&nehx04);
2428 Cvar_RegisterVariable (&nehx05);
2429 Cvar_RegisterVariable (&nehx06);
2430 Cvar_RegisterVariable (&nehx07);
2431 Cvar_RegisterVariable (&nehx08);
2432 Cvar_RegisterVariable (&nehx09);
2433 Cvar_RegisterVariable (&nehx10);
2434 Cvar_RegisterVariable (&nehx11);
2435 Cvar_RegisterVariable (&nehx12);
2436 Cvar_RegisterVariable (&nehx13);
2437 Cvar_RegisterVariable (&nehx14);
2438 Cvar_RegisterVariable (&nehx15);
2439 Cvar_RegisterVariable (&nehx16);
2440 Cvar_RegisterVariable (&nehx17);
2441 Cvar_RegisterVariable (&nehx18);
2442 Cvar_RegisterVariable (&nehx19);
2444 Cvar_RegisterVariable (&cutscene); // for Nehahra but useful to other mods as well
2447 #define REQFIELDS (sizeof(reqfields) / sizeof(prvm_required_field_t))
2449 prvm_required_field_t reqfields[] =
2451 {ev_entity, "cursor_trace_ent"},
2452 {ev_entity, "drawonlytoclient"},
2453 {ev_entity, "exteriormodeltoclient"},
2454 {ev_entity, "nodrawtoclient"},
2455 {ev_entity, "tag_entity"},
2456 {ev_entity, "viewmodelforclient"},
2457 {ev_float, "alpha"},
2458 {ev_float, "ammo_cells1"},
2459 {ev_float, "ammo_lava_nails"},
2460 {ev_float, "ammo_multi_rockets"},
2461 {ev_float, "ammo_nails1"},
2462 {ev_float, "ammo_plasma"},
2463 {ev_float, "ammo_rockets1"},
2464 {ev_float, "ammo_shells1"},
2465 {ev_float, "button3"},
2466 {ev_float, "button4"},
2467 {ev_float, "button5"},
2468 {ev_float, "button6"},
2469 {ev_float, "button7"},
2470 {ev_float, "button8"},
2471 {ev_float, "button9"},
2472 {ev_float, "button10"},
2473 {ev_float, "button11"},
2474 {ev_float, "button12"},
2475 {ev_float, "button13"},
2476 {ev_float, "button14"},
2477 {ev_float, "button15"},
2478 {ev_float, "button16"},
2479 {ev_float, "buttonchat"},
2480 {ev_float, "buttonuse"},
2481 {ev_float, "clientcolors"},
2482 {ev_float, "cursor_active"},
2483 {ev_float, "fullbright"},
2484 {ev_float, "glow_color"},
2485 {ev_float, "glow_size"},
2486 {ev_float, "glow_trail"},
2487 {ev_float, "gravity"},
2488 {ev_float, "idealpitch"},
2489 {ev_float, "items2"},
2490 {ev_float, "light_lev"},
2491 {ev_float, "pflags"},
2493 {ev_float, "pitch_speed"},
2494 {ev_float, "pmodel"},
2495 {ev_float, "renderamt"}, // HalfLife support
2496 {ev_float, "rendermode"}, // HalfLife support
2497 {ev_float, "scale"},
2498 {ev_float, "style"},
2499 {ev_float, "tag_index"},
2500 {ev_float, "Version"},
2501 {ev_float, "viewzoom"},
2502 {ev_vector, "color"},
2503 {ev_vector, "colormod"},
2504 {ev_vector, "cursor_screen"},
2505 {ev_vector, "cursor_trace_endpos"},
2506 {ev_vector, "cursor_trace_start"},
2507 {ev_vector, "movement"},
2508 {ev_vector, "punchvector"},
2509 {ev_string, "playermodel"},
2510 {ev_string, "playerskin"},
2511 {ev_function, "SendEntity"},
2512 {ev_function, "customizeentityforclient"},
2513 // DRESK - Support for Entity Contents Transition Event
2514 {ev_function, "contentstransition"},
2517 void SV_VM_Setup(void)
2519 extern cvar_t csqc_progname; //[515]: csqc crc check and right csprogs name according to progs.dat
2520 extern cvar_t csqc_progcrc;
2521 extern cvar_t csqc_progsize;
2522 size_t csprogsdatasize;
2524 PRVM_InitProg( PRVM_SERVERPROG );
2526 // allocate the mempools
2527 // TODO: move the magic numbers/constants into #defines [9/13/2006 Black]
2528 prog->progs_mempool = Mem_AllocPool("Server Progs", 0, NULL);
2529 prog->builtins = vm_sv_builtins;
2530 prog->numbuiltins = vm_sv_numbuiltins;
2531 prog->headercrc = PROGHEADER_CRC;
2532 prog->max_edicts = 512;
2533 prog->limit_edicts = MAX_EDICTS;
2534 prog->reserved_edicts = svs.maxclients;
2535 prog->edictprivate_size = sizeof(edict_engineprivate_t);
2536 prog->name = "server";
2537 prog->extensionstring = vm_sv_extensions;
2538 prog->loadintoworld = true;
2540 prog->begin_increase_edicts = SV_VM_CB_BeginIncreaseEdicts;
2541 prog->end_increase_edicts = SV_VM_CB_EndIncreaseEdicts;
2542 prog->init_edict = SV_VM_CB_InitEdict;
2543 prog->free_edict = SV_VM_CB_FreeEdict;
2544 prog->count_edicts = SV_VM_CB_CountEdicts;
2545 prog->load_edict = SV_VM_CB_LoadEdict;
2546 prog->init_cmd = VM_SV_Cmd_Init;
2547 prog->reset_cmd = VM_SV_Cmd_Reset;
2548 prog->error_cmd = Host_Error;
2550 // TODO: add a requiredfuncs list (ask LH if this is necessary at all)
2551 PRVM_LoadProgs( sv_progs.string, 0, NULL, REQFIELDS, reqfields, 0, NULL );
2553 // some mods compiled with scrambling compilers lack certain critical
2554 // global names and field names such as "self" and "time" and "nextthink"
2555 // so we have to set these offsets manually, matching the entvars_t
2556 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, angles);
2557 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, chain);
2558 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, classname);
2559 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, frame);
2560 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, groundentity);
2561 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ideal_yaw);
2562 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, nextthink);
2563 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, think);
2564 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, yaw_speed);
2565 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, self);
2566 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, time);
2567 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_forward);
2568 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_right);
2569 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_up);
2570 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_allsolid);
2571 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_startsolid);
2572 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_fraction);
2573 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inwater);
2574 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inopen);
2575 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_endpos);
2576 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_normal);
2577 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_dist);
2578 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_ent);
2579 // OP_STATE is always supported on server (due to entvars_t)
2580 prog->flag |= PRVM_OP_STATE;
2582 VM_AutoSentStats_Clear();//[515]: csqc
2583 EntityFrameCSQC_ClearVersions();//[515]: csqc
2587 // 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
2588 sv.csqc_progname[0] = 0;
2589 sv.csqc_progcrc = FS_CRCFile(csqc_progname.string, &csprogsdatasize);
2590 sv.csqc_progsize = csprogsdatasize;
2591 if (sv.csqc_progsize > 0)
2593 strlcpy(sv.csqc_progname, csqc_progname.string, sizeof(sv.csqc_progname));
2594 Con_DPrintf("server detected csqc progs file \"%s\" with size %i and crc %i\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc);
2598 void SV_VM_Begin(void)
2601 PRVM_SetProg( PRVM_SERVERPROG );
2603 prog->globals.server->time = (float) sv.time;
2606 void SV_VM_End(void)