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_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!)"};
50 static cvar_t sv_cullentities_stats = {0, "sv_cullentities_stats", "0", "displays stats on network entities culled by various methods for each client"};
51 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)"};
53 cvar_t sv_gameplayfix_grenadebouncedownslopes = {0, "sv_gameplayfix_grenadebouncedownslopes", "1", "prevents MOVETYPE_BOUNCE (grenades) from getting stuck when fired down a downward sloping surface"};
54 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"};
55 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)"};
56 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)"};
57 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"};
58 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"};
59 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"};
60 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"};
61 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"};
62 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"};
63 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)"};
65 cvar_t sv_progs = {0, "sv_progs", "progs.dat", "selects which quakec progs.dat file to run" };
67 // TODO: move these cvars here
68 extern cvar_t sv_clmovement_enable;
69 extern cvar_t sv_clmovement_minping;
70 extern cvar_t sv_clmovement_minping_disabletime;
71 extern cvar_t sv_clmovement_waitforinput;
76 mempool_t *sv_mempool = NULL;
78 //============================================================================
80 extern void SV_Phys_Init (void);
81 static void SV_SaveEntFile_f(void);
82 static void SV_StartDownload_f(void);
83 static void SV_Download_f(void);
85 void SV_AreaStats_f(void)
87 World_PrintAreaStats(&sv.world, "server");
97 // init the csqc progs cvars, since they are updated/used by the server code
98 // TODO: fix this since this is a quick hack to make some of [515]'s broken code run ;) [9/13/2006 Black]
99 extern cvar_t csqc_progname; //[515]: csqc crc check and right csprogs name according to progs.dat
100 extern cvar_t csqc_progcrc;
101 extern cvar_t csqc_progsize;
102 Cvar_RegisterVariable (&csqc_progname);
103 Cvar_RegisterVariable (&csqc_progcrc);
104 Cvar_RegisterVariable (&csqc_progsize);
106 Cmd_AddCommand("sv_saveentfile", SV_SaveEntFile_f, "save map entities to .ent file (to allow external editing)");
107 Cmd_AddCommand("sv_areastats", SV_AreaStats_f, "prints statistics on entity culling during collision traces");
108 Cmd_AddCommand_WithClientCommand("sv_startdownload", NULL, SV_StartDownload_f, "begins sending a file to the client (network protocol use only)");
109 Cmd_AddCommand_WithClientCommand("download", NULL, SV_Download_f, "downloads a specified file from the server");
110 Cvar_RegisterVariable (&sv_maxvelocity);
111 Cvar_RegisterVariable (&sv_gravity);
112 Cvar_RegisterVariable (&sv_friction);
113 Cvar_RegisterVariable (&sv_waterfriction);
114 Cvar_RegisterVariable (&sv_edgefriction);
115 Cvar_RegisterVariable (&sv_stopspeed);
116 Cvar_RegisterVariable (&sv_maxspeed);
117 Cvar_RegisterVariable (&sv_maxairspeed);
118 Cvar_RegisterVariable (&sv_accelerate);
119 Cvar_RegisterVariable (&sv_airaccelerate);
120 Cvar_RegisterVariable (&sv_wateraccelerate);
121 Cvar_RegisterVariable (&sv_clmovement_enable);
122 Cvar_RegisterVariable (&sv_clmovement_minping);
123 Cvar_RegisterVariable (&sv_clmovement_minping_disabletime);
124 Cvar_RegisterVariable (&sv_clmovement_waitforinput);
125 Cvar_RegisterVariable (&sv_idealpitchscale);
126 Cvar_RegisterVariable (&sv_aim);
127 Cvar_RegisterVariable (&sv_nostep);
128 Cvar_RegisterVariable (&sv_cullentities_pvs);
129 Cvar_RegisterVariable (&sv_cullentities_trace);
130 Cvar_RegisterVariable (&sv_cullentities_nevercullbmodels);
131 Cvar_RegisterVariable (&sv_cullentities_stats);
132 Cvar_RegisterVariable (&sv_entpatch);
133 Cvar_RegisterVariable (&sv_gameplayfix_grenadebouncedownslopes);
134 Cvar_RegisterVariable (&sv_gameplayfix_noairborncorpse);
135 Cvar_RegisterVariable (&sv_gameplayfix_stepdown);
136 Cvar_RegisterVariable (&sv_gameplayfix_stepwhilejumping);
137 Cvar_RegisterVariable (&sv_gameplayfix_swiminbmodels);
138 Cvar_RegisterVariable (&sv_gameplayfix_setmodelrealbox);
139 Cvar_RegisterVariable (&sv_gameplayfix_blowupfallenzombies);
140 Cvar_RegisterVariable (&sv_gameplayfix_findradiusdistancetobox);
141 Cvar_RegisterVariable (&sv_gameplayfix_qwplayerphysics);
142 Cvar_RegisterVariable (&sv_gameplayfix_upwardvelocityclearsongroundflag);
143 Cvar_RegisterVariable (&sv_gameplayfix_droptofloorstartsolid);
144 Cvar_RegisterVariable (&sv_protocolname);
145 Cvar_RegisterVariable (&sv_ratelimitlocalplayer);
146 Cvar_RegisterVariable (&sv_maxrate);
147 Cvar_RegisterVariable (&sv_allowdownloads);
148 Cvar_RegisterVariable (&sv_allowdownloads_inarchive);
149 Cvar_RegisterVariable (&sv_allowdownloads_archive);
150 Cvar_RegisterVariable (&sv_allowdownloads_config);
151 Cvar_RegisterVariable (&sv_allowdownloads_dlcache);
152 Cvar_RegisterVariable (&sv_progs);
157 sv_mempool = Mem_AllocPool("server", 0, NULL);
160 static void SV_SaveEntFile_f(void)
162 char basename[MAX_QPATH];
163 if (!sv.active || !sv.worldmodel)
165 Con_Print("Not running a server\n");
168 FS_StripExtension(sv.worldmodel->name, basename, sizeof(basename));
169 FS_WriteFile(va("%s.ent", basename), sv.worldmodel->brush.entities, (fs_offset_t)strlen(sv.worldmodel->brush.entities));
174 =============================================================================
178 =============================================================================
185 Make sure the event gets sent to all clients
188 void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count)
192 if (sv.datagram.cursize > MAX_PACKETFRAGMENT-18)
194 MSG_WriteByte (&sv.datagram, svc_particle);
195 MSG_WriteCoord (&sv.datagram, org[0], sv.protocol);
196 MSG_WriteCoord (&sv.datagram, org[1], sv.protocol);
197 MSG_WriteCoord (&sv.datagram, org[2], sv.protocol);
198 for (i=0 ; i<3 ; i++)
199 MSG_WriteChar (&sv.datagram, (int)bound(-128, dir[i]*16, 127));
200 MSG_WriteByte (&sv.datagram, count);
201 MSG_WriteByte (&sv.datagram, color);
208 Make sure the event gets sent to all clients
211 void SV_StartEffect (vec3_t org, int modelindex, int startframe, int framecount, int framerate)
213 if (modelindex >= 256 || startframe >= 256)
215 if (sv.datagram.cursize > MAX_PACKETFRAGMENT-19)
217 MSG_WriteByte (&sv.datagram, svc_effect2);
218 MSG_WriteCoord (&sv.datagram, org[0], sv.protocol);
219 MSG_WriteCoord (&sv.datagram, org[1], sv.protocol);
220 MSG_WriteCoord (&sv.datagram, org[2], sv.protocol);
221 MSG_WriteShort (&sv.datagram, modelindex);
222 MSG_WriteShort (&sv.datagram, startframe);
223 MSG_WriteByte (&sv.datagram, framecount);
224 MSG_WriteByte (&sv.datagram, framerate);
228 if (sv.datagram.cursize > MAX_PACKETFRAGMENT-17)
230 MSG_WriteByte (&sv.datagram, svc_effect);
231 MSG_WriteCoord (&sv.datagram, org[0], sv.protocol);
232 MSG_WriteCoord (&sv.datagram, org[1], sv.protocol);
233 MSG_WriteCoord (&sv.datagram, org[2], sv.protocol);
234 MSG_WriteByte (&sv.datagram, modelindex);
235 MSG_WriteByte (&sv.datagram, startframe);
236 MSG_WriteByte (&sv.datagram, framecount);
237 MSG_WriteByte (&sv.datagram, framerate);
245 Each entity can have eight independant sound sources, like voice,
248 Channel 0 is an auto-allocate channel, the others override anything
249 already running on that entity/channel pair.
251 An attenuation of 0 will play full volume everywhere in the level.
252 Larger attenuations will drop off. (max 4 attenuation)
256 void SV_StartSound (prvm_edict_t *entity, int channel, const char *sample, int volume, float attenuation)
258 int sound_num, field_mask, i, ent;
260 if (volume < 0 || volume > 255)
262 Con_Printf ("SV_StartSound: volume = %i\n", volume);
266 if (attenuation < 0 || attenuation > 4)
268 Con_Printf ("SV_StartSound: attenuation = %f\n", attenuation);
272 if (channel < 0 || channel > 7)
274 Con_Printf ("SV_StartSound: channel = %i\n", channel);
278 if (sv.datagram.cursize > MAX_PACKETFRAGMENT-21)
281 // find precache number for sound
282 sound_num = SV_SoundIndex(sample, 1);
286 ent = PRVM_NUM_FOR_EDICT(entity);
289 if (volume != DEFAULT_SOUND_PACKET_VOLUME)
290 field_mask |= SND_VOLUME;
291 if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)
292 field_mask |= SND_ATTENUATION;
294 field_mask |= SND_LARGEENTITY;
295 if (sound_num >= 256 || channel >= 8)
296 field_mask |= SND_LARGESOUND;
298 // directed messages go only to the entity they are targeted on
299 MSG_WriteByte (&sv.datagram, svc_sound);
300 MSG_WriteByte (&sv.datagram, field_mask);
301 if (field_mask & SND_VOLUME)
302 MSG_WriteByte (&sv.datagram, volume);
303 if (field_mask & SND_ATTENUATION)
304 MSG_WriteByte (&sv.datagram, (int)(attenuation*64));
305 if (field_mask & SND_LARGEENTITY)
307 MSG_WriteShort (&sv.datagram, ent);
308 MSG_WriteByte (&sv.datagram, channel);
311 MSG_WriteShort (&sv.datagram, (ent<<3) | channel);
312 if (field_mask & SND_LARGESOUND)
313 MSG_WriteShort (&sv.datagram, sound_num);
315 MSG_WriteByte (&sv.datagram, sound_num);
316 for (i = 0;i < 3;i++)
317 MSG_WriteCoord (&sv.datagram, entity->fields.server->origin[i]+0.5*(entity->fields.server->mins[i]+entity->fields.server->maxs[i]), sv.protocol);
321 ==============================================================================
325 ==============================================================================
332 Sends the first message from the server to a connected client.
333 This will be sent on the initial connection and upon each server load.
336 void SV_SendServerinfo (client_t *client)
341 // we know that this client has a netconnection and thus is not a bot
343 // edicts get reallocated on level changes, so we need to update it here
344 client->edict = PRVM_EDICT_NUM((client - svs.clients) + 1);
346 // clear cached stuff that depends on the level
347 client->weaponmodel[0] = 0;
348 client->weaponmodelindex = 0;
350 // LordHavoc: clear entityframe tracking
351 client->latestframenum = 0;
353 if (client->entitydatabase)
354 EntityFrame_FreeDatabase(client->entitydatabase);
355 if (client->entitydatabase4)
356 EntityFrame4_FreeDatabase(client->entitydatabase4);
357 if (client->entitydatabase5)
358 EntityFrame5_FreeDatabase(client->entitydatabase5);
360 if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE)
362 if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3)
363 client->entitydatabase = EntityFrame_AllocDatabase(sv_mempool);
364 else if (sv.protocol == PROTOCOL_DARKPLACES4)
365 client->entitydatabase4 = EntityFrame4_AllocDatabase(sv_mempool);
367 client->entitydatabase5 = EntityFrame5_AllocDatabase(sv_mempool);
370 SZ_Clear (&client->netconnection->message);
371 MSG_WriteByte (&client->netconnection->message, svc_print);
372 dpsnprintf (message, sizeof (message), "\nServer: %s build %s (progs %i crc)", gamename, buildstring, prog->filecrc);
373 MSG_WriteString (&client->netconnection->message,message);
375 //[515]: init csprogs according to version of svprogs, check the crc, etc.
376 if (sv.csqc_progname[0])
379 Con_DPrintf("sending csqc info to client (\"%s\" with size %i and crc %i)\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc);
380 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
381 MSG_WriteString (&client->netconnection->message, va("csqc_progname %s\n", sv.csqc_progname));
382 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
383 MSG_WriteString (&client->netconnection->message, va("csqc_progsize %i\n", sv.csqc_progsize));
384 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
385 MSG_WriteString (&client->netconnection->message, va("csqc_progcrc %i\n", sv.csqc_progcrc));
386 //[515]: init stufftext string (it is sent before svc_serverinfo)
387 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.SV_InitCmd);
390 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
391 MSG_WriteString (&client->netconnection->message, va("%s\n", PRVM_GetString(val->string)));
395 if (sv_allowdownloads.integer)
397 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
398 MSG_WriteString (&client->netconnection->message, "cl_serverextension_download 1");
401 MSG_WriteByte (&client->netconnection->message, svc_serverinfo);
402 MSG_WriteLong (&client->netconnection->message, Protocol_NumberForEnum(sv.protocol));
403 MSG_WriteByte (&client->netconnection->message, svs.maxclients);
405 if (!coop.integer && deathmatch.integer)
406 MSG_WriteByte (&client->netconnection->message, GAME_DEATHMATCH);
408 MSG_WriteByte (&client->netconnection->message, GAME_COOP);
410 MSG_WriteString (&client->netconnection->message,PRVM_GetString(prog->edicts->fields.server->message));
412 for (i = 1;i < MAX_MODELS && sv.model_precache[i][0];i++)
413 MSG_WriteString (&client->netconnection->message, sv.model_precache[i]);
414 MSG_WriteByte (&client->netconnection->message, 0);
416 for (i = 1;i < MAX_SOUNDS && sv.sound_precache[i][0];i++)
417 MSG_WriteString (&client->netconnection->message, sv.sound_precache[i]);
418 MSG_WriteByte (&client->netconnection->message, 0);
421 MSG_WriteByte (&client->netconnection->message, svc_cdtrack);
422 MSG_WriteByte (&client->netconnection->message, (int)prog->edicts->fields.server->sounds);
423 MSG_WriteByte (&client->netconnection->message, (int)prog->edicts->fields.server->sounds);
426 MSG_WriteByte (&client->netconnection->message, svc_setview);
427 MSG_WriteShort (&client->netconnection->message, PRVM_NUM_FOR_EDICT(client->edict));
429 MSG_WriteByte (&client->netconnection->message, svc_signonnum);
430 MSG_WriteByte (&client->netconnection->message, 1);
435 host_client = client;
436 Curl_SendRequirements();
440 client->spawned = false; // need prespawn, spawn, etc
447 Initializes a client_t for a new net connection. This will only be called
448 once for a player each game, not once for each level change.
451 void SV_ConnectClient (int clientnum, netconn_t *netconnection)
455 float spawn_parms[NUM_SPAWN_PARMS];
457 client = svs.clients + clientnum;
459 if(netconnection)//[515]: bots don't play with csqc =)
460 EntityFrameCSQC_InitClientVersions(clientnum, false);
462 // set up the client_t
464 memcpy (spawn_parms, client->spawn_parms, sizeof(spawn_parms));
465 memset (client, 0, sizeof(*client));
466 client->active = true;
467 client->netconnection = netconnection;
469 Con_DPrintf("Client %s connected\n", client->netconnection ? client->netconnection->address : "botclient");
471 strlcpy(client->name, "unconnected", sizeof(client->name));
472 strlcpy(client->old_name, "unconnected", sizeof(client->old_name));
473 client->spawned = false;
474 client->edict = PRVM_EDICT_NUM(clientnum+1);
475 if (client->netconnection)
476 client->netconnection->message.allowoverflow = true; // we can catch it
477 // updated by receiving "rate" command from client
478 client->rate = NET_MINRATE;
479 // no limits for local player
480 if (client->netconnection && LHNETADDRESS_GetAddressType(&client->netconnection->peeraddress) == LHNETADDRESSTYPE_LOOP)
481 client->rate = 1000000000;
482 client->connecttime = realtime;
485 memcpy (client->spawn_parms, spawn_parms, sizeof(spawn_parms));
488 // call the progs to get default spawn parms for the new client
489 // set self to world to intentionally cause errors with broken SetNewParms code in some mods
490 prog->globals.server->self = 0;
491 PRVM_ExecuteProgram (prog->globals.server->SetNewParms, "QC function SetNewParms is missing");
492 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
493 client->spawn_parms[i] = (&prog->globals.server->parm1)[i];
495 // set up the entity for this client (including .colormap, .team, etc)
496 PRVM_ED_ClearEdict(client->edict);
499 // don't call SendServerinfo for a fresh botclient because its fields have
500 // not been set up by the qc yet
501 if (client->netconnection)
502 SV_SendServerinfo (client);
504 client->spawned = true;
509 ===============================================================================
513 ===============================================================================
522 void SV_ClearDatagram (void)
524 SZ_Clear (&sv.datagram);
528 =============================================================================
530 The PVS must include a small area around the client to allow head bobbing
531 or other small motion on the client side. Otherwise, a bob might cause an
532 entity that should be visible to not show up, especially when the bob
535 =============================================================================
538 int sv_writeentitiestoclient_pvsbytes;
539 unsigned char sv_writeentitiestoclient_pvs[MAX_MAP_LEAFS/8];
541 static int numsendentities;
542 static entity_state_t sendentities[MAX_EDICTS];
543 static entity_state_t *sendentitiesindex[MAX_EDICTS];
545 qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int e)
548 unsigned int modelindex, effects, flags, glowsize, lightstyle, lightpflags, light[4], specialvisibilityradius;
549 unsigned int customizeentityforclient;
551 vec3_t cullmins, cullmaxs;
555 // EF_NODRAW prevents sending for any reason except for your own
556 // client, so we must keep all clients in this superset
557 effects = (unsigned)ent->fields.server->effects;
559 // we can omit invisible entities with no effects that are not clients
560 // LordHavoc: this could kill tags attached to an invisible entity, I
561 // just hope we never have to support that case
562 i = (int)ent->fields.server->modelindex;
563 modelindex = (i >= 1 && i < MAX_MODELS && *PRVM_GetString(ent->fields.server->model)) ? i : 0;
566 i = (int)(PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_size)->_float * 0.25f);
567 glowsize = (unsigned char)bound(0, i, 255);
568 if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_trail)->_float)
569 flags |= RENDER_GLOWTRAIL;
571 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[0]*256;
572 light[0] = (unsigned short)bound(0, f, 65535);
573 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[1]*256;
574 light[1] = (unsigned short)bound(0, f, 65535);
575 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[2]*256;
576 light[2] = (unsigned short)bound(0, f, 65535);
577 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.light_lev)->_float;
578 light[3] = (unsigned short)bound(0, f, 65535);
579 lightstyle = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.style)->_float;
580 lightpflags = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.pflags)->_float;
582 if (gamemode == GAME_TENEBRAE)
584 // tenebrae's EF_FULLDYNAMIC conflicts with Q2's EF_NODRAW
588 lightpflags |= PFLAGS_FULLDYNAMIC;
590 // tenebrae's EF_GREEN conflicts with DP's EF_ADDITIVE
594 light[0] = (int)(0.2*256);
595 light[1] = (int)(1.0*256);
596 light[2] = (int)(0.2*256);
598 lightpflags |= PFLAGS_FULLDYNAMIC;
602 specialvisibilityradius = 0;
603 if (lightpflags & PFLAGS_FULLDYNAMIC)
604 specialvisibilityradius = max(specialvisibilityradius, light[3]);
606 specialvisibilityradius = max(specialvisibilityradius, glowsize * 4);
607 if (flags & RENDER_GLOWTRAIL)
608 specialvisibilityradius = max(specialvisibilityradius, 100);
609 if (effects & (EF_BRIGHTFIELD | EF_MUZZLEFLASH | EF_BRIGHTLIGHT | EF_DIMLIGHT | EF_RED | EF_BLUE | EF_FLAME | EF_STARDUST))
611 if (effects & EF_BRIGHTFIELD)
612 specialvisibilityradius = max(specialvisibilityradius, 80);
613 if (effects & EF_MUZZLEFLASH)
614 specialvisibilityradius = max(specialvisibilityradius, 100);
615 if (effects & EF_BRIGHTLIGHT)
616 specialvisibilityradius = max(specialvisibilityradius, 400);
617 if (effects & EF_DIMLIGHT)
618 specialvisibilityradius = max(specialvisibilityradius, 200);
619 if (effects & EF_RED)
620 specialvisibilityradius = max(specialvisibilityradius, 200);
621 if (effects & EF_BLUE)
622 specialvisibilityradius = max(specialvisibilityradius, 200);
623 if (effects & EF_FLAME)
624 specialvisibilityradius = max(specialvisibilityradius, 250);
625 if (effects & EF_STARDUST)
626 specialvisibilityradius = max(specialvisibilityradius, 100);
629 // early culling checks
630 // (final culling is done by SV_MarkWriteEntityStateToClient)
631 customizeentityforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.customizeentityforclient)->function;
632 if (!customizeentityforclient)
634 if (e > svs.maxclients && (!modelindex && !specialvisibilityradius))
636 // this 2 billion unit check is actually to detect NAN origins
637 // (we really don't want to send those)
638 if (VectorLength2(ent->fields.server->origin) > 2000000000.0*2000000000.0)
646 VectorCopy(ent->fields.server->origin, cs->origin);
647 VectorCopy(ent->fields.server->angles, cs->angles);
649 cs->effects = effects;
650 cs->colormap = (unsigned)ent->fields.server->colormap;
651 cs->modelindex = modelindex;
652 cs->skin = (unsigned)ent->fields.server->skin;
653 cs->frame = (unsigned)ent->fields.server->frame;
654 cs->viewmodelforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewmodelforclient)->edict;
655 cs->exteriormodelforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.exteriormodeltoclient)->edict;
656 cs->nodrawtoclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.nodrawtoclient)->edict;
657 cs->drawonlytoclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.drawonlytoclient)->edict;
658 cs->customizeentityforclient = customizeentityforclient;
659 cs->tagentity = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)->edict;
660 cs->tagindex = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_index)->_float;
661 cs->glowsize = glowsize;
663 // don't need to init cs->colormod because the defaultstate did that for us
664 //cs->colormod[0] = cs->colormod[1] = cs->colormod[2] = 32;
665 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.colormod);
666 if (val->vector[0] || val->vector[1] || val->vector[2])
668 i = (int)(val->vector[0] * 32.0f);cs->colormod[0] = bound(0, i, 255);
669 i = (int)(val->vector[1] * 32.0f);cs->colormod[1] = bound(0, i, 255);
670 i = (int)(val->vector[2] * 32.0f);cs->colormod[2] = bound(0, i, 255);
673 cs->modelindex = modelindex;
676 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.alpha)->_float * 255.0f);
680 cs->alpha = (unsigned char)bound(0, i, 255);
683 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderamt)->_float);
687 cs->alpha = (unsigned char)bound(0, i, 255);
691 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale)->_float * 16.0f);
695 cs->scale = (unsigned char)bound(0, i, 255);
699 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_color)->_float);
701 cs->glowcolor = (int)f;
703 if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.fullbright)->_float)
704 cs->effects |= EF_FULLBRIGHT;
706 if (ent->fields.server->movetype == MOVETYPE_STEP)
707 cs->flags |= RENDER_STEP;
708 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)
709 cs->flags |= RENDER_LOWPRECISION;
710 if (ent->fields.server->colormap >= 1024)
711 cs->flags |= RENDER_COLORMAPPED;
712 if (cs->viewmodelforclient)
713 cs->flags |= RENDER_VIEWMODEL; // show relative to the view
715 cs->light[0] = light[0];
716 cs->light[1] = light[1];
717 cs->light[2] = light[2];
718 cs->light[3] = light[3];
719 cs->lightstyle = lightstyle;
720 cs->lightpflags = lightpflags;
722 cs->specialvisibilityradius = specialvisibilityradius;
724 // calculate the visible box of this entity (don't use the physics box
725 // as that is often smaller than a model, and would not count
726 // specialvisibilityradius)
727 if ((model = sv.models[modelindex]))
729 float scale = cs->scale * (1.0f / 16.0f);
730 if (cs->angles[0] || cs->angles[2]) // pitch and roll
732 VectorMA(cs->origin, scale, model->rotatedmins, cullmins);
733 VectorMA(cs->origin, scale, model->rotatedmaxs, cullmaxs);
735 else if (cs->angles[1])
737 VectorMA(cs->origin, scale, model->yawmins, cullmins);
738 VectorMA(cs->origin, scale, model->yawmaxs, cullmaxs);
742 VectorMA(cs->origin, scale, model->normalmins, cullmins);
743 VectorMA(cs->origin, scale, model->normalmaxs, cullmaxs);
748 // if there is no model (or it could not be loaded), use the physics box
749 VectorAdd(cs->origin, ent->fields.server->mins, cullmins);
750 VectorAdd(cs->origin, ent->fields.server->maxs, cullmaxs);
752 if (specialvisibilityradius)
754 cullmins[0] = min(cullmins[0], cs->origin[0] - specialvisibilityradius);
755 cullmins[1] = min(cullmins[1], cs->origin[1] - specialvisibilityradius);
756 cullmins[2] = min(cullmins[2], cs->origin[2] - specialvisibilityradius);
757 cullmaxs[0] = max(cullmaxs[0], cs->origin[0] + specialvisibilityradius);
758 cullmaxs[1] = max(cullmaxs[1], cs->origin[1] + specialvisibilityradius);
759 cullmaxs[2] = max(cullmaxs[2], cs->origin[2] + specialvisibilityradius);
761 if (!VectorCompare(cullmins, ent->priv.server->cullmins) || !VectorCompare(cullmaxs, ent->priv.server->cullmaxs))
763 VectorCopy(cullmins, ent->priv.server->cullmins);
764 VectorCopy(cullmaxs, ent->priv.server->cullmaxs);
765 ent->priv.server->pvs_numclusters = -1;
766 if (sv.worldmodel && sv.worldmodel->brush.FindBoxClusters)
768 i = sv.worldmodel->brush.FindBoxClusters(sv.worldmodel, cullmins, cullmaxs, MAX_ENTITYCLUSTERS, ent->priv.server->pvs_clusterlist);
769 if (i <= MAX_ENTITYCLUSTERS)
770 ent->priv.server->pvs_numclusters = i;
777 void SV_PrepareEntitiesForSending(void)
781 // send all entities that touch the pvs
783 sendentitiesindex[0] = NULL;
784 memset(sendentitiesindex, 0, prog->num_edicts * sizeof(entity_state_t *));
785 for (e = 1, ent = PRVM_NEXT_EDICT(prog->edicts);e < prog->num_edicts;e++, ent = PRVM_NEXT_EDICT(ent))
787 if (!ent->priv.server->free && SV_PrepareEntityForSending(ent, sendentities + numsendentities, e))
789 sendentitiesindex[e] = sendentities + numsendentities;
795 static int sententitiesmark = 0;
796 static int sententities[MAX_EDICTS];
797 static int sententitiesconsideration[MAX_EDICTS];
798 static int sv_writeentitiestoclient_culled_pvs;
799 static int sv_writeentitiestoclient_culled_trace;
800 static int sv_writeentitiestoclient_visibleentities;
801 static int sv_writeentitiestoclient_totalentities;
802 //static entity_frame_t sv_writeentitiestoclient_entityframe;
803 static int sv_writeentitiestoclient_clentnum;
804 static vec3_t sv_writeentitiestoclient_testeye;
805 static client_t *sv_writeentitiestoclient_client;
807 void SV_MarkWriteEntityStateToClient(entity_state_t *s)
813 if (sententitiesconsideration[s->number] == sententitiesmark)
815 sententitiesconsideration[s->number] = sententitiesmark;
816 sv_writeentitiestoclient_totalentities++;
818 if (s->customizeentityforclient)
820 prog->globals.server->self = s->number;
821 prog->globals.server->other = sv_writeentitiestoclient_clentnum;
822 PRVM_ExecuteProgram(s->customizeentityforclient, "customizeentityforclient: NULL function");
823 if(!PRVM_G_FLOAT(OFS_RETURN) || !SV_PrepareEntityForSending(PRVM_EDICT_NUM(s->number), s, s->number))
827 // never reject player
828 if (s->number != sv_writeentitiestoclient_clentnum)
830 // check various rejection conditions
831 if (s->nodrawtoclient == sv_writeentitiestoclient_clentnum)
833 if (s->drawonlytoclient && s->drawonlytoclient != sv_writeentitiestoclient_clentnum)
835 if (s->effects & EF_NODRAW)
837 // LordHavoc: only send entities with a model or important effects
838 if (!s->modelindex && s->specialvisibilityradius == 0)
841 isbmodel = (model = sv.models[s->modelindex]) != NULL && model->name[0] == '*';
842 // viewmodels don't have visibility checking
843 if (s->viewmodelforclient)
845 if (s->viewmodelforclient != sv_writeentitiestoclient_clentnum)
848 else if (s->tagentity)
850 // tag attached entities simply check their parent
851 if (!sendentitiesindex[s->tagentity])
853 SV_MarkWriteEntityStateToClient(sendentitiesindex[s->tagentity]);
854 if (sententities[s->tagentity] != sententitiesmark)
857 // always send world submodels in newer protocols because they don't
858 // generate much traffic (in old protocols they hog bandwidth)
859 // but only if sv_cullentities_alwayssendbmodels is on
860 else if (!(s->effects & EF_NODEPTHTEST) && (!isbmodel || !sv_cullentities_nevercullbmodels.integer || sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE))
862 // entity has survived every check so far, check if visible
863 ed = PRVM_EDICT_NUM(s->number);
865 // if not touching a visible leaf
866 if (sv_cullentities_pvs.integer && sv_writeentitiestoclient_pvsbytes)
868 if (ed->priv.server->pvs_numclusters < 0)
870 // entity too big for clusters list
871 if (sv.worldmodel && sv.worldmodel->brush.BoxTouchingPVS && !sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, sv_writeentitiestoclient_pvs, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
873 sv_writeentitiestoclient_culled_pvs++;
880 // check cached clusters list
881 for (i = 0;i < ed->priv.server->pvs_numclusters;i++)
882 if (CHECKPVSBIT(sv_writeentitiestoclient_pvs, ed->priv.server->pvs_clusterlist[i]))
884 if (i == ed->priv.server->pvs_numclusters)
886 sv_writeentitiestoclient_culled_pvs++;
892 // or not seen by random tracelines
893 if (sv_cullentities_trace.integer && !isbmodel)
895 // LordHavoc: test center first
896 testorigin[0] = (ed->priv.server->cullmins[0] + ed->priv.server->cullmaxs[0]) * 0.5f;
897 testorigin[1] = (ed->priv.server->cullmins[1] + ed->priv.server->cullmaxs[1]) * 0.5f;
898 testorigin[2] = (ed->priv.server->cullmins[2] + ed->priv.server->cullmaxs[2]) * 0.5f;
899 if (sv.worldmodel->brush.TraceLineOfSight(sv.worldmodel, sv_writeentitiestoclient_testeye, testorigin))
900 sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
903 // LordHavoc: test random offsets, to maximize chance of detection
904 testorigin[0] = lhrandom(ed->priv.server->cullmins[0], ed->priv.server->cullmaxs[0]);
905 testorigin[1] = lhrandom(ed->priv.server->cullmins[1], ed->priv.server->cullmaxs[1]);
906 testorigin[2] = lhrandom(ed->priv.server->cullmins[2], ed->priv.server->cullmaxs[2]);
907 if (sv.worldmodel->brush.TraceLineOfSight(sv.worldmodel, sv_writeentitiestoclient_testeye, testorigin))
908 sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
911 if (s->specialvisibilityradius)
913 // LordHavoc: test random offsets, to maximize chance of detection
914 testorigin[0] = lhrandom(ed->priv.server->cullmins[0], ed->priv.server->cullmaxs[0]);
915 testorigin[1] = lhrandom(ed->priv.server->cullmins[1], ed->priv.server->cullmaxs[1]);
916 testorigin[2] = lhrandom(ed->priv.server->cullmins[2], ed->priv.server->cullmaxs[2]);
917 if (sv.worldmodel->brush.TraceLineOfSight(sv.worldmodel, sv_writeentitiestoclient_testeye, testorigin))
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_EDICTFIELDVALUE(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_EDICTFIELDVALUE(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_EDICTFIELDVALUE(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_EDICTFIELDVALUE(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_EDICTFIELDVALUE(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_EDICTFIELDVALUE(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_EDICTFIELDVALUE(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_EDICTFIELDVALUE(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);
1725 // MUST match effectnameindex_t in client.h
1726 static const char *standardeffectnames[EFFECT_TOTAL] =
1734 "TE_SUPERSPIKEQUAD",
1750 "TE_TEI_BIGEXPLOSION",
1768 SV_ParticleEffectIndex
1772 int SV_ParticleEffectIndex(const char *name)
1774 int i, argc, linenumber, effectnameindex;
1775 fs_offset_t filesize;
1776 unsigned char *filedata;
1777 const char *text, *textstart, *textend;
1778 char argv[16][1024];
1779 if (!sv.particleeffectnamesloaded)
1781 sv.particleeffectnamesloaded = true;
1782 memset(sv.particleeffectname, 0, sizeof(sv.particleeffectname));
1783 for (i = 0;i < EFFECT_TOTAL;i++)
1784 strlcpy(sv.particleeffectname[i], standardeffectnames[i], sizeof(sv.particleeffectname[i]));
1785 filedata = FS_LoadFile("effectinfo.txt", tempmempool, true, &filesize);
1788 textstart = (const char *)filedata;
1789 textend = (const char *)filedata + filesize;
1791 for (linenumber = 1;;linenumber++)
1796 if (!COM_ParseToken(&text, true) || !strcmp(com_token, "\n"))
1800 strlcpy(argv[argc], com_token, sizeof(argv[argc]));
1804 if (com_token[0] == 0)
1805 break; // if the loop exited and it's not a \n, it's EOF
1808 if (!strcmp(argv[0], "effect"))
1812 for (effectnameindex = 1;effectnameindex < SV_MAX_PARTICLEEFFECTNAME;effectnameindex++)
1814 if (sv.particleeffectname[effectnameindex][0])
1816 if (!strcmp(sv.particleeffectname[effectnameindex], argv[1]))
1821 strlcpy(sv.particleeffectname[effectnameindex], argv[1], sizeof(sv.particleeffectname[effectnameindex]));
1825 // if we run out of names, abort
1826 if (effectnameindex == SV_MAX_PARTICLEEFFECTNAME)
1828 Con_Printf("effectinfo.txt:%i: too many effects!\n", linenumber);
1837 // search for the name
1838 for (effectnameindex = 1;effectnameindex < SV_MAX_PARTICLEEFFECTNAME && sv.particleeffectname[effectnameindex][0];effectnameindex++)
1839 if (!strcmp(sv.particleeffectname[effectnameindex], name))
1840 return effectnameindex;
1841 // return 0 if we couldn't find it
1851 void SV_CreateBaseline (void)
1853 int i, entnum, large;
1854 prvm_edict_t *svent;
1856 // LordHavoc: clear *all* states (note just active ones)
1857 for (entnum = 0;entnum < prog->max_edicts;entnum++)
1859 // get the current server version
1860 svent = PRVM_EDICT_NUM(entnum);
1862 // LordHavoc: always clear state values, whether the entity is in use or not
1863 svent->priv.server->baseline = defaultstate;
1865 if (svent->priv.server->free)
1867 if (entnum > svs.maxclients && !svent->fields.server->modelindex)
1870 // create entity baseline
1871 VectorCopy (svent->fields.server->origin, svent->priv.server->baseline.origin);
1872 VectorCopy (svent->fields.server->angles, svent->priv.server->baseline.angles);
1873 svent->priv.server->baseline.frame = (int)svent->fields.server->frame;
1874 svent->priv.server->baseline.skin = (int)svent->fields.server->skin;
1875 if (entnum > 0 && entnum <= svs.maxclients)
1877 svent->priv.server->baseline.colormap = entnum;
1878 svent->priv.server->baseline.modelindex = SV_ModelIndex("progs/player.mdl", 1);
1882 svent->priv.server->baseline.colormap = 0;
1883 svent->priv.server->baseline.modelindex = (int)svent->fields.server->modelindex;
1887 if (svent->priv.server->baseline.modelindex & 0xFF00 || svent->priv.server->baseline.frame & 0xFF00)
1890 // add to the message
1892 MSG_WriteByte (&sv.signon, svc_spawnbaseline2);
1894 MSG_WriteByte (&sv.signon, svc_spawnbaseline);
1895 MSG_WriteShort (&sv.signon, entnum);
1899 MSG_WriteShort (&sv.signon, svent->priv.server->baseline.modelindex);
1900 MSG_WriteShort (&sv.signon, svent->priv.server->baseline.frame);
1904 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.modelindex);
1905 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.frame);
1907 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.colormap);
1908 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.skin);
1909 for (i=0 ; i<3 ; i++)
1911 MSG_WriteCoord(&sv.signon, svent->priv.server->baseline.origin[i], sv.protocol);
1912 MSG_WriteAngle(&sv.signon, svent->priv.server->baseline.angles[i], sv.protocol);
1922 Grabs the current state of each client for saving across the
1923 transition to another level
1926 void SV_SaveSpawnparms (void)
1930 svs.serverflags = (int)prog->globals.server->serverflags;
1932 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1934 if (!host_client->active)
1937 // call the progs to get default spawn parms for the new client
1938 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1939 PRVM_ExecuteProgram (prog->globals.server->SetChangeParms, "QC function SetChangeParms is missing");
1940 for (j=0 ; j<NUM_SPAWN_PARMS ; j++)
1941 host_client->spawn_parms[j] = (&prog->globals.server->parm1)[j];
1945 void SV_IncreaseEdicts(void)
1949 int oldmax_edicts = prog->max_edicts;
1950 void *oldedictsengineprivate = prog->edictprivate;
1951 void *oldedictsfields = prog->edictsfields;
1952 void *oldmoved_edicts = sv.moved_edicts;
1954 if (prog->max_edicts >= MAX_EDICTS)
1957 // links don't survive the transition, so unlink everything
1958 for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
1960 if (!ent->priv.server->free)
1961 SV_UnlinkEdict(prog->edicts + i);
1962 memset(&ent->priv.server->areagrid, 0, sizeof(ent->priv.server->areagrid));
1964 World_Clear(&sv.world);
1966 prog->max_edicts = min(prog->max_edicts + 256, MAX_EDICTS);
1967 prog->edictprivate = PR_Alloc(prog->max_edicts * sizeof(edict_engineprivate_t));
1968 prog->edictsfields = PR_Alloc(prog->max_edicts * prog->edict_size);
1969 sv.moved_edicts = PR_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
1971 memcpy(prog->edictprivate, oldedictsengineprivate, oldmax_edicts * sizeof(edict_engineprivate_t));
1972 memcpy(prog->edictsfields, oldedictsfields, oldmax_edicts * prog->edict_size);
1974 for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
1976 ent->priv.vp = (unsigned char*) prog->edictprivate + i * prog->edictprivate_size;
1977 ent->fields.server = (void *)((unsigned char *)prog->edictsfields + i * prog->edict_size);
1978 // link every entity except world
1979 if (!ent->priv.server->free)
1980 SV_LinkEdict(ent, false);
1983 PR_Free(oldedictsengineprivate);
1984 PR_Free(oldedictsfields);
1985 PR_Free(oldmoved_edicts);
1992 This is called at the start of each level
1995 extern float scr_centertime_off;
1997 void SV_SpawnServer (const char *server)
2002 model_t *worldmodel;
2003 char modelname[sizeof(sv.modelname)];
2005 Con_DPrintf("SpawnServer: %s\n", server);
2007 if (cls.state != ca_dedicated)
2008 SCR_BeginLoadingPlaque();
2010 dpsnprintf (modelname, sizeof(modelname), "maps/%s.bsp", server);
2011 worldmodel = Mod_ForName(modelname, false, true, true);
2012 if (!worldmodel || !worldmodel->TraceBox)
2014 Con_Printf("Couldn't load map %s\n", modelname);
2018 // let's not have any servers with no name
2019 if (hostname.string[0] == 0)
2020 Cvar_Set ("hostname", "UNNAMED");
2021 scr_centertime_off = 0;
2023 svs.changelevel_issued = false; // now safe to issue another
2025 // make the map a required file for clients
2026 Curl_ClearRequirements();
2027 Curl_RequireFile(modelname);
2030 // tell all connected clients that we are going to a new level
2035 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
2037 if (client->netconnection)
2039 MSG_WriteByte(&client->netconnection->message, svc_stufftext);
2040 MSG_WriteString(&client->netconnection->message, "reconnect\n");
2047 NetConn_OpenServerPorts(true);
2051 // make cvars consistant
2054 Cvar_SetValue ("deathmatch", 0);
2055 // LordHavoc: it can be useful to have skills outside the range 0-3...
2056 //current_skill = bound(0, (int)(skill.value + 0.5), 3);
2057 //Cvar_SetValue ("skill", (float)current_skill);
2058 current_skill = (int)(skill.value + 0.5);
2061 // set up the new server
2063 memset (&sv, 0, sizeof(sv));
2064 // if running a local client, make sure it doesn't try to access the last
2065 // level's data which is no longer valiud
2068 if(*sv_random_seed.string)
2070 srand(sv_random_seed.integer);
2071 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);
2078 strlcpy (sv.name, server, sizeof (sv.name));
2080 sv.protocol = Protocol_EnumForName(sv_protocolname.string);
2081 if (sv.protocol == PROTOCOL_UNKNOWN)
2084 Protocol_Names(buffer, sizeof(buffer));
2085 Con_Printf("Unknown sv_protocolname \"%s\", valid values are:\n%s\n", sv_protocolname.string, buffer);
2086 sv.protocol = PROTOCOL_QUAKE;
2091 // load progs to get entity field count
2092 //PR_LoadProgs ( sv_progs.string );
2094 // allocate server memory
2095 /*// start out with just enough room for clients and a reasonable estimate of entities
2096 prog->max_edicts = max(svs.maxclients + 1, 512);
2097 prog->max_edicts = min(prog->max_edicts, MAX_EDICTS);
2099 // prvm_edict_t structures (hidden from progs)
2100 prog->edicts = PR_Alloc(MAX_EDICTS * sizeof(prvm_edict_t));
2101 // engine private structures (hidden from progs)
2102 prog->edictprivate = PR_Alloc(prog->max_edicts * sizeof(edict_engineprivate_t));
2103 // progs fields, often accessed by server
2104 prog->edictsfields = PR_Alloc(prog->max_edicts * prog->edict_size);*/
2105 // used by PushMove to move back pushed entities
2106 sv.moved_edicts = (prvm_edict_t **)PRVM_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
2107 /*for (i = 0;i < prog->max_edicts;i++)
2109 ent = prog->edicts + i;
2110 ent->priv.vp = (unsigned char*) prog->edictprivate + i * prog->edictprivate_size;
2111 ent->fields.server = (void *)((unsigned char *)prog->edictsfields + i * prog->edict_size);
2114 // reset client csqc entity versions right away.
2115 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2116 EntityFrameCSQC_InitClientVersions(i, true);
2118 sv.datagram.maxsize = sizeof(sv.datagram_buf);
2119 sv.datagram.cursize = 0;
2120 sv.datagram.data = sv.datagram_buf;
2122 sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf);
2123 sv.reliable_datagram.cursize = 0;
2124 sv.reliable_datagram.data = sv.reliable_datagram_buf;
2126 sv.signon.maxsize = sizeof(sv.signon_buf);
2127 sv.signon.cursize = 0;
2128 sv.signon.data = sv.signon_buf;
2130 // leave slots at start for clients only
2131 //prog->num_edicts = svs.maxclients+1;
2133 sv.state = ss_loading;
2134 prog->allowworldwrites = true;
2137 prog->globals.server->time = sv.time = 1.0;
2140 worldmodel->used = true;
2142 strlcpy (sv.name, server, sizeof (sv.name));
2143 strlcpy(sv.modelname, modelname, sizeof(sv.modelname));
2144 sv.worldmodel = worldmodel;
2145 sv.models[1] = sv.worldmodel;
2148 // clear world interaction links
2150 VectorCopy(sv.worldmodel->normalmins, sv.world.areagrid_mins);
2151 VectorCopy(sv.worldmodel->normalmaxs, sv.world.areagrid_maxs);
2152 World_Clear(&sv.world);
2154 strlcpy(sv.sound_precache[0], "", sizeof(sv.sound_precache[0]));
2156 strlcpy(sv.model_precache[0], "", sizeof(sv.model_precache[0]));
2157 strlcpy(sv.model_precache[1], sv.modelname, sizeof(sv.model_precache[1]));
2158 for (i = 1;i < sv.worldmodel->brush.numsubmodels;i++)
2160 dpsnprintf(sv.model_precache[i+1], sizeof(sv.model_precache[i+1]), "*%i", i);
2161 sv.models[i+1] = Mod_ForName (sv.model_precache[i+1], false, false, false);
2165 // load the rest of the entities
2167 // AK possible hack since num_edicts is still 0
2168 ent = PRVM_EDICT_NUM(0);
2169 memset (ent->fields.server, 0, prog->progs->entityfields * 4);
2170 ent->priv.server->free = false;
2171 ent->fields.server->model = PRVM_SetEngineString(sv.modelname);
2172 ent->fields.server->modelindex = 1; // world model
2173 ent->fields.server->solid = SOLID_BSP;
2174 ent->fields.server->movetype = MOVETYPE_PUSH;
2175 VectorCopy(sv.worldmodel->normalmins, ent->fields.server->mins);
2176 VectorCopy(sv.worldmodel->normalmaxs, ent->fields.server->maxs);
2177 VectorCopy(sv.worldmodel->normalmins, ent->fields.server->absmin);
2178 VectorCopy(sv.worldmodel->normalmaxs, ent->fields.server->absmax);
2181 prog->globals.server->coop = coop.integer;
2183 prog->globals.server->deathmatch = deathmatch.integer;
2185 prog->globals.server->mapname = PRVM_SetEngineString(sv.name);
2187 // serverflags are for cross level information (sigils)
2188 prog->globals.server->serverflags = svs.serverflags;
2190 // we need to reset the spawned flag on all connected clients here so that
2191 // their thinks don't run during startup (before PutClientInServer)
2192 // we also need to set up the client entities now
2193 // and we need to set the ->edict pointers to point into the progs edicts
2194 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2196 host_client->spawned = false;
2197 host_client->edict = PRVM_EDICT_NUM(i + 1);
2198 PRVM_ED_ClearEdict(host_client->edict);
2201 // load replacement entity file if found
2202 if (sv_entpatch.integer && (entities = (char *)FS_LoadFile(va("maps/%s.ent", sv.name), tempmempool, true, NULL)))
2204 Con_Printf("Loaded maps/%s.ent\n", sv.name);
2205 PRVM_ED_LoadFromFile (entities);
2209 PRVM_ED_LoadFromFile (sv.worldmodel->brush.entities);
2212 // LordHavoc: clear world angles (to fix e3m3.bsp)
2213 VectorClear(prog->edicts->fields.server->angles);
2215 // all setup is completed, any further precache statements are errors
2216 sv.state = ss_active;
2217 prog->allowworldwrites = false;
2219 // run two frames to allow everything to settle
2220 for (i = 0;i < 2;i++)
2228 // create a baseline for more efficient communications
2229 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
2230 SV_CreateBaseline ();
2232 // send serverinfo to all connected clients, and set up botclients coming back from a level change
2233 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2235 if (!host_client->active)
2237 if (host_client->netconnection)
2238 SV_SendServerinfo(host_client);
2242 // if client is a botclient coming from a level change, we need to
2243 // set up client info that normally requires networking
2245 // copy spawn parms out of the client_t
2246 for (j=0 ; j< NUM_SPAWN_PARMS ; j++)
2247 (&prog->globals.server->parm1)[j] = host_client->spawn_parms[j];
2249 // call the spawn function
2250 host_client->clientconnectcalled = true;
2251 prog->globals.server->time = sv.time;
2252 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
2253 PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
2254 PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
2255 host_client->spawned = true;
2259 Con_DPrint("Server spawned.\n");
2260 NetConn_Heartbeat (2);
2265 /////////////////////////////////////////////////////
2268 void SV_VM_CB_BeginIncreaseEdicts(void)
2273 PRVM_Free( sv.moved_edicts );
2274 sv.moved_edicts = (prvm_edict_t **)PRVM_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
2276 // links don't survive the transition, so unlink everything
2277 for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
2279 if (!ent->priv.server->free)
2280 World_UnlinkEdict(prog->edicts + i);
2281 memset(&ent->priv.server->areagrid, 0, sizeof(ent->priv.server->areagrid));
2283 World_Clear(&sv.world);
2286 void SV_VM_CB_EndIncreaseEdicts(void)
2291 // link every entity except world
2292 for (i = 1, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
2293 if (!ent->priv.server->free)
2294 SV_LinkEdict(ent, false);
2297 void SV_VM_CB_InitEdict(prvm_edict_t *e)
2299 // LordHavoc: for consistency set these here
2300 int num = PRVM_NUM_FOR_EDICT(e) - 1;
2302 e->priv.server->move = false; // don't move on first frame
2304 if (num >= 0 && num < svs.maxclients)
2307 // set colormap and team on newly created player entity
2308 e->fields.server->colormap = num + 1;
2309 e->fields.server->team = (svs.clients[num].colors & 15) + 1;
2310 // set netname/clientcolors back to client values so that
2311 // DP_SV_CLIENTNAME and DP_SV_CLIENTCOLORS will not immediately
2313 e->fields.server->netname = PRVM_SetEngineString(svs.clients[num].name);
2314 if ((val = PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.clientcolors)))
2315 val->_float = svs.clients[num].colors;
2316 // NEXUIZ_PLAYERMODEL and NEXUIZ_PLAYERSKIN
2317 if( prog->fieldoffsets.playermodel >= 0 )
2318 PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(svs.clients[num].playermodel);
2319 if( prog->fieldoffsets.playerskin >= 0 )
2320 PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(svs.clients[num].playerskin);
2324 void SV_VM_CB_FreeEdict(prvm_edict_t *ed)
2326 World_UnlinkEdict(ed); // unlink from world bsp
2328 ed->fields.server->model = 0;
2329 ed->fields.server->takedamage = 0;
2330 ed->fields.server->modelindex = 0;
2331 ed->fields.server->colormap = 0;
2332 ed->fields.server->skin = 0;
2333 ed->fields.server->frame = 0;
2334 VectorClear(ed->fields.server->origin);
2335 VectorClear(ed->fields.server->angles);
2336 ed->fields.server->nextthink = -1;
2337 ed->fields.server->solid = 0;
2340 void SV_VM_CB_CountEdicts(void)
2344 int active, models, solid, step;
2346 active = models = solid = step = 0;
2347 for (i=0 ; i<prog->num_edicts ; i++)
2349 ent = PRVM_EDICT_NUM(i);
2350 if (ent->priv.server->free)
2353 if (ent->fields.server->solid)
2355 if (ent->fields.server->model)
2357 if (ent->fields.server->movetype == MOVETYPE_STEP)
2361 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
2362 Con_Printf("active :%3i\n", active);
2363 Con_Printf("view :%3i\n", models);
2364 Con_Printf("touch :%3i\n", solid);
2365 Con_Printf("step :%3i\n", step);
2368 qboolean SV_VM_CB_LoadEdict(prvm_edict_t *ent)
2370 // remove things from different skill levels or deathmatch
2371 if (gamemode != GAME_TRANSFUSION) //Transfusion does this in QC
2373 if (deathmatch.integer)
2375 if (((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_DEATHMATCH))
2380 else if ((current_skill <= 0 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_EASY ))
2381 || (current_skill == 1 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_MEDIUM))
2382 || (current_skill >= 2 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_HARD )))
2390 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)"};
2391 cvar_t nomonsters = {0, "nomonsters", "0", "unused cvar in quake, can be used by mods"};
2392 cvar_t gamecfg = {0, "gamecfg", "0", "unused cvar in quake, can be used by mods"};
2393 cvar_t scratch1 = {0, "scratch1", "0", "unused cvar in quake, can be used by mods"};
2394 cvar_t scratch2 = {0,"scratch2", "0", "unused cvar in quake, can be used by mods"};
2395 cvar_t scratch3 = {0, "scratch3", "0", "unused cvar in quake, can be used by mods"};
2396 cvar_t scratch4 = {0, "scratch4", "0", "unused cvar in quake, can be used by mods"};
2397 cvar_t savedgamecfg = {CVAR_SAVE, "savedgamecfg", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2398 cvar_t saved1 = {CVAR_SAVE, "saved1", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2399 cvar_t saved2 = {CVAR_SAVE, "saved2", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2400 cvar_t saved3 = {CVAR_SAVE, "saved3", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2401 cvar_t saved4 = {CVAR_SAVE, "saved4", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2402 cvar_t nehx00 = {0, "nehx00", "0", "nehahra data storage cvar (used in singleplayer)"};
2403 cvar_t nehx01 = {0, "nehx01", "0", "nehahra data storage cvar (used in singleplayer)"};
2404 cvar_t nehx02 = {0, "nehx02", "0", "nehahra data storage cvar (used in singleplayer)"};
2405 cvar_t nehx03 = {0, "nehx03", "0", "nehahra data storage cvar (used in singleplayer)"};
2406 cvar_t nehx04 = {0, "nehx04", "0", "nehahra data storage cvar (used in singleplayer)"};
2407 cvar_t nehx05 = {0, "nehx05", "0", "nehahra data storage cvar (used in singleplayer)"};
2408 cvar_t nehx06 = {0, "nehx06", "0", "nehahra data storage cvar (used in singleplayer)"};
2409 cvar_t nehx07 = {0, "nehx07", "0", "nehahra data storage cvar (used in singleplayer)"};
2410 cvar_t nehx08 = {0, "nehx08", "0", "nehahra data storage cvar (used in singleplayer)"};
2411 cvar_t nehx09 = {0, "nehx09", "0", "nehahra data storage cvar (used in singleplayer)"};
2412 cvar_t nehx10 = {0, "nehx10", "0", "nehahra data storage cvar (used in singleplayer)"};
2413 cvar_t nehx11 = {0, "nehx11", "0", "nehahra data storage cvar (used in singleplayer)"};
2414 cvar_t nehx12 = {0, "nehx12", "0", "nehahra data storage cvar (used in singleplayer)"};
2415 cvar_t nehx13 = {0, "nehx13", "0", "nehahra data storage cvar (used in singleplayer)"};
2416 cvar_t nehx14 = {0, "nehx14", "0", "nehahra data storage cvar (used in singleplayer)"};
2417 cvar_t nehx15 = {0, "nehx15", "0", "nehahra data storage cvar (used in singleplayer)"};
2418 cvar_t nehx16 = {0, "nehx16", "0", "nehahra data storage cvar (used in singleplayer)"};
2419 cvar_t nehx17 = {0, "nehx17", "0", "nehahra data storage cvar (used in singleplayer)"};
2420 cvar_t nehx18 = {0, "nehx18", "0", "nehahra data storage cvar (used in singleplayer)"};
2421 cvar_t nehx19 = {0, "nehx19", "0", "nehahra data storage cvar (used in singleplayer)"};
2422 cvar_t cutscene = {0, "cutscene", "1", "enables cutscenes in nehahra, can be used by other mods"};
2424 void SV_VM_Init(void)
2426 Cvar_RegisterVariable (&pr_checkextension);
2427 Cvar_RegisterVariable (&nomonsters);
2428 Cvar_RegisterVariable (&gamecfg);
2429 Cvar_RegisterVariable (&scratch1);
2430 Cvar_RegisterVariable (&scratch2);
2431 Cvar_RegisterVariable (&scratch3);
2432 Cvar_RegisterVariable (&scratch4);
2433 Cvar_RegisterVariable (&savedgamecfg);
2434 Cvar_RegisterVariable (&saved1);
2435 Cvar_RegisterVariable (&saved2);
2436 Cvar_RegisterVariable (&saved3);
2437 Cvar_RegisterVariable (&saved4);
2438 // LordHavoc: Nehahra uses these to pass data around cutscene demos
2439 if (gamemode == GAME_NEHAHRA)
2441 Cvar_RegisterVariable (&nehx00);
2442 Cvar_RegisterVariable (&nehx01);
2443 Cvar_RegisterVariable (&nehx02);
2444 Cvar_RegisterVariable (&nehx03);
2445 Cvar_RegisterVariable (&nehx04);
2446 Cvar_RegisterVariable (&nehx05);
2447 Cvar_RegisterVariable (&nehx06);
2448 Cvar_RegisterVariable (&nehx07);
2449 Cvar_RegisterVariable (&nehx08);
2450 Cvar_RegisterVariable (&nehx09);
2451 Cvar_RegisterVariable (&nehx10);
2452 Cvar_RegisterVariable (&nehx11);
2453 Cvar_RegisterVariable (&nehx12);
2454 Cvar_RegisterVariable (&nehx13);
2455 Cvar_RegisterVariable (&nehx14);
2456 Cvar_RegisterVariable (&nehx15);
2457 Cvar_RegisterVariable (&nehx16);
2458 Cvar_RegisterVariable (&nehx17);
2459 Cvar_RegisterVariable (&nehx18);
2460 Cvar_RegisterVariable (&nehx19);
2462 Cvar_RegisterVariable (&cutscene); // for Nehahra but useful to other mods as well
2465 #define REQFIELDS (sizeof(reqfields) / sizeof(prvm_required_field_t))
2467 prvm_required_field_t reqfields[] =
2469 {ev_entity, "cursor_trace_ent"},
2470 {ev_entity, "drawonlytoclient"},
2471 {ev_entity, "exteriormodeltoclient"},
2472 {ev_entity, "nodrawtoclient"},
2473 {ev_entity, "tag_entity"},
2474 {ev_entity, "viewmodelforclient"},
2475 {ev_float, "alpha"},
2476 {ev_float, "ammo_cells1"},
2477 {ev_float, "ammo_lava_nails"},
2478 {ev_float, "ammo_multi_rockets"},
2479 {ev_float, "ammo_nails1"},
2480 {ev_float, "ammo_plasma"},
2481 {ev_float, "ammo_rockets1"},
2482 {ev_float, "ammo_shells1"},
2483 {ev_float, "button3"},
2484 {ev_float, "button4"},
2485 {ev_float, "button5"},
2486 {ev_float, "button6"},
2487 {ev_float, "button7"},
2488 {ev_float, "button8"},
2489 {ev_float, "button9"},
2490 {ev_float, "button10"},
2491 {ev_float, "button11"},
2492 {ev_float, "button12"},
2493 {ev_float, "button13"},
2494 {ev_float, "button14"},
2495 {ev_float, "button15"},
2496 {ev_float, "button16"},
2497 {ev_float, "buttonchat"},
2498 {ev_float, "buttonuse"},
2499 {ev_float, "clientcolors"},
2500 {ev_float, "cursor_active"},
2501 {ev_float, "fullbright"},
2502 {ev_float, "glow_color"},
2503 {ev_float, "glow_size"},
2504 {ev_float, "glow_trail"},
2505 {ev_float, "gravity"},
2506 {ev_float, "idealpitch"},
2507 {ev_float, "items2"},
2508 {ev_float, "light_lev"},
2509 {ev_float, "pflags"},
2511 {ev_float, "pitch_speed"},
2512 {ev_float, "pmodel"},
2513 {ev_float, "renderamt"}, // HalfLife support
2514 {ev_float, "rendermode"}, // HalfLife support
2515 {ev_float, "scale"},
2516 {ev_float, "style"},
2517 {ev_float, "tag_index"},
2518 {ev_float, "Version"},
2519 {ev_float, "viewzoom"},
2520 {ev_vector, "color"},
2521 {ev_vector, "colormod"},
2522 {ev_vector, "cursor_screen"},
2523 {ev_vector, "cursor_trace_endpos"},
2524 {ev_vector, "cursor_trace_start"},
2525 {ev_vector, "movement"},
2526 {ev_vector, "punchvector"},
2527 {ev_string, "playermodel"},
2528 {ev_string, "playerskin"},
2529 {ev_function, "SendEntity"},
2530 {ev_function, "customizeentityforclient"},
2531 // DRESK - Support for Entity Contents Transition Event
2532 {ev_function, "contentstransition"},
2535 void SV_VM_Setup(void)
2537 extern cvar_t csqc_progname; //[515]: csqc crc check and right csprogs name according to progs.dat
2538 extern cvar_t csqc_progcrc;
2539 extern cvar_t csqc_progsize;
2540 size_t csprogsdatasize;
2542 PRVM_InitProg( PRVM_SERVERPROG );
2544 // allocate the mempools
2545 // TODO: move the magic numbers/constants into #defines [9/13/2006 Black]
2546 prog->progs_mempool = Mem_AllocPool("Server Progs", 0, NULL);
2547 prog->builtins = vm_sv_builtins;
2548 prog->numbuiltins = vm_sv_numbuiltins;
2549 prog->headercrc = PROGHEADER_CRC;
2550 prog->max_edicts = 512;
2551 prog->limit_edicts = MAX_EDICTS;
2552 prog->reserved_edicts = svs.maxclients;
2553 prog->edictprivate_size = sizeof(edict_engineprivate_t);
2554 prog->name = "server";
2555 prog->extensionstring = vm_sv_extensions;
2556 prog->loadintoworld = true;
2558 prog->begin_increase_edicts = SV_VM_CB_BeginIncreaseEdicts;
2559 prog->end_increase_edicts = SV_VM_CB_EndIncreaseEdicts;
2560 prog->init_edict = SV_VM_CB_InitEdict;
2561 prog->free_edict = SV_VM_CB_FreeEdict;
2562 prog->count_edicts = SV_VM_CB_CountEdicts;
2563 prog->load_edict = SV_VM_CB_LoadEdict;
2564 prog->init_cmd = VM_SV_Cmd_Init;
2565 prog->reset_cmd = VM_SV_Cmd_Reset;
2566 prog->error_cmd = Host_Error;
2568 // TODO: add a requiredfuncs list (ask LH if this is necessary at all)
2569 PRVM_LoadProgs( sv_progs.string, 0, NULL, REQFIELDS, reqfields, 0, NULL );
2571 // some mods compiled with scrambling compilers lack certain critical
2572 // global names and field names such as "self" and "time" and "nextthink"
2573 // so we have to set these offsets manually, matching the entvars_t
2574 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, angles);
2575 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, chain);
2576 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, classname);
2577 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, frame);
2578 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, groundentity);
2579 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ideal_yaw);
2580 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, nextthink);
2581 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, think);
2582 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, yaw_speed);
2583 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, self);
2584 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, time);
2585 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_forward);
2586 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_right);
2587 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_up);
2588 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_allsolid);
2589 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_startsolid);
2590 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_fraction);
2591 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inwater);
2592 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inopen);
2593 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_endpos);
2594 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_normal);
2595 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_dist);
2596 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_ent);
2597 // OP_STATE is always supported on server (due to entvars_t)
2598 prog->flag |= PRVM_OP_STATE;
2600 VM_AutoSentStats_Clear();//[515]: csqc
2601 EntityFrameCSQC_ClearVersions();//[515]: csqc
2605 // 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
2606 sv.csqc_progname[0] = 0;
2607 sv.csqc_progcrc = FS_CRCFile(csqc_progname.string, &csprogsdatasize);
2608 sv.csqc_progsize = csprogsdatasize;
2609 if (sv.csqc_progsize > 0)
2611 strlcpy(sv.csqc_progname, csqc_progname.string, sizeof(sv.csqc_progname));
2612 Con_DPrintf("server detected csqc progs file \"%s\" with size %i and crc %i\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc);
2616 void SV_VM_Begin(void)
2619 PRVM_SetProg( PRVM_SERVERPROG );
2621 prog->globals.server->time = (float) sv.time;
2624 void SV_VM_End(void)