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_stats = {0, "sv_cullentities_stats", "0", "displays stats on network entities culled by various methods for each client"};
50 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)"};
52 cvar_t sv_gameplayfix_grenadebouncedownslopes = {0, "sv_gameplayfix_grenadebouncedownslopes", "1", "prevents MOVETYPE_BOUNCE (grenades) from getting stuck when fired down a downward sloping surface"};
53 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"};
54 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)"};
55 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)"};
56 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"};
57 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"};
58 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"};
59 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"};
60 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"};
61 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"};
62 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)"};
64 cvar_t sv_progs = {0, "sv_progs", "progs.dat", "selects which quakec progs.dat file to run" };
66 // TODO: move these cvars here
67 extern cvar_t sv_clmovement_enable;
68 extern cvar_t sv_clmovement_minping;
69 extern cvar_t sv_clmovement_minping_disabletime;
70 extern cvar_t sv_clmovement_waitforinput;
75 mempool_t *sv_mempool = NULL;
77 //============================================================================
79 extern void SV_Phys_Init (void);
80 static void SV_SaveEntFile_f(void);
81 static void SV_StartDownload_f(void);
82 static void SV_Download_f(void);
84 void SV_AreaStats_f(void)
86 World_PrintAreaStats(&sv.world, "server");
96 // init the csqc progs cvars, since they are updated/used by the server code
97 // TODO: fix this since this is a quick hack to make some of [515]'s broken code run ;) [9/13/2006 Black]
98 extern cvar_t csqc_progname; //[515]: csqc crc check and right csprogs name according to progs.dat
99 extern cvar_t csqc_progcrc;
100 extern cvar_t csqc_progsize;
101 Cvar_RegisterVariable (&csqc_progname);
102 Cvar_RegisterVariable (&csqc_progcrc);
103 Cvar_RegisterVariable (&csqc_progsize);
105 Cmd_AddCommand("sv_saveentfile", SV_SaveEntFile_f, "save map entities to .ent file (to allow external editing)");
106 Cmd_AddCommand("sv_areastats", SV_AreaStats_f, "prints statistics on entity culling during collision traces");
107 Cmd_AddCommand_WithClientCommand("sv_startdownload", NULL, SV_StartDownload_f, "begins sending a file to the client (network protocol use only)");
108 Cmd_AddCommand_WithClientCommand("download", NULL, SV_Download_f, "downloads a specified file from the server");
109 Cvar_RegisterVariable (&sv_maxvelocity);
110 Cvar_RegisterVariable (&sv_gravity);
111 Cvar_RegisterVariable (&sv_friction);
112 Cvar_RegisterVariable (&sv_waterfriction);
113 Cvar_RegisterVariable (&sv_edgefriction);
114 Cvar_RegisterVariable (&sv_stopspeed);
115 Cvar_RegisterVariable (&sv_maxspeed);
116 Cvar_RegisterVariable (&sv_maxairspeed);
117 Cvar_RegisterVariable (&sv_accelerate);
118 Cvar_RegisterVariable (&sv_airaccelerate);
119 Cvar_RegisterVariable (&sv_wateraccelerate);
120 Cvar_RegisterVariable (&sv_clmovement_enable);
121 Cvar_RegisterVariable (&sv_clmovement_minping);
122 Cvar_RegisterVariable (&sv_clmovement_minping_disabletime);
123 Cvar_RegisterVariable (&sv_clmovement_waitforinput);
124 Cvar_RegisterVariable (&sv_idealpitchscale);
125 Cvar_RegisterVariable (&sv_aim);
126 Cvar_RegisterVariable (&sv_nostep);
127 Cvar_RegisterVariable (&sv_cullentities_pvs);
128 Cvar_RegisterVariable (&sv_cullentities_trace);
129 Cvar_RegisterVariable (&sv_cullentities_stats);
130 Cvar_RegisterVariable (&sv_entpatch);
131 Cvar_RegisterVariable (&sv_gameplayfix_grenadebouncedownslopes);
132 Cvar_RegisterVariable (&sv_gameplayfix_noairborncorpse);
133 Cvar_RegisterVariable (&sv_gameplayfix_stepdown);
134 Cvar_RegisterVariable (&sv_gameplayfix_stepwhilejumping);
135 Cvar_RegisterVariable (&sv_gameplayfix_swiminbmodels);
136 Cvar_RegisterVariable (&sv_gameplayfix_setmodelrealbox);
137 Cvar_RegisterVariable (&sv_gameplayfix_blowupfallenzombies);
138 Cvar_RegisterVariable (&sv_gameplayfix_findradiusdistancetobox);
139 Cvar_RegisterVariable (&sv_gameplayfix_qwplayerphysics);
140 Cvar_RegisterVariable (&sv_gameplayfix_upwardvelocityclearsongroundflag);
141 Cvar_RegisterVariable (&sv_gameplayfix_droptofloorstartsolid);
142 Cvar_RegisterVariable (&sv_protocolname);
143 Cvar_RegisterVariable (&sv_ratelimitlocalplayer);
144 Cvar_RegisterVariable (&sv_maxrate);
145 Cvar_RegisterVariable (&sv_allowdownloads);
146 Cvar_RegisterVariable (&sv_allowdownloads_inarchive);
147 Cvar_RegisterVariable (&sv_allowdownloads_archive);
148 Cvar_RegisterVariable (&sv_allowdownloads_config);
149 Cvar_RegisterVariable (&sv_allowdownloads_dlcache);
150 Cvar_RegisterVariable (&sv_progs);
155 sv_mempool = Mem_AllocPool("server", 0, NULL);
158 static void SV_SaveEntFile_f(void)
160 char basename[MAX_QPATH];
161 if (!sv.active || !sv.worldmodel)
163 Con_Print("Not running a server\n");
166 FS_StripExtension(sv.worldmodel->name, basename, sizeof(basename));
167 FS_WriteFile(va("%s.ent", basename), sv.worldmodel->brush.entities, (fs_offset_t)strlen(sv.worldmodel->brush.entities));
172 =============================================================================
176 =============================================================================
183 Make sure the event gets sent to all clients
186 void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count)
190 if (sv.datagram.cursize > MAX_PACKETFRAGMENT-18)
192 MSG_WriteByte (&sv.datagram, svc_particle);
193 MSG_WriteCoord (&sv.datagram, org[0], sv.protocol);
194 MSG_WriteCoord (&sv.datagram, org[1], sv.protocol);
195 MSG_WriteCoord (&sv.datagram, org[2], sv.protocol);
196 for (i=0 ; i<3 ; i++)
197 MSG_WriteChar (&sv.datagram, (int)bound(-128, dir[i]*16, 127));
198 MSG_WriteByte (&sv.datagram, count);
199 MSG_WriteByte (&sv.datagram, color);
206 Make sure the event gets sent to all clients
209 void SV_StartEffect (vec3_t org, int modelindex, int startframe, int framecount, int framerate)
211 if (modelindex >= 256 || startframe >= 256)
213 if (sv.datagram.cursize > MAX_PACKETFRAGMENT-19)
215 MSG_WriteByte (&sv.datagram, svc_effect2);
216 MSG_WriteCoord (&sv.datagram, org[0], sv.protocol);
217 MSG_WriteCoord (&sv.datagram, org[1], sv.protocol);
218 MSG_WriteCoord (&sv.datagram, org[2], sv.protocol);
219 MSG_WriteShort (&sv.datagram, modelindex);
220 MSG_WriteShort (&sv.datagram, startframe);
221 MSG_WriteByte (&sv.datagram, framecount);
222 MSG_WriteByte (&sv.datagram, framerate);
226 if (sv.datagram.cursize > MAX_PACKETFRAGMENT-17)
228 MSG_WriteByte (&sv.datagram, svc_effect);
229 MSG_WriteCoord (&sv.datagram, org[0], sv.protocol);
230 MSG_WriteCoord (&sv.datagram, org[1], sv.protocol);
231 MSG_WriteCoord (&sv.datagram, org[2], sv.protocol);
232 MSG_WriteByte (&sv.datagram, modelindex);
233 MSG_WriteByte (&sv.datagram, startframe);
234 MSG_WriteByte (&sv.datagram, framecount);
235 MSG_WriteByte (&sv.datagram, framerate);
243 Each entity can have eight independant sound sources, like voice,
246 Channel 0 is an auto-allocate channel, the others override anything
247 already running on that entity/channel pair.
249 An attenuation of 0 will play full volume everywhere in the level.
250 Larger attenuations will drop off. (max 4 attenuation)
254 void SV_StartSound (prvm_edict_t *entity, int channel, const char *sample, int volume, float attenuation)
256 int sound_num, field_mask, i, ent;
258 if (volume < 0 || volume > 255)
260 Con_Printf ("SV_StartSound: volume = %i\n", volume);
264 if (attenuation < 0 || attenuation > 4)
266 Con_Printf ("SV_StartSound: attenuation = %f\n", attenuation);
270 if (channel < 0 || channel > 7)
272 Con_Printf ("SV_StartSound: channel = %i\n", channel);
276 if (sv.datagram.cursize > MAX_PACKETFRAGMENT-21)
279 // find precache number for sound
280 sound_num = SV_SoundIndex(sample, 1);
284 ent = PRVM_NUM_FOR_EDICT(entity);
287 if (volume != DEFAULT_SOUND_PACKET_VOLUME)
288 field_mask |= SND_VOLUME;
289 if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)
290 field_mask |= SND_ATTENUATION;
292 field_mask |= SND_LARGEENTITY;
293 if (sound_num >= 256 || channel >= 8)
294 field_mask |= SND_LARGESOUND;
296 // directed messages go only to the entity they are targeted on
297 MSG_WriteByte (&sv.datagram, svc_sound);
298 MSG_WriteByte (&sv.datagram, field_mask);
299 if (field_mask & SND_VOLUME)
300 MSG_WriteByte (&sv.datagram, volume);
301 if (field_mask & SND_ATTENUATION)
302 MSG_WriteByte (&sv.datagram, (int)(attenuation*64));
303 if (field_mask & SND_LARGEENTITY)
305 MSG_WriteShort (&sv.datagram, ent);
306 MSG_WriteByte (&sv.datagram, channel);
309 MSG_WriteShort (&sv.datagram, (ent<<3) | channel);
310 if (field_mask & SND_LARGESOUND)
311 MSG_WriteShort (&sv.datagram, sound_num);
313 MSG_WriteByte (&sv.datagram, sound_num);
314 for (i = 0;i < 3;i++)
315 MSG_WriteCoord (&sv.datagram, entity->fields.server->origin[i]+0.5*(entity->fields.server->mins[i]+entity->fields.server->maxs[i]), sv.protocol);
319 ==============================================================================
323 ==============================================================================
330 Sends the first message from the server to a connected client.
331 This will be sent on the initial connection and upon each server load.
334 void SV_SendServerinfo (client_t *client)
339 // we know that this client has a netconnection and thus is not a bot
341 // edicts get reallocated on level changes, so we need to update it here
342 client->edict = PRVM_EDICT_NUM((client - svs.clients) + 1);
344 // clear cached stuff that depends on the level
345 client->weaponmodel[0] = 0;
346 client->weaponmodelindex = 0;
348 // LordHavoc: clear entityframe tracking
349 client->latestframenum = 0;
351 if (client->entitydatabase)
352 EntityFrame_FreeDatabase(client->entitydatabase);
353 if (client->entitydatabase4)
354 EntityFrame4_FreeDatabase(client->entitydatabase4);
355 if (client->entitydatabase5)
356 EntityFrame5_FreeDatabase(client->entitydatabase5);
358 if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE)
360 if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3)
361 client->entitydatabase = EntityFrame_AllocDatabase(sv_mempool);
362 else if (sv.protocol == PROTOCOL_DARKPLACES4)
363 client->entitydatabase4 = EntityFrame4_AllocDatabase(sv_mempool);
365 client->entitydatabase5 = EntityFrame5_AllocDatabase(sv_mempool);
368 SZ_Clear (&client->netconnection->message);
369 MSG_WriteByte (&client->netconnection->message, svc_print);
370 dpsnprintf (message, sizeof (message), "\nServer: %s build %s (progs %i crc)", gamename, buildstring, prog->filecrc);
371 MSG_WriteString (&client->netconnection->message,message);
373 //[515]: init csprogs according to version of svprogs, check the crc, etc.
374 if (sv.csqc_progname[0])
377 Con_DPrintf("sending csqc info to client (\"%s\" with size %i and crc %i)\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc);
378 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
379 MSG_WriteString (&client->netconnection->message, va("csqc_progname %s\n", sv.csqc_progname));
380 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
381 MSG_WriteString (&client->netconnection->message, va("csqc_progsize %i\n", sv.csqc_progsize));
382 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
383 MSG_WriteString (&client->netconnection->message, va("csqc_progcrc %i\n", sv.csqc_progcrc));
384 //[515]: init stufftext string (it is sent before svc_serverinfo)
385 val = PRVM_GETGLOBALFIELDVALUE(prog->globaloffsets.SV_InitCmd);
388 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
389 MSG_WriteString (&client->netconnection->message, va("%s\n", PRVM_GetString(val->string)));
393 if (sv_allowdownloads.integer)
395 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
396 MSG_WriteString (&client->netconnection->message, "cl_serverextension_download 1");
399 MSG_WriteByte (&client->netconnection->message, svc_serverinfo);
400 MSG_WriteLong (&client->netconnection->message, Protocol_NumberForEnum(sv.protocol));
401 MSG_WriteByte (&client->netconnection->message, svs.maxclients);
403 if (!coop.integer && deathmatch.integer)
404 MSG_WriteByte (&client->netconnection->message, GAME_DEATHMATCH);
406 MSG_WriteByte (&client->netconnection->message, GAME_COOP);
408 MSG_WriteString (&client->netconnection->message,PRVM_GetString(prog->edicts->fields.server->message));
410 for (i = 1;i < MAX_MODELS && sv.model_precache[i][0];i++)
411 MSG_WriteString (&client->netconnection->message, sv.model_precache[i]);
412 MSG_WriteByte (&client->netconnection->message, 0);
414 for (i = 1;i < MAX_SOUNDS && sv.sound_precache[i][0];i++)
415 MSG_WriteString (&client->netconnection->message, sv.sound_precache[i]);
416 MSG_WriteByte (&client->netconnection->message, 0);
419 MSG_WriteByte (&client->netconnection->message, svc_cdtrack);
420 MSG_WriteByte (&client->netconnection->message, (int)prog->edicts->fields.server->sounds);
421 MSG_WriteByte (&client->netconnection->message, (int)prog->edicts->fields.server->sounds);
424 MSG_WriteByte (&client->netconnection->message, svc_setview);
425 MSG_WriteShort (&client->netconnection->message, PRVM_NUM_FOR_EDICT(client->edict));
427 MSG_WriteByte (&client->netconnection->message, svc_signonnum);
428 MSG_WriteByte (&client->netconnection->message, 1);
433 host_client = client;
434 Curl_SendRequirements();
438 client->spawned = false; // need prespawn, spawn, etc
445 Initializes a client_t for a new net connection. This will only be called
446 once for a player each game, not once for each level change.
449 void SV_ConnectClient (int clientnum, netconn_t *netconnection)
453 float spawn_parms[NUM_SPAWN_PARMS];
455 client = svs.clients + clientnum;
457 if(netconnection)//[515]: bots don't play with csqc =)
458 EntityFrameCSQC_InitClientVersions(clientnum, false);
460 // set up the client_t
462 memcpy (spawn_parms, client->spawn_parms, sizeof(spawn_parms));
463 memset (client, 0, sizeof(*client));
464 client->active = true;
465 client->netconnection = netconnection;
467 Con_DPrintf("Client %s connected\n", client->netconnection ? client->netconnection->address : "botclient");
469 strlcpy(client->name, "unconnected", sizeof(client->name));
470 strlcpy(client->old_name, "unconnected", sizeof(client->old_name));
471 client->spawned = false;
472 client->edict = PRVM_EDICT_NUM(clientnum+1);
473 if (client->netconnection)
474 client->netconnection->message.allowoverflow = true; // we can catch it
475 // updated by receiving "rate" command from client
476 client->rate = NET_MINRATE;
477 // no limits for local player
478 if (client->netconnection && LHNETADDRESS_GetAddressType(&client->netconnection->peeraddress) == LHNETADDRESSTYPE_LOOP)
479 client->rate = 1000000000;
480 client->connecttime = realtime;
483 memcpy (client->spawn_parms, spawn_parms, sizeof(spawn_parms));
486 // call the progs to get default spawn parms for the new client
487 // set self to world to intentionally cause errors with broken SetNewParms code in some mods
488 prog->globals.server->self = 0;
489 PRVM_ExecuteProgram (prog->globals.server->SetNewParms, "QC function SetNewParms is missing");
490 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
491 client->spawn_parms[i] = (&prog->globals.server->parm1)[i];
493 // set up the entity for this client (including .colormap, .team, etc)
494 PRVM_ED_ClearEdict(client->edict);
497 // don't call SendServerinfo for a fresh botclient because its fields have
498 // not been set up by the qc yet
499 if (client->netconnection)
500 SV_SendServerinfo (client);
502 client->spawned = true;
507 ===============================================================================
511 ===============================================================================
520 void SV_ClearDatagram (void)
522 SZ_Clear (&sv.datagram);
526 =============================================================================
528 The PVS must include a small area around the client to allow head bobbing
529 or other small motion on the client side. Otherwise, a bob might cause an
530 entity that should be visible to not show up, especially when the bob
533 =============================================================================
536 int sv_writeentitiestoclient_pvsbytes;
537 unsigned char sv_writeentitiestoclient_pvs[MAX_MAP_LEAFS/8];
539 static int numsendentities;
540 static entity_state_t sendentities[MAX_EDICTS];
541 static entity_state_t *sendentitiesindex[MAX_EDICTS];
543 qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int e)
546 unsigned int modelindex, effects, flags, glowsize, lightstyle, lightpflags, light[4], specialvisibilityradius;
547 unsigned int customizeentityforclient;
549 vec3_t cullmins, cullmaxs;
553 // EF_NODRAW prevents sending for any reason except for your own
554 // client, so we must keep all clients in this superset
555 effects = (unsigned)ent->fields.server->effects;
557 // we can omit invisible entities with no effects that are not clients
558 // LordHavoc: this could kill tags attached to an invisible entity, I
559 // just hope we never have to support that case
560 i = (int)ent->fields.server->modelindex;
561 modelindex = (i >= 1 && i < MAX_MODELS && *PRVM_GetString(ent->fields.server->model)) ? i : 0;
564 i = (int)(PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.glow_size)->_float * 0.25f);
565 glowsize = (unsigned char)bound(0, i, 255);
566 if (PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.glow_trail)->_float)
567 flags |= RENDER_GLOWTRAIL;
569 f = PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[0]*256;
570 light[0] = (unsigned short)bound(0, f, 65535);
571 f = PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[1]*256;
572 light[1] = (unsigned short)bound(0, f, 65535);
573 f = PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[2]*256;
574 light[2] = (unsigned short)bound(0, f, 65535);
575 f = PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.light_lev)->_float;
576 light[3] = (unsigned short)bound(0, f, 65535);
577 lightstyle = (unsigned char)PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.style)->_float;
578 lightpflags = (unsigned char)PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.pflags)->_float;
580 if (gamemode == GAME_TENEBRAE)
582 // tenebrae's EF_FULLDYNAMIC conflicts with Q2's EF_NODRAW
586 lightpflags |= PFLAGS_FULLDYNAMIC;
588 // tenebrae's EF_GREEN conflicts with DP's EF_ADDITIVE
592 light[0] = (int)(0.2*256);
593 light[1] = (int)(1.0*256);
594 light[2] = (int)(0.2*256);
596 lightpflags |= PFLAGS_FULLDYNAMIC;
600 specialvisibilityradius = 0;
601 if (lightpflags & PFLAGS_FULLDYNAMIC)
602 specialvisibilityradius = max(specialvisibilityradius, light[3]);
604 specialvisibilityradius = max(specialvisibilityradius, glowsize * 4);
605 if (flags & RENDER_GLOWTRAIL)
606 specialvisibilityradius = max(specialvisibilityradius, 100);
607 if (effects & (EF_BRIGHTFIELD | EF_MUZZLEFLASH | EF_BRIGHTLIGHT | EF_DIMLIGHT | EF_RED | EF_BLUE | EF_FLAME | EF_STARDUST))
609 if (effects & EF_BRIGHTFIELD)
610 specialvisibilityradius = max(specialvisibilityradius, 80);
611 if (effects & EF_MUZZLEFLASH)
612 specialvisibilityradius = max(specialvisibilityradius, 100);
613 if (effects & EF_BRIGHTLIGHT)
614 specialvisibilityradius = max(specialvisibilityradius, 400);
615 if (effects & EF_DIMLIGHT)
616 specialvisibilityradius = max(specialvisibilityradius, 200);
617 if (effects & EF_RED)
618 specialvisibilityradius = max(specialvisibilityradius, 200);
619 if (effects & EF_BLUE)
620 specialvisibilityradius = max(specialvisibilityradius, 200);
621 if (effects & EF_FLAME)
622 specialvisibilityradius = max(specialvisibilityradius, 250);
623 if (effects & EF_STARDUST)
624 specialvisibilityradius = max(specialvisibilityradius, 100);
627 // early culling checks
628 // (final culling is done by SV_MarkWriteEntityStateToClient)
629 customizeentityforclient = PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.customizeentityforclient)->function;
630 if (!customizeentityforclient)
632 if (e > svs.maxclients && (!modelindex && !specialvisibilityradius))
634 // this 2 billion unit check is actually to detect NAN origins
635 // (we really don't want to send those)
636 if (VectorLength2(ent->fields.server->origin) > 2000000000.0*2000000000.0)
644 VectorCopy(ent->fields.server->origin, cs->origin);
645 VectorCopy(ent->fields.server->angles, cs->angles);
647 cs->effects = effects;
648 cs->colormap = (unsigned)ent->fields.server->colormap;
649 cs->modelindex = modelindex;
650 cs->skin = (unsigned)ent->fields.server->skin;
651 cs->frame = (unsigned)ent->fields.server->frame;
652 cs->viewmodelforclient = PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.viewmodelforclient)->edict;
653 cs->exteriormodelforclient = PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.exteriormodeltoclient)->edict;
654 cs->nodrawtoclient = PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.nodrawtoclient)->edict;
655 cs->drawonlytoclient = PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.drawonlytoclient)->edict;
656 cs->customizeentityforclient = customizeentityforclient;
657 cs->tagentity = PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)->edict;
658 cs->tagindex = (unsigned char)PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.tag_index)->_float;
659 cs->glowsize = glowsize;
661 // don't need to init cs->colormod because the defaultstate did that for us
662 //cs->colormod[0] = cs->colormod[1] = cs->colormod[2] = 32;
663 val = PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.colormod);
664 if (val->vector[0] || val->vector[1] || val->vector[2])
666 i = (int)(val->vector[0] * 32.0f);cs->colormod[0] = bound(0, i, 255);
667 i = (int)(val->vector[1] * 32.0f);cs->colormod[1] = bound(0, i, 255);
668 i = (int)(val->vector[2] * 32.0f);cs->colormod[2] = bound(0, i, 255);
671 cs->modelindex = modelindex;
674 f = (PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.alpha)->_float * 255.0f);
678 cs->alpha = (unsigned char)bound(0, i, 255);
681 f = (PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.renderamt)->_float);
685 cs->alpha = (unsigned char)bound(0, i, 255);
689 f = (PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.scale)->_float * 16.0f);
693 cs->scale = (unsigned char)bound(0, i, 255);
697 f = (PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.glow_color)->_float);
699 cs->glowcolor = (int)f;
701 if (PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.fullbright)->_float)
702 cs->effects |= EF_FULLBRIGHT;
704 if (ent->fields.server->movetype == MOVETYPE_STEP)
705 cs->flags |= RENDER_STEP;
706 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)
707 cs->flags |= RENDER_LOWPRECISION;
708 if (ent->fields.server->colormap >= 1024)
709 cs->flags |= RENDER_COLORMAPPED;
710 if (cs->viewmodelforclient)
711 cs->flags |= RENDER_VIEWMODEL; // show relative to the view
713 cs->light[0] = light[0];
714 cs->light[1] = light[1];
715 cs->light[2] = light[2];
716 cs->light[3] = light[3];
717 cs->lightstyle = lightstyle;
718 cs->lightpflags = lightpflags;
720 cs->specialvisibilityradius = specialvisibilityradius;
722 // calculate the visible box of this entity (don't use the physics box
723 // as that is often smaller than a model, and would not count
724 // specialvisibilityradius)
725 if ((model = sv.models[modelindex]))
727 float scale = cs->scale * (1.0f / 16.0f);
728 if (cs->angles[0] || cs->angles[2]) // pitch and roll
730 VectorMA(cs->origin, scale, model->rotatedmins, cullmins);
731 VectorMA(cs->origin, scale, model->rotatedmaxs, cullmaxs);
733 else if (cs->angles[1])
735 VectorMA(cs->origin, scale, model->yawmins, cullmins);
736 VectorMA(cs->origin, scale, model->yawmaxs, cullmaxs);
740 VectorMA(cs->origin, scale, model->normalmins, cullmins);
741 VectorMA(cs->origin, scale, model->normalmaxs, cullmaxs);
746 // if there is no model (or it could not be loaded), use the physics box
747 VectorAdd(cs->origin, ent->fields.server->mins, cullmins);
748 VectorAdd(cs->origin, ent->fields.server->maxs, cullmaxs);
750 if (specialvisibilityradius)
752 cullmins[0] = min(cullmins[0], cs->origin[0] - specialvisibilityradius);
753 cullmins[1] = min(cullmins[1], cs->origin[1] - specialvisibilityradius);
754 cullmins[2] = min(cullmins[2], cs->origin[2] - specialvisibilityradius);
755 cullmaxs[0] = max(cullmaxs[0], cs->origin[0] + specialvisibilityradius);
756 cullmaxs[1] = max(cullmaxs[1], cs->origin[1] + specialvisibilityradius);
757 cullmaxs[2] = max(cullmaxs[2], cs->origin[2] + specialvisibilityradius);
759 if (!VectorCompare(cullmins, ent->priv.server->cullmins) || !VectorCompare(cullmaxs, ent->priv.server->cullmaxs))
761 VectorCopy(cullmins, ent->priv.server->cullmins);
762 VectorCopy(cullmaxs, ent->priv.server->cullmaxs);
763 ent->priv.server->pvs_numclusters = -1;
764 if (sv.worldmodel && sv.worldmodel->brush.FindBoxClusters)
766 i = sv.worldmodel->brush.FindBoxClusters(sv.worldmodel, cullmins, cullmaxs, MAX_ENTITYCLUSTERS, ent->priv.server->pvs_clusterlist);
767 if (i <= MAX_ENTITYCLUSTERS)
768 ent->priv.server->pvs_numclusters = i;
775 void SV_PrepareEntitiesForSending(void)
779 // send all entities that touch the pvs
781 sendentitiesindex[0] = NULL;
782 memset(sendentitiesindex, 0, prog->num_edicts * sizeof(entity_state_t *));
783 for (e = 1, ent = PRVM_NEXT_EDICT(prog->edicts);e < prog->num_edicts;e++, ent = PRVM_NEXT_EDICT(ent))
785 if (!ent->priv.server->free && SV_PrepareEntityForSending(ent, sendentities + numsendentities, e))
787 sendentitiesindex[e] = sendentities + numsendentities;
793 static int sententitiesmark = 0;
794 static int sententities[MAX_EDICTS];
795 static int sententitiesconsideration[MAX_EDICTS];
796 static int sv_writeentitiestoclient_culled_pvs;
797 static int sv_writeentitiestoclient_culled_trace;
798 static int sv_writeentitiestoclient_visibleentities;
799 static int sv_writeentitiestoclient_totalentities;
800 //static entity_frame_t sv_writeentitiestoclient_entityframe;
801 static int sv_writeentitiestoclient_clentnum;
802 static vec3_t sv_writeentitiestoclient_testeye;
803 static client_t *sv_writeentitiestoclient_client;
805 void SV_MarkWriteEntityStateToClient(entity_state_t *s)
812 if (sententitiesconsideration[s->number] == sententitiesmark)
814 sententitiesconsideration[s->number] = sententitiesmark;
815 sv_writeentitiestoclient_totalentities++;
817 if (s->customizeentityforclient)
819 prog->globals.server->self = s->number;
820 prog->globals.server->other = sv_writeentitiestoclient_clentnum;
821 PRVM_ExecuteProgram(s->customizeentityforclient, "customizeentityforclient: NULL function");
822 if(!PRVM_G_FLOAT(OFS_RETURN) || !SV_PrepareEntityForSending(PRVM_EDICT_NUM(s->number), s, s->number))
826 // never reject player
827 if (s->number != sv_writeentitiestoclient_clentnum)
829 // check various rejection conditions
830 if (s->nodrawtoclient == sv_writeentitiestoclient_clentnum)
832 if (s->drawonlytoclient && s->drawonlytoclient != sv_writeentitiestoclient_clentnum)
834 if (s->effects & EF_NODRAW)
836 // LordHavoc: only send entities with a model or important effects
837 if (!s->modelindex && s->specialvisibilityradius == 0)
840 // viewmodels don't have visibility checking
841 if (s->viewmodelforclient)
843 if (s->viewmodelforclient != sv_writeentitiestoclient_clentnum)
846 else if (s->tagentity)
848 // tag attached entities simply check their parent
849 if (!sendentitiesindex[s->tagentity])
851 SV_MarkWriteEntityStateToClient(sendentitiesindex[s->tagentity]);
852 if (sententities[s->tagentity] != sententitiesmark)
855 // always send world submodels in newer protocols because they don't
856 // generate much traffic (in old protocols they hog bandwidth)
857 else if (!(s->effects & EF_NODEPTHTEST) && !((isbmodel = (model = sv.models[s->modelindex]) != NULL && model->name[0] == '*') && (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE)))
859 // entity has survived every check so far, check if visible
860 ed = PRVM_EDICT_NUM(s->number);
862 // if not touching a visible leaf
863 if (sv_cullentities_pvs.integer && sv_writeentitiestoclient_pvsbytes)
865 if (ed->priv.server->pvs_numclusters < 0)
867 // entity too big for clusters list
868 if (sv.worldmodel && sv.worldmodel->brush.BoxTouchingPVS && !sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, sv_writeentitiestoclient_pvs, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
870 sv_writeentitiestoclient_culled_pvs++;
877 // check cached clusters list
878 for (i = 0;i < ed->priv.server->pvs_numclusters;i++)
879 if (CHECKPVSBIT(sv_writeentitiestoclient_pvs, ed->priv.server->pvs_clusterlist[i]))
881 if (i == ed->priv.server->pvs_numclusters)
883 sv_writeentitiestoclient_culled_pvs++;
889 // or not seen by random tracelines
890 if (sv_cullentities_trace.integer && !isbmodel)
892 // LordHavoc: test center first
893 testorigin[0] = (ed->priv.server->cullmins[0] + ed->priv.server->cullmaxs[0]) * 0.5f;
894 testorigin[1] = (ed->priv.server->cullmins[1] + ed->priv.server->cullmaxs[1]) * 0.5f;
895 testorigin[2] = (ed->priv.server->cullmins[2] + ed->priv.server->cullmaxs[2]) * 0.5f;
896 sv.worldmodel->TraceBox(sv.worldmodel, 0, &trace, sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, SUPERCONTENTS_SOLID);
897 if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
898 sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
901 // LordHavoc: test random offsets, to maximize chance of detection
902 testorigin[0] = lhrandom(ed->priv.server->cullmins[0], ed->priv.server->cullmaxs[0]);
903 testorigin[1] = lhrandom(ed->priv.server->cullmins[1], ed->priv.server->cullmaxs[1]);
904 testorigin[2] = lhrandom(ed->priv.server->cullmins[2], ed->priv.server->cullmaxs[2]);
905 sv.worldmodel->TraceBox(sv.worldmodel, 0, &trace, sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, SUPERCONTENTS_SOLID);
906 if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
907 sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
910 if (s->specialvisibilityradius)
912 // LordHavoc: test random offsets, to maximize chance of detection
913 testorigin[0] = lhrandom(ed->priv.server->cullmins[0], ed->priv.server->cullmaxs[0]);
914 testorigin[1] = lhrandom(ed->priv.server->cullmins[1], ed->priv.server->cullmaxs[1]);
915 testorigin[2] = lhrandom(ed->priv.server->cullmins[2], ed->priv.server->cullmaxs[2]);
916 sv.worldmodel->TraceBox(sv.worldmodel, 0, &trace, sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, SUPERCONTENTS_SOLID);
917 if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
918 sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
922 if (realtime > sv_writeentitiestoclient_client->visibletime[s->number])
924 sv_writeentitiestoclient_culled_trace++;
931 // this just marks it for sending
932 // FIXME: it would be more efficient to send here, but the entity
933 // compressor isn't that flexible
934 sv_writeentitiestoclient_visibleentities++;
935 sententities[s->number] = sententitiesmark;
938 entity_state_t sendstates[MAX_EDICTS];
939 extern int csqc_clent;
941 void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg, int *stats)
943 int i, numsendstates;
946 // if there isn't enough space to accomplish anything, skip it
947 if (msg->cursize + 25 > msg->maxsize)
950 sv_writeentitiestoclient_client = client;
952 sv_writeentitiestoclient_culled_pvs = 0;
953 sv_writeentitiestoclient_culled_trace = 0;
954 sv_writeentitiestoclient_visibleentities = 0;
955 sv_writeentitiestoclient_totalentities = 0;
957 // find the client's PVS
958 // the real place being tested from
959 VectorAdd(clent->fields.server->origin, clent->fields.server->view_ofs, sv_writeentitiestoclient_testeye);
960 sv_writeentitiestoclient_pvsbytes = 0;
961 if (sv.worldmodel && sv.worldmodel->brush.FatPVS)
962 sv_writeentitiestoclient_pvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, sv_writeentitiestoclient_testeye, 8, sv_writeentitiestoclient_pvs, sizeof(sv_writeentitiestoclient_pvs));
964 csqc_clent = sv_writeentitiestoclient_clentnum = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
968 for (i = 0;i < numsendentities;i++)
969 SV_MarkWriteEntityStateToClient(sendentities + i);
972 for (i = 0;i < numsendentities;i++)
974 if (sententities[sendentities[i].number] == sententitiesmark)
976 s = &sendstates[numsendstates++];
977 *s = sendentities[i];
978 if (s->exteriormodelforclient && s->exteriormodelforclient == sv_writeentitiestoclient_clentnum)
979 s->flags |= RENDER_EXTERIORMODEL;
983 if (sv_cullentities_stats.integer)
984 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);
986 EntityFrameCSQC_WriteFrame(msg, numsendstates, sendstates);
988 if (client->entitydatabase5)
989 EntityFrame5_WriteFrame(msg, client->entitydatabase5, numsendstates, sendstates, client - svs.clients + 1, stats, client->movesequence);
990 else if (client->entitydatabase4)
991 EntityFrame4_WriteFrame(msg, client->entitydatabase4, numsendstates, sendstates);
992 else if (client->entitydatabase)
993 EntityFrame_WriteFrame(msg, client->entitydatabase, numsendstates, sendstates, client - svs.clients + 1);
995 EntityFrameQuake_WriteFrame(msg, numsendstates, sendstates);
1004 void SV_CleanupEnts (void)
1009 ent = PRVM_NEXT_EDICT(prog->edicts);
1010 for (e=1 ; e<prog->num_edicts ; e++, ent = PRVM_NEXT_EDICT(ent))
1011 ent->fields.server->effects = (int)ent->fields.server->effects & ~EF_MUZZLEFLASH;
1016 SV_WriteClientdataToMessage
1020 void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats)
1024 prvm_edict_t *other;
1032 // send a damage message
1034 if (ent->fields.server->dmg_take || ent->fields.server->dmg_save)
1036 other = PRVM_PROG_TO_EDICT(ent->fields.server->dmg_inflictor);
1037 MSG_WriteByte (msg, svc_damage);
1038 MSG_WriteByte (msg, (int)ent->fields.server->dmg_save);
1039 MSG_WriteByte (msg, (int)ent->fields.server->dmg_take);
1040 for (i=0 ; i<3 ; i++)
1041 MSG_WriteCoord (msg, other->fields.server->origin[i] + 0.5*(other->fields.server->mins[i] + other->fields.server->maxs[i]), sv.protocol);
1043 ent->fields.server->dmg_take = 0;
1044 ent->fields.server->dmg_save = 0;
1048 // send the current viewpos offset from the view entity
1050 SV_SetIdealPitch (); // how much to look up / down ideally
1052 // a fixangle might get lost in a dropped packet. Oh well.
1053 if ( ent->fields.server->fixangle )
1055 MSG_WriteByte (msg, svc_setangle);
1056 for (i=0 ; i < 3 ; i++)
1057 MSG_WriteAngle (msg, ent->fields.server->angles[i], sv.protocol);
1058 ent->fields.server->fixangle = 0;
1061 // stuff the sigil bits into the high bits of items for sbar, or else
1063 val = PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.items2);
1064 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1065 items = (int)ent->fields.server->items | ((int)val->_float << 23);
1067 items = (int)ent->fields.server->items | ((int)prog->globals.server->serverflags << 28);
1069 VectorClear(punchvector);
1070 if ((val = PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.punchvector)))
1071 VectorCopy(val->vector, punchvector);
1073 // cache weapon model name and index in client struct to save time
1074 // (this search can be almost 1% of cpu time!)
1075 s = PRVM_GetString(ent->fields.server->weaponmodel);
1076 if (strcmp(s, client->weaponmodel))
1078 strlcpy(client->weaponmodel, s, sizeof(client->weaponmodel));
1079 client->weaponmodelindex = SV_ModelIndex(s, 1);
1083 if ((val = PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.viewzoom)))
1084 viewzoom = (int)(val->_float * 255.0f);
1090 if ((int)ent->fields.server->flags & FL_ONGROUND)
1091 bits |= SU_ONGROUND;
1092 if (ent->fields.server->waterlevel >= 2)
1094 if (ent->fields.server->idealpitch)
1095 bits |= SU_IDEALPITCH;
1097 for (i=0 ; i<3 ; i++)
1099 if (ent->fields.server->punchangle[i])
1100 bits |= (SU_PUNCH1<<i);
1101 if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE)
1103 bits |= (SU_PUNCHVEC1<<i);
1104 if (ent->fields.server->velocity[i])
1105 bits |= (SU_VELOCITY1<<i);
1108 memset(stats, 0, sizeof(int[MAX_CL_STATS]));
1109 stats[STAT_VIEWHEIGHT] = (int)ent->fields.server->view_ofs[2];
1110 stats[STAT_ITEMS] = items;
1111 stats[STAT_WEAPONFRAME] = (int)ent->fields.server->weaponframe;
1112 stats[STAT_ARMOR] = (int)ent->fields.server->armorvalue;
1113 stats[STAT_WEAPON] = client->weaponmodelindex;
1114 stats[STAT_HEALTH] = (int)ent->fields.server->health;
1115 stats[STAT_AMMO] = (int)ent->fields.server->currentammo;
1116 stats[STAT_SHELLS] = (int)ent->fields.server->ammo_shells;
1117 stats[STAT_NAILS] = (int)ent->fields.server->ammo_nails;
1118 stats[STAT_ROCKETS] = (int)ent->fields.server->ammo_rockets;
1119 stats[STAT_CELLS] = (int)ent->fields.server->ammo_cells;
1120 stats[STAT_ACTIVEWEAPON] = (int)ent->fields.server->weapon;
1121 stats[STAT_VIEWZOOM] = viewzoom;
1122 stats[STAT_TOTALSECRETS] = prog->globals.server->total_secrets;
1123 stats[STAT_TOTALMONSTERS] = prog->globals.server->total_monsters;
1124 // the QC bumps these itself by sending svc_'s, so we have to keep them
1125 // zero or they'll be corrected by the engine
1126 //stats[STAT_SECRETS] = prog->globals.server->found_secrets;
1127 //stats[STAT_MONSTERS] = prog->globals.server->killed_monsters;
1129 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)
1131 if (stats[STAT_VIEWHEIGHT] != DEFAULT_VIEWHEIGHT) bits |= SU_VIEWHEIGHT;
1133 if (stats[STAT_WEAPONFRAME]) bits |= SU_WEAPONFRAME;
1134 if (stats[STAT_ARMOR]) bits |= SU_ARMOR;
1136 // FIXME: which protocols support this? does PROTOCOL_DARKPLACES3 support viewzoom?
1137 if (sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5)
1138 if (viewzoom != 255)
1139 bits |= SU_VIEWZOOM;
1144 if (bits >= 16777216)
1148 MSG_WriteByte (msg, svc_clientdata);
1149 MSG_WriteShort (msg, bits);
1150 if (bits & SU_EXTEND1)
1151 MSG_WriteByte(msg, bits >> 16);
1152 if (bits & SU_EXTEND2)
1153 MSG_WriteByte(msg, bits >> 24);
1155 if (bits & SU_VIEWHEIGHT)
1156 MSG_WriteChar (msg, stats[STAT_VIEWHEIGHT]);
1158 if (bits & SU_IDEALPITCH)
1159 MSG_WriteChar (msg, (int)ent->fields.server->idealpitch);
1161 for (i=0 ; i<3 ; i++)
1163 if (bits & (SU_PUNCH1<<i))
1165 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
1166 MSG_WriteChar(msg, (int)ent->fields.server->punchangle[i]);
1168 MSG_WriteAngle16i(msg, ent->fields.server->punchangle[i]);
1170 if (bits & (SU_PUNCHVEC1<<i))
1172 if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
1173 MSG_WriteCoord16i(msg, punchvector[i]);
1175 MSG_WriteCoord32f(msg, punchvector[i]);
1177 if (bits & (SU_VELOCITY1<<i))
1179 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
1180 MSG_WriteChar(msg, (int)(ent->fields.server->velocity[i] * (1.0f / 16.0f)));
1182 MSG_WriteCoord32f(msg, ent->fields.server->velocity[i]);
1186 if (bits & SU_ITEMS)
1187 MSG_WriteLong (msg, stats[STAT_ITEMS]);
1189 if (sv.protocol == PROTOCOL_DARKPLACES5)
1191 if (bits & SU_WEAPONFRAME)
1192 MSG_WriteShort (msg, stats[STAT_WEAPONFRAME]);
1193 if (bits & SU_ARMOR)
1194 MSG_WriteShort (msg, stats[STAT_ARMOR]);
1195 if (bits & SU_WEAPON)
1196 MSG_WriteShort (msg, stats[STAT_WEAPON]);
1197 MSG_WriteShort (msg, stats[STAT_HEALTH]);
1198 MSG_WriteShort (msg, stats[STAT_AMMO]);
1199 MSG_WriteShort (msg, stats[STAT_SHELLS]);
1200 MSG_WriteShort (msg, stats[STAT_NAILS]);
1201 MSG_WriteShort (msg, stats[STAT_ROCKETS]);
1202 MSG_WriteShort (msg, stats[STAT_CELLS]);
1203 MSG_WriteShort (msg, stats[STAT_ACTIVEWEAPON]);
1204 if (bits & SU_VIEWZOOM)
1205 MSG_WriteShort (msg, bound(0, stats[STAT_VIEWZOOM], 65535));
1207 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)
1209 if (bits & SU_WEAPONFRAME)
1210 MSG_WriteByte (msg, stats[STAT_WEAPONFRAME]);
1211 if (bits & SU_ARMOR)
1212 MSG_WriteByte (msg, stats[STAT_ARMOR]);
1213 if (bits & SU_WEAPON)
1214 MSG_WriteByte (msg, stats[STAT_WEAPON]);
1215 MSG_WriteShort (msg, stats[STAT_HEALTH]);
1216 MSG_WriteByte (msg, stats[STAT_AMMO]);
1217 MSG_WriteByte (msg, stats[STAT_SHELLS]);
1218 MSG_WriteByte (msg, stats[STAT_NAILS]);
1219 MSG_WriteByte (msg, stats[STAT_ROCKETS]);
1220 MSG_WriteByte (msg, stats[STAT_CELLS]);
1221 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE || gamemode == GAME_NEXUIZ)
1223 for (i = 0;i < 32;i++)
1224 if (stats[STAT_WEAPON] & (1<<i))
1226 MSG_WriteByte (msg, i);
1229 MSG_WriteByte (msg, stats[STAT_WEAPON]);
1230 if (bits & SU_VIEWZOOM)
1232 if (sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
1233 MSG_WriteByte (msg, bound(0, stats[STAT_VIEWZOOM], 255));
1235 MSG_WriteShort (msg, bound(0, stats[STAT_VIEWZOOM], 65535));
1241 =======================
1242 SV_SendClientDatagram
1243 =======================
1245 static unsigned char sv_sendclientdatagram_buf[NET_MAXMESSAGE]; // FIXME?
1246 void SV_SendClientDatagram (client_t *client)
1248 int rate, maxrate, maxsize, maxsize2, downloadsize;
1250 int stats[MAX_CL_STATS];
1252 if (LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) == LHNETADDRESSTYPE_LOOP && !sv_ratelimitlocalplayer.integer)
1254 // for good singleplayer, send huge packets
1255 maxsize = sizeof(sv_sendclientdatagram_buf);
1256 maxsize2 = sizeof(sv_sendclientdatagram_buf);
1258 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)
1260 // no rate limiting support on older protocols because dp protocols
1261 // 1-4 kick the client off if they overflow, and quake protocol shows
1262 // less than the full entity set if rate limited
1268 // PROTOCOL_DARKPLACES5 and later support packet size limiting of updates
1269 maxrate = max(NET_MINRATE, sv_maxrate.integer);
1270 if (sv_maxrate.integer != maxrate)
1271 Cvar_SetValueQuick(&sv_maxrate, maxrate);
1273 // this rate limiting does not understand sys_ticrate 0
1274 // (but no one should be running that on a server!)
1275 rate = bound(NET_MINRATE, client->rate, maxrate);
1276 rate = (int)(rate * sys_ticrate.value);
1277 maxsize = bound(50, rate, 1400);
1281 // while downloading, limit entity updates to half the packet
1282 // (any leftover space will be used for downloading)
1283 if (host_client->download_file)
1286 msg.data = sv_sendclientdatagram_buf;
1287 msg.maxsize = maxsize;
1290 if (host_client->spawned)
1292 MSG_WriteByte (&msg, svc_time);
1293 MSG_WriteFloat (&msg, sv.time);
1295 // add the client specific data to the datagram
1296 SV_WriteClientdataToMessage (client, client->edict, &msg, stats);
1297 VM_SV_WriteAutoSentStats (client, client->edict, &msg, stats);
1298 SV_WriteEntitiesToClient (client, client->edict, &msg, stats);
1300 // expand packet size to allow effects to go over the rate limit
1301 // (dropping them is FAR too ugly)
1302 msg.maxsize = maxsize2;
1304 // copy the server datagram if there is space
1305 // FIXME: put in delayed queue of effects to send
1306 if (sv.datagram.cursize > 0 && msg.cursize + sv.datagram.cursize <= msg.maxsize)
1307 SZ_Write (&msg, sv.datagram.data, sv.datagram.cursize);
1309 else if (realtime > client->keepalivetime)
1311 // the player isn't totally in the game yet
1312 // send small keepalive messages if too much time has passed
1313 msg.maxsize = maxsize2;
1314 client->keepalivetime = realtime + 5;
1315 MSG_WriteChar (&msg, svc_nop);
1318 msg.maxsize = maxsize2;
1320 // if a download is active, see if there is room to fit some download data
1322 downloadsize = maxsize * 2 - msg.cursize - 7;
1323 if (host_client->download_file && host_client->download_started && downloadsize > 0)
1325 fs_offset_t downloadstart;
1326 unsigned char data[1400];
1327 downloadstart = FS_Tell(host_client->download_file);
1328 downloadsize = min(downloadsize, (int)sizeof(data));
1329 downloadsize = FS_Read(host_client->download_file, data, downloadsize);
1330 // note this sends empty messages if at the end of the file, which is
1331 // necessary to keep the packet loss logic working
1332 // (the last blocks may be lost and need to be re-sent, and that will
1333 // only occur if the client acks the empty end messages, revealing
1334 // a gap in the download progress, causing the last blocks to be
1336 MSG_WriteChar (&msg, svc_downloaddata);
1337 MSG_WriteLong (&msg, downloadstart);
1338 MSG_WriteShort (&msg, downloadsize);
1339 if (downloadsize > 0)
1340 SZ_Write (&msg, data, downloadsize);
1343 // send the datagram
1344 NetConn_SendUnreliableMessage (client->netconnection, &msg, sv.protocol);
1348 =======================
1349 SV_UpdateToReliableMessages
1350 =======================
1352 void SV_UpdateToReliableMessages (void)
1361 // check for changes to be sent over the reliable streams
1362 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1364 // update the host_client fields we care about according to the entity fields
1365 host_client->edict = PRVM_EDICT_NUM(i+1);
1368 name = PRVM_GetString(host_client->edict->fields.server->netname);
1371 // always point the string back at host_client->name to keep it safe
1372 strlcpy (host_client->name, name, sizeof (host_client->name));
1373 host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
1374 if (strcmp(host_client->old_name, host_client->name))
1376 if (host_client->spawned)
1377 SV_BroadcastPrintf("%s changed name to %s\n", host_client->old_name, host_client->name);
1378 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1379 // send notification to all clients
1380 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1381 MSG_WriteByte (&sv.reliable_datagram, i);
1382 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1385 // DP_SV_CLIENTCOLORS
1386 // this is always found (since it's added by the progs loader)
1387 if ((val = PRVM_GETEDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
1388 host_client->colors = (int)val->_float;
1389 if (host_client->old_colors != host_client->colors)
1391 host_client->old_colors = host_client->colors;
1392 // send notification to all clients
1393 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1394 MSG_WriteByte (&sv.reliable_datagram, i);
1395 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1398 // NEXUIZ_PLAYERMODEL
1399 if( prog->fieldoffsets.playermodel >= 0 ) {
1400 model = PRVM_GetString(PRVM_GETEDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string);
1403 // always point the string back at host_client->name to keep it safe
1404 strlcpy (host_client->playermodel, model, sizeof (host_client->playermodel));
1405 PRVM_GETEDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
1408 // NEXUIZ_PLAYERSKIN
1409 if( prog->fieldoffsets.playerskin >= 0 ) {
1410 skin = PRVM_GetString(PRVM_GETEDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string);
1413 // always point the string back at host_client->name to keep it safe
1414 strlcpy (host_client->playerskin, skin, sizeof (host_client->playerskin));
1415 PRVM_GETEDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
1419 host_client->frags = (int)host_client->edict->fields.server->frags;
1420 if (host_client->old_frags != host_client->frags)
1422 host_client->old_frags = host_client->frags;
1423 // send notification to all clients
1424 MSG_WriteByte (&sv.reliable_datagram, svc_updatefrags);
1425 MSG_WriteByte (&sv.reliable_datagram, i);
1426 MSG_WriteShort (&sv.reliable_datagram, host_client->frags);
1430 for (j = 0, client = svs.clients;j < svs.maxclients;j++, client++)
1431 if (client->netconnection)
1432 SZ_Write (&client->netconnection->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize);
1434 SZ_Clear (&sv.reliable_datagram);
1439 =======================
1440 SV_SendClientMessages
1441 =======================
1443 void SV_SendClientMessages (void)
1445 int i, prepared = false;
1447 if (sv.protocol == PROTOCOL_QUAKEWORLD)
1448 Sys_Error("SV_SendClientMessages: no quakeworld support\n");
1450 // update frags, names, etc
1451 SV_UpdateToReliableMessages();
1453 // build individual updates
1454 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1456 if (!host_client->active)
1458 if (!host_client->netconnection)
1461 if (host_client->netconnection->message.overflowed)
1463 SV_DropClient (true); // if the message couldn't send, kick off
1470 // only prepare entities once per frame
1471 SV_PrepareEntitiesForSending();
1473 SV_SendClientDatagram (host_client);
1476 // clear muzzle flashes
1480 void SV_StartDownload_f(void)
1482 if (host_client->download_file)
1483 host_client->download_started = true;
1486 void SV_Download_f(void)
1488 const char *whichpack, *whichpack2, *extension;
1490 if (Cmd_Argc() != 2)
1492 SV_ClientPrintf("usage: download <filename>\n");
1496 if (FS_CheckNastyPath(Cmd_Argv(1), false))
1498 SV_ClientPrintf("Download rejected: nasty filename \"%s\"\n", Cmd_Argv(1));
1502 if (host_client->download_file)
1504 // at this point we'll assume the previous download should be aborted
1505 Con_DPrintf("Download of %s aborted by %s starting a new download\n", host_client->download_name, host_client->name);
1506 Host_ClientCommands("\nstopdownload\n");
1508 // close the file and reset variables
1509 FS_Close(host_client->download_file);
1510 host_client->download_file = NULL;
1511 host_client->download_name[0] = 0;
1512 host_client->download_expectedposition = 0;
1513 host_client->download_started = false;
1516 if (!sv_allowdownloads.integer)
1518 SV_ClientPrintf("Downloads are disabled on this server\n");
1519 Host_ClientCommands("\nstopdownload\n");
1523 strlcpy(host_client->download_name, Cmd_Argv(1), sizeof(host_client->download_name));
1524 extension = FS_FileExtension(host_client->download_name);
1526 // host_client is asking to download a specified file
1527 if (developer.integer >= 100)
1528 Con_Printf("Download request for %s by %s\n", host_client->download_name, host_client->name);
1530 if (!FS_FileExists(host_client->download_name))
1532 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);
1533 Host_ClientCommands("\nstopdownload\n");
1537 // check if the user is trying to download part of registered Quake(r)
1538 whichpack = FS_WhichPack(host_client->download_name);
1539 whichpack2 = FS_WhichPack("gfx/pop.lmp");
1540 if ((whichpack && whichpack2 && !strcasecmp(whichpack, whichpack2)) || FS_IsRegisteredQuakePack(host_client->download_name))
1542 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);
1543 Host_ClientCommands("\nstopdownload\n");
1547 // check if the server has forbidden archive downloads entirely
1548 if (!sv_allowdownloads_inarchive.integer)
1550 whichpack = FS_WhichPack(host_client->download_name);
1553 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);
1554 Host_ClientCommands("\nstopdownload\n");
1559 if (!sv_allowdownloads_config.integer)
1561 if (!strcasecmp(extension, "cfg"))
1563 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);
1564 Host_ClientCommands("\nstopdownload\n");
1569 if (!sv_allowdownloads_dlcache.integer)
1571 if (!strncasecmp(host_client->download_name, "dlcache/", 8))
1573 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);
1574 Host_ClientCommands("\nstopdownload\n");
1579 if (!sv_allowdownloads_archive.integer)
1581 if (!strcasecmp(extension, "pak") || !strcasecmp(extension, "pk3"))
1583 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);
1584 Host_ClientCommands("\nstopdownload\n");
1589 host_client->download_file = FS_Open(host_client->download_name, "rb", true, false);
1590 if (!host_client->download_file)
1592 SV_ClientPrintf("Download rejected: server could not open the file \"%s\"\n", host_client->download_name);
1593 Host_ClientCommands("\nstopdownload\n");
1597 if (FS_FileSize(host_client->download_file) > 1<<30)
1599 SV_ClientPrintf("Download rejected: file \"%s\" is very large\n", host_client->download_name);
1600 Host_ClientCommands("\nstopdownload\n");
1601 FS_Close(host_client->download_file);
1602 host_client->download_file = NULL;
1606 Con_DPrintf("Downloading %s to %s\n", host_client->download_name, host_client->name);
1608 Host_ClientCommands("\ncl_downloadbegin %i %s\n", (int)FS_FileSize(host_client->download_file), host_client->download_name);
1610 host_client->download_expectedposition = 0;
1611 host_client->download_started = false;
1613 // the rest of the download process is handled in SV_SendClientDatagram
1614 // and other code dealing with svc_downloaddata and clc_ackdownloaddata
1616 // no svc_downloaddata messages will be sent until sv_startdownload is
1617 // sent by the client
1621 ==============================================================================
1625 ==============================================================================
1634 int SV_ModelIndex(const char *s, int precachemode)
1636 int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) ? 256 : MAX_MODELS);
1637 char filename[MAX_QPATH];
1641 //if (precachemode == 2)
1643 strlcpy(filename, s, sizeof(filename));
1644 for (i = 2;i < limit;i++)
1646 if (!sv.model_precache[i][0])
1650 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))
1652 Con_Printf("SV_ModelIndex(\"%s\"): precache_model can only be done in spawn functions\n", filename);
1655 if (precachemode == 1)
1656 Con_Printf("SV_ModelIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename);
1657 strlcpy(sv.model_precache[i], filename, sizeof(sv.model_precache[i]));
1658 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, false);
1659 if (sv.state != ss_loading)
1661 MSG_WriteByte(&sv.reliable_datagram, svc_precache);
1662 MSG_WriteShort(&sv.reliable_datagram, i);
1663 MSG_WriteString(&sv.reliable_datagram, filename);
1667 Con_Printf("SV_ModelIndex(\"%s\"): not precached\n", filename);
1670 if (!strcmp(sv.model_precache[i], filename))
1673 Con_Printf("SV_ModelIndex(\"%s\"): i (%i) == MAX_MODELS (%i)\n", filename, i, MAX_MODELS);
1683 int SV_SoundIndex(const char *s, int precachemode)
1685 int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) ? 256 : MAX_SOUNDS);
1686 char filename[MAX_QPATH];
1690 //if (precachemode == 2)
1692 strlcpy(filename, s, sizeof(filename));
1693 for (i = 1;i < limit;i++)
1695 if (!sv.sound_precache[i][0])
1699 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))
1701 Con_Printf("SV_SoundIndex(\"%s\"): precache_sound can only be done in spawn functions\n", filename);
1704 if (precachemode == 1)
1705 Con_Printf("SV_SoundIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename);
1706 strlcpy(sv.sound_precache[i], filename, sizeof(sv.sound_precache[i]));
1707 if (sv.state != ss_loading)
1709 MSG_WriteByte(&sv.reliable_datagram, svc_precache);
1710 MSG_WriteShort(&sv.reliable_datagram, i + 32768);
1711 MSG_WriteString(&sv.reliable_datagram, filename);
1715 Con_Printf("SV_SoundIndex(\"%s\"): not precached\n", filename);
1718 if (!strcmp(sv.sound_precache[i], filename))
1721 Con_Printf("SV_SoundIndex(\"%s\"): i (%i) == MAX_SOUNDS (%i)\n", filename, i, MAX_SOUNDS);
1731 void SV_CreateBaseline (void)
1733 int i, entnum, large;
1734 prvm_edict_t *svent;
1736 // LordHavoc: clear *all* states (note just active ones)
1737 for (entnum = 0;entnum < prog->max_edicts;entnum++)
1739 // get the current server version
1740 svent = PRVM_EDICT_NUM(entnum);
1742 // LordHavoc: always clear state values, whether the entity is in use or not
1743 svent->priv.server->baseline = defaultstate;
1745 if (svent->priv.server->free)
1747 if (entnum > svs.maxclients && !svent->fields.server->modelindex)
1750 // create entity baseline
1751 VectorCopy (svent->fields.server->origin, svent->priv.server->baseline.origin);
1752 VectorCopy (svent->fields.server->angles, svent->priv.server->baseline.angles);
1753 svent->priv.server->baseline.frame = (int)svent->fields.server->frame;
1754 svent->priv.server->baseline.skin = (int)svent->fields.server->skin;
1755 if (entnum > 0 && entnum <= svs.maxclients)
1757 svent->priv.server->baseline.colormap = entnum;
1758 svent->priv.server->baseline.modelindex = SV_ModelIndex("progs/player.mdl", 1);
1762 svent->priv.server->baseline.colormap = 0;
1763 svent->priv.server->baseline.modelindex = (int)svent->fields.server->modelindex;
1767 if (svent->priv.server->baseline.modelindex & 0xFF00 || svent->priv.server->baseline.frame & 0xFF00)
1770 // add to the message
1772 MSG_WriteByte (&sv.signon, svc_spawnbaseline2);
1774 MSG_WriteByte (&sv.signon, svc_spawnbaseline);
1775 MSG_WriteShort (&sv.signon, entnum);
1779 MSG_WriteShort (&sv.signon, svent->priv.server->baseline.modelindex);
1780 MSG_WriteShort (&sv.signon, svent->priv.server->baseline.frame);
1784 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.modelindex);
1785 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.frame);
1787 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.colormap);
1788 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.skin);
1789 for (i=0 ; i<3 ; i++)
1791 MSG_WriteCoord(&sv.signon, svent->priv.server->baseline.origin[i], sv.protocol);
1792 MSG_WriteAngle(&sv.signon, svent->priv.server->baseline.angles[i], sv.protocol);
1802 Grabs the current state of each client for saving across the
1803 transition to another level
1806 void SV_SaveSpawnparms (void)
1810 svs.serverflags = (int)prog->globals.server->serverflags;
1812 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1814 if (!host_client->active)
1817 // call the progs to get default spawn parms for the new client
1818 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1819 PRVM_ExecuteProgram (prog->globals.server->SetChangeParms, "QC function SetChangeParms is missing");
1820 for (j=0 ; j<NUM_SPAWN_PARMS ; j++)
1821 host_client->spawn_parms[j] = (&prog->globals.server->parm1)[j];
1825 void SV_IncreaseEdicts(void)
1829 int oldmax_edicts = prog->max_edicts;
1830 void *oldedictsengineprivate = prog->edictprivate;
1831 void *oldedictsfields = prog->edictsfields;
1832 void *oldmoved_edicts = sv.moved_edicts;
1834 if (prog->max_edicts >= MAX_EDICTS)
1837 // links don't survive the transition, so unlink everything
1838 for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
1840 if (!ent->priv.server->free)
1841 SV_UnlinkEdict(prog->edicts + i);
1842 memset(&ent->priv.server->areagrid, 0, sizeof(ent->priv.server->areagrid));
1844 World_Clear(&sv.world);
1846 prog->max_edicts = min(prog->max_edicts + 256, MAX_EDICTS);
1847 prog->edictprivate = PR_Alloc(prog->max_edicts * sizeof(edict_engineprivate_t));
1848 prog->edictsfields = PR_Alloc(prog->max_edicts * prog->edict_size);
1849 sv.moved_edicts = PR_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
1851 memcpy(prog->edictprivate, oldedictsengineprivate, oldmax_edicts * sizeof(edict_engineprivate_t));
1852 memcpy(prog->edictsfields, oldedictsfields, oldmax_edicts * prog->edict_size);
1854 for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
1856 ent->priv.vp = (unsigned char*) prog->edictprivate + i * prog->edictprivate_size;
1857 ent->fields.server = (void *)((unsigned char *)prog->edictsfields + i * prog->edict_size);
1858 // link every entity except world
1859 if (!ent->priv.server->free)
1860 SV_LinkEdict(ent, false);
1863 PR_Free(oldedictsengineprivate);
1864 PR_Free(oldedictsfields);
1865 PR_Free(oldmoved_edicts);
1872 This is called at the start of each level
1875 extern float scr_centertime_off;
1877 void SV_SpawnServer (const char *server)
1882 model_t *worldmodel;
1883 char modelname[sizeof(sv.modelname)];
1885 Con_DPrintf("SpawnServer: %s\n", server);
1887 if (cls.state != ca_dedicated)
1888 SCR_BeginLoadingPlaque();
1890 dpsnprintf (modelname, sizeof(modelname), "maps/%s.bsp", server);
1891 worldmodel = Mod_ForName(modelname, false, true, true);
1892 if (!worldmodel || !worldmodel->TraceBox)
1894 Con_Printf("Couldn't load map %s\n", modelname);
1898 // let's not have any servers with no name
1899 if (hostname.string[0] == 0)
1900 Cvar_Set ("hostname", "UNNAMED");
1901 scr_centertime_off = 0;
1903 svs.changelevel_issued = false; // now safe to issue another
1905 // make the map a required file for clients
1906 Curl_ClearRequirements();
1907 Curl_RequireFile(modelname);
1910 // tell all connected clients that we are going to a new level
1915 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1917 if (client->netconnection)
1919 MSG_WriteByte(&client->netconnection->message, svc_stufftext);
1920 MSG_WriteString(&client->netconnection->message, "reconnect\n");
1927 NetConn_OpenServerPorts(true);
1931 // make cvars consistant
1934 Cvar_SetValue ("deathmatch", 0);
1935 // LordHavoc: it can be useful to have skills outside the range 0-3...
1936 //current_skill = bound(0, (int)(skill.value + 0.5), 3);
1937 //Cvar_SetValue ("skill", (float)current_skill);
1938 current_skill = (int)(skill.value + 0.5);
1941 // set up the new server
1943 memset (&sv, 0, sizeof(sv));
1944 // if running a local client, make sure it doesn't try to access the last
1945 // level's data which is no longer valiud
1948 if(*sv_random_seed.string)
1950 srand(sv_random_seed.integer);
1951 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);
1958 strlcpy (sv.name, server, sizeof (sv.name));
1960 sv.protocol = Protocol_EnumForName(sv_protocolname.string);
1961 if (sv.protocol == PROTOCOL_UNKNOWN)
1964 Protocol_Names(buffer, sizeof(buffer));
1965 Con_Printf("Unknown sv_protocolname \"%s\", valid values are:\n%s\n", sv_protocolname.string, buffer);
1966 sv.protocol = PROTOCOL_QUAKE;
1971 // load progs to get entity field count
1972 //PR_LoadProgs ( sv_progs.string );
1974 // allocate server memory
1975 /*// start out with just enough room for clients and a reasonable estimate of entities
1976 prog->max_edicts = max(svs.maxclients + 1, 512);
1977 prog->max_edicts = min(prog->max_edicts, MAX_EDICTS);
1979 // prvm_edict_t structures (hidden from progs)
1980 prog->edicts = PR_Alloc(MAX_EDICTS * sizeof(prvm_edict_t));
1981 // engine private structures (hidden from progs)
1982 prog->edictprivate = PR_Alloc(prog->max_edicts * sizeof(edict_engineprivate_t));
1983 // progs fields, often accessed by server
1984 prog->edictsfields = PR_Alloc(prog->max_edicts * prog->edict_size);*/
1985 // used by PushMove to move back pushed entities
1986 sv.moved_edicts = (prvm_edict_t **)PRVM_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
1987 /*for (i = 0;i < prog->max_edicts;i++)
1989 ent = prog->edicts + i;
1990 ent->priv.vp = (unsigned char*) prog->edictprivate + i * prog->edictprivate_size;
1991 ent->fields.server = (void *)((unsigned char *)prog->edictsfields + i * prog->edict_size);
1994 // reset client csqc entity versions right away.
1995 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1996 EntityFrameCSQC_InitClientVersions(i, true);
1998 sv.datagram.maxsize = sizeof(sv.datagram_buf);
1999 sv.datagram.cursize = 0;
2000 sv.datagram.data = sv.datagram_buf;
2002 sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf);
2003 sv.reliable_datagram.cursize = 0;
2004 sv.reliable_datagram.data = sv.reliable_datagram_buf;
2006 sv.signon.maxsize = sizeof(sv.signon_buf);
2007 sv.signon.cursize = 0;
2008 sv.signon.data = sv.signon_buf;
2010 // leave slots at start for clients only
2011 //prog->num_edicts = svs.maxclients+1;
2013 sv.state = ss_loading;
2014 prog->allowworldwrites = true;
2017 prog->globals.server->time = sv.time = 1.0;
2020 worldmodel->used = true;
2022 strlcpy (sv.name, server, sizeof (sv.name));
2023 strlcpy(sv.modelname, modelname, sizeof(sv.modelname));
2024 sv.worldmodel = worldmodel;
2025 sv.models[1] = sv.worldmodel;
2028 // clear world interaction links
2030 VectorCopy(sv.worldmodel->normalmins, sv.world.areagrid_mins);
2031 VectorCopy(sv.worldmodel->normalmaxs, sv.world.areagrid_maxs);
2032 World_Clear(&sv.world);
2034 strlcpy(sv.sound_precache[0], "", sizeof(sv.sound_precache[0]));
2036 strlcpy(sv.model_precache[0], "", sizeof(sv.model_precache[0]));
2037 strlcpy(sv.model_precache[1], sv.modelname, sizeof(sv.model_precache[1]));
2038 for (i = 1;i < sv.worldmodel->brush.numsubmodels;i++)
2040 dpsnprintf(sv.model_precache[i+1], sizeof(sv.model_precache[i+1]), "*%i", i);
2041 sv.models[i+1] = Mod_ForName (sv.model_precache[i+1], false, false, false);
2045 // load the rest of the entities
2047 // AK possible hack since num_edicts is still 0
2048 ent = PRVM_EDICT_NUM(0);
2049 memset (ent->fields.server, 0, prog->progs->entityfields * 4);
2050 ent->priv.server->free = false;
2051 ent->fields.server->model = PRVM_SetEngineString(sv.modelname);
2052 ent->fields.server->modelindex = 1; // world model
2053 ent->fields.server->solid = SOLID_BSP;
2054 ent->fields.server->movetype = MOVETYPE_PUSH;
2055 VectorCopy(sv.worldmodel->normalmins, ent->fields.server->mins);
2056 VectorCopy(sv.worldmodel->normalmaxs, ent->fields.server->maxs);
2057 VectorCopy(sv.worldmodel->normalmins, ent->fields.server->absmin);
2058 VectorCopy(sv.worldmodel->normalmaxs, ent->fields.server->absmax);
2061 prog->globals.server->coop = coop.integer;
2063 prog->globals.server->deathmatch = deathmatch.integer;
2065 prog->globals.server->mapname = PRVM_SetEngineString(sv.name);
2067 // serverflags are for cross level information (sigils)
2068 prog->globals.server->serverflags = svs.serverflags;
2070 // we need to reset the spawned flag on all connected clients here so that
2071 // their thinks don't run during startup (before PutClientInServer)
2072 // we also need to set up the client entities now
2073 // and we need to set the ->edict pointers to point into the progs edicts
2074 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2076 host_client->spawned = false;
2077 host_client->edict = PRVM_EDICT_NUM(i + 1);
2078 PRVM_ED_ClearEdict(host_client->edict);
2081 // load replacement entity file if found
2082 if (sv_entpatch.integer && (entities = (char *)FS_LoadFile(va("maps/%s.ent", sv.name), tempmempool, true, NULL)))
2084 Con_Printf("Loaded maps/%s.ent\n", sv.name);
2085 PRVM_ED_LoadFromFile (entities);
2089 PRVM_ED_LoadFromFile (sv.worldmodel->brush.entities);
2092 // LordHavoc: clear world angles (to fix e3m3.bsp)
2093 VectorClear(prog->edicts->fields.server->angles);
2095 // all setup is completed, any further precache statements are errors
2096 sv.state = ss_active;
2097 prog->allowworldwrites = false;
2099 // run two frames to allow everything to settle
2100 for (i = 0;i < 2;i++)
2108 // create a baseline for more efficient communications
2109 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
2110 SV_CreateBaseline ();
2112 // send serverinfo to all connected clients, and set up botclients coming back from a level change
2113 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2115 if (!host_client->active)
2117 if (host_client->netconnection)
2118 SV_SendServerinfo(host_client);
2122 // if client is a botclient coming from a level change, we need to
2123 // set up client info that normally requires networking
2125 // copy spawn parms out of the client_t
2126 for (j=0 ; j< NUM_SPAWN_PARMS ; j++)
2127 (&prog->globals.server->parm1)[j] = host_client->spawn_parms[j];
2129 // call the spawn function
2130 host_client->clientconnectcalled = true;
2131 prog->globals.server->time = sv.time;
2132 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
2133 PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
2134 PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
2135 host_client->spawned = true;
2139 Con_DPrint("Server spawned.\n");
2140 NetConn_Heartbeat (2);
2145 /////////////////////////////////////////////////////
2148 void SV_VM_CB_BeginIncreaseEdicts(void)
2153 PRVM_Free( sv.moved_edicts );
2154 sv.moved_edicts = (prvm_edict_t **)PRVM_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
2156 // links don't survive the transition, so unlink everything
2157 for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
2159 if (!ent->priv.server->free)
2160 World_UnlinkEdict(prog->edicts + i);
2161 memset(&ent->priv.server->areagrid, 0, sizeof(ent->priv.server->areagrid));
2163 World_Clear(&sv.world);
2166 void SV_VM_CB_EndIncreaseEdicts(void)
2171 // link every entity except world
2172 for (i = 1, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
2173 if (!ent->priv.server->free)
2174 SV_LinkEdict(ent, false);
2177 void SV_VM_CB_InitEdict(prvm_edict_t *e)
2179 // LordHavoc: for consistency set these here
2180 int num = PRVM_NUM_FOR_EDICT(e) - 1;
2182 e->priv.server->move = false; // don't move on first frame
2184 if (num >= 0 && num < svs.maxclients)
2187 // set colormap and team on newly created player entity
2188 e->fields.server->colormap = num + 1;
2189 e->fields.server->team = (svs.clients[num].colors & 15) + 1;
2190 // set netname/clientcolors back to client values so that
2191 // DP_SV_CLIENTNAME and DP_SV_CLIENTCOLORS will not immediately
2193 e->fields.server->netname = PRVM_SetEngineString(svs.clients[num].name);
2194 if ((val = PRVM_GETEDICTFIELDVALUE(e, prog->fieldoffsets.clientcolors)))
2195 val->_float = svs.clients[num].colors;
2196 // NEXUIZ_PLAYERMODEL and NEXUIZ_PLAYERSKIN
2197 if( prog->fieldoffsets.playermodel >= 0 )
2198 PRVM_GETEDICTFIELDVALUE(e, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(svs.clients[num].playermodel);
2199 if( prog->fieldoffsets.playerskin >= 0 )
2200 PRVM_GETEDICTFIELDVALUE(e, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(svs.clients[num].playerskin);
2204 void SV_VM_CB_FreeEdict(prvm_edict_t *ed)
2206 World_UnlinkEdict(ed); // unlink from world bsp
2208 ed->fields.server->model = 0;
2209 ed->fields.server->takedamage = 0;
2210 ed->fields.server->modelindex = 0;
2211 ed->fields.server->colormap = 0;
2212 ed->fields.server->skin = 0;
2213 ed->fields.server->frame = 0;
2214 VectorClear(ed->fields.server->origin);
2215 VectorClear(ed->fields.server->angles);
2216 ed->fields.server->nextthink = -1;
2217 ed->fields.server->solid = 0;
2220 void SV_VM_CB_CountEdicts(void)
2224 int active, models, solid, step;
2226 active = models = solid = step = 0;
2227 for (i=0 ; i<prog->num_edicts ; i++)
2229 ent = PRVM_EDICT_NUM(i);
2230 if (ent->priv.server->free)
2233 if (ent->fields.server->solid)
2235 if (ent->fields.server->model)
2237 if (ent->fields.server->movetype == MOVETYPE_STEP)
2241 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
2242 Con_Printf("active :%3i\n", active);
2243 Con_Printf("view :%3i\n", models);
2244 Con_Printf("touch :%3i\n", solid);
2245 Con_Printf("step :%3i\n", step);
2248 qboolean SV_VM_CB_LoadEdict(prvm_edict_t *ent)
2250 // remove things from different skill levels or deathmatch
2251 if (gamemode != GAME_TRANSFUSION) //Transfusion does this in QC
2253 if (deathmatch.integer)
2255 if (((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_DEATHMATCH))
2260 else if ((current_skill <= 0 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_EASY ))
2261 || (current_skill == 1 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_MEDIUM))
2262 || (current_skill >= 2 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_HARD )))
2270 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)"};
2271 cvar_t nomonsters = {0, "nomonsters", "0", "unused cvar in quake, can be used by mods"};
2272 cvar_t gamecfg = {0, "gamecfg", "0", "unused cvar in quake, can be used by mods"};
2273 cvar_t scratch1 = {0, "scratch1", "0", "unused cvar in quake, can be used by mods"};
2274 cvar_t scratch2 = {0,"scratch2", "0", "unused cvar in quake, can be used by mods"};
2275 cvar_t scratch3 = {0, "scratch3", "0", "unused cvar in quake, can be used by mods"};
2276 cvar_t scratch4 = {0, "scratch4", "0", "unused cvar in quake, can be used by mods"};
2277 cvar_t savedgamecfg = {CVAR_SAVE, "savedgamecfg", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2278 cvar_t saved1 = {CVAR_SAVE, "saved1", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2279 cvar_t saved2 = {CVAR_SAVE, "saved2", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2280 cvar_t saved3 = {CVAR_SAVE, "saved3", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2281 cvar_t saved4 = {CVAR_SAVE, "saved4", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2282 cvar_t nehx00 = {0, "nehx00", "0", "nehahra data storage cvar (used in singleplayer)"};
2283 cvar_t nehx01 = {0, "nehx01", "0", "nehahra data storage cvar (used in singleplayer)"};
2284 cvar_t nehx02 = {0, "nehx02", "0", "nehahra data storage cvar (used in singleplayer)"};
2285 cvar_t nehx03 = {0, "nehx03", "0", "nehahra data storage cvar (used in singleplayer)"};
2286 cvar_t nehx04 = {0, "nehx04", "0", "nehahra data storage cvar (used in singleplayer)"};
2287 cvar_t nehx05 = {0, "nehx05", "0", "nehahra data storage cvar (used in singleplayer)"};
2288 cvar_t nehx06 = {0, "nehx06", "0", "nehahra data storage cvar (used in singleplayer)"};
2289 cvar_t nehx07 = {0, "nehx07", "0", "nehahra data storage cvar (used in singleplayer)"};
2290 cvar_t nehx08 = {0, "nehx08", "0", "nehahra data storage cvar (used in singleplayer)"};
2291 cvar_t nehx09 = {0, "nehx09", "0", "nehahra data storage cvar (used in singleplayer)"};
2292 cvar_t nehx10 = {0, "nehx10", "0", "nehahra data storage cvar (used in singleplayer)"};
2293 cvar_t nehx11 = {0, "nehx11", "0", "nehahra data storage cvar (used in singleplayer)"};
2294 cvar_t nehx12 = {0, "nehx12", "0", "nehahra data storage cvar (used in singleplayer)"};
2295 cvar_t nehx13 = {0, "nehx13", "0", "nehahra data storage cvar (used in singleplayer)"};
2296 cvar_t nehx14 = {0, "nehx14", "0", "nehahra data storage cvar (used in singleplayer)"};
2297 cvar_t nehx15 = {0, "nehx15", "0", "nehahra data storage cvar (used in singleplayer)"};
2298 cvar_t nehx16 = {0, "nehx16", "0", "nehahra data storage cvar (used in singleplayer)"};
2299 cvar_t nehx17 = {0, "nehx17", "0", "nehahra data storage cvar (used in singleplayer)"};
2300 cvar_t nehx18 = {0, "nehx18", "0", "nehahra data storage cvar (used in singleplayer)"};
2301 cvar_t nehx19 = {0, "nehx19", "0", "nehahra data storage cvar (used in singleplayer)"};
2302 cvar_t cutscene = {0, "cutscene", "1", "enables cutscenes in nehahra, can be used by other mods"};
2304 void SV_VM_Init(void)
2306 Cvar_RegisterVariable (&pr_checkextension);
2307 Cvar_RegisterVariable (&nomonsters);
2308 Cvar_RegisterVariable (&gamecfg);
2309 Cvar_RegisterVariable (&scratch1);
2310 Cvar_RegisterVariable (&scratch2);
2311 Cvar_RegisterVariable (&scratch3);
2312 Cvar_RegisterVariable (&scratch4);
2313 Cvar_RegisterVariable (&savedgamecfg);
2314 Cvar_RegisterVariable (&saved1);
2315 Cvar_RegisterVariable (&saved2);
2316 Cvar_RegisterVariable (&saved3);
2317 Cvar_RegisterVariable (&saved4);
2318 // LordHavoc: Nehahra uses these to pass data around cutscene demos
2319 if (gamemode == GAME_NEHAHRA)
2321 Cvar_RegisterVariable (&nehx00);
2322 Cvar_RegisterVariable (&nehx01);
2323 Cvar_RegisterVariable (&nehx02);
2324 Cvar_RegisterVariable (&nehx03);
2325 Cvar_RegisterVariable (&nehx04);
2326 Cvar_RegisterVariable (&nehx05);
2327 Cvar_RegisterVariable (&nehx06);
2328 Cvar_RegisterVariable (&nehx07);
2329 Cvar_RegisterVariable (&nehx08);
2330 Cvar_RegisterVariable (&nehx09);
2331 Cvar_RegisterVariable (&nehx10);
2332 Cvar_RegisterVariable (&nehx11);
2333 Cvar_RegisterVariable (&nehx12);
2334 Cvar_RegisterVariable (&nehx13);
2335 Cvar_RegisterVariable (&nehx14);
2336 Cvar_RegisterVariable (&nehx15);
2337 Cvar_RegisterVariable (&nehx16);
2338 Cvar_RegisterVariable (&nehx17);
2339 Cvar_RegisterVariable (&nehx18);
2340 Cvar_RegisterVariable (&nehx19);
2342 Cvar_RegisterVariable (&cutscene); // for Nehahra but useful to other mods as well
2345 #define REQFIELDS (sizeof(reqfields) / sizeof(prvm_required_field_t))
2347 prvm_required_field_t reqfields[] =
2349 {ev_entity, "cursor_trace_ent"},
2350 {ev_entity, "drawonlytoclient"},
2351 {ev_entity, "exteriormodeltoclient"},
2352 {ev_entity, "nodrawtoclient"},
2353 {ev_entity, "tag_entity"},
2354 {ev_entity, "viewmodelforclient"},
2355 {ev_float, "alpha"},
2356 {ev_float, "ammo_cells1"},
2357 {ev_float, "ammo_lava_nails"},
2358 {ev_float, "ammo_multi_rockets"},
2359 {ev_float, "ammo_nails1"},
2360 {ev_float, "ammo_plasma"},
2361 {ev_float, "ammo_rockets1"},
2362 {ev_float, "ammo_shells1"},
2363 {ev_float, "button3"},
2364 {ev_float, "button4"},
2365 {ev_float, "button5"},
2366 {ev_float, "button6"},
2367 {ev_float, "button7"},
2368 {ev_float, "button8"},
2369 {ev_float, "button9"},
2370 {ev_float, "button10"},
2371 {ev_float, "button11"},
2372 {ev_float, "button12"},
2373 {ev_float, "button13"},
2374 {ev_float, "button14"},
2375 {ev_float, "button15"},
2376 {ev_float, "button16"},
2377 {ev_float, "buttonchat"},
2378 {ev_float, "buttonuse"},
2379 {ev_float, "clientcolors"},
2380 {ev_float, "cursor_active"},
2381 {ev_float, "fullbright"},
2382 {ev_float, "glow_color"},
2383 {ev_float, "glow_size"},
2384 {ev_float, "glow_trail"},
2385 {ev_float, "gravity"},
2386 {ev_float, "idealpitch"},
2387 {ev_float, "items2"},
2388 {ev_float, "light_lev"},
2389 {ev_float, "pflags"},
2391 {ev_float, "pitch_speed"},
2392 {ev_float, "pmodel"},
2393 {ev_float, "renderamt"}, // HalfLife support
2394 {ev_float, "rendermode"}, // HalfLife support
2395 {ev_float, "scale"},
2396 {ev_float, "style"},
2397 {ev_float, "tag_index"},
2398 {ev_float, "Version"},
2399 {ev_float, "viewzoom"},
2400 {ev_vector, "color"},
2401 {ev_vector, "colormod"},
2402 {ev_vector, "cursor_screen"},
2403 {ev_vector, "cursor_trace_endpos"},
2404 {ev_vector, "cursor_trace_start"},
2405 {ev_vector, "movement"},
2406 {ev_vector, "punchvector"},
2407 {ev_string, "playermodel"},
2408 {ev_string, "playerskin"},
2409 {ev_function, "SendEntity"},
2410 {ev_function, "customizeentityforclient"},
2411 // DRESK - Support for Entity Contents Transition Event
2412 {ev_function, "contentstransition"},
2415 void SV_VM_Setup(void)
2417 extern cvar_t csqc_progname; //[515]: csqc crc check and right csprogs name according to progs.dat
2418 extern cvar_t csqc_progcrc;
2419 extern cvar_t csqc_progsize;
2420 size_t csprogsdatasize;
2422 PRVM_InitProg( PRVM_SERVERPROG );
2424 // allocate the mempools
2425 // TODO: move the magic numbers/constants into #defines [9/13/2006 Black]
2426 prog->progs_mempool = Mem_AllocPool("Server Progs", 0, NULL);
2427 prog->builtins = vm_sv_builtins;
2428 prog->numbuiltins = vm_sv_numbuiltins;
2429 prog->headercrc = PROGHEADER_CRC;
2430 prog->max_edicts = 512;
2431 prog->limit_edicts = MAX_EDICTS;
2432 prog->reserved_edicts = svs.maxclients;
2433 prog->edictprivate_size = sizeof(edict_engineprivate_t);
2434 prog->name = "server";
2435 prog->extensionstring = vm_sv_extensions;
2436 prog->loadintoworld = true;
2438 prog->begin_increase_edicts = SV_VM_CB_BeginIncreaseEdicts;
2439 prog->end_increase_edicts = SV_VM_CB_EndIncreaseEdicts;
2440 prog->init_edict = SV_VM_CB_InitEdict;
2441 prog->free_edict = SV_VM_CB_FreeEdict;
2442 prog->count_edicts = SV_VM_CB_CountEdicts;
2443 prog->load_edict = SV_VM_CB_LoadEdict;
2444 prog->init_cmd = VM_SV_Cmd_Init;
2445 prog->reset_cmd = VM_SV_Cmd_Reset;
2446 prog->error_cmd = Host_Error;
2448 // TODO: add a requiredfuncs list (ask LH if this is necessary at all)
2449 PRVM_LoadProgs( sv_progs.string, 0, NULL, REQFIELDS, reqfields, 0, NULL );
2451 VM_AutoSentStats_Clear();//[515]: csqc
2452 EntityFrameCSQC_ClearVersions();//[515]: csqc
2456 // 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
2457 sv.csqc_progname[0] = 0;
2458 sv.csqc_progcrc = FS_CRCFile(csqc_progname.string, &csprogsdatasize);
2459 sv.csqc_progsize = csprogsdatasize;
2460 if (sv.csqc_progsize > 0)
2462 strlcpy(sv.csqc_progname, csqc_progname.string, sizeof(sv.csqc_progname));
2463 Con_DPrintf("server detected csqc progs file \"%s\" with size %i and crc %i\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc);
2467 void SV_VM_Begin(void)
2470 PRVM_SetProg( PRVM_SERVERPROG );
2472 prog->globals.server->time = (float) sv.time;
2475 void SV_VM_End(void)