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)
814 if (sententitiesconsideration[s->number] == sententitiesmark)
816 sententitiesconsideration[s->number] = sententitiesmark;
817 sv_writeentitiestoclient_totalentities++;
819 if (s->customizeentityforclient)
821 prog->globals.server->self = s->number;
822 prog->globals.server->other = sv_writeentitiestoclient_clentnum;
823 PRVM_ExecuteProgram(s->customizeentityforclient, "customizeentityforclient: NULL function");
824 if(!PRVM_G_FLOAT(OFS_RETURN) || !SV_PrepareEntityForSending(PRVM_EDICT_NUM(s->number), s, s->number))
828 // never reject player
829 if (s->number != sv_writeentitiestoclient_clentnum)
831 // check various rejection conditions
832 if (s->nodrawtoclient == sv_writeentitiestoclient_clentnum)
834 if (s->drawonlytoclient && s->drawonlytoclient != sv_writeentitiestoclient_clentnum)
836 if (s->effects & EF_NODRAW)
838 // LordHavoc: only send entities with a model or important effects
839 if (!s->modelindex && s->specialvisibilityradius == 0)
842 isbmodel = (model = sv.models[s->modelindex]) != NULL && model->name[0] == '*';
843 // viewmodels don't have visibility checking
844 if (s->viewmodelforclient)
846 if (s->viewmodelforclient != sv_writeentitiestoclient_clentnum)
849 else if (s->tagentity)
851 // tag attached entities simply check their parent
852 if (!sendentitiesindex[s->tagentity])
854 SV_MarkWriteEntityStateToClient(sendentitiesindex[s->tagentity]);
855 if (sententities[s->tagentity] != sententitiesmark)
858 // always send world submodels in newer protocols because they don't
859 // generate much traffic (in old protocols they hog bandwidth)
860 // but only if sv_cullentities_alwayssendbmodels is on
861 else if (!(s->effects & EF_NODEPTHTEST) && (!isbmodel || !sv_cullentities_nevercullbmodels.integer || sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE))
863 // entity has survived every check so far, check if visible
864 ed = PRVM_EDICT_NUM(s->number);
866 // if not touching a visible leaf
867 if (sv_cullentities_pvs.integer && sv_writeentitiestoclient_pvsbytes)
869 if (ed->priv.server->pvs_numclusters < 0)
871 // entity too big for clusters list
872 if (sv.worldmodel && sv.worldmodel->brush.BoxTouchingPVS && !sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, sv_writeentitiestoclient_pvs, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
874 sv_writeentitiestoclient_culled_pvs++;
881 // check cached clusters list
882 for (i = 0;i < ed->priv.server->pvs_numclusters;i++)
883 if (CHECKPVSBIT(sv_writeentitiestoclient_pvs, ed->priv.server->pvs_clusterlist[i]))
885 if (i == ed->priv.server->pvs_numclusters)
887 sv_writeentitiestoclient_culled_pvs++;
893 // or not seen by random tracelines
894 if (sv_cullentities_trace.integer && !isbmodel)
896 // LordHavoc: test center first
897 testorigin[0] = (ed->priv.server->cullmins[0] + ed->priv.server->cullmaxs[0]) * 0.5f;
898 testorigin[1] = (ed->priv.server->cullmins[1] + ed->priv.server->cullmaxs[1]) * 0.5f;
899 testorigin[2] = (ed->priv.server->cullmins[2] + ed->priv.server->cullmaxs[2]) * 0.5f;
900 sv.worldmodel->TraceBox(sv.worldmodel, 0, &trace, sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, SUPERCONTENTS_SOLID);
901 if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
902 sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
905 // LordHavoc: test random offsets, to maximize chance of detection
906 testorigin[0] = lhrandom(ed->priv.server->cullmins[0], ed->priv.server->cullmaxs[0]);
907 testorigin[1] = lhrandom(ed->priv.server->cullmins[1], ed->priv.server->cullmaxs[1]);
908 testorigin[2] = lhrandom(ed->priv.server->cullmins[2], ed->priv.server->cullmaxs[2]);
909 sv.worldmodel->TraceBox(sv.worldmodel, 0, &trace, sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, SUPERCONTENTS_SOLID);
910 if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
911 sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
914 if (s->specialvisibilityradius)
916 // LordHavoc: test random offsets, to maximize chance of detection
917 testorigin[0] = lhrandom(ed->priv.server->cullmins[0], ed->priv.server->cullmaxs[0]);
918 testorigin[1] = lhrandom(ed->priv.server->cullmins[1], ed->priv.server->cullmaxs[1]);
919 testorigin[2] = lhrandom(ed->priv.server->cullmins[2], ed->priv.server->cullmaxs[2]);
920 sv.worldmodel->TraceBox(sv.worldmodel, 0, &trace, sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, SUPERCONTENTS_SOLID);
921 if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
922 sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
926 if (realtime > sv_writeentitiestoclient_client->visibletime[s->number])
928 sv_writeentitiestoclient_culled_trace++;
935 // this just marks it for sending
936 // FIXME: it would be more efficient to send here, but the entity
937 // compressor isn't that flexible
938 sv_writeentitiestoclient_visibleentities++;
939 sententities[s->number] = sententitiesmark;
942 entity_state_t sendstates[MAX_EDICTS];
943 extern int csqc_clent;
945 void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg, int *stats)
947 int i, numsendstates;
950 // if there isn't enough space to accomplish anything, skip it
951 if (msg->cursize + 25 > msg->maxsize)
954 sv_writeentitiestoclient_client = client;
956 sv_writeentitiestoclient_culled_pvs = 0;
957 sv_writeentitiestoclient_culled_trace = 0;
958 sv_writeentitiestoclient_visibleentities = 0;
959 sv_writeentitiestoclient_totalentities = 0;
961 // find the client's PVS
962 // the real place being tested from
963 VectorAdd(clent->fields.server->origin, clent->fields.server->view_ofs, sv_writeentitiestoclient_testeye);
964 sv_writeentitiestoclient_pvsbytes = 0;
965 if (sv.worldmodel && sv.worldmodel->brush.FatPVS)
966 sv_writeentitiestoclient_pvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, sv_writeentitiestoclient_testeye, 8, sv_writeentitiestoclient_pvs, sizeof(sv_writeentitiestoclient_pvs));
968 csqc_clent = sv_writeentitiestoclient_clentnum = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
972 for (i = 0;i < numsendentities;i++)
973 SV_MarkWriteEntityStateToClient(sendentities + i);
976 for (i = 0;i < numsendentities;i++)
978 if (sententities[sendentities[i].number] == sententitiesmark)
980 s = &sendstates[numsendstates++];
981 *s = sendentities[i];
982 if (s->exteriormodelforclient && s->exteriormodelforclient == sv_writeentitiestoclient_clentnum)
983 s->flags |= RENDER_EXTERIORMODEL;
987 if (sv_cullentities_stats.integer)
988 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);
990 EntityFrameCSQC_WriteFrame(msg, numsendstates, sendstates);
992 if (client->entitydatabase5)
993 EntityFrame5_WriteFrame(msg, client->entitydatabase5, numsendstates, sendstates, client - svs.clients + 1, stats, client->movesequence);
994 else if (client->entitydatabase4)
995 EntityFrame4_WriteFrame(msg, client->entitydatabase4, numsendstates, sendstates);
996 else if (client->entitydatabase)
997 EntityFrame_WriteFrame(msg, client->entitydatabase, numsendstates, sendstates, client - svs.clients + 1);
999 EntityFrameQuake_WriteFrame(msg, numsendstates, sendstates);
1008 void SV_CleanupEnts (void)
1013 ent = PRVM_NEXT_EDICT(prog->edicts);
1014 for (e=1 ; e<prog->num_edicts ; e++, ent = PRVM_NEXT_EDICT(ent))
1015 ent->fields.server->effects = (int)ent->fields.server->effects & ~EF_MUZZLEFLASH;
1020 SV_WriteClientdataToMessage
1024 void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats)
1028 prvm_edict_t *other;
1036 // send a damage message
1038 if (ent->fields.server->dmg_take || ent->fields.server->dmg_save)
1040 other = PRVM_PROG_TO_EDICT(ent->fields.server->dmg_inflictor);
1041 MSG_WriteByte (msg, svc_damage);
1042 MSG_WriteByte (msg, (int)ent->fields.server->dmg_save);
1043 MSG_WriteByte (msg, (int)ent->fields.server->dmg_take);
1044 for (i=0 ; i<3 ; i++)
1045 MSG_WriteCoord (msg, other->fields.server->origin[i] + 0.5*(other->fields.server->mins[i] + other->fields.server->maxs[i]), sv.protocol);
1047 ent->fields.server->dmg_take = 0;
1048 ent->fields.server->dmg_save = 0;
1052 // send the current viewpos offset from the view entity
1054 SV_SetIdealPitch (); // how much to look up / down ideally
1056 // a fixangle might get lost in a dropped packet. Oh well.
1057 if ( ent->fields.server->fixangle )
1059 MSG_WriteByte (msg, svc_setangle);
1060 for (i=0 ; i < 3 ; i++)
1061 MSG_WriteAngle (msg, ent->fields.server->angles[i], sv.protocol);
1062 ent->fields.server->fixangle = 0;
1065 // stuff the sigil bits into the high bits of items for sbar, or else
1067 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.items2);
1068 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1069 items = (int)ent->fields.server->items | ((int)val->_float << 23);
1071 items = (int)ent->fields.server->items | ((int)prog->globals.server->serverflags << 28);
1073 VectorClear(punchvector);
1074 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.punchvector)))
1075 VectorCopy(val->vector, punchvector);
1077 // cache weapon model name and index in client struct to save time
1078 // (this search can be almost 1% of cpu time!)
1079 s = PRVM_GetString(ent->fields.server->weaponmodel);
1080 if (strcmp(s, client->weaponmodel))
1082 strlcpy(client->weaponmodel, s, sizeof(client->weaponmodel));
1083 client->weaponmodelindex = SV_ModelIndex(s, 1);
1087 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewzoom)))
1088 viewzoom = (int)(val->_float * 255.0f);
1094 if ((int)ent->fields.server->flags & FL_ONGROUND)
1095 bits |= SU_ONGROUND;
1096 if (ent->fields.server->waterlevel >= 2)
1098 if (ent->fields.server->idealpitch)
1099 bits |= SU_IDEALPITCH;
1101 for (i=0 ; i<3 ; i++)
1103 if (ent->fields.server->punchangle[i])
1104 bits |= (SU_PUNCH1<<i);
1105 if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE)
1107 bits |= (SU_PUNCHVEC1<<i);
1108 if (ent->fields.server->velocity[i])
1109 bits |= (SU_VELOCITY1<<i);
1112 memset(stats, 0, sizeof(int[MAX_CL_STATS]));
1113 stats[STAT_VIEWHEIGHT] = (int)ent->fields.server->view_ofs[2];
1114 stats[STAT_ITEMS] = items;
1115 stats[STAT_WEAPONFRAME] = (int)ent->fields.server->weaponframe;
1116 stats[STAT_ARMOR] = (int)ent->fields.server->armorvalue;
1117 stats[STAT_WEAPON] = client->weaponmodelindex;
1118 stats[STAT_HEALTH] = (int)ent->fields.server->health;
1119 stats[STAT_AMMO] = (int)ent->fields.server->currentammo;
1120 stats[STAT_SHELLS] = (int)ent->fields.server->ammo_shells;
1121 stats[STAT_NAILS] = (int)ent->fields.server->ammo_nails;
1122 stats[STAT_ROCKETS] = (int)ent->fields.server->ammo_rockets;
1123 stats[STAT_CELLS] = (int)ent->fields.server->ammo_cells;
1124 stats[STAT_ACTIVEWEAPON] = (int)ent->fields.server->weapon;
1125 stats[STAT_VIEWZOOM] = viewzoom;
1126 stats[STAT_TOTALSECRETS] = prog->globals.server->total_secrets;
1127 stats[STAT_TOTALMONSTERS] = prog->globals.server->total_monsters;
1128 // the QC bumps these itself by sending svc_'s, so we have to keep them
1129 // zero or they'll be corrected by the engine
1130 //stats[STAT_SECRETS] = prog->globals.server->found_secrets;
1131 //stats[STAT_MONSTERS] = prog->globals.server->killed_monsters;
1133 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)
1135 if (stats[STAT_VIEWHEIGHT] != DEFAULT_VIEWHEIGHT) bits |= SU_VIEWHEIGHT;
1137 if (stats[STAT_WEAPONFRAME]) bits |= SU_WEAPONFRAME;
1138 if (stats[STAT_ARMOR]) bits |= SU_ARMOR;
1140 // FIXME: which protocols support this? does PROTOCOL_DARKPLACES3 support viewzoom?
1141 if (sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5)
1142 if (viewzoom != 255)
1143 bits |= SU_VIEWZOOM;
1148 if (bits >= 16777216)
1152 MSG_WriteByte (msg, svc_clientdata);
1153 MSG_WriteShort (msg, bits);
1154 if (bits & SU_EXTEND1)
1155 MSG_WriteByte(msg, bits >> 16);
1156 if (bits & SU_EXTEND2)
1157 MSG_WriteByte(msg, bits >> 24);
1159 if (bits & SU_VIEWHEIGHT)
1160 MSG_WriteChar (msg, stats[STAT_VIEWHEIGHT]);
1162 if (bits & SU_IDEALPITCH)
1163 MSG_WriteChar (msg, (int)ent->fields.server->idealpitch);
1165 for (i=0 ; i<3 ; i++)
1167 if (bits & (SU_PUNCH1<<i))
1169 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
1170 MSG_WriteChar(msg, (int)ent->fields.server->punchangle[i]);
1172 MSG_WriteAngle16i(msg, ent->fields.server->punchangle[i]);
1174 if (bits & (SU_PUNCHVEC1<<i))
1176 if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
1177 MSG_WriteCoord16i(msg, punchvector[i]);
1179 MSG_WriteCoord32f(msg, punchvector[i]);
1181 if (bits & (SU_VELOCITY1<<i))
1183 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
1184 MSG_WriteChar(msg, (int)(ent->fields.server->velocity[i] * (1.0f / 16.0f)));
1186 MSG_WriteCoord32f(msg, ent->fields.server->velocity[i]);
1190 if (bits & SU_ITEMS)
1191 MSG_WriteLong (msg, stats[STAT_ITEMS]);
1193 if (sv.protocol == PROTOCOL_DARKPLACES5)
1195 if (bits & SU_WEAPONFRAME)
1196 MSG_WriteShort (msg, stats[STAT_WEAPONFRAME]);
1197 if (bits & SU_ARMOR)
1198 MSG_WriteShort (msg, stats[STAT_ARMOR]);
1199 if (bits & SU_WEAPON)
1200 MSG_WriteShort (msg, stats[STAT_WEAPON]);
1201 MSG_WriteShort (msg, stats[STAT_HEALTH]);
1202 MSG_WriteShort (msg, stats[STAT_AMMO]);
1203 MSG_WriteShort (msg, stats[STAT_SHELLS]);
1204 MSG_WriteShort (msg, stats[STAT_NAILS]);
1205 MSG_WriteShort (msg, stats[STAT_ROCKETS]);
1206 MSG_WriteShort (msg, stats[STAT_CELLS]);
1207 MSG_WriteShort (msg, stats[STAT_ACTIVEWEAPON]);
1208 if (bits & SU_VIEWZOOM)
1209 MSG_WriteShort (msg, bound(0, stats[STAT_VIEWZOOM], 65535));
1211 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)
1213 if (bits & SU_WEAPONFRAME)
1214 MSG_WriteByte (msg, stats[STAT_WEAPONFRAME]);
1215 if (bits & SU_ARMOR)
1216 MSG_WriteByte (msg, stats[STAT_ARMOR]);
1217 if (bits & SU_WEAPON)
1218 MSG_WriteByte (msg, stats[STAT_WEAPON]);
1219 MSG_WriteShort (msg, stats[STAT_HEALTH]);
1220 MSG_WriteByte (msg, stats[STAT_AMMO]);
1221 MSG_WriteByte (msg, stats[STAT_SHELLS]);
1222 MSG_WriteByte (msg, stats[STAT_NAILS]);
1223 MSG_WriteByte (msg, stats[STAT_ROCKETS]);
1224 MSG_WriteByte (msg, stats[STAT_CELLS]);
1225 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE || gamemode == GAME_NEXUIZ)
1227 for (i = 0;i < 32;i++)
1228 if (stats[STAT_WEAPON] & (1<<i))
1230 MSG_WriteByte (msg, i);
1233 MSG_WriteByte (msg, stats[STAT_WEAPON]);
1234 if (bits & SU_VIEWZOOM)
1236 if (sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
1237 MSG_WriteByte (msg, bound(0, stats[STAT_VIEWZOOM], 255));
1239 MSG_WriteShort (msg, bound(0, stats[STAT_VIEWZOOM], 65535));
1245 =======================
1246 SV_SendClientDatagram
1247 =======================
1249 static unsigned char sv_sendclientdatagram_buf[NET_MAXMESSAGE]; // FIXME?
1250 void SV_SendClientDatagram (client_t *client)
1252 int rate, maxrate, maxsize, maxsize2, downloadsize;
1254 int stats[MAX_CL_STATS];
1256 if (LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) == LHNETADDRESSTYPE_LOOP && !sv_ratelimitlocalplayer.integer)
1258 // for good singleplayer, send huge packets
1259 maxsize = sizeof(sv_sendclientdatagram_buf);
1260 maxsize2 = sizeof(sv_sendclientdatagram_buf);
1262 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)
1264 // no rate limiting support on older protocols because dp protocols
1265 // 1-4 kick the client off if they overflow, and quake protocol shows
1266 // less than the full entity set if rate limited
1272 // PROTOCOL_DARKPLACES5 and later support packet size limiting of updates
1273 maxrate = max(NET_MINRATE, sv_maxrate.integer);
1274 if (sv_maxrate.integer != maxrate)
1275 Cvar_SetValueQuick(&sv_maxrate, maxrate);
1277 // this rate limiting does not understand sys_ticrate 0
1278 // (but no one should be running that on a server!)
1279 rate = bound(NET_MINRATE, client->rate, maxrate);
1280 rate = (int)(rate * sys_ticrate.value);
1281 maxsize = bound(50, rate, 1400);
1285 // while downloading, limit entity updates to half the packet
1286 // (any leftover space will be used for downloading)
1287 if (host_client->download_file)
1290 msg.data = sv_sendclientdatagram_buf;
1291 msg.maxsize = maxsize;
1294 if (host_client->spawned)
1296 MSG_WriteByte (&msg, svc_time);
1297 MSG_WriteFloat (&msg, sv.time);
1299 // add the client specific data to the datagram
1300 SV_WriteClientdataToMessage (client, client->edict, &msg, stats);
1301 VM_SV_WriteAutoSentStats (client, client->edict, &msg, stats);
1302 SV_WriteEntitiesToClient (client, client->edict, &msg, stats);
1304 // expand packet size to allow effects to go over the rate limit
1305 // (dropping them is FAR too ugly)
1306 msg.maxsize = maxsize2;
1308 // copy the server datagram if there is space
1309 // FIXME: put in delayed queue of effects to send
1310 if (sv.datagram.cursize > 0 && msg.cursize + sv.datagram.cursize <= msg.maxsize)
1311 SZ_Write (&msg, sv.datagram.data, sv.datagram.cursize);
1313 else if (realtime > client->keepalivetime)
1315 // the player isn't totally in the game yet
1316 // send small keepalive messages if too much time has passed
1317 msg.maxsize = maxsize2;
1318 client->keepalivetime = realtime + 5;
1319 MSG_WriteChar (&msg, svc_nop);
1322 msg.maxsize = maxsize2;
1324 // if a download is active, see if there is room to fit some download data
1326 downloadsize = maxsize * 2 - msg.cursize - 7;
1327 if (host_client->download_file && host_client->download_started && downloadsize > 0)
1329 fs_offset_t downloadstart;
1330 unsigned char data[1400];
1331 downloadstart = FS_Tell(host_client->download_file);
1332 downloadsize = min(downloadsize, (int)sizeof(data));
1333 downloadsize = FS_Read(host_client->download_file, data, downloadsize);
1334 // note this sends empty messages if at the end of the file, which is
1335 // necessary to keep the packet loss logic working
1336 // (the last blocks may be lost and need to be re-sent, and that will
1337 // only occur if the client acks the empty end messages, revealing
1338 // a gap in the download progress, causing the last blocks to be
1340 MSG_WriteChar (&msg, svc_downloaddata);
1341 MSG_WriteLong (&msg, downloadstart);
1342 MSG_WriteShort (&msg, downloadsize);
1343 if (downloadsize > 0)
1344 SZ_Write (&msg, data, downloadsize);
1347 // send the datagram
1348 NetConn_SendUnreliableMessage (client->netconnection, &msg, sv.protocol);
1352 =======================
1353 SV_UpdateToReliableMessages
1354 =======================
1356 void SV_UpdateToReliableMessages (void)
1365 // check for changes to be sent over the reliable streams
1366 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1368 // update the host_client fields we care about according to the entity fields
1369 host_client->edict = PRVM_EDICT_NUM(i+1);
1372 name = PRVM_GetString(host_client->edict->fields.server->netname);
1375 // always point the string back at host_client->name to keep it safe
1376 strlcpy (host_client->name, name, sizeof (host_client->name));
1377 host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
1378 if (strcmp(host_client->old_name, host_client->name))
1380 if (host_client->spawned)
1381 SV_BroadcastPrintf("%s changed name to %s\n", host_client->old_name, host_client->name);
1382 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1383 // send notification to all clients
1384 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1385 MSG_WriteByte (&sv.reliable_datagram, i);
1386 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1389 // DP_SV_CLIENTCOLORS
1390 // this is always found (since it's added by the progs loader)
1391 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
1392 host_client->colors = (int)val->_float;
1393 if (host_client->old_colors != host_client->colors)
1395 host_client->old_colors = host_client->colors;
1396 // send notification to all clients
1397 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1398 MSG_WriteByte (&sv.reliable_datagram, i);
1399 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1402 // NEXUIZ_PLAYERMODEL
1403 if( prog->fieldoffsets.playermodel >= 0 ) {
1404 model = PRVM_GetString(PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string);
1407 // always point the string back at host_client->name to keep it safe
1408 strlcpy (host_client->playermodel, model, sizeof (host_client->playermodel));
1409 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
1412 // NEXUIZ_PLAYERSKIN
1413 if( prog->fieldoffsets.playerskin >= 0 ) {
1414 skin = PRVM_GetString(PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string);
1417 // always point the string back at host_client->name to keep it safe
1418 strlcpy (host_client->playerskin, skin, sizeof (host_client->playerskin));
1419 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
1423 host_client->frags = (int)host_client->edict->fields.server->frags;
1424 if (host_client->old_frags != host_client->frags)
1426 host_client->old_frags = host_client->frags;
1427 // send notification to all clients
1428 MSG_WriteByte (&sv.reliable_datagram, svc_updatefrags);
1429 MSG_WriteByte (&sv.reliable_datagram, i);
1430 MSG_WriteShort (&sv.reliable_datagram, host_client->frags);
1434 for (j = 0, client = svs.clients;j < svs.maxclients;j++, client++)
1435 if (client->netconnection)
1436 SZ_Write (&client->netconnection->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize);
1438 SZ_Clear (&sv.reliable_datagram);
1443 =======================
1444 SV_SendClientMessages
1445 =======================
1447 void SV_SendClientMessages (void)
1449 int i, prepared = false;
1451 if (sv.protocol == PROTOCOL_QUAKEWORLD)
1452 Sys_Error("SV_SendClientMessages: no quakeworld support\n");
1454 // update frags, names, etc
1455 SV_UpdateToReliableMessages();
1457 // build individual updates
1458 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1460 if (!host_client->active)
1462 if (!host_client->netconnection)
1465 if (host_client->netconnection->message.overflowed)
1467 SV_DropClient (true); // if the message couldn't send, kick off
1474 // only prepare entities once per frame
1475 SV_PrepareEntitiesForSending();
1477 SV_SendClientDatagram (host_client);
1480 // clear muzzle flashes
1484 void SV_StartDownload_f(void)
1486 if (host_client->download_file)
1487 host_client->download_started = true;
1490 void SV_Download_f(void)
1492 const char *whichpack, *whichpack2, *extension;
1494 if (Cmd_Argc() != 2)
1496 SV_ClientPrintf("usage: download <filename>\n");
1500 if (FS_CheckNastyPath(Cmd_Argv(1), false))
1502 SV_ClientPrintf("Download rejected: nasty filename \"%s\"\n", Cmd_Argv(1));
1506 if (host_client->download_file)
1508 // at this point we'll assume the previous download should be aborted
1509 Con_DPrintf("Download of %s aborted by %s starting a new download\n", host_client->download_name, host_client->name);
1510 Host_ClientCommands("\nstopdownload\n");
1512 // close the file and reset variables
1513 FS_Close(host_client->download_file);
1514 host_client->download_file = NULL;
1515 host_client->download_name[0] = 0;
1516 host_client->download_expectedposition = 0;
1517 host_client->download_started = false;
1520 if (!sv_allowdownloads.integer)
1522 SV_ClientPrintf("Downloads are disabled on this server\n");
1523 Host_ClientCommands("\nstopdownload\n");
1527 strlcpy(host_client->download_name, Cmd_Argv(1), sizeof(host_client->download_name));
1528 extension = FS_FileExtension(host_client->download_name);
1530 // host_client is asking to download a specified file
1531 if (developer.integer >= 100)
1532 Con_Printf("Download request for %s by %s\n", host_client->download_name, host_client->name);
1534 if (!FS_FileExists(host_client->download_name))
1536 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);
1537 Host_ClientCommands("\nstopdownload\n");
1541 // check if the user is trying to download part of registered Quake(r)
1542 whichpack = FS_WhichPack(host_client->download_name);
1543 whichpack2 = FS_WhichPack("gfx/pop.lmp");
1544 if ((whichpack && whichpack2 && !strcasecmp(whichpack, whichpack2)) || FS_IsRegisteredQuakePack(host_client->download_name))
1546 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);
1547 Host_ClientCommands("\nstopdownload\n");
1551 // check if the server has forbidden archive downloads entirely
1552 if (!sv_allowdownloads_inarchive.integer)
1554 whichpack = FS_WhichPack(host_client->download_name);
1557 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);
1558 Host_ClientCommands("\nstopdownload\n");
1563 if (!sv_allowdownloads_config.integer)
1565 if (!strcasecmp(extension, "cfg"))
1567 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);
1568 Host_ClientCommands("\nstopdownload\n");
1573 if (!sv_allowdownloads_dlcache.integer)
1575 if (!strncasecmp(host_client->download_name, "dlcache/", 8))
1577 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);
1578 Host_ClientCommands("\nstopdownload\n");
1583 if (!sv_allowdownloads_archive.integer)
1585 if (!strcasecmp(extension, "pak") || !strcasecmp(extension, "pk3"))
1587 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);
1588 Host_ClientCommands("\nstopdownload\n");
1593 host_client->download_file = FS_Open(host_client->download_name, "rb", true, false);
1594 if (!host_client->download_file)
1596 SV_ClientPrintf("Download rejected: server could not open the file \"%s\"\n", host_client->download_name);
1597 Host_ClientCommands("\nstopdownload\n");
1601 if (FS_FileSize(host_client->download_file) > 1<<30)
1603 SV_ClientPrintf("Download rejected: file \"%s\" is very large\n", host_client->download_name);
1604 Host_ClientCommands("\nstopdownload\n");
1605 FS_Close(host_client->download_file);
1606 host_client->download_file = NULL;
1610 Con_DPrintf("Downloading %s to %s\n", host_client->download_name, host_client->name);
1612 Host_ClientCommands("\ncl_downloadbegin %i %s\n", (int)FS_FileSize(host_client->download_file), host_client->download_name);
1614 host_client->download_expectedposition = 0;
1615 host_client->download_started = false;
1617 // the rest of the download process is handled in SV_SendClientDatagram
1618 // and other code dealing with svc_downloaddata and clc_ackdownloaddata
1620 // no svc_downloaddata messages will be sent until sv_startdownload is
1621 // sent by the client
1625 ==============================================================================
1629 ==============================================================================
1638 int SV_ModelIndex(const char *s, int precachemode)
1640 int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) ? 256 : MAX_MODELS);
1641 char filename[MAX_QPATH];
1645 //if (precachemode == 2)
1647 strlcpy(filename, s, sizeof(filename));
1648 for (i = 2;i < limit;i++)
1650 if (!sv.model_precache[i][0])
1654 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))
1656 Con_Printf("SV_ModelIndex(\"%s\"): precache_model can only be done in spawn functions\n", filename);
1659 if (precachemode == 1)
1660 Con_Printf("SV_ModelIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename);
1661 strlcpy(sv.model_precache[i], filename, sizeof(sv.model_precache[i]));
1662 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, false);
1663 if (sv.state != ss_loading)
1665 MSG_WriteByte(&sv.reliable_datagram, svc_precache);
1666 MSG_WriteShort(&sv.reliable_datagram, i);
1667 MSG_WriteString(&sv.reliable_datagram, filename);
1671 Con_Printf("SV_ModelIndex(\"%s\"): not precached\n", filename);
1674 if (!strcmp(sv.model_precache[i], filename))
1677 Con_Printf("SV_ModelIndex(\"%s\"): i (%i) == MAX_MODELS (%i)\n", filename, i, MAX_MODELS);
1687 int SV_SoundIndex(const char *s, int precachemode)
1689 int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) ? 256 : MAX_SOUNDS);
1690 char filename[MAX_QPATH];
1694 //if (precachemode == 2)
1696 strlcpy(filename, s, sizeof(filename));
1697 for (i = 1;i < limit;i++)
1699 if (!sv.sound_precache[i][0])
1703 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))
1705 Con_Printf("SV_SoundIndex(\"%s\"): precache_sound can only be done in spawn functions\n", filename);
1708 if (precachemode == 1)
1709 Con_Printf("SV_SoundIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename);
1710 strlcpy(sv.sound_precache[i], filename, sizeof(sv.sound_precache[i]));
1711 if (sv.state != ss_loading)
1713 MSG_WriteByte(&sv.reliable_datagram, svc_precache);
1714 MSG_WriteShort(&sv.reliable_datagram, i + 32768);
1715 MSG_WriteString(&sv.reliable_datagram, filename);
1719 Con_Printf("SV_SoundIndex(\"%s\"): not precached\n", filename);
1722 if (!strcmp(sv.sound_precache[i], filename))
1725 Con_Printf("SV_SoundIndex(\"%s\"): i (%i) == MAX_SOUNDS (%i)\n", filename, i, MAX_SOUNDS);
1729 // MUST match effectnameindex_t in client.h
1730 static const char *standardeffectnames[EFFECT_TOTAL] =
1738 "TE_SUPERSPIKEQUAD",
1754 "TE_TEI_BIGEXPLOSION",
1772 SV_ParticleEffectIndex
1776 int SV_ParticleEffectIndex(const char *name)
1778 int i, argc, linenumber, effectnameindex;
1779 fs_offset_t filesize;
1780 unsigned char *filedata;
1781 const char *text, *textstart, *textend;
1782 char argv[16][1024];
1783 if (!sv.particleeffectnamesloaded)
1785 sv.particleeffectnamesloaded = true;
1786 memset(sv.particleeffectname, 0, sizeof(sv.particleeffectname));
1787 for (i = 0;i < EFFECT_TOTAL;i++)
1788 strlcpy(sv.particleeffectname[i], standardeffectnames[i], sizeof(sv.particleeffectname[i]));
1789 filedata = FS_LoadFile("effectinfo.txt", tempmempool, true, &filesize);
1792 textstart = (const char *)filedata;
1793 textend = (const char *)filedata + filesize;
1795 for (linenumber = 1;;linenumber++)
1800 if (!COM_ParseToken(&text, true) || !strcmp(com_token, "\n"))
1804 strlcpy(argv[argc], com_token, sizeof(argv[argc]));
1808 if (com_token[0] == 0)
1809 break; // if the loop exited and it's not a \n, it's EOF
1812 if (!strcmp(argv[0], "effect"))
1816 for (effectnameindex = 1;effectnameindex < SV_MAX_PARTICLEEFFECTNAME;effectnameindex++)
1818 if (sv.particleeffectname[effectnameindex][0])
1820 if (!strcmp(sv.particleeffectname[effectnameindex], argv[1]))
1825 strlcpy(sv.particleeffectname[effectnameindex], argv[1], sizeof(sv.particleeffectname[effectnameindex]));
1829 // if we run out of names, abort
1830 if (effectnameindex == SV_MAX_PARTICLEEFFECTNAME)
1832 Con_Printf("effectinfo.txt:%i: too many effects!\n", linenumber);
1841 // search for the name
1842 for (effectnameindex = 1;effectnameindex < SV_MAX_PARTICLEEFFECTNAME && sv.particleeffectname[effectnameindex][0];effectnameindex++)
1843 if (!strcmp(sv.particleeffectname[effectnameindex], name))
1844 return effectnameindex;
1845 // return 0 if we couldn't find it
1855 void SV_CreateBaseline (void)
1857 int i, entnum, large;
1858 prvm_edict_t *svent;
1860 // LordHavoc: clear *all* states (note just active ones)
1861 for (entnum = 0;entnum < prog->max_edicts;entnum++)
1863 // get the current server version
1864 svent = PRVM_EDICT_NUM(entnum);
1866 // LordHavoc: always clear state values, whether the entity is in use or not
1867 svent->priv.server->baseline = defaultstate;
1869 if (svent->priv.server->free)
1871 if (entnum > svs.maxclients && !svent->fields.server->modelindex)
1874 // create entity baseline
1875 VectorCopy (svent->fields.server->origin, svent->priv.server->baseline.origin);
1876 VectorCopy (svent->fields.server->angles, svent->priv.server->baseline.angles);
1877 svent->priv.server->baseline.frame = (int)svent->fields.server->frame;
1878 svent->priv.server->baseline.skin = (int)svent->fields.server->skin;
1879 if (entnum > 0 && entnum <= svs.maxclients)
1881 svent->priv.server->baseline.colormap = entnum;
1882 svent->priv.server->baseline.modelindex = SV_ModelIndex("progs/player.mdl", 1);
1886 svent->priv.server->baseline.colormap = 0;
1887 svent->priv.server->baseline.modelindex = (int)svent->fields.server->modelindex;
1891 if (svent->priv.server->baseline.modelindex & 0xFF00 || svent->priv.server->baseline.frame & 0xFF00)
1894 // add to the message
1896 MSG_WriteByte (&sv.signon, svc_spawnbaseline2);
1898 MSG_WriteByte (&sv.signon, svc_spawnbaseline);
1899 MSG_WriteShort (&sv.signon, entnum);
1903 MSG_WriteShort (&sv.signon, svent->priv.server->baseline.modelindex);
1904 MSG_WriteShort (&sv.signon, svent->priv.server->baseline.frame);
1908 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.modelindex);
1909 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.frame);
1911 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.colormap);
1912 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.skin);
1913 for (i=0 ; i<3 ; i++)
1915 MSG_WriteCoord(&sv.signon, svent->priv.server->baseline.origin[i], sv.protocol);
1916 MSG_WriteAngle(&sv.signon, svent->priv.server->baseline.angles[i], sv.protocol);
1926 Grabs the current state of each client for saving across the
1927 transition to another level
1930 void SV_SaveSpawnparms (void)
1934 svs.serverflags = (int)prog->globals.server->serverflags;
1936 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1938 if (!host_client->active)
1941 // call the progs to get default spawn parms for the new client
1942 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1943 PRVM_ExecuteProgram (prog->globals.server->SetChangeParms, "QC function SetChangeParms is missing");
1944 for (j=0 ; j<NUM_SPAWN_PARMS ; j++)
1945 host_client->spawn_parms[j] = (&prog->globals.server->parm1)[j];
1949 void SV_IncreaseEdicts(void)
1953 int oldmax_edicts = prog->max_edicts;
1954 void *oldedictsengineprivate = prog->edictprivate;
1955 void *oldedictsfields = prog->edictsfields;
1956 void *oldmoved_edicts = sv.moved_edicts;
1958 if (prog->max_edicts >= MAX_EDICTS)
1961 // links don't survive the transition, so unlink everything
1962 for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
1964 if (!ent->priv.server->free)
1965 SV_UnlinkEdict(prog->edicts + i);
1966 memset(&ent->priv.server->areagrid, 0, sizeof(ent->priv.server->areagrid));
1968 World_Clear(&sv.world);
1970 prog->max_edicts = min(prog->max_edicts + 256, MAX_EDICTS);
1971 prog->edictprivate = PR_Alloc(prog->max_edicts * sizeof(edict_engineprivate_t));
1972 prog->edictsfields = PR_Alloc(prog->max_edicts * prog->edict_size);
1973 sv.moved_edicts = PR_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
1975 memcpy(prog->edictprivate, oldedictsengineprivate, oldmax_edicts * sizeof(edict_engineprivate_t));
1976 memcpy(prog->edictsfields, oldedictsfields, oldmax_edicts * prog->edict_size);
1978 for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
1980 ent->priv.vp = (unsigned char*) prog->edictprivate + i * prog->edictprivate_size;
1981 ent->fields.server = (void *)((unsigned char *)prog->edictsfields + i * prog->edict_size);
1982 // link every entity except world
1983 if (!ent->priv.server->free)
1984 SV_LinkEdict(ent, false);
1987 PR_Free(oldedictsengineprivate);
1988 PR_Free(oldedictsfields);
1989 PR_Free(oldmoved_edicts);
1996 This is called at the start of each level
1999 extern float scr_centertime_off;
2001 void SV_SpawnServer (const char *server)
2006 model_t *worldmodel;
2007 char modelname[sizeof(sv.modelname)];
2009 Con_DPrintf("SpawnServer: %s\n", server);
2011 if (cls.state != ca_dedicated)
2012 SCR_BeginLoadingPlaque();
2014 dpsnprintf (modelname, sizeof(modelname), "maps/%s.bsp", server);
2015 worldmodel = Mod_ForName(modelname, false, true, true);
2016 if (!worldmodel || !worldmodel->TraceBox)
2018 Con_Printf("Couldn't load map %s\n", modelname);
2022 // let's not have any servers with no name
2023 if (hostname.string[0] == 0)
2024 Cvar_Set ("hostname", "UNNAMED");
2025 scr_centertime_off = 0;
2027 svs.changelevel_issued = false; // now safe to issue another
2029 // make the map a required file for clients
2030 Curl_ClearRequirements();
2031 Curl_RequireFile(modelname);
2034 // tell all connected clients that we are going to a new level
2039 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
2041 if (client->netconnection)
2043 MSG_WriteByte(&client->netconnection->message, svc_stufftext);
2044 MSG_WriteString(&client->netconnection->message, "reconnect\n");
2051 NetConn_OpenServerPorts(true);
2055 // make cvars consistant
2058 Cvar_SetValue ("deathmatch", 0);
2059 // LordHavoc: it can be useful to have skills outside the range 0-3...
2060 //current_skill = bound(0, (int)(skill.value + 0.5), 3);
2061 //Cvar_SetValue ("skill", (float)current_skill);
2062 current_skill = (int)(skill.value + 0.5);
2065 // set up the new server
2067 memset (&sv, 0, sizeof(sv));
2068 // if running a local client, make sure it doesn't try to access the last
2069 // level's data which is no longer valiud
2072 if(*sv_random_seed.string)
2074 srand(sv_random_seed.integer);
2075 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);
2082 strlcpy (sv.name, server, sizeof (sv.name));
2084 sv.protocol = Protocol_EnumForName(sv_protocolname.string);
2085 if (sv.protocol == PROTOCOL_UNKNOWN)
2088 Protocol_Names(buffer, sizeof(buffer));
2089 Con_Printf("Unknown sv_protocolname \"%s\", valid values are:\n%s\n", sv_protocolname.string, buffer);
2090 sv.protocol = PROTOCOL_QUAKE;
2095 // load progs to get entity field count
2096 //PR_LoadProgs ( sv_progs.string );
2098 // allocate server memory
2099 /*// start out with just enough room for clients and a reasonable estimate of entities
2100 prog->max_edicts = max(svs.maxclients + 1, 512);
2101 prog->max_edicts = min(prog->max_edicts, MAX_EDICTS);
2103 // prvm_edict_t structures (hidden from progs)
2104 prog->edicts = PR_Alloc(MAX_EDICTS * sizeof(prvm_edict_t));
2105 // engine private structures (hidden from progs)
2106 prog->edictprivate = PR_Alloc(prog->max_edicts * sizeof(edict_engineprivate_t));
2107 // progs fields, often accessed by server
2108 prog->edictsfields = PR_Alloc(prog->max_edicts * prog->edict_size);*/
2109 // used by PushMove to move back pushed entities
2110 sv.moved_edicts = (prvm_edict_t **)PRVM_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
2111 /*for (i = 0;i < prog->max_edicts;i++)
2113 ent = prog->edicts + i;
2114 ent->priv.vp = (unsigned char*) prog->edictprivate + i * prog->edictprivate_size;
2115 ent->fields.server = (void *)((unsigned char *)prog->edictsfields + i * prog->edict_size);
2118 // reset client csqc entity versions right away.
2119 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2120 EntityFrameCSQC_InitClientVersions(i, true);
2122 sv.datagram.maxsize = sizeof(sv.datagram_buf);
2123 sv.datagram.cursize = 0;
2124 sv.datagram.data = sv.datagram_buf;
2126 sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf);
2127 sv.reliable_datagram.cursize = 0;
2128 sv.reliable_datagram.data = sv.reliable_datagram_buf;
2130 sv.signon.maxsize = sizeof(sv.signon_buf);
2131 sv.signon.cursize = 0;
2132 sv.signon.data = sv.signon_buf;
2134 // leave slots at start for clients only
2135 //prog->num_edicts = svs.maxclients+1;
2137 sv.state = ss_loading;
2138 prog->allowworldwrites = true;
2141 prog->globals.server->time = sv.time = 1.0;
2144 worldmodel->used = true;
2146 strlcpy (sv.name, server, sizeof (sv.name));
2147 strlcpy(sv.modelname, modelname, sizeof(sv.modelname));
2148 sv.worldmodel = worldmodel;
2149 sv.models[1] = sv.worldmodel;
2152 // clear world interaction links
2154 VectorCopy(sv.worldmodel->normalmins, sv.world.areagrid_mins);
2155 VectorCopy(sv.worldmodel->normalmaxs, sv.world.areagrid_maxs);
2156 World_Clear(&sv.world);
2158 strlcpy(sv.sound_precache[0], "", sizeof(sv.sound_precache[0]));
2160 strlcpy(sv.model_precache[0], "", sizeof(sv.model_precache[0]));
2161 strlcpy(sv.model_precache[1], sv.modelname, sizeof(sv.model_precache[1]));
2162 for (i = 1;i < sv.worldmodel->brush.numsubmodels;i++)
2164 dpsnprintf(sv.model_precache[i+1], sizeof(sv.model_precache[i+1]), "*%i", i);
2165 sv.models[i+1] = Mod_ForName (sv.model_precache[i+1], false, false, false);
2169 // load the rest of the entities
2171 // AK possible hack since num_edicts is still 0
2172 ent = PRVM_EDICT_NUM(0);
2173 memset (ent->fields.server, 0, prog->progs->entityfields * 4);
2174 ent->priv.server->free = false;
2175 ent->fields.server->model = PRVM_SetEngineString(sv.modelname);
2176 ent->fields.server->modelindex = 1; // world model
2177 ent->fields.server->solid = SOLID_BSP;
2178 ent->fields.server->movetype = MOVETYPE_PUSH;
2179 VectorCopy(sv.worldmodel->normalmins, ent->fields.server->mins);
2180 VectorCopy(sv.worldmodel->normalmaxs, ent->fields.server->maxs);
2181 VectorCopy(sv.worldmodel->normalmins, ent->fields.server->absmin);
2182 VectorCopy(sv.worldmodel->normalmaxs, ent->fields.server->absmax);
2185 prog->globals.server->coop = coop.integer;
2187 prog->globals.server->deathmatch = deathmatch.integer;
2189 prog->globals.server->mapname = PRVM_SetEngineString(sv.name);
2191 // serverflags are for cross level information (sigils)
2192 prog->globals.server->serverflags = svs.serverflags;
2194 // we need to reset the spawned flag on all connected clients here so that
2195 // their thinks don't run during startup (before PutClientInServer)
2196 // we also need to set up the client entities now
2197 // and we need to set the ->edict pointers to point into the progs edicts
2198 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2200 host_client->spawned = false;
2201 host_client->edict = PRVM_EDICT_NUM(i + 1);
2202 PRVM_ED_ClearEdict(host_client->edict);
2205 // load replacement entity file if found
2206 if (sv_entpatch.integer && (entities = (char *)FS_LoadFile(va("maps/%s.ent", sv.name), tempmempool, true, NULL)))
2208 Con_Printf("Loaded maps/%s.ent\n", sv.name);
2209 PRVM_ED_LoadFromFile (entities);
2213 PRVM_ED_LoadFromFile (sv.worldmodel->brush.entities);
2216 // LordHavoc: clear world angles (to fix e3m3.bsp)
2217 VectorClear(prog->edicts->fields.server->angles);
2219 // all setup is completed, any further precache statements are errors
2220 sv.state = ss_active;
2221 prog->allowworldwrites = false;
2223 // run two frames to allow everything to settle
2224 for (i = 0;i < 2;i++)
2232 // create a baseline for more efficient communications
2233 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
2234 SV_CreateBaseline ();
2236 // send serverinfo to all connected clients, and set up botclients coming back from a level change
2237 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2239 if (!host_client->active)
2241 if (host_client->netconnection)
2242 SV_SendServerinfo(host_client);
2246 // if client is a botclient coming from a level change, we need to
2247 // set up client info that normally requires networking
2249 // copy spawn parms out of the client_t
2250 for (j=0 ; j< NUM_SPAWN_PARMS ; j++)
2251 (&prog->globals.server->parm1)[j] = host_client->spawn_parms[j];
2253 // call the spawn function
2254 host_client->clientconnectcalled = true;
2255 prog->globals.server->time = sv.time;
2256 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
2257 PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
2258 PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
2259 host_client->spawned = true;
2263 Con_DPrint("Server spawned.\n");
2264 NetConn_Heartbeat (2);
2269 /////////////////////////////////////////////////////
2272 void SV_VM_CB_BeginIncreaseEdicts(void)
2277 PRVM_Free( sv.moved_edicts );
2278 sv.moved_edicts = (prvm_edict_t **)PRVM_Alloc(prog->max_edicts * sizeof(prvm_edict_t *));
2280 // links don't survive the transition, so unlink everything
2281 for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
2283 if (!ent->priv.server->free)
2284 World_UnlinkEdict(prog->edicts + i);
2285 memset(&ent->priv.server->areagrid, 0, sizeof(ent->priv.server->areagrid));
2287 World_Clear(&sv.world);
2290 void SV_VM_CB_EndIncreaseEdicts(void)
2295 // link every entity except world
2296 for (i = 1, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
2297 if (!ent->priv.server->free)
2298 SV_LinkEdict(ent, false);
2301 void SV_VM_CB_InitEdict(prvm_edict_t *e)
2303 // LordHavoc: for consistency set these here
2304 int num = PRVM_NUM_FOR_EDICT(e) - 1;
2306 e->priv.server->move = false; // don't move on first frame
2308 if (num >= 0 && num < svs.maxclients)
2311 // set colormap and team on newly created player entity
2312 e->fields.server->colormap = num + 1;
2313 e->fields.server->team = (svs.clients[num].colors & 15) + 1;
2314 // set netname/clientcolors back to client values so that
2315 // DP_SV_CLIENTNAME and DP_SV_CLIENTCOLORS will not immediately
2317 e->fields.server->netname = PRVM_SetEngineString(svs.clients[num].name);
2318 if ((val = PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.clientcolors)))
2319 val->_float = svs.clients[num].colors;
2320 // NEXUIZ_PLAYERMODEL and NEXUIZ_PLAYERSKIN
2321 if( prog->fieldoffsets.playermodel >= 0 )
2322 PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(svs.clients[num].playermodel);
2323 if( prog->fieldoffsets.playerskin >= 0 )
2324 PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(svs.clients[num].playerskin);
2328 void SV_VM_CB_FreeEdict(prvm_edict_t *ed)
2330 World_UnlinkEdict(ed); // unlink from world bsp
2332 ed->fields.server->model = 0;
2333 ed->fields.server->takedamage = 0;
2334 ed->fields.server->modelindex = 0;
2335 ed->fields.server->colormap = 0;
2336 ed->fields.server->skin = 0;
2337 ed->fields.server->frame = 0;
2338 VectorClear(ed->fields.server->origin);
2339 VectorClear(ed->fields.server->angles);
2340 ed->fields.server->nextthink = -1;
2341 ed->fields.server->solid = 0;
2344 void SV_VM_CB_CountEdicts(void)
2348 int active, models, solid, step;
2350 active = models = solid = step = 0;
2351 for (i=0 ; i<prog->num_edicts ; i++)
2353 ent = PRVM_EDICT_NUM(i);
2354 if (ent->priv.server->free)
2357 if (ent->fields.server->solid)
2359 if (ent->fields.server->model)
2361 if (ent->fields.server->movetype == MOVETYPE_STEP)
2365 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
2366 Con_Printf("active :%3i\n", active);
2367 Con_Printf("view :%3i\n", models);
2368 Con_Printf("touch :%3i\n", solid);
2369 Con_Printf("step :%3i\n", step);
2372 qboolean SV_VM_CB_LoadEdict(prvm_edict_t *ent)
2374 // remove things from different skill levels or deathmatch
2375 if (gamemode != GAME_TRANSFUSION) //Transfusion does this in QC
2377 if (deathmatch.integer)
2379 if (((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_DEATHMATCH))
2384 else if ((current_skill <= 0 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_EASY ))
2385 || (current_skill == 1 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_MEDIUM))
2386 || (current_skill >= 2 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_HARD )))
2394 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)"};
2395 cvar_t nomonsters = {0, "nomonsters", "0", "unused cvar in quake, can be used by mods"};
2396 cvar_t gamecfg = {0, "gamecfg", "0", "unused cvar in quake, can be used by mods"};
2397 cvar_t scratch1 = {0, "scratch1", "0", "unused cvar in quake, can be used by mods"};
2398 cvar_t scratch2 = {0,"scratch2", "0", "unused cvar in quake, can be used by mods"};
2399 cvar_t scratch3 = {0, "scratch3", "0", "unused cvar in quake, can be used by mods"};
2400 cvar_t scratch4 = {0, "scratch4", "0", "unused cvar in quake, can be used by mods"};
2401 cvar_t savedgamecfg = {CVAR_SAVE, "savedgamecfg", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2402 cvar_t saved1 = {CVAR_SAVE, "saved1", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2403 cvar_t saved2 = {CVAR_SAVE, "saved2", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2404 cvar_t saved3 = {CVAR_SAVE, "saved3", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2405 cvar_t saved4 = {CVAR_SAVE, "saved4", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
2406 cvar_t nehx00 = {0, "nehx00", "0", "nehahra data storage cvar (used in singleplayer)"};
2407 cvar_t nehx01 = {0, "nehx01", "0", "nehahra data storage cvar (used in singleplayer)"};
2408 cvar_t nehx02 = {0, "nehx02", "0", "nehahra data storage cvar (used in singleplayer)"};
2409 cvar_t nehx03 = {0, "nehx03", "0", "nehahra data storage cvar (used in singleplayer)"};
2410 cvar_t nehx04 = {0, "nehx04", "0", "nehahra data storage cvar (used in singleplayer)"};
2411 cvar_t nehx05 = {0, "nehx05", "0", "nehahra data storage cvar (used in singleplayer)"};
2412 cvar_t nehx06 = {0, "nehx06", "0", "nehahra data storage cvar (used in singleplayer)"};
2413 cvar_t nehx07 = {0, "nehx07", "0", "nehahra data storage cvar (used in singleplayer)"};
2414 cvar_t nehx08 = {0, "nehx08", "0", "nehahra data storage cvar (used in singleplayer)"};
2415 cvar_t nehx09 = {0, "nehx09", "0", "nehahra data storage cvar (used in singleplayer)"};
2416 cvar_t nehx10 = {0, "nehx10", "0", "nehahra data storage cvar (used in singleplayer)"};
2417 cvar_t nehx11 = {0, "nehx11", "0", "nehahra data storage cvar (used in singleplayer)"};
2418 cvar_t nehx12 = {0, "nehx12", "0", "nehahra data storage cvar (used in singleplayer)"};
2419 cvar_t nehx13 = {0, "nehx13", "0", "nehahra data storage cvar (used in singleplayer)"};
2420 cvar_t nehx14 = {0, "nehx14", "0", "nehahra data storage cvar (used in singleplayer)"};
2421 cvar_t nehx15 = {0, "nehx15", "0", "nehahra data storage cvar (used in singleplayer)"};
2422 cvar_t nehx16 = {0, "nehx16", "0", "nehahra data storage cvar (used in singleplayer)"};
2423 cvar_t nehx17 = {0, "nehx17", "0", "nehahra data storage cvar (used in singleplayer)"};
2424 cvar_t nehx18 = {0, "nehx18", "0", "nehahra data storage cvar (used in singleplayer)"};
2425 cvar_t nehx19 = {0, "nehx19", "0", "nehahra data storage cvar (used in singleplayer)"};
2426 cvar_t cutscene = {0, "cutscene", "1", "enables cutscenes in nehahra, can be used by other mods"};
2428 void SV_VM_Init(void)
2430 Cvar_RegisterVariable (&pr_checkextension);
2431 Cvar_RegisterVariable (&nomonsters);
2432 Cvar_RegisterVariable (&gamecfg);
2433 Cvar_RegisterVariable (&scratch1);
2434 Cvar_RegisterVariable (&scratch2);
2435 Cvar_RegisterVariable (&scratch3);
2436 Cvar_RegisterVariable (&scratch4);
2437 Cvar_RegisterVariable (&savedgamecfg);
2438 Cvar_RegisterVariable (&saved1);
2439 Cvar_RegisterVariable (&saved2);
2440 Cvar_RegisterVariable (&saved3);
2441 Cvar_RegisterVariable (&saved4);
2442 // LordHavoc: Nehahra uses these to pass data around cutscene demos
2443 if (gamemode == GAME_NEHAHRA)
2445 Cvar_RegisterVariable (&nehx00);
2446 Cvar_RegisterVariable (&nehx01);
2447 Cvar_RegisterVariable (&nehx02);
2448 Cvar_RegisterVariable (&nehx03);
2449 Cvar_RegisterVariable (&nehx04);
2450 Cvar_RegisterVariable (&nehx05);
2451 Cvar_RegisterVariable (&nehx06);
2452 Cvar_RegisterVariable (&nehx07);
2453 Cvar_RegisterVariable (&nehx08);
2454 Cvar_RegisterVariable (&nehx09);
2455 Cvar_RegisterVariable (&nehx10);
2456 Cvar_RegisterVariable (&nehx11);
2457 Cvar_RegisterVariable (&nehx12);
2458 Cvar_RegisterVariable (&nehx13);
2459 Cvar_RegisterVariable (&nehx14);
2460 Cvar_RegisterVariable (&nehx15);
2461 Cvar_RegisterVariable (&nehx16);
2462 Cvar_RegisterVariable (&nehx17);
2463 Cvar_RegisterVariable (&nehx18);
2464 Cvar_RegisterVariable (&nehx19);
2466 Cvar_RegisterVariable (&cutscene); // for Nehahra but useful to other mods as well
2469 #define REQFIELDS (sizeof(reqfields) / sizeof(prvm_required_field_t))
2471 prvm_required_field_t reqfields[] =
2473 {ev_entity, "cursor_trace_ent"},
2474 {ev_entity, "drawonlytoclient"},
2475 {ev_entity, "exteriormodeltoclient"},
2476 {ev_entity, "nodrawtoclient"},
2477 {ev_entity, "tag_entity"},
2478 {ev_entity, "viewmodelforclient"},
2479 {ev_float, "alpha"},
2480 {ev_float, "ammo_cells1"},
2481 {ev_float, "ammo_lava_nails"},
2482 {ev_float, "ammo_multi_rockets"},
2483 {ev_float, "ammo_nails1"},
2484 {ev_float, "ammo_plasma"},
2485 {ev_float, "ammo_rockets1"},
2486 {ev_float, "ammo_shells1"},
2487 {ev_float, "button3"},
2488 {ev_float, "button4"},
2489 {ev_float, "button5"},
2490 {ev_float, "button6"},
2491 {ev_float, "button7"},
2492 {ev_float, "button8"},
2493 {ev_float, "button9"},
2494 {ev_float, "button10"},
2495 {ev_float, "button11"},
2496 {ev_float, "button12"},
2497 {ev_float, "button13"},
2498 {ev_float, "button14"},
2499 {ev_float, "button15"},
2500 {ev_float, "button16"},
2501 {ev_float, "buttonchat"},
2502 {ev_float, "buttonuse"},
2503 {ev_float, "clientcolors"},
2504 {ev_float, "cursor_active"},
2505 {ev_float, "fullbright"},
2506 {ev_float, "glow_color"},
2507 {ev_float, "glow_size"},
2508 {ev_float, "glow_trail"},
2509 {ev_float, "gravity"},
2510 {ev_float, "idealpitch"},
2511 {ev_float, "items2"},
2512 {ev_float, "light_lev"},
2513 {ev_float, "pflags"},
2515 {ev_float, "pitch_speed"},
2516 {ev_float, "pmodel"},
2517 {ev_float, "renderamt"}, // HalfLife support
2518 {ev_float, "rendermode"}, // HalfLife support
2519 {ev_float, "scale"},
2520 {ev_float, "style"},
2521 {ev_float, "tag_index"},
2522 {ev_float, "Version"},
2523 {ev_float, "viewzoom"},
2524 {ev_vector, "color"},
2525 {ev_vector, "colormod"},
2526 {ev_vector, "cursor_screen"},
2527 {ev_vector, "cursor_trace_endpos"},
2528 {ev_vector, "cursor_trace_start"},
2529 {ev_vector, "movement"},
2530 {ev_vector, "punchvector"},
2531 {ev_string, "playermodel"},
2532 {ev_string, "playerskin"},
2533 {ev_function, "SendEntity"},
2534 {ev_function, "customizeentityforclient"},
2535 // DRESK - Support for Entity Contents Transition Event
2536 {ev_function, "contentstransition"},
2539 void SV_VM_Setup(void)
2541 extern cvar_t csqc_progname; //[515]: csqc crc check and right csprogs name according to progs.dat
2542 extern cvar_t csqc_progcrc;
2543 extern cvar_t csqc_progsize;
2544 size_t csprogsdatasize;
2546 PRVM_InitProg( PRVM_SERVERPROG );
2548 // allocate the mempools
2549 // TODO: move the magic numbers/constants into #defines [9/13/2006 Black]
2550 prog->progs_mempool = Mem_AllocPool("Server Progs", 0, NULL);
2551 prog->builtins = vm_sv_builtins;
2552 prog->numbuiltins = vm_sv_numbuiltins;
2553 prog->headercrc = PROGHEADER_CRC;
2554 prog->max_edicts = 512;
2555 prog->limit_edicts = MAX_EDICTS;
2556 prog->reserved_edicts = svs.maxclients;
2557 prog->edictprivate_size = sizeof(edict_engineprivate_t);
2558 prog->name = "server";
2559 prog->extensionstring = vm_sv_extensions;
2560 prog->loadintoworld = true;
2562 prog->begin_increase_edicts = SV_VM_CB_BeginIncreaseEdicts;
2563 prog->end_increase_edicts = SV_VM_CB_EndIncreaseEdicts;
2564 prog->init_edict = SV_VM_CB_InitEdict;
2565 prog->free_edict = SV_VM_CB_FreeEdict;
2566 prog->count_edicts = SV_VM_CB_CountEdicts;
2567 prog->load_edict = SV_VM_CB_LoadEdict;
2568 prog->init_cmd = VM_SV_Cmd_Init;
2569 prog->reset_cmd = VM_SV_Cmd_Reset;
2570 prog->error_cmd = Host_Error;
2572 // TODO: add a requiredfuncs list (ask LH if this is necessary at all)
2573 PRVM_LoadProgs( sv_progs.string, 0, NULL, REQFIELDS, reqfields, 0, NULL );
2575 // some mods compiled with scrambling compilers lack certain critical
2576 // global names and field names such as "self" and "time" and "nextthink"
2577 // so we have to set these offsets manually, matching the entvars_t
2578 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, angles);
2579 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, chain);
2580 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, classname);
2581 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, frame);
2582 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, groundentity);
2583 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ideal_yaw);
2584 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, nextthink);
2585 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, think);
2586 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, yaw_speed);
2587 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, self);
2588 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, time);
2589 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_forward);
2590 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_right);
2591 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_up);
2592 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_allsolid);
2593 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_startsolid);
2594 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_fraction);
2595 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inwater);
2596 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inopen);
2597 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_endpos);
2598 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_normal);
2599 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_dist);
2600 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_ent);
2601 // OP_STATE is always supported on server (due to entvars_t)
2602 prog->flag |= PRVM_OP_STATE;
2604 VM_AutoSentStats_Clear();//[515]: csqc
2605 EntityFrameCSQC_ClearVersions();//[515]: csqc
2609 // 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
2610 sv.csqc_progname[0] = 0;
2611 sv.csqc_progcrc = FS_CRCFile(csqc_progname.string, &csprogsdatasize);
2612 sv.csqc_progsize = csprogsdatasize;
2613 if (sv.csqc_progsize > 0)
2615 strlcpy(sv.csqc_progname, csqc_progname.string, sizeof(sv.csqc_progname));
2616 Con_DPrintf("server detected csqc progs file \"%s\" with size %i and crc %i\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc);
2620 void SV_VM_Begin(void)
2623 PRVM_SetProg( PRVM_SERVERPROG );
2625 prog->globals.server->time = (float) sv.time;
2628 void SV_VM_End(void)