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
27 static void SV_SaveEntFile_f(void);
28 static void SV_StartDownload_f(void);
29 static void SV_Download_f(void);
30 static void SV_VM_Setup(void);
31 extern cvar_t net_connecttimeout;
33 void VM_CustomStats_Clear (void);
34 void VM_SV_UpdateCustomStats (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats);
36 cvar_t sv_worldmessage = {CVAR_READONLY, "sv_worldmessage", "", "title of current level"};
37 cvar_t sv_worldname = {CVAR_READONLY, "sv_worldname", "", "name of current worldmodel"};
38 cvar_t sv_worldnamenoextension = {CVAR_READONLY, "sv_worldnamenoextension", "", "name of current worldmodel without extension"};
39 cvar_t sv_worldbasename = {CVAR_READONLY, "sv_worldbasename", "", "name of current worldmodel without maps/ prefix or extension"};
41 cvar_t coop = {0, "coop","0", "coop mode, 0 = no coop, 1 = coop mode, multiple players playing through the singleplayer game (coop mode also shuts off deathmatch)"};
42 cvar_t deathmatch = {0, "deathmatch","0", "deathmatch mode, values depend on mod but typically 0 = no deathmatch, 1 = normal deathmatch with respawning weapons, 2 = weapons stay (players can only pick up new weapons)"};
43 cvar_t fraglimit = {CVAR_NOTIFY, "fraglimit","0", "ends level if this many frags is reached by any player"};
44 cvar_t gamecfg = {0, "gamecfg", "0", "unused cvar in quake, can be used by mods"};
45 cvar_t noexit = {CVAR_NOTIFY, "noexit","0", "kills anyone attempting to use an exit"};
46 cvar_t nomonsters = {0, "nomonsters", "0", "unused cvar in quake, can be used by mods"};
47 cvar_t pausable = {0, "pausable","1", "allow players to pause or not"};
48 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)"};
49 cvar_t samelevel = {CVAR_NOTIFY, "samelevel","0", "repeats same level if level ends (due to timelimit or someone hitting an exit)"};
50 cvar_t skill = {0, "skill","1", "difficulty level of game, affects monster layouts in levels, 0 = easy, 1 = normal, 2 = hard, 3 = nightmare (same layout as hard but monsters fire twice)"};
51 cvar_t slowmo = {0, "slowmo", "1.0", "controls game speed, 0.5 is half speed, 2 is double speed"};
53 cvar_t sv_accelerate = {0, "sv_accelerate", "10", "rate at which a player accelerates to sv_maxspeed"};
54 cvar_t sv_aim = {CVAR_SAVE, "sv_aim", "2", "maximum cosine angle for quake's vertical autoaim, a value above 1 completely disables the autoaim, quake used 0.93"};
55 cvar_t sv_airaccel_qw = {0, "sv_airaccel_qw", "1", "ratio of QW-style air control as opposed to simple acceleration; when < 0, the speed is clamped against the maximum allowed forward speed after the move"};
56 cvar_t sv_airaccel_sideways_friction = {0, "sv_airaccel_sideways_friction", "", "anti-sideways movement stabilization (reduces speed gain when zigzagging); when < 0, only so much friction is applied that braking (by accelerating backwards) cannot be stronger"};
57 cvar_t sv_airaccelerate = {0, "sv_airaccelerate", "-1", "rate at which a player accelerates to sv_maxairspeed while in the air, if less than 0 the sv_accelerate variable is used instead"};
58 cvar_t sv_airstopaccelerate = {0, "sv_airstopaccelerate", "0", "when set, replacement for sv_airaccelerate when moving backwards"};
59 cvar_t sv_airspeedlimit_nonqw = {0, "sv_airspeedlimit_nonqw", "0", "when set, this is a soft speed limit while in air when using airaccel_qw not equal to 1"};
60 cvar_t sv_airstrafeaccelerate = {0, "sv_airstrafeaccelerate", "0", "when set, replacement for sv_airaccelerate when just strafing"};
61 cvar_t sv_maxairstrafespeed = {0, "sv_maxairstrafespeed", "0", "when set, replacement for sv_maxairspeed when just strafing"};
62 cvar_t sv_airstrafeaccel_qw = {0, "sv_airstrafeaccel_qw", "0", "when set, replacement for sv_airaccel_qw when just strafing"};
63 cvar_t sv_aircontrol = {0, "sv_aircontrol", "0", "CPMA-style air control"};
64 cvar_t sv_aircontrol_power = {0, "sv_aircontrol_power", "2", "CPMA-style air control exponent"};
65 cvar_t sv_allowdownloads = {0, "sv_allowdownloads", "1", "whether to allow clients to download files from the server (does not affect http downloads)"};
66 cvar_t sv_allowdownloads_archive = {0, "sv_allowdownloads_archive", "0", "whether to allow downloads of archives (pak/pk3)"};
67 cvar_t sv_allowdownloads_config = {0, "sv_allowdownloads_config", "0", "whether to allow downloads of config files (cfg)"};
68 cvar_t sv_allowdownloads_dlcache = {0, "sv_allowdownloads_dlcache", "0", "whether to allow downloads of dlcache files (dlcache/)"};
69 cvar_t sv_allowdownloads_inarchive = {0, "sv_allowdownloads_inarchive", "0", "whether to allow downloads from archives (pak/pk3)"};
70 cvar_t sv_areagrid_mingridsize = {CVAR_NOTIFY, "sv_areagrid_mingridsize", "128", "minimum areagrid cell size, smaller values work better for lots of small objects, higher values for large objects"};
71 cvar_t sv_checkforpacketsduringsleep = {0, "sv_checkforpacketsduringsleep", "0", "uses select() function to wait between frames which can be interrupted by packets being received, instead of Sleep()/usleep()/SDL_Sleep() functions which do not check for packets"};
72 cvar_t sv_clmovement_enable = {0, "sv_clmovement_enable", "1", "whether to allow clients to use cl_movement prediction, which can cause choppy movement on the server which may annoy other players"};
73 cvar_t sv_clmovement_minping = {0, "sv_clmovement_minping", "0", "if client ping is below this time in milliseconds, then their ability to use cl_movement prediction is disabled for a while (as they don't need it)"};
74 cvar_t sv_clmovement_minping_disabletime = {0, "sv_clmovement_minping_disabletime", "1000", "when client falls below minping, disable their prediction for this many milliseconds (should be at least 1000 or else their prediction may turn on/off frequently)"};
75 cvar_t sv_clmovement_inputtimeout = {0, "sv_clmovement_inputtimeout", "0.2", "when a client does not send input for this many seconds, force them to move anyway (unlike QuakeWorld)"};
76 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!)"};
77 cvar_t sv_cullentities_pvs = {0, "sv_cullentities_pvs", "1", "fast but loose culling of hidden entities"};
78 cvar_t sv_cullentities_stats = {0, "sv_cullentities_stats", "0", "displays stats on network entities culled by various methods for each client"};
79 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"};
80 cvar_t sv_cullentities_trace_delay = {0, "sv_cullentities_trace_delay", "1", "number of seconds until the entity gets actually culled"};
81 cvar_t sv_cullentities_trace_delay_players = {0, "sv_cullentities_trace_delay_players", "0.2", "number of seconds until the entity gets actually culled if it is a player entity"};
82 cvar_t sv_cullentities_trace_enlarge = {0, "sv_cullentities_trace_enlarge", "0", "box enlargement for entity culling"};
83 cvar_t sv_cullentities_trace_prediction = {0, "sv_cullentities_trace_prediction", "1", "also trace from the predicted player position"};
84 cvar_t sv_cullentities_trace_prediction_time = {0, "sv_cullentities_trace_prediction_time", "0.2", "how many seconds of prediction to use"};
85 cvar_t sv_cullentities_trace_entityocclusion = {0, "sv_cullentities_trace_entityocclusion", "0", "also check if doors and other bsp models are in the way"};
86 cvar_t sv_cullentities_trace_samples = {0, "sv_cullentities_trace_samples", "2", "number of samples to test for entity culling"};
87 cvar_t sv_cullentities_trace_samples_extra = {0, "sv_cullentities_trace_samples_extra", "2", "number of samples to test for entity culling when the entity affects its surroundings by e.g. dlight"};
88 cvar_t sv_cullentities_trace_samples_players = {0, "sv_cullentities_trace_samples_players", "8", "number of samples to test for entity culling when the entity is a player entity"};
89 cvar_t sv_debugmove = {CVAR_NOTIFY, "sv_debugmove", "0", "disables collision detection optimizations for debugging purposes"};
90 cvar_t sv_echobprint = {CVAR_SAVE, "sv_echobprint", "1", "prints gamecode bprint() calls to server console"};
91 cvar_t sv_edgefriction = {0, "edgefriction", "1", "how much you slow down when nearing a ledge you might fall off, multiplier of sv_friction (Quake used 2, QuakeWorld used 1 due to a bug in physics code)"};
92 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)"};
93 cvar_t sv_fixedframeratesingleplayer = {0, "sv_fixedframeratesingleplayer", "1", "allows you to use server-style timing system in singleplayer (don't run faster than sys_ticrate)"};
94 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
95 cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4", "how fast you slow down"};
96 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"};
97 cvar_t sv_gameplayfix_consistentplayerprethink = {0, "sv_gameplayfix_consistentplayerprethink", "0", "improves fairness in multiplayer by running all PlayerPreThink functions (which fire weapons) before performing physics, then runing all PlayerPostThink functions"};
98 cvar_t sv_gameplayfix_delayprojectiles = {0, "sv_gameplayfix_delayprojectiles", "1", "causes entities to not move on the same frame they are spawned, meaning that projectiles wait until the next frame to perform their first move, giving proper interpolation and rocket trails, but making weapons harder to use at low framerates"};
99 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)"};
100 cvar_t sv_gameplayfix_droptofloorstartsolid_nudgetocorrect = {0, "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect", "1", "tries to nudge stuck items and monsters out of walls before droptofloor is performed"};
101 cvar_t sv_gameplayfix_easierwaterjump = {0, "sv_gameplayfix_easierwaterjump", "1", "changes water jumping to make it easier to get out of water (exactly like in QuakeWorld)"};
102 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"};
103 cvar_t sv_gameplayfix_gravityunaffectedbyticrate = {0, "sv_gameplayfix_gravityunaffectedbyticrate", "0", "fix some ticrate issues in physics."};
104 cvar_t sv_gameplayfix_grenadebouncedownslopes = {0, "sv_gameplayfix_grenadebouncedownslopes", "1", "prevents MOVETYPE_BOUNCE (grenades) from getting stuck when fired down a downward sloping surface"};
105 cvar_t sv_gameplayfix_multiplethinksperframe = {0, "sv_gameplayfix_multiplethinksperframe", "1", "allows entities to think more often than the server framerate, primarily useful for very high fire rate weapons"};
106 cvar_t sv_gameplayfix_noairborncorpse = {0, "sv_gameplayfix_noairborncorpse", "1", "causes entities (corpses, items, etc) sitting ontop of moving entities (players) to fall when the moving entity (player) is no longer supporting them"};
107 cvar_t sv_gameplayfix_noairborncorpse_allowsuspendeditems = {0, "sv_gameplayfix_noairborncorpse_allowsuspendeditems", "1", "causes entities sitting ontop of objects that are instantaneously remove to float in midair (special hack to allow a common level design trick for floating items)"};
108 cvar_t sv_gameplayfix_nudgeoutofsolid = {0, "sv_gameplayfix_nudgeoutofsolid", "1", "attempts to fix physics errors (where an object ended up in solid for some reason)"};
109 cvar_t sv_gameplayfix_nudgeoutofsolid_bias = {0, "sv_gameplayfix_nudgeoutofsolid_bias", "0", "over-correction on nudgeoutofsolid logic, to prevent constant contact"};
110 cvar_t sv_gameplayfix_q2airaccelerate = {0, "sv_gameplayfix_q2airaccelerate", "0", "Quake2-style air acceleration"};
111 cvar_t sv_gameplayfix_nogravityonground = {0, "sv_gameplayfix_nogravityonground", "0", "Quake2-style air acceleration"};
112 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"};
113 cvar_t sv_gameplayfix_slidemoveprojectiles = {0, "sv_gameplayfix_slidemoveprojectiles", "1", "allows MOVETYPE_FLY/FLYMISSILE/TOSS/BOUNCE/BOUNCEMISSILE entities to finish their move in a frame even if they hit something, fixes 'gravity accumulation' bug for grenades on steep slopes"};
114 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)"};
115 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)"};
116 cvar_t sv_gameplayfix_stepmultipletimes = {0, "sv_gameplayfix_stepmultipletimes", "1", "applies step-up onto a ledge more than once in a single frame, when running quickly up stairs"};
117 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"};
118 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"};
119 cvar_t sv_gameplayfix_downtracesupportsongroundflag = {0, "sv_gameplayfix_downtracesupportsongroundflag", "1", "prevents very short moves from clearing onground (which may make the player stick to the floor at high netfps)"};
120 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
121 cvar_t sv_idealpitchscale = {0, "sv_idealpitchscale","0.8", "how much to look up/down slopes and stairs when not using freelook"};
122 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
123 cvar_t sv_jumpvelocity = {0, "sv_jumpvelocity", "270", "cvar that can be used by QuakeC code for jump velocity"};
124 cvar_t sv_maxairspeed = {0, "sv_maxairspeed", "30", "maximum speed a player can accelerate to when airborn (note that it is possible to completely stop by moving the opposite direction)"};
125 cvar_t sv_maxrate = {CVAR_SAVE | CVAR_NOTIFY, "sv_maxrate", "1000000", "upper limit on client rate cvar, should reflect your network connection quality"};
126 cvar_t sv_maxspeed = {CVAR_NOTIFY, "sv_maxspeed", "320", "maximum speed a player can accelerate to when on ground (can be exceeded by tricks)"};
127 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
128 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
129 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
130 cvar_t sv_progs = {0, "sv_progs", "progs.dat", "selects which quakec progs.dat file to run" };
131 cvar_t sv_protocolname = {0, "sv_protocolname", "DP7", "selects network protocol to host for (values include QUAKE, QUAKEDP, NEHAHRAMOVIE, DP1 and up)"};
132 cvar_t sv_random_seed = {0, "sv_random_seed", "", "random seed; when set, on every map start this random seed is used to initialize the random number generator. Don't touch it unless for benchmarking or debugging"};
133 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)"};
134 cvar_t sv_sound_land = {0, "sv_sound_land", "demon/dland2.wav", "sound to play when MOVETYPE_STEP entity hits the ground at high speed (empty cvar disables the sound)"};
135 cvar_t sv_sound_watersplash = {0, "sv_sound_watersplash", "misc/h2ohit1.wav", "sound to play when MOVETYPE_FLY/TOSS/BOUNCE/STEP entity enters or leaves water (empty cvar disables the sound)"};
136 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
137 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
138 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
139 cvar_t sv_wateraccelerate = {0, "sv_wateraccelerate", "-1", "rate at which a player accelerates to sv_maxspeed while in the air, if less than 0 the sv_accelerate variable is used instead"};
140 cvar_t sv_waterfriction = {CVAR_NOTIFY, "sv_waterfriction","-1", "how fast you slow down, if less than 0 the sv_friction variable is used instead"};
141 cvar_t sv_warsowbunny_airforwardaccel = {0, "sv_warsowbunny_airforwardaccel", "1.00001", "how fast you accelerate until you reach sv_maxspeed"};
142 cvar_t sv_warsowbunny_accel = {0, "sv_warsowbunny_accel", "0.1585", "how fast you accelerate until after reaching sv_maxspeed (it gets harder as you near sv_warsowbunny_topspeed)"};
143 cvar_t sv_warsowbunny_topspeed = {0, "sv_warsowbunny_topspeed", "925", "soft speed limit (can get faster with rjs and on ramps)"};
144 cvar_t sv_warsowbunny_turnaccel = {0, "sv_warsowbunny_turnaccel", "0", "max sharpness of turns (also master switch for the sv_warsowbunny_* mode; set this to 9 to enable)"};
145 cvar_t sv_warsowbunny_backtosideratio = {0, "sv_warsowbunny_backtosideratio", "0.8", "lower values make it easier to change direction without losing speed; the drawback is \"understeering\" in sharp turns"};
146 cvar_t sv_onlycsqcnetworking = {0, "sv_onlycsqcnetworking", "0", "disables legacy entity networking code for higher performance (except on clients, which can still be legacy)"};
147 cvar_t sys_ticrate = {CVAR_SAVE, "sys_ticrate","0.0138889", "how long a server frame is in seconds, 0.05 is 20fps server rate, 0.1 is 10fps (can not be set higher than 0.1), 0 runs as many server frames as possible (makes games against bots a little smoother, overwhelms network players), 0.0138889 matches QuakeWorld physics"};
148 cvar_t teamplay = {CVAR_NOTIFY, "teamplay","0", "teamplay mode, values depend on mod but typically 0 = no teams, 1 = no team damage no self damage, 2 = team damage and self damage, some mods support 3 = no team damage but can damage self"};
149 cvar_t timelimit = {CVAR_NOTIFY, "timelimit","0", "ends level at this time (in minutes)"};
151 cvar_t saved1 = {CVAR_SAVE, "saved1", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
152 cvar_t saved2 = {CVAR_SAVE, "saved2", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
153 cvar_t saved3 = {CVAR_SAVE, "saved3", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
154 cvar_t saved4 = {CVAR_SAVE, "saved4", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
155 cvar_t savedgamecfg = {CVAR_SAVE, "savedgamecfg", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
156 cvar_t scratch1 = {0, "scratch1", "0", "unused cvar in quake, can be used by mods"};
157 cvar_t scratch2 = {0,"scratch2", "0", "unused cvar in quake, can be used by mods"};
158 cvar_t scratch3 = {0, "scratch3", "0", "unused cvar in quake, can be used by mods"};
159 cvar_t scratch4 = {0, "scratch4", "0", "unused cvar in quake, can be used by mods"};
160 cvar_t temp1 = {0, "temp1","0", "general cvar for mods to use, in stock id1 this selects which death animation to use on players (0 = random death, other values select specific death scenes)"};
162 cvar_t nehx00 = {0, "nehx00", "0", "nehahra data storage cvar (used in singleplayer)"};
163 cvar_t nehx01 = {0, "nehx01", "0", "nehahra data storage cvar (used in singleplayer)"};
164 cvar_t nehx02 = {0, "nehx02", "0", "nehahra data storage cvar (used in singleplayer)"};
165 cvar_t nehx03 = {0, "nehx03", "0", "nehahra data storage cvar (used in singleplayer)"};
166 cvar_t nehx04 = {0, "nehx04", "0", "nehahra data storage cvar (used in singleplayer)"};
167 cvar_t nehx05 = {0, "nehx05", "0", "nehahra data storage cvar (used in singleplayer)"};
168 cvar_t nehx06 = {0, "nehx06", "0", "nehahra data storage cvar (used in singleplayer)"};
169 cvar_t nehx07 = {0, "nehx07", "0", "nehahra data storage cvar (used in singleplayer)"};
170 cvar_t nehx08 = {0, "nehx08", "0", "nehahra data storage cvar (used in singleplayer)"};
171 cvar_t nehx09 = {0, "nehx09", "0", "nehahra data storage cvar (used in singleplayer)"};
172 cvar_t nehx10 = {0, "nehx10", "0", "nehahra data storage cvar (used in singleplayer)"};
173 cvar_t nehx11 = {0, "nehx11", "0", "nehahra data storage cvar (used in singleplayer)"};
174 cvar_t nehx12 = {0, "nehx12", "0", "nehahra data storage cvar (used in singleplayer)"};
175 cvar_t nehx13 = {0, "nehx13", "0", "nehahra data storage cvar (used in singleplayer)"};
176 cvar_t nehx14 = {0, "nehx14", "0", "nehahra data storage cvar (used in singleplayer)"};
177 cvar_t nehx15 = {0, "nehx15", "0", "nehahra data storage cvar (used in singleplayer)"};
178 cvar_t nehx16 = {0, "nehx16", "0", "nehahra data storage cvar (used in singleplayer)"};
179 cvar_t nehx17 = {0, "nehx17", "0", "nehahra data storage cvar (used in singleplayer)"};
180 cvar_t nehx18 = {0, "nehx18", "0", "nehahra data storage cvar (used in singleplayer)"};
181 cvar_t nehx19 = {0, "nehx19", "0", "nehahra data storage cvar (used in singleplayer)"};
182 cvar_t cutscene = {0, "cutscene", "1", "enables cutscenes in nehahra, can be used by other mods"};
184 cvar_t sv_autodemo_perclient = {CVAR_SAVE, "sv_autodemo_perclient", "0", "set to 1 to enable autorecorded per-client demos (they'll start to record at the beginning of a match); set it to 2 to also record client->server packets (for debugging)"};
185 cvar_t sv_autodemo_perclient_nameformat = {CVAR_SAVE, "sv_autodemo_perclient_nameformat", "sv_autodemos/%Y-%m-%d_%H-%M", "The format of the sv_autodemo_perclient filename, followed by the map name, the client number and the IP address + port number, separated by underscores (the date is encoded using strftime escapes)" };
186 cvar_t sv_autodemo_perclient_discardable = {CVAR_SAVE, "sv_autodemo_perclient_discardable", "0", "Allow game code to decide whether a demo should be kept or discarded."};
188 cvar_t halflifebsp = {0, "halflifebsp", "0", "indicates the current map is hlbsp format (useful to know because of different bounding box sizes)"};
193 mempool_t *sv_mempool = NULL;
195 extern cvar_t slowmo;
196 extern float scr_centertime_off;
198 // MUST match effectnameindex_t in client.h
199 static const char *standardeffectnames[EFFECT_TOTAL] =
223 "TE_TEI_BIGEXPLOSION",
239 #define REQFIELDS (sizeof(reqfields) / sizeof(prvm_required_field_t))
241 prvm_required_field_t reqfields[] =
243 {ev_entity, "cursor_trace_ent"},
244 {ev_entity, "drawonlytoclient"},
245 {ev_entity, "exteriormodeltoclient"},
246 {ev_entity, "nodrawtoclient"},
247 {ev_entity, "tag_entity"},
248 {ev_entity, "viewmodelforclient"},
249 {ev_float, "SendFlags"},
250 {ev_float, "Version"},
252 {ev_float, "ammo_cells1"},
253 {ev_float, "ammo_lava_nails"},
254 {ev_float, "ammo_multi_rockets"},
255 {ev_float, "ammo_nails1"},
256 {ev_float, "ammo_plasma"},
257 {ev_float, "ammo_rockets1"},
258 {ev_float, "ammo_shells1"},
259 {ev_float, "button3"},
260 {ev_float, "button4"},
261 {ev_float, "button5"},
262 {ev_float, "button6"},
263 {ev_float, "button7"},
264 {ev_float, "button8"},
265 {ev_float, "button9"},
266 {ev_float, "button10"},
267 {ev_float, "button11"},
268 {ev_float, "button12"},
269 {ev_float, "button13"},
270 {ev_float, "button14"},
271 {ev_float, "button15"},
272 {ev_float, "button16"},
273 {ev_float, "buttonchat"},
274 {ev_float, "buttonuse"},
275 {ev_float, "clientcolors"},
276 {ev_float, "cursor_active"},
277 {ev_float, "disableclientprediction"},
278 {ev_float, "fullbright"},
279 {ev_float, "glow_color"},
280 {ev_float, "glow_size"},
281 {ev_float, "glow_trail"},
282 {ev_float, "gravity"},
283 {ev_float, "idealpitch"},
284 {ev_float, "items2"},
285 {ev_float, "light_lev"},
286 {ev_float, "modelflags"},
287 {ev_float, "pflags"},
289 {ev_float, "pitch_speed"},
290 {ev_float, "pmodel"},
291 {ev_float, "renderamt"}, // HalfLife support
292 {ev_float, "rendermode"}, // HalfLife support
295 {ev_float, "tag_index"},
296 {ev_float, "viewzoom"},
297 {ev_function, "SendEntity"},
298 {ev_function, "contentstransition"}, // DRESK - Support for Entity Contents Transition Event
299 {ev_function, "customizeentityforclient"},
300 {ev_function, "movetypesteplandevent"}, // DRESK - Support for MOVETYPE_STEP Entity Land Event
301 {ev_string, "netaddress"},
302 {ev_string, "playermodel"},
303 {ev_string, "playerskin"},
304 {ev_vector, "color"},
305 {ev_vector, "colormod"},
306 {ev_vector, "cursor_screen"},
307 {ev_vector, "cursor_trace_endpos"},
308 {ev_vector, "cursor_trace_start"},
309 {ev_vector, "glowmod"},
310 {ev_vector, "movement"},
311 {ev_vector, "punchvector"},
314 //{ev_float, "solid"},
315 //{ev_float, "movetype"},
316 //{ev_float, "modelindex"},
318 //{ev_vector, "origin"},
319 //{ev_vector, "velocity"},
320 //{ev_vector, "axis_forward"},
321 //{ev_vector, "axis_left"},
322 //{ev_vector, "axis_up"},
323 //{ev_vector, "spinvelocity"},
324 //{ev_vector, "angles"},
325 //{ev_vector, "avelocity"},
331 //============================================================================
333 void SV_AreaStats_f(void)
335 World_PrintAreaStats(&sv.world, "server");
345 // init the csqc progs cvars, since they are updated/used by the server code
346 // TODO: fix this since this is a quick hack to make some of [515]'s broken code run ;) [9/13/2006 Black]
347 extern cvar_t csqc_progname; //[515]: csqc crc check and right csprogs name according to progs.dat
348 extern cvar_t csqc_progcrc;
349 extern cvar_t csqc_progsize;
351 Cvar_RegisterVariable(&sv_worldmessage);
352 Cvar_RegisterVariable(&sv_worldname);
353 Cvar_RegisterVariable(&sv_worldnamenoextension);
354 Cvar_RegisterVariable(&sv_worldbasename);
356 Cvar_RegisterVariable (&csqc_progname);
357 Cvar_RegisterVariable (&csqc_progcrc);
358 Cvar_RegisterVariable (&csqc_progsize);
360 Cmd_AddCommand("sv_saveentfile", SV_SaveEntFile_f, "save map entities to .ent file (to allow external editing)");
361 Cmd_AddCommand("sv_areastats", SV_AreaStats_f, "prints statistics on entity culling during collision traces");
362 Cmd_AddCommand_WithClientCommand("sv_startdownload", NULL, SV_StartDownload_f, "begins sending a file to the client (network protocol use only)");
363 Cmd_AddCommand_WithClientCommand("download", NULL, SV_Download_f, "downloads a specified file from the server");
365 Cvar_RegisterVariable (&coop);
366 Cvar_RegisterVariable (&deathmatch);
367 Cvar_RegisterVariable (&fraglimit);
368 Cvar_RegisterVariable (&gamecfg);
369 Cvar_RegisterVariable (&noexit);
370 Cvar_RegisterVariable (&nomonsters);
371 Cvar_RegisterVariable (&pausable);
372 Cvar_RegisterVariable (&pr_checkextension);
373 Cvar_RegisterVariable (&samelevel);
374 Cvar_RegisterVariable (&skill);
375 Cvar_RegisterVariable (&slowmo);
376 Cvar_RegisterVariable (&sv_accelerate);
377 Cvar_RegisterVariable (&sv_aim);
378 Cvar_RegisterVariable (&sv_airaccel_qw);
379 Cvar_RegisterVariable (&sv_airaccel_sideways_friction);
380 Cvar_RegisterVariable (&sv_airaccelerate);
381 Cvar_RegisterVariable (&sv_airstopaccelerate);
382 Cvar_RegisterVariable (&sv_airstrafeaccelerate);
383 Cvar_RegisterVariable (&sv_maxairstrafespeed);
384 Cvar_RegisterVariable (&sv_airstrafeaccel_qw);
385 Cvar_RegisterVariable (&sv_airspeedlimit_nonqw);
386 Cvar_RegisterVariable (&sv_aircontrol);
387 Cvar_RegisterVariable (&sv_aircontrol_power);
388 Cvar_RegisterVariable (&sv_allowdownloads);
389 Cvar_RegisterVariable (&sv_allowdownloads_archive);
390 Cvar_RegisterVariable (&sv_allowdownloads_config);
391 Cvar_RegisterVariable (&sv_allowdownloads_dlcache);
392 Cvar_RegisterVariable (&sv_allowdownloads_inarchive);
393 Cvar_RegisterVariable (&sv_areagrid_mingridsize);
394 Cvar_RegisterVariable (&sv_checkforpacketsduringsleep);
395 Cvar_RegisterVariable (&sv_clmovement_enable);
396 Cvar_RegisterVariable (&sv_clmovement_minping);
397 Cvar_RegisterVariable (&sv_clmovement_minping_disabletime);
398 Cvar_RegisterVariable (&sv_clmovement_inputtimeout);
399 Cvar_RegisterVariable (&sv_cullentities_nevercullbmodels);
400 Cvar_RegisterVariable (&sv_cullentities_pvs);
401 Cvar_RegisterVariable (&sv_cullentities_stats);
402 Cvar_RegisterVariable (&sv_cullentities_trace);
403 Cvar_RegisterVariable (&sv_cullentities_trace_delay);
404 Cvar_RegisterVariable (&sv_cullentities_trace_delay_players);
405 Cvar_RegisterVariable (&sv_cullentities_trace_enlarge);
406 Cvar_RegisterVariable (&sv_cullentities_trace_entityocclusion);
407 Cvar_RegisterVariable (&sv_cullentities_trace_prediction);
408 Cvar_RegisterVariable (&sv_cullentities_trace_prediction_time);
409 Cvar_RegisterVariable (&sv_cullentities_trace_samples);
410 Cvar_RegisterVariable (&sv_cullentities_trace_samples_extra);
411 Cvar_RegisterVariable (&sv_cullentities_trace_samples_players);
412 Cvar_RegisterVariable (&sv_debugmove);
413 Cvar_RegisterVariable (&sv_echobprint);
414 Cvar_RegisterVariable (&sv_edgefriction);
415 Cvar_RegisterVariable (&sv_entpatch);
416 Cvar_RegisterVariable (&sv_fixedframeratesingleplayer);
417 Cvar_RegisterVariable (&sv_freezenonclients);
418 Cvar_RegisterVariable (&sv_friction);
419 Cvar_RegisterVariable (&sv_gameplayfix_blowupfallenzombies);
420 Cvar_RegisterVariable (&sv_gameplayfix_consistentplayerprethink);
421 Cvar_RegisterVariable (&sv_gameplayfix_delayprojectiles);
422 Cvar_RegisterVariable (&sv_gameplayfix_droptofloorstartsolid);
423 Cvar_RegisterVariable (&sv_gameplayfix_droptofloorstartsolid_nudgetocorrect);
424 Cvar_RegisterVariable (&sv_gameplayfix_easierwaterjump);
425 Cvar_RegisterVariable (&sv_gameplayfix_findradiusdistancetobox);
426 Cvar_RegisterVariable (&sv_gameplayfix_gravityunaffectedbyticrate);
427 Cvar_RegisterVariable (&sv_gameplayfix_grenadebouncedownslopes);
428 Cvar_RegisterVariable (&sv_gameplayfix_multiplethinksperframe);
429 Cvar_RegisterVariable (&sv_gameplayfix_noairborncorpse);
430 Cvar_RegisterVariable (&sv_gameplayfix_noairborncorpse_allowsuspendeditems);
431 Cvar_RegisterVariable (&sv_gameplayfix_nudgeoutofsolid);
432 Cvar_RegisterVariable (&sv_gameplayfix_nudgeoutofsolid_bias);
433 Cvar_RegisterVariable (&sv_gameplayfix_q2airaccelerate);
434 Cvar_RegisterVariable (&sv_gameplayfix_nogravityonground);
435 Cvar_RegisterVariable (&sv_gameplayfix_setmodelrealbox);
436 Cvar_RegisterVariable (&sv_gameplayfix_slidemoveprojectiles);
437 Cvar_RegisterVariable (&sv_gameplayfix_stepdown);
438 Cvar_RegisterVariable (&sv_gameplayfix_stepwhilejumping);
439 Cvar_RegisterVariable (&sv_gameplayfix_stepmultipletimes);
440 Cvar_RegisterVariable (&sv_gameplayfix_swiminbmodels);
441 Cvar_RegisterVariable (&sv_gameplayfix_upwardvelocityclearsongroundflag);
442 Cvar_RegisterVariable (&sv_gameplayfix_downtracesupportsongroundflag);
443 Cvar_RegisterVariable (&sv_gravity);
444 Cvar_RegisterVariable (&sv_idealpitchscale);
445 Cvar_RegisterVariable (&sv_jumpstep);
446 Cvar_RegisterVariable (&sv_jumpvelocity);
447 Cvar_RegisterVariable (&sv_maxairspeed);
448 Cvar_RegisterVariable (&sv_maxrate);
449 Cvar_RegisterVariable (&sv_maxspeed);
450 Cvar_RegisterVariable (&sv_maxvelocity);
451 Cvar_RegisterVariable (&sv_nostep);
452 Cvar_RegisterVariable (&sv_playerphysicsqc);
453 Cvar_RegisterVariable (&sv_progs);
454 Cvar_RegisterVariable (&sv_protocolname);
455 Cvar_RegisterVariable (&sv_random_seed);
456 Cvar_RegisterVariable (&sv_ratelimitlocalplayer);
457 Cvar_RegisterVariable (&sv_sound_land);
458 Cvar_RegisterVariable (&sv_sound_watersplash);
459 Cvar_RegisterVariable (&sv_stepheight);
460 Cvar_RegisterVariable (&sv_stopspeed);
461 Cvar_RegisterVariable (&sv_wallfriction);
462 Cvar_RegisterVariable (&sv_wateraccelerate);
463 Cvar_RegisterVariable (&sv_waterfriction);
464 Cvar_RegisterVariable (&sv_warsowbunny_airforwardaccel);
465 Cvar_RegisterVariable (&sv_warsowbunny_accel);
466 Cvar_RegisterVariable (&sv_warsowbunny_topspeed);
467 Cvar_RegisterVariable (&sv_warsowbunny_turnaccel);
468 Cvar_RegisterVariable (&sv_warsowbunny_backtosideratio);
469 Cvar_RegisterVariable (&sv_onlycsqcnetworking);
470 Cvar_RegisterVariable (&sys_ticrate);
471 Cvar_RegisterVariable (&teamplay);
472 Cvar_RegisterVariable (&timelimit);
474 Cvar_RegisterVariable (&saved1);
475 Cvar_RegisterVariable (&saved2);
476 Cvar_RegisterVariable (&saved3);
477 Cvar_RegisterVariable (&saved4);
478 Cvar_RegisterVariable (&savedgamecfg);
479 Cvar_RegisterVariable (&scratch1);
480 Cvar_RegisterVariable (&scratch2);
481 Cvar_RegisterVariable (&scratch3);
482 Cvar_RegisterVariable (&scratch4);
483 Cvar_RegisterVariable (&temp1);
485 // LordHavoc: Nehahra uses these to pass data around cutscene demos
486 if (gamemode == GAME_NEHAHRA)
488 Cvar_RegisterVariable (&nehx00);
489 Cvar_RegisterVariable (&nehx01);
490 Cvar_RegisterVariable (&nehx02);
491 Cvar_RegisterVariable (&nehx03);
492 Cvar_RegisterVariable (&nehx04);
493 Cvar_RegisterVariable (&nehx05);
494 Cvar_RegisterVariable (&nehx06);
495 Cvar_RegisterVariable (&nehx07);
496 Cvar_RegisterVariable (&nehx08);
497 Cvar_RegisterVariable (&nehx09);
498 Cvar_RegisterVariable (&nehx10);
499 Cvar_RegisterVariable (&nehx11);
500 Cvar_RegisterVariable (&nehx12);
501 Cvar_RegisterVariable (&nehx13);
502 Cvar_RegisterVariable (&nehx14);
503 Cvar_RegisterVariable (&nehx15);
504 Cvar_RegisterVariable (&nehx16);
505 Cvar_RegisterVariable (&nehx17);
506 Cvar_RegisterVariable (&nehx18);
507 Cvar_RegisterVariable (&nehx19);
509 Cvar_RegisterVariable (&cutscene); // for Nehahra but useful to other mods as well
511 Cvar_RegisterVariable (&sv_autodemo_perclient);
512 Cvar_RegisterVariable (&sv_autodemo_perclient_nameformat);
513 Cvar_RegisterVariable (&sv_autodemo_perclient_discardable);
515 Cvar_RegisterVariable (&halflifebsp);
517 // any special defaults for gamemodes go here
518 if (gamemode == GAME_NEHAHRA)
520 // Nehahra pushable crates malfunction in some levels if this is on
521 Cvar_SetValueQuick (&sv_gameplayfix_upwardvelocityclearsongroundflag, 0);
522 // Nehahra NPC AI is confused by this feature
523 Cvar_SetValueQuick (&sv_gameplayfix_blowupfallenzombies, 0);
525 if (gamemode == GAME_HIPNOTIC)
527 // hipnotic mission pack has issues in their 'friendly monster' ai, which seem to attempt to attack themselves for some reason when findradius() returns non-solid entities.
528 Cvar_SetValueQuick (&sv_gameplayfix_blowupfallenzombies, 0);
529 // hipnotic mission pack has issues with bobbing water entities 'jittering' between different heights on alternate frames at the default 0.0138889 ticrate, 0.02 avoids this issue
530 Cvar_SetValueQuick (&sys_ticrate, 0.02);
531 // hipnotic mission pack has issues in their proximity mine sticking code, which causes them to bounce off.
532 Cvar_SetValueQuick (&sv_gameplayfix_slidemoveprojectiles, 0);
534 if (gamemode == GAME_ROGUE)
536 // rogue mission pack has a guardian boss that does not wake up if findradius returns one of the entities around its spawn area
537 Cvar_SetValueQuick (&sv_gameplayfix_findradiusdistancetobox, 0);
539 if (gamemode == GAME_NEXUIZ)
541 Cvar_SetValueQuick (&sv_gameplayfix_q2airaccelerate, 1);
544 sv_mempool = Mem_AllocPool("server", 0, NULL);
547 static void SV_SaveEntFile_f(void)
549 if (!sv.active || !sv.worldmodel)
551 Con_Print("Not running a server\n");
554 FS_WriteFile(va("%s.ent", sv.worldnamenoextension), sv.worldmodel->brush.entities, (fs_offset_t)strlen(sv.worldmodel->brush.entities));
559 =============================================================================
563 =============================================================================
570 Make sure the event gets sent to all clients
573 void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count)
577 if (sv.datagram.cursize > MAX_PACKETFRAGMENT-18)
579 MSG_WriteByte (&sv.datagram, svc_particle);
580 MSG_WriteCoord (&sv.datagram, org[0], sv.protocol);
581 MSG_WriteCoord (&sv.datagram, org[1], sv.protocol);
582 MSG_WriteCoord (&sv.datagram, org[2], sv.protocol);
583 for (i=0 ; i<3 ; i++)
584 MSG_WriteChar (&sv.datagram, (int)bound(-128, dir[i]*16, 127));
585 MSG_WriteByte (&sv.datagram, count);
586 MSG_WriteByte (&sv.datagram, color);
587 SV_FlushBroadcastMessages();
594 Make sure the event gets sent to all clients
597 void SV_StartEffect (vec3_t org, int modelindex, int startframe, int framecount, int framerate)
599 if (modelindex >= 256 || startframe >= 256)
601 if (sv.datagram.cursize > MAX_PACKETFRAGMENT-19)
603 MSG_WriteByte (&sv.datagram, svc_effect2);
604 MSG_WriteCoord (&sv.datagram, org[0], sv.protocol);
605 MSG_WriteCoord (&sv.datagram, org[1], sv.protocol);
606 MSG_WriteCoord (&sv.datagram, org[2], sv.protocol);
607 MSG_WriteShort (&sv.datagram, modelindex);
608 MSG_WriteShort (&sv.datagram, startframe);
609 MSG_WriteByte (&sv.datagram, framecount);
610 MSG_WriteByte (&sv.datagram, framerate);
614 if (sv.datagram.cursize > MAX_PACKETFRAGMENT-17)
616 MSG_WriteByte (&sv.datagram, svc_effect);
617 MSG_WriteCoord (&sv.datagram, org[0], sv.protocol);
618 MSG_WriteCoord (&sv.datagram, org[1], sv.protocol);
619 MSG_WriteCoord (&sv.datagram, org[2], sv.protocol);
620 MSG_WriteByte (&sv.datagram, modelindex);
621 MSG_WriteByte (&sv.datagram, startframe);
622 MSG_WriteByte (&sv.datagram, framecount);
623 MSG_WriteByte (&sv.datagram, framerate);
625 SV_FlushBroadcastMessages();
632 Each entity can have eight independant sound sources, like voice,
635 Channel 0 is an auto-allocate channel, the others override anything
636 already running on that entity/channel pair.
638 An attenuation of 0 will play full volume everywhere in the level.
639 Larger attenuations will drop off. (max 4 attenuation)
643 void SV_StartSound (prvm_edict_t *entity, int channel, const char *sample, int volume, float attenuation)
645 int sound_num, field_mask, i, ent;
647 if (volume < 0 || volume > 255)
649 Con_Printf ("SV_StartSound: volume = %i\n", volume);
653 if (attenuation < 0 || attenuation > 4)
655 Con_Printf ("SV_StartSound: attenuation = %f\n", attenuation);
659 if (channel < 0 || channel > 7)
661 Con_Printf ("SV_StartSound: channel = %i\n", channel);
665 if (sv.datagram.cursize > MAX_PACKETFRAGMENT-21)
668 // find precache number for sound
669 sound_num = SV_SoundIndex(sample, 1);
673 ent = PRVM_NUM_FOR_EDICT(entity);
676 if (volume != DEFAULT_SOUND_PACKET_VOLUME)
677 field_mask |= SND_VOLUME;
678 if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)
679 field_mask |= SND_ATTENUATION;
681 field_mask |= SND_LARGEENTITY;
682 if (sound_num >= 256 || channel >= 8)
683 field_mask |= SND_LARGESOUND;
685 // directed messages go only to the entity they are targeted on
686 MSG_WriteByte (&sv.datagram, svc_sound);
687 MSG_WriteByte (&sv.datagram, field_mask);
688 if (field_mask & SND_VOLUME)
689 MSG_WriteByte (&sv.datagram, volume);
690 if (field_mask & SND_ATTENUATION)
691 MSG_WriteByte (&sv.datagram, (int)(attenuation*64));
692 if (field_mask & SND_LARGEENTITY)
694 MSG_WriteShort (&sv.datagram, ent);
695 MSG_WriteByte (&sv.datagram, channel);
698 MSG_WriteShort (&sv.datagram, (ent<<3) | channel);
699 if ((field_mask & SND_LARGESOUND) || sv.protocol == PROTOCOL_NEHAHRABJP2)
700 MSG_WriteShort (&sv.datagram, sound_num);
702 MSG_WriteByte (&sv.datagram, sound_num);
703 for (i = 0;i < 3;i++)
704 MSG_WriteCoord (&sv.datagram, entity->fields.server->origin[i]+0.5*(entity->fields.server->mins[i]+entity->fields.server->maxs[i]), sv.protocol);
705 SV_FlushBroadcastMessages();
712 Nearly the same logic as SV_StartSound, except an origin
713 instead of an entity is provided and channel is omitted.
715 The entity sent to the client is 0 (world) and the channel
716 is 0 (CHAN_AUTO). SND_LARGEENTITY will never occur in this
717 function, therefore the check for it is omitted.
721 void SV_StartPointSound (vec3_t origin, const char *sample, int volume, float attenuation)
723 int sound_num, field_mask, i;
725 if (volume < 0 || volume > 255)
727 Con_Printf ("SV_StartPointSound: volume = %i\n", volume);
731 if (attenuation < 0 || attenuation > 4)
733 Con_Printf ("SV_StartPointSound: attenuation = %f\n", attenuation);
737 if (sv.datagram.cursize > MAX_PACKETFRAGMENT-21)
740 // find precache number for sound
741 sound_num = SV_SoundIndex(sample, 1);
746 if (volume != DEFAULT_SOUND_PACKET_VOLUME)
747 field_mask |= SND_VOLUME;
748 if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)
749 field_mask |= SND_ATTENUATION;
750 if (sound_num >= 256)
751 field_mask |= SND_LARGESOUND;
753 // directed messages go only to the entity they are targeted on
754 MSG_WriteByte (&sv.datagram, svc_sound);
755 MSG_WriteByte (&sv.datagram, field_mask);
756 if (field_mask & SND_VOLUME)
757 MSG_WriteByte (&sv.datagram, volume);
758 if (field_mask & SND_ATTENUATION)
759 MSG_WriteByte (&sv.datagram, (int)(attenuation*64));
760 // Always write entnum 0 for the world entity
761 MSG_WriteShort (&sv.datagram, (0<<3) | 0);
762 if (field_mask & SND_LARGESOUND)
763 MSG_WriteShort (&sv.datagram, sound_num);
765 MSG_WriteByte (&sv.datagram, sound_num);
766 for (i = 0;i < 3;i++)
767 MSG_WriteCoord (&sv.datagram, origin[i], sv.protocol);
768 SV_FlushBroadcastMessages();
772 ==============================================================================
776 ==============================================================================
783 Sends the first message from the server to a connected client.
784 This will be sent on the initial connection and upon each server load.
787 void SV_SendServerinfo (client_t *client)
792 // we know that this client has a netconnection and thus is not a bot
794 // edicts get reallocated on level changes, so we need to update it here
795 client->edict = PRVM_EDICT_NUM((client - svs.clients) + 1);
797 // clear cached stuff that depends on the level
798 client->weaponmodel[0] = 0;
799 client->weaponmodelindex = 0;
801 // LordHavoc: clear entityframe tracking
802 client->latestframenum = 0;
804 // initialize the movetime, so a speedhack can't make use of the time before this client joined
805 client->cmd.time = sv.time;
807 if (client->entitydatabase)
808 EntityFrame_FreeDatabase(client->entitydatabase);
809 if (client->entitydatabase4)
810 EntityFrame4_FreeDatabase(client->entitydatabase4);
811 if (client->entitydatabase5)
812 EntityFrame5_FreeDatabase(client->entitydatabase5);
814 memset(client->stats, 0, sizeof(client->stats));
815 memset(client->statsdeltabits, 0, sizeof(client->statsdeltabits));
817 if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE && sv.protocol != PROTOCOL_NEHAHRABJP && sv.protocol != PROTOCOL_NEHAHRABJP2 && sv.protocol != PROTOCOL_NEHAHRABJP3)
819 if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3)
820 client->entitydatabase = EntityFrame_AllocDatabase(sv_mempool);
821 else if (sv.protocol == PROTOCOL_DARKPLACES4)
822 client->entitydatabase4 = EntityFrame4_AllocDatabase(sv_mempool);
824 client->entitydatabase5 = EntityFrame5_AllocDatabase(sv_mempool);
827 // reset csqc entity versions
828 for (i = 0;i < prog->max_edicts;i++)
830 client->csqcentityscope[i] = 0;
831 client->csqcentitysendflags[i] = 0xFFFFFF;
832 client->csqcentityglobalhistory[i] = 0;
834 for (i = 0;i < NUM_CSQCENTITYDB_FRAMES;i++)
836 client->csqcentityframehistory[i].num = 0;
837 client->csqcentityframehistory[i].framenum = -1;
839 client->csqcnumedicts = 0;
840 client->csqcentityframehistory_next = 0;
842 SZ_Clear (&client->netconnection->message);
843 MSG_WriteByte (&client->netconnection->message, svc_print);
844 dpsnprintf (message, sizeof (message), "\nServer: %s build %s (progs %i crc)\n", gamename, buildstring, prog->filecrc);
845 MSG_WriteString (&client->netconnection->message,message);
847 SV_StopDemoRecording(client); // to split up demos into different files
848 if(sv_autodemo_perclient.integer && client->netconnection)
850 char demofile[MAX_OSPATH];
851 char ipaddress[MAX_QPATH];
854 // start a new demo file
855 LHNETADDRESS_ToString(&(client->netconnection->peeraddress), ipaddress, sizeof(ipaddress), true);
856 for(i = 0; ipaddress[i]; ++i)
857 if(!isalnum(ipaddress[i]))
859 dpsnprintf (demofile, sizeof(demofile), "%s_%s_%d_%s.dem", Sys_TimeString (sv_autodemo_perclient_nameformat.string), sv.worldbasename, PRVM_NUM_FOR_EDICT(client->edict), ipaddress);
861 SV_StartDemoRecording(client, demofile, -1);
864 //[515]: init csprogs according to version of svprogs, check the crc, etc.
865 if (sv.csqc_progname[0])
868 Con_DPrintf("sending csqc info to client (\"%s\" with size %i and crc %i)\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc);
869 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
870 MSG_WriteString (&client->netconnection->message, va("csqc_progname %s\n", sv.csqc_progname));
871 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
872 MSG_WriteString (&client->netconnection->message, va("csqc_progsize %i\n", sv.csqc_progsize));
873 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
874 MSG_WriteString (&client->netconnection->message, va("csqc_progcrc %i\n", sv.csqc_progcrc));
876 if(client->sv_demo_file != NULL)
879 static char buf[NET_MAXMESSAGE];
882 sb.data = (unsigned char *) buf;
883 sb.maxsize = sizeof(buf);
885 while(MakeDownloadPacket(sv.csqc_progname, svs.csqc_progdata, sv.csqc_progsize, sv.csqc_progcrc, i++, &sb, sv.protocol))
886 SV_WriteDemoMessage(client, &sb, false);
889 //[515]: init stufftext string (it is sent before svc_serverinfo)
890 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.SV_InitCmd);
893 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
894 MSG_WriteString (&client->netconnection->message, va("%s\n", PRVM_GetString(val->string)));
898 //if (sv_allowdownloads.integer)
899 // always send the info that the server supports the protocol, even if downloads are forbidden
900 // only because of that, the CSQC exception can work
902 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
903 MSG_WriteString (&client->netconnection->message, "cl_serverextension_download 2\n");
906 // send at this time so it's guaranteed to get executed at the right time
910 host_client = client;
911 Curl_SendRequirements();
915 MSG_WriteByte (&client->netconnection->message, svc_serverinfo);
916 MSG_WriteLong (&client->netconnection->message, Protocol_NumberForEnum(sv.protocol));
917 MSG_WriteByte (&client->netconnection->message, svs.maxclients);
919 if (!coop.integer && deathmatch.integer)
920 MSG_WriteByte (&client->netconnection->message, GAME_DEATHMATCH);
922 MSG_WriteByte (&client->netconnection->message, GAME_COOP);
924 MSG_WriteString (&client->netconnection->message,PRVM_GetString(prog->edicts->fields.server->message));
926 for (i = 1;i < MAX_MODELS && sv.model_precache[i][0];i++)
927 MSG_WriteString (&client->netconnection->message, sv.model_precache[i]);
928 MSG_WriteByte (&client->netconnection->message, 0);
930 for (i = 1;i < MAX_SOUNDS && sv.sound_precache[i][0];i++)
931 MSG_WriteString (&client->netconnection->message, sv.sound_precache[i]);
932 MSG_WriteByte (&client->netconnection->message, 0);
935 MSG_WriteByte (&client->netconnection->message, svc_cdtrack);
936 MSG_WriteByte (&client->netconnection->message, (int)prog->edicts->fields.server->sounds);
937 MSG_WriteByte (&client->netconnection->message, (int)prog->edicts->fields.server->sounds);
940 // store this in clientcamera, too
941 client->clientcamera = PRVM_NUM_FOR_EDICT(client->edict);
942 MSG_WriteByte (&client->netconnection->message, svc_setview);
943 MSG_WriteShort (&client->netconnection->message, client->clientcamera);
945 MSG_WriteByte (&client->netconnection->message, svc_signonnum);
946 MSG_WriteByte (&client->netconnection->message, 1);
948 client->spawned = false; // need prespawn, spawn, etc
949 client->sendsignon = 1; // send this message, and increment to 2, 2 will be set to 0 by the prespawn command
951 // clear movement info until client enters the new level properly
952 memset(&client->cmd, 0, sizeof(client->cmd));
953 client->movesequence = 0;
954 client->movement_highestsequence_seen = 0;
955 memset(&client->movement_count, 0, sizeof(client->movement_count));
956 #ifdef NUM_PING_TIMES
957 for (i = 0;i < NUM_PING_TIMES;i++)
958 client->ping_times[i] = 0;
959 client->num_pings = 0;
963 // allow the client some time to send his keepalives, even if map loading took ages
964 client->netconnection->timeout = realtime + net_connecttimeout.value;
971 Initializes a client_t for a new net connection. This will only be called
972 once for a player each game, not once for each level change.
975 void SV_ConnectClient (int clientnum, netconn_t *netconnection)
979 float spawn_parms[NUM_SPAWN_PARMS];
981 client = svs.clients + clientnum;
983 // set up the client_t
985 memcpy (spawn_parms, client->spawn_parms, sizeof(spawn_parms));
986 memset (client, 0, sizeof(*client));
987 client->active = true;
988 client->netconnection = netconnection;
990 Con_DPrintf("Client %s connected\n", client->netconnection ? client->netconnection->address : "botclient");
992 strlcpy(client->name, "unconnected", sizeof(client->name));
993 strlcpy(client->old_name, "unconnected", sizeof(client->old_name));
994 client->spawned = false;
995 client->edict = PRVM_EDICT_NUM(clientnum+1);
996 if (client->netconnection)
997 client->netconnection->message.allowoverflow = true; // we can catch it
998 // prepare the unreliable message buffer
999 client->unreliablemsg.data = client->unreliablemsg_data;
1000 client->unreliablemsg.maxsize = sizeof(client->unreliablemsg_data);
1001 // updated by receiving "rate" command from client, this is also the default if not using a DP client
1002 client->rate = 1000000000;
1003 // no limits for local player
1004 if (client->netconnection && LHNETADDRESS_GetAddressType(&client->netconnection->peeraddress) == LHNETADDRESSTYPE_LOOP)
1005 client->rate = 1000000000;
1006 client->connecttime = realtime;
1009 memcpy (client->spawn_parms, spawn_parms, sizeof(spawn_parms));
1012 // call the progs to get default spawn parms for the new client
1013 // set self to world to intentionally cause errors with broken SetNewParms code in some mods
1014 prog->globals.server->self = 0;
1015 PRVM_ExecuteProgram (prog->globals.server->SetNewParms, "QC function SetNewParms is missing");
1016 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
1017 client->spawn_parms[i] = (&prog->globals.server->parm1)[i];
1019 // set up the entity for this client (including .colormap, .team, etc)
1020 PRVM_ED_ClearEdict(client->edict);
1023 // don't call SendServerinfo for a fresh botclient because its fields have
1024 // not been set up by the qc yet
1025 if (client->netconnection)
1026 SV_SendServerinfo (client);
1028 client->spawned = true;
1033 ===============================================================================
1037 ===============================================================================
1041 =============================================================================
1043 The PVS must include a small area around the client to allow head bobbing
1044 or other small motion on the client side. Otherwise, a bob might cause an
1045 entity that should be visible to not show up, especially when the bob
1046 crosses a waterline.
1048 =============================================================================
1051 static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int enumber)
1054 unsigned int sendflags;
1055 unsigned int version;
1056 unsigned int modelindex, effects, flags, glowsize, lightstyle, lightpflags, light[4], specialvisibilityradius;
1057 unsigned int customizeentityforclient;
1058 unsigned int sendentity;
1060 vec3_t cullmins, cullmaxs;
1062 prvm_eval_t *val, *val2;
1064 // fast path for games that do not use legacy entity networking
1065 // note: still networks clients even if they are legacy
1066 sendentity = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.SendEntity)->function;
1067 if (sv_onlycsqcnetworking.integer && !sendentity && enumber > svs.maxclients)
1070 // this 2 billion unit check is actually to detect NAN origins
1071 // (we really don't want to send those)
1072 if (!(VectorLength2(ent->fields.server->origin) < 2000000000.0*2000000000.0))
1075 // EF_NODRAW prevents sending for any reason except for your own
1076 // client, so we must keep all clients in this superset
1077 effects = (unsigned)ent->fields.server->effects;
1079 // we can omit invisible entities with no effects that are not clients
1080 // LordHavoc: this could kill tags attached to an invisible entity, I
1081 // just hope we never have to support that case
1082 i = (int)ent->fields.server->modelindex;
1083 modelindex = (i >= 1 && i < MAX_MODELS && ent->fields.server->model && *PRVM_GetString(ent->fields.server->model) && sv.models[i]) ? i : 0;
1086 i = (int)(PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_size)->_float * 0.25f);
1087 glowsize = (unsigned char)bound(0, i, 255);
1088 if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_trail)->_float)
1089 flags |= RENDER_GLOWTRAIL;
1090 if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewmodelforclient)->edict)
1091 flags |= RENDER_VIEWMODEL;
1093 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[0]*256;
1094 light[0] = (unsigned short)bound(0, f, 65535);
1095 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[1]*256;
1096 light[1] = (unsigned short)bound(0, f, 65535);
1097 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[2]*256;
1098 light[2] = (unsigned short)bound(0, f, 65535);
1099 f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.light_lev)->_float;
1100 light[3] = (unsigned short)bound(0, f, 65535);
1101 lightstyle = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.style)->_float;
1102 lightpflags = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.pflags)->_float;
1104 if (gamemode == GAME_TENEBRAE)
1106 // tenebrae's EF_FULLDYNAMIC conflicts with Q2's EF_NODRAW
1110 lightpflags |= PFLAGS_FULLDYNAMIC;
1112 // tenebrae's EF_GREEN conflicts with DP's EF_ADDITIVE
1116 light[0] = (int)(0.2*256);
1117 light[1] = (int)(1.0*256);
1118 light[2] = (int)(0.2*256);
1120 lightpflags |= PFLAGS_FULLDYNAMIC;
1124 specialvisibilityradius = 0;
1125 if (lightpflags & PFLAGS_FULLDYNAMIC)
1126 specialvisibilityradius = max(specialvisibilityradius, light[3]);
1128 specialvisibilityradius = max(specialvisibilityradius, glowsize * 4);
1129 if (flags & RENDER_GLOWTRAIL)
1130 specialvisibilityradius = max(specialvisibilityradius, 100);
1131 if (effects & (EF_BRIGHTFIELD | EF_MUZZLEFLASH | EF_BRIGHTLIGHT | EF_DIMLIGHT | EF_RED | EF_BLUE | EF_FLAME | EF_STARDUST))
1133 if (effects & EF_BRIGHTFIELD)
1134 specialvisibilityradius = max(specialvisibilityradius, 80);
1135 if (effects & EF_MUZZLEFLASH)
1136 specialvisibilityradius = max(specialvisibilityradius, 100);
1137 if (effects & EF_BRIGHTLIGHT)
1138 specialvisibilityradius = max(specialvisibilityradius, 400);
1139 if (effects & EF_DIMLIGHT)
1140 specialvisibilityradius = max(specialvisibilityradius, 200);
1141 if (effects & EF_RED)
1142 specialvisibilityradius = max(specialvisibilityradius, 200);
1143 if (effects & EF_BLUE)
1144 specialvisibilityradius = max(specialvisibilityradius, 200);
1145 if (effects & EF_FLAME)
1146 specialvisibilityradius = max(specialvisibilityradius, 250);
1147 if (effects & EF_STARDUST)
1148 specialvisibilityradius = max(specialvisibilityradius, 100);
1151 // early culling checks
1152 // (final culling is done by SV_MarkWriteEntityStateToClient)
1153 customizeentityforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.customizeentityforclient)->function;
1154 if (!customizeentityforclient && enumber > svs.maxclients && (!modelindex && !specialvisibilityradius))
1158 cs->active = ACTIVE_NETWORK;
1159 cs->number = enumber;
1160 VectorCopy(ent->fields.server->origin, cs->origin);
1161 VectorCopy(ent->fields.server->angles, cs->angles);
1163 cs->effects = effects;
1164 cs->colormap = (unsigned)ent->fields.server->colormap;
1165 cs->modelindex = modelindex;
1166 cs->skin = (unsigned)ent->fields.server->skin;
1167 cs->frame = (unsigned)ent->fields.server->frame;
1168 cs->viewmodelforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewmodelforclient)->edict;
1169 cs->exteriormodelforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.exteriormodeltoclient)->edict;
1170 cs->nodrawtoclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.nodrawtoclient)->edict;
1171 cs->drawonlytoclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.drawonlytoclient)->edict;
1172 cs->customizeentityforclient = customizeentityforclient;
1173 cs->tagentity = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)->edict;
1174 cs->tagindex = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_index)->_float;
1175 cs->glowsize = glowsize;
1177 // don't need to init cs->colormod because the defaultstate did that for us
1178 //cs->colormod[0] = cs->colormod[1] = cs->colormod[2] = 32;
1179 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.colormod);
1180 if (val->vector[0] || val->vector[1] || val->vector[2])
1182 i = (int)(val->vector[0] * 32.0f);cs->colormod[0] = bound(0, i, 255);
1183 i = (int)(val->vector[1] * 32.0f);cs->colormod[1] = bound(0, i, 255);
1184 i = (int)(val->vector[2] * 32.0f);cs->colormod[2] = bound(0, i, 255);
1187 // don't need to init cs->glowmod because the defaultstate did that for us
1188 //cs->glowmod[0] = cs->glowmod[1] = cs->glowmod[2] = 32;
1189 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glowmod);
1190 if (val->vector[0] || val->vector[1] || val->vector[2])
1192 i = (int)(val->vector[0] * 32.0f);cs->glowmod[0] = bound(0, i, 255);
1193 i = (int)(val->vector[1] * 32.0f);cs->glowmod[1] = bound(0, i, 255);
1194 i = (int)(val->vector[2] * 32.0f);cs->glowmod[2] = bound(0, i, 255);
1197 cs->modelindex = modelindex;
1200 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.alpha)->_float * 255.0f);
1204 cs->alpha = (unsigned char)bound(0, i, 255);
1207 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderamt)->_float);
1211 cs->alpha = (unsigned char)bound(0, i, 255);
1215 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale)->_float * 16.0f);
1219 cs->scale = (unsigned char)bound(0, i, 255);
1222 cs->glowcolor = 254;
1223 f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_color)->_float);
1225 cs->glowcolor = (int)f;
1227 if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.fullbright)->_float)
1228 cs->effects |= EF_FULLBRIGHT;
1230 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.modelflags);
1231 if (val && val->_float)
1232 cs->effects |= ((unsigned int)val->_float & 0xff) << 24;
1234 if (ent->fields.server->movetype == MOVETYPE_STEP)
1235 cs->flags |= RENDER_STEP;
1236 if (cs->number != sv.writeentitiestoclient_cliententitynumber && (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)
1237 cs->flags |= RENDER_LOWPRECISION;
1238 if (ent->fields.server->colormap >= 1024)
1239 cs->flags |= RENDER_COLORMAPPED;
1240 if (cs->viewmodelforclient)
1241 cs->flags |= RENDER_VIEWMODEL; // show relative to the view
1243 cs->light[0] = light[0];
1244 cs->light[1] = light[1];
1245 cs->light[2] = light[2];
1246 cs->light[3] = light[3];
1247 cs->lightstyle = lightstyle;
1248 cs->lightpflags = lightpflags;
1250 cs->specialvisibilityradius = specialvisibilityradius;
1252 // calculate the visible box of this entity (don't use the physics box
1253 // as that is often smaller than a model, and would not count
1254 // specialvisibilityradius)
1255 if ((model = SV_GetModelByIndex(modelindex)) && (model->type != mod_null))
1257 float scale = cs->scale * (1.0f / 16.0f);
1258 if (cs->angles[0] || cs->angles[2]) // pitch and roll
1260 VectorMA(cs->origin, scale, model->rotatedmins, cullmins);
1261 VectorMA(cs->origin, scale, model->rotatedmaxs, cullmaxs);
1263 else if (cs->angles[1] || ((effects | model->effects) & EF_ROTATE))
1265 VectorMA(cs->origin, scale, model->yawmins, cullmins);
1266 VectorMA(cs->origin, scale, model->yawmaxs, cullmaxs);
1270 VectorMA(cs->origin, scale, model->normalmins, cullmins);
1271 VectorMA(cs->origin, scale, model->normalmaxs, cullmaxs);
1276 // if there is no model (or it could not be loaded), use the physics box
1277 VectorAdd(cs->origin, ent->fields.server->mins, cullmins);
1278 VectorAdd(cs->origin, ent->fields.server->maxs, cullmaxs);
1280 if (specialvisibilityradius)
1282 cullmins[0] = min(cullmins[0], cs->origin[0] - specialvisibilityradius);
1283 cullmins[1] = min(cullmins[1], cs->origin[1] - specialvisibilityradius);
1284 cullmins[2] = min(cullmins[2], cs->origin[2] - specialvisibilityradius);
1285 cullmaxs[0] = max(cullmaxs[0], cs->origin[0] + specialvisibilityradius);
1286 cullmaxs[1] = max(cullmaxs[1], cs->origin[1] + specialvisibilityradius);
1287 cullmaxs[2] = max(cullmaxs[2], cs->origin[2] + specialvisibilityradius);
1290 // calculate center of bbox for network prioritization purposes
1291 VectorMAM(0.5f, cullmins, 0.5f, cullmaxs, cs->netcenter);
1293 // if culling box has moved, update pvs cluster links
1294 if (!VectorCompare(cullmins, ent->priv.server->cullmins) || !VectorCompare(cullmaxs, ent->priv.server->cullmaxs))
1296 VectorCopy(cullmins, ent->priv.server->cullmins);
1297 VectorCopy(cullmaxs, ent->priv.server->cullmaxs);
1298 // a value of -1 for pvs_numclusters indicates that the links are not
1299 // cached, and should be re-tested each time, this is the case if the
1300 // culling box touches too many pvs clusters to store, or if the world
1301 // model does not support FindBoxClusters
1302 ent->priv.server->pvs_numclusters = -1;
1303 if (sv.worldmodel && sv.worldmodel->brush.FindBoxClusters)
1305 i = sv.worldmodel->brush.FindBoxClusters(sv.worldmodel, cullmins, cullmaxs, MAX_ENTITYCLUSTERS, ent->priv.server->pvs_clusterlist);
1306 if (i <= MAX_ENTITYCLUSTERS)
1307 ent->priv.server->pvs_numclusters = i;
1311 // we need to do some csqc entity upkeep here
1312 // get self.SendFlags and clear them
1313 // (to let the QC know that they've been read)
1316 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.SendFlags);
1317 sendflags = (unsigned int)val->_float;
1319 // legacy self.Version system
1320 val2 = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.Version);
1323 version = (unsigned int)val2->_float;
1324 if (sv.csqcentityversion[enumber] != version)
1325 sendflags = 0xFFFFFF;
1326 sv.csqcentityversion[enumber] = version;
1328 // move sendflags into the per-client sendflags
1330 for (i = 0;i < svs.maxclients;i++)
1331 svs.clients[i].csqcentitysendflags[enumber] |= sendflags;
1332 // mark it as inactive for non-csqc networking
1333 cs->active = ACTIVE_SHARED;
1339 void SV_PrepareEntitiesForSending(void)
1343 // send all entities that touch the pvs
1344 sv.numsendentities = 0;
1345 sv.sendentitiesindex[0] = NULL;
1346 memset(sv.sendentitiesindex, 0, prog->num_edicts * sizeof(*sv.sendentitiesindex));
1347 for (e = 1, ent = PRVM_NEXT_EDICT(prog->edicts);e < prog->num_edicts;e++, ent = PRVM_NEXT_EDICT(ent))
1349 if (!ent->priv.server->free && SV_PrepareEntityForSending(ent, sv.sendentities + sv.numsendentities, e))
1351 sv.sendentitiesindex[e] = sv.sendentities + sv.numsendentities;
1352 sv.numsendentities++;
1357 #define MAX_LINEOFSIGHTTRACES 64
1359 qboolean SV_CanSeeBox(int numtraces, vec_t enlarge, vec3_t eye, vec3_t entboxmins, vec3_t entboxmaxs)
1363 float starttransformed[3], endtransformed[3];
1366 int originalnumtouchedicts;
1367 int numtouchedicts = 0;
1369 matrix4x4_t matrix, imatrix;
1371 prvm_edict_t *touch;
1372 static prvm_edict_t *touchedicts[MAX_EDICTS];
1373 vec3_t boxmins, boxmaxs;
1374 vec3_t clipboxmins, clipboxmaxs;
1375 vec3_t endpoints[MAX_LINEOFSIGHTTRACES];
1377 numtraces = min(numtraces, MAX_LINEOFSIGHTTRACES);
1379 // expand the box a little
1380 boxmins[0] = (enlarge+1) * entboxmins[0] - enlarge * entboxmaxs[0];
1381 boxmaxs[0] = (enlarge+1) * entboxmaxs[0] - enlarge * entboxmins[0];
1382 boxmins[1] = (enlarge+1) * entboxmins[1] - enlarge * entboxmaxs[1];
1383 boxmaxs[1] = (enlarge+1) * entboxmaxs[1] - enlarge * entboxmins[1];
1384 boxmins[2] = (enlarge+1) * entboxmins[2] - enlarge * entboxmaxs[2];
1385 boxmaxs[2] = (enlarge+1) * entboxmaxs[2] - enlarge * entboxmins[2];
1387 VectorMAM(0.5f, boxmins, 0.5f, boxmaxs, endpoints[0]);
1388 for (traceindex = 1;traceindex < numtraces;traceindex++)
1389 VectorSet(endpoints[traceindex], lhrandom(boxmins[0], boxmaxs[0]), lhrandom(boxmins[1], boxmaxs[1]), lhrandom(boxmins[2], boxmaxs[2]));
1391 // calculate sweep box for the entire swarm of traces
1392 VectorCopy(eye, clipboxmins);
1393 VectorCopy(eye, clipboxmaxs);
1394 for (traceindex = 0;traceindex < numtraces;traceindex++)
1396 clipboxmins[0] = min(clipboxmins[0], endpoints[traceindex][0]);
1397 clipboxmins[1] = min(clipboxmins[1], endpoints[traceindex][1]);
1398 clipboxmins[2] = min(clipboxmins[2], endpoints[traceindex][2]);
1399 clipboxmaxs[0] = max(clipboxmaxs[0], endpoints[traceindex][0]);
1400 clipboxmaxs[1] = max(clipboxmaxs[1], endpoints[traceindex][1]);
1401 clipboxmaxs[2] = max(clipboxmaxs[2], endpoints[traceindex][2]);
1404 // get the list of entities in the sweep box
1405 if (sv_cullentities_trace_entityocclusion.integer)
1406 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
1407 if (numtouchedicts > MAX_EDICTS)
1409 // this never happens
1410 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
1411 numtouchedicts = MAX_EDICTS;
1413 // iterate the entities found in the sweep box and filter them
1414 originalnumtouchedicts = numtouchedicts;
1416 for (touchindex = 0;touchindex < originalnumtouchedicts;touchindex++)
1418 touch = touchedicts[touchindex];
1419 if (touch->fields.server->solid != SOLID_BSP)
1421 model = SV_GetModelFromEdict(touch);
1422 if (!model || !model->brush.TraceLineOfSight)
1424 // skip obviously transparent entities
1425 alpha = PRVM_EDICTFIELDVALUE(touch, prog->fieldoffsets.alpha)->_float;
1426 if (alpha && alpha < 1)
1428 if ((int)touch->fields.server->effects & EF_ADDITIVE)
1430 touchedicts[numtouchedicts++] = touch;
1433 // now that we have a filtered list of "interesting" entities, fire each
1434 // ray against all of them, this gives us an early-out case when something
1435 // is visible (which it often is)
1437 for (traceindex = 0;traceindex < numtraces;traceindex++)
1439 // check world occlusion
1440 if (sv.worldmodel && sv.worldmodel->brush.TraceLineOfSight)
1441 if (!sv.worldmodel->brush.TraceLineOfSight(sv.worldmodel, eye, endpoints[traceindex]))
1443 for (touchindex = 0;touchindex < numtouchedicts;touchindex++)
1445 touch = touchedicts[touchindex];
1446 model = SV_GetModelFromEdict(touch);
1447 if(model && model->brush.TraceLineOfSight)
1449 // get the entity matrix
1450 pitchsign = SV_GetPitchSign(touch);
1451 Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
1452 Matrix4x4_Invert_Simple(&imatrix, &matrix);
1453 // see if the ray hits this entity
1454 Matrix4x4_Transform(&imatrix, eye, starttransformed);
1455 Matrix4x4_Transform(&imatrix, endpoints[traceindex], endtransformed);
1456 if (!model->brush.TraceLineOfSight(model, starttransformed, endtransformed))
1463 // check if the ray was blocked
1464 if (touchindex < numtouchedicts)
1466 // return if the ray was not blocked
1474 void SV_MarkWriteEntityStateToClient(entity_state_t *s)
1479 if (sv.sententitiesconsideration[s->number] == sv.sententitiesmark)
1481 sv.sententitiesconsideration[s->number] = sv.sententitiesmark;
1482 sv.writeentitiestoclient_stats_totalentities++;
1484 if (s->customizeentityforclient)
1486 prog->globals.server->self = s->number;
1487 prog->globals.server->other = sv.writeentitiestoclient_cliententitynumber;
1488 PRVM_ExecuteProgram(s->customizeentityforclient, "customizeentityforclient: NULL function");
1489 if(!PRVM_G_FLOAT(OFS_RETURN) || !SV_PrepareEntityForSending(PRVM_EDICT_NUM(s->number), s, s->number))
1493 // never reject player
1494 if (s->number != sv.writeentitiestoclient_cliententitynumber)
1496 // check various rejection conditions
1497 if (s->nodrawtoclient == sv.writeentitiestoclient_cliententitynumber)
1499 if (s->drawonlytoclient && s->drawonlytoclient != sv.writeentitiestoclient_cliententitynumber)
1501 if (s->effects & EF_NODRAW)
1503 // LordHavoc: only send entities with a model or important effects
1504 if (!s->modelindex && s->specialvisibilityradius == 0)
1507 isbmodel = (model = SV_GetModelByIndex(s->modelindex)) != NULL && model->name[0] == '*';
1508 // viewmodels don't have visibility checking
1509 if (s->viewmodelforclient)
1511 if (s->viewmodelforclient != sv.writeentitiestoclient_cliententitynumber)
1514 else if (s->tagentity)
1516 // tag attached entities simply check their parent
1517 if (!sv.sendentitiesindex[s->tagentity])
1519 SV_MarkWriteEntityStateToClient(sv.sendentitiesindex[s->tagentity]);
1520 if (sv.sententities[s->tagentity] != sv.sententitiesmark)
1523 // always send world submodels in newer protocols because they don't
1524 // generate much traffic (in old protocols they hog bandwidth)
1525 // but only if sv_cullentities_nevercullbmodels is off
1526 else if (!(s->effects & EF_NODEPTHTEST) && (!isbmodel || !sv_cullentities_nevercullbmodels.integer || sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE))
1528 // entity has survived every check so far, check if visible
1529 ed = PRVM_EDICT_NUM(s->number);
1531 // if not touching a visible leaf
1532 if (sv_cullentities_pvs.integer && !r_novis.integer && sv.writeentitiestoclient_pvsbytes)
1534 if (ed->priv.server->pvs_numclusters < 0)
1536 // entity too big for clusters list
1537 if (sv.worldmodel && sv.worldmodel->brush.BoxTouchingPVS && !sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, sv.writeentitiestoclient_pvs, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
1539 sv.writeentitiestoclient_stats_culled_pvs++;
1546 // check cached clusters list
1547 for (i = 0;i < ed->priv.server->pvs_numclusters;i++)
1548 if (CHECKPVSBIT(sv.writeentitiestoclient_pvs, ed->priv.server->pvs_clusterlist[i]))
1550 if (i == ed->priv.server->pvs_numclusters)
1552 sv.writeentitiestoclient_stats_culled_pvs++;
1558 // or not seen by random tracelines
1559 if (sv_cullentities_trace.integer && !isbmodel && sv.worldmodel->brush.TraceLineOfSight)
1562 s->number <= svs.maxclients
1563 ? sv_cullentities_trace_samples_players.integer
1565 s->specialvisibilityradius
1566 ? sv_cullentities_trace_samples_extra.integer
1567 : sv_cullentities_trace_samples.integer;
1568 float enlarge = sv_cullentities_trace_enlarge.value;
1573 for (eyeindex = 0;eyeindex < sv.writeentitiestoclient_numeyes;eyeindex++)
1574 if(SV_CanSeeBox(samples, enlarge, sv.writeentitiestoclient_eyes[eyeindex], ed->priv.server->cullmins, ed->priv.server->cullmaxs))
1576 if(eyeindex < sv.writeentitiestoclient_numeyes)
1577 svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[s->number] =
1579 s->number <= svs.maxclients
1580 ? sv_cullentities_trace_delay_players.value
1581 : sv_cullentities_trace_delay.value
1583 else if (realtime > svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[s->number])
1585 sv.writeentitiestoclient_stats_culled_trace++;
1593 // this just marks it for sending
1594 // FIXME: it would be more efficient to send here, but the entity
1595 // compressor isn't that flexible
1596 sv.writeentitiestoclient_stats_visibleentities++;
1597 sv.sententities[s->number] = sv.sententitiesmark;
1600 #if MAX_LEVELNETWORKEYES > 0
1601 #define MAX_EYE_RECURSION 1 // increase if recursion gets supported by portals
1602 void SV_AddCameraEyes(void)
1606 int cameras[MAX_LEVELNETWORKEYES];
1607 vec3_t camera_origins[MAX_LEVELNETWORKEYES];
1608 int eye_levels[MAX_CLIENTNETWORKEYES];
1611 prvm_eval_t *valendpos, *val;
1613 if(!prog->fieldoffsets.camera_transform)
1615 valendpos = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_endpos);
1619 for(i = 0; i < sv.writeentitiestoclient_numeyes; ++i)
1622 // check line of sight to portal entities and add them to PVS
1623 for (e = 1, ed = PRVM_NEXT_EDICT(prog->edicts);e < prog->num_edicts;e++, ed = PRVM_NEXT_EDICT(ed))
1625 if (!ed->priv.server->free)
1627 if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.camera_transform)) && val->function)
1629 prog->globals.server->self = e;
1630 prog->globals.server->other = sv.writeentitiestoclient_cliententitynumber;
1631 VectorCopy(sv.writeentitiestoclient_eyes[0], valendpos->vector);
1632 VectorCopy(sv.writeentitiestoclient_eyes[0], PRVM_G_VECTOR(OFS_PARM0));
1633 VectorClear(PRVM_G_VECTOR(OFS_PARM1));
1634 PRVM_ExecuteProgram(val->function, "QC function e.camera_transform is missing");
1635 if(!VectorCompare(valendpos->vector, sv.writeentitiestoclient_eyes[0]))
1636 VectorCopy(valendpos->vector, camera_origins[n_cameras]);
1637 cameras[n_cameras] = e;
1639 if(n_cameras >= MAX_LEVELNETWORKEYES)
1648 // i is loop counter, is reset to 0 when an eye got added
1649 // j is camera index to check
1650 for(i = 0, j = 0; sv.writeentitiestoclient_numeyes < MAX_CLIENTNETWORKEYES && i < n_cameras; ++i, ++j, j %= n_cameras)
1654 ed = PRVM_EDICT_NUM(cameras[j]);
1655 VectorAdd(ed->fields.server->origin, ed->fields.server->mins, mi);
1656 VectorAdd(ed->fields.server->origin, ed->fields.server->maxs, ma);
1657 for(k = 0; k < sv.writeentitiestoclient_numeyes; ++k)
1658 if(eye_levels[k] <= MAX_EYE_RECURSION)
1660 if(SV_CanSeeBox(sv_cullentities_trace_samples.integer, sv_cullentities_trace_enlarge.value, sv.writeentitiestoclient_eyes[k], mi, ma))
1662 eye_levels[sv.writeentitiestoclient_numeyes] = eye_levels[k] + 1;
1663 VectorCopy(camera_origins[j], sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes]);
1664 // Con_Printf("added eye %d: %f %f %f because we can see %f %f %f .. %f %f %f from eye %d\n", j, sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes][0], sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes][1], sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes][2], mi[0], mi[1], mi[2], ma[0], ma[1], ma[2], k);
1665 sv.writeentitiestoclient_numeyes++;
1674 void SV_AddCameraEyes(void)
1679 void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg, int maxsize)
1681 qboolean need_empty = false;
1682 int i, numsendstates, numcsqcsendstates;
1684 prvm_edict_t *camera;
1688 // if there isn't enough space to accomplish anything, skip it
1689 if (msg->cursize + 25 > maxsize)
1692 sv.writeentitiestoclient_msg = msg;
1693 sv.writeentitiestoclient_clientnumber = client - svs.clients;
1695 sv.writeentitiestoclient_stats_culled_pvs = 0;
1696 sv.writeentitiestoclient_stats_culled_trace = 0;
1697 sv.writeentitiestoclient_stats_visibleentities = 0;
1698 sv.writeentitiestoclient_stats_totalentities = 0;
1699 sv.writeentitiestoclient_numeyes = 0;
1702 sv.writeentitiestoclient_cliententitynumber = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
1703 camera = PRVM_EDICT_NUM( client->clientcamera );
1704 VectorAdd(camera->fields.server->origin, clent->fields.server->view_ofs, eye);
1705 sv.writeentitiestoclient_pvsbytes = 0;
1706 // get the PVS values for the eye location, later FatPVS calls will merge
1707 if (sv.worldmodel && sv.worldmodel->brush.FatPVS)
1708 sv.writeentitiestoclient_pvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, eye, 8, sv.writeentitiestoclient_pvs, sizeof(sv.writeentitiestoclient_pvs), sv.writeentitiestoclient_pvsbytes != 0);
1710 // add the eye to a list for SV_CanSeeBox tests
1711 VectorCopy(eye, sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes]);
1712 sv.writeentitiestoclient_numeyes++;
1714 // calculate predicted eye origin for SV_CanSeeBox tests
1715 if (sv_cullentities_trace_prediction.integer)
1717 vec_t predtime = bound(0, host_client->ping, sv_cullentities_trace_prediction_time.value);
1719 VectorMA(eye, predtime, camera->fields.server->velocity, predeye);
1720 if (SV_CanSeeBox(1, 0, eye, predeye, predeye))
1722 VectorCopy(predeye, sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes]);
1723 sv.writeentitiestoclient_numeyes++;
1725 //if (!sv.writeentitiestoclient_useprediction)
1726 // Con_DPrintf("Trying to walk into solid in a pingtime... not predicting for culling\n");
1731 // build PVS from the new eyes
1732 if (sv.worldmodel && sv.worldmodel->brush.FatPVS)
1733 for(i = 1; i < sv.writeentitiestoclient_numeyes; ++i)
1734 sv.writeentitiestoclient_pvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, sv.writeentitiestoclient_eyes[i], 8, sv.writeentitiestoclient_pvs, sizeof(sv.writeentitiestoclient_pvs), sv.writeentitiestoclient_pvsbytes != 0);
1736 sv.sententitiesmark++;
1738 for (i = 0;i < sv.numsendentities;i++)
1739 SV_MarkWriteEntityStateToClient(sv.sendentities + i);
1742 numcsqcsendstates = 0;
1743 for (i = 0;i < sv.numsendentities;i++)
1745 s = &sv.sendentities[i];
1746 if (sv.sententities[s->number] == sv.sententitiesmark)
1748 if(s->active == ACTIVE_NETWORK)
1750 if (s->exteriormodelforclient)
1752 if (s->exteriormodelforclient == sv.writeentitiestoclient_cliententitynumber)
1753 s->flags |= RENDER_EXTERIORMODEL;
1755 s->flags &= ~RENDER_EXTERIORMODEL;
1757 sv.writeentitiestoclient_sendstates[numsendstates++] = s;
1759 else if(sv.sendentities[i].active == ACTIVE_SHARED)
1760 sv.writeentitiestoclient_csqcsendstates[numcsqcsendstates++] = s->number;
1762 Con_Printf("entity %d is in sv.sendentities and marked, but not active, please breakpoint me\n", s->number);
1766 if (sv_cullentities_stats.integer)
1767 Con_Printf("client \"%s\" entities: %d total, %d visible, %d culled by: %d pvs %d trace\n", client->name, sv.writeentitiestoclient_stats_totalentities, sv.writeentitiestoclient_stats_visibleentities, sv.writeentitiestoclient_stats_culled_pvs + sv.writeentitiestoclient_stats_culled_trace, sv.writeentitiestoclient_stats_culled_pvs, sv.writeentitiestoclient_stats_culled_trace);
1769 if(client->entitydatabase5)
1770 need_empty = EntityFrameCSQC_WriteFrame(msg, maxsize, numcsqcsendstates, sv.writeentitiestoclient_csqcsendstates, client->entitydatabase5->latestframenum + 1);
1772 EntityFrameCSQC_WriteFrame(msg, maxsize, numcsqcsendstates, sv.writeentitiestoclient_csqcsendstates, 0);
1774 if(client->num_skippedentityframes >= 10)
1775 need_empty = true; // force every 10th frame to be not empty (or cl_movement replay takes too long)
1777 if (client->entitydatabase5)
1778 success = EntityFrame5_WriteFrame(msg, maxsize, client->entitydatabase5, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1, client->movesequence, need_empty);
1779 else if (client->entitydatabase4)
1781 success = EntityFrame4_WriteFrame(msg, maxsize, client->entitydatabase4, numsendstates, sv.writeentitiestoclient_sendstates);
1782 Protocol_WriteStatsReliable();
1784 else if (client->entitydatabase)
1786 success = EntityFrame_WriteFrame(msg, maxsize, client->entitydatabase, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1);
1787 Protocol_WriteStatsReliable();
1791 success = EntityFrameQuake_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates);
1792 Protocol_WriteStatsReliable();
1796 client->num_skippedentityframes = 0;
1798 ++client->num_skippedentityframes;
1807 static void SV_CleanupEnts (void)
1812 ent = PRVM_NEXT_EDICT(prog->edicts);
1813 for (e=1 ; e<prog->num_edicts ; e++, ent = PRVM_NEXT_EDICT(ent))
1814 ent->fields.server->effects = (int)ent->fields.server->effects & ~EF_MUZZLEFLASH;
1819 SV_WriteClientdataToMessage
1823 void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats)
1827 prvm_edict_t *other;
1833 float *statsf = (float *)stats;
1836 // send a damage message
1838 if (ent->fields.server->dmg_take || ent->fields.server->dmg_save)
1840 other = PRVM_PROG_TO_EDICT(ent->fields.server->dmg_inflictor);
1841 MSG_WriteByte (msg, svc_damage);
1842 MSG_WriteByte (msg, (int)ent->fields.server->dmg_save);
1843 MSG_WriteByte (msg, (int)ent->fields.server->dmg_take);
1844 for (i=0 ; i<3 ; i++)
1845 MSG_WriteCoord (msg, other->fields.server->origin[i] + 0.5*(other->fields.server->mins[i] + other->fields.server->maxs[i]), sv.protocol);
1847 ent->fields.server->dmg_take = 0;
1848 ent->fields.server->dmg_save = 0;
1852 // send the current viewpos offset from the view entity
1854 SV_SetIdealPitch (); // how much to look up / down ideally
1856 // a fixangle might get lost in a dropped packet. Oh well.
1857 if(ent->fields.server->fixangle)
1859 // angle fixing was requested by global thinking code...
1860 // so store the current angles for later use
1861 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
1862 host_client->fixangle_angles_set = TRUE;
1864 // and clear fixangle for the next frame
1865 ent->fields.server->fixangle = 0;
1868 if (host_client->fixangle_angles_set)
1870 MSG_WriteByte (msg, svc_setangle);
1871 for (i=0 ; i < 3 ; i++)
1872 MSG_WriteAngle (msg, host_client->fixangle_angles[i], sv.protocol);
1873 host_client->fixangle_angles_set = FALSE;
1876 // stuff the sigil bits into the high bits of items for sbar, or else
1878 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.items2);
1879 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1880 items = (int)ent->fields.server->items | ((int)val->_float << 23);
1882 items = (int)ent->fields.server->items | ((int)prog->globals.server->serverflags << 28);
1884 VectorClear(punchvector);
1885 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.punchvector)))
1886 VectorCopy(val->vector, punchvector);
1888 // cache weapon model name and index in client struct to save time
1889 // (this search can be almost 1% of cpu time!)
1890 s = PRVM_GetString(ent->fields.server->weaponmodel);
1891 if (strcmp(s, client->weaponmodel))
1893 strlcpy(client->weaponmodel, s, sizeof(client->weaponmodel));
1894 client->weaponmodelindex = SV_ModelIndex(s, 1);
1898 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewzoom)))
1899 viewzoom = (int)(val->_float * 255.0f);
1905 if ((int)ent->fields.server->flags & FL_ONGROUND)
1906 bits |= SU_ONGROUND;
1907 if (ent->fields.server->waterlevel >= 2)
1909 if (ent->fields.server->idealpitch)
1910 bits |= SU_IDEALPITCH;
1912 for (i=0 ; i<3 ; i++)
1914 if (ent->fields.server->punchangle[i])
1915 bits |= (SU_PUNCH1<<i);
1916 if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE && sv.protocol != PROTOCOL_NEHAHRABJP && sv.protocol != PROTOCOL_NEHAHRABJP2 && sv.protocol != PROTOCOL_NEHAHRABJP3)
1918 bits |= (SU_PUNCHVEC1<<i);
1919 if (ent->fields.server->velocity[i])
1920 bits |= (SU_VELOCITY1<<i);
1923 memset(stats, 0, sizeof(int[MAX_CL_STATS]));
1924 stats[STAT_VIEWHEIGHT] = (int)ent->fields.server->view_ofs[2];
1925 stats[STAT_ITEMS] = items;
1926 stats[STAT_WEAPONFRAME] = (int)ent->fields.server->weaponframe;
1927 stats[STAT_ARMOR] = (int)ent->fields.server->armorvalue;
1928 stats[STAT_WEAPON] = client->weaponmodelindex;
1929 stats[STAT_HEALTH] = (int)ent->fields.server->health;
1930 stats[STAT_AMMO] = (int)ent->fields.server->currentammo;
1931 stats[STAT_SHELLS] = (int)ent->fields.server->ammo_shells;
1932 stats[STAT_NAILS] = (int)ent->fields.server->ammo_nails;
1933 stats[STAT_ROCKETS] = (int)ent->fields.server->ammo_rockets;
1934 stats[STAT_CELLS] = (int)ent->fields.server->ammo_cells;
1935 stats[STAT_ACTIVEWEAPON] = (int)ent->fields.server->weapon;
1936 stats[STAT_VIEWZOOM] = viewzoom;
1937 stats[STAT_TOTALSECRETS] = (int)prog->globals.server->total_secrets;
1938 stats[STAT_TOTALMONSTERS] = (int)prog->globals.server->total_monsters;
1939 // the QC bumps these itself by sending svc_'s, so we have to keep them
1940 // zero or they'll be corrected by the engine
1941 //stats[STAT_SECRETS] = prog->globals.server->found_secrets;
1942 //stats[STAT_MONSTERS] = prog->globals.server->killed_monsters;
1944 // movement settings for prediction
1945 // note: these are not sent in protocols with lower MAX_CL_STATS limits
1946 stats[STAT_MOVEFLAGS] = MOVEFLAG_VALID
1947 | (sv_gameplayfix_q2airaccelerate.integer ? MOVEFLAG_Q2AIRACCELERATE : 0)
1948 | (sv_gameplayfix_nogravityonground.integer ? MOVEFLAG_NOGRAVITYONGROUND : 0)
1949 | (sv_gameplayfix_gravityunaffectedbyticrate.integer ? MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE : 0)
1951 statsf[STAT_MOVEVARS_TICRATE] = sys_ticrate.value;
1952 statsf[STAT_MOVEVARS_TIMESCALE] = slowmo.value;
1953 statsf[STAT_MOVEVARS_GRAVITY] = sv_gravity.value;
1954 statsf[STAT_MOVEVARS_STOPSPEED] = sv_stopspeed.value;
1955 statsf[STAT_MOVEVARS_MAXSPEED] = sv_maxspeed.value;
1956 statsf[STAT_MOVEVARS_SPECTATORMAXSPEED] = sv_maxspeed.value; // FIXME: QW has a separate cvar for this
1957 statsf[STAT_MOVEVARS_ACCELERATE] = sv_accelerate.value;
1958 statsf[STAT_MOVEVARS_AIRACCELERATE] = sv_airaccelerate.value >= 0 ? sv_airaccelerate.value : sv_accelerate.value;
1959 statsf[STAT_MOVEVARS_WATERACCELERATE] = sv_wateraccelerate.value >= 0 ? sv_wateraccelerate.value : sv_accelerate.value;
1960 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1961 statsf[STAT_MOVEVARS_ENTGRAVITY] = (val && val->_float != 0) ? val->_float : 1.0f;
1962 statsf[STAT_MOVEVARS_JUMPVELOCITY] = sv_jumpvelocity.value;
1963 statsf[STAT_MOVEVARS_EDGEFRICTION] = sv_edgefriction.value;
1964 statsf[STAT_MOVEVARS_MAXAIRSPEED] = sv_maxairspeed.value;
1965 statsf[STAT_MOVEVARS_STEPHEIGHT] = sv_stepheight.value;
1966 statsf[STAT_MOVEVARS_AIRACCEL_QW] = sv_airaccel_qw.value;
1967 statsf[STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION] = sv_airaccel_sideways_friction.value;
1968 statsf[STAT_MOVEVARS_FRICTION] = sv_friction.value;
1969 statsf[STAT_MOVEVARS_WATERFRICTION] = sv_waterfriction.value >= 0 ? sv_waterfriction.value : sv_friction.value;
1970 statsf[STAT_MOVEVARS_AIRSTOPACCELERATE] = sv_airstopaccelerate.value;
1971 statsf[STAT_MOVEVARS_AIRSTRAFEACCELERATE] = sv_airstrafeaccelerate.value;
1972 statsf[STAT_MOVEVARS_MAXAIRSTRAFESPEED] = sv_maxairstrafespeed.value;
1973 statsf[STAT_MOVEVARS_AIRSTRAFEACCEL_QW] = sv_airstrafeaccel_qw.value;
1974 statsf[STAT_MOVEVARS_AIRCONTROL] = sv_aircontrol.value;
1975 statsf[STAT_MOVEVARS_AIRCONTROL_POWER] = sv_aircontrol_power.value;
1976 statsf[STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL] = sv_warsowbunny_airforwardaccel.value;
1977 statsf[STAT_MOVEVARS_WARSOWBUNNY_ACCEL] = sv_warsowbunny_accel.value;
1978 statsf[STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED] = sv_warsowbunny_topspeed.value;
1979 statsf[STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL] = sv_warsowbunny_turnaccel.value;
1980 statsf[STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO] = sv_warsowbunny_backtosideratio.value;
1981 statsf[STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW] = sv_airspeedlimit_nonqw.value;
1982 statsf[STAT_FRAGLIMIT] = fraglimit.value;
1983 statsf[STAT_TIMELIMIT] = timelimit.value;
1985 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5)
1987 if (stats[STAT_VIEWHEIGHT] != DEFAULT_VIEWHEIGHT) bits |= SU_VIEWHEIGHT;
1989 if (stats[STAT_WEAPONFRAME]) bits |= SU_WEAPONFRAME;
1990 if (stats[STAT_ARMOR]) bits |= SU_ARMOR;
1992 // FIXME: which protocols support this? does PROTOCOL_DARKPLACES3 support viewzoom?
1993 if (sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5)
1994 if (viewzoom != 255)
1995 bits |= SU_VIEWZOOM;
2000 if (bits >= 16777216)
2004 MSG_WriteByte (msg, svc_clientdata);
2005 MSG_WriteShort (msg, bits);
2006 if (bits & SU_EXTEND1)
2007 MSG_WriteByte(msg, bits >> 16);
2008 if (bits & SU_EXTEND2)
2009 MSG_WriteByte(msg, bits >> 24);
2011 if (bits & SU_VIEWHEIGHT)
2012 MSG_WriteChar (msg, stats[STAT_VIEWHEIGHT]);
2014 if (bits & SU_IDEALPITCH)
2015 MSG_WriteChar (msg, (int)ent->fields.server->idealpitch);
2017 for (i=0 ; i<3 ; i++)
2019 if (bits & (SU_PUNCH1<<i))
2021 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
2022 MSG_WriteChar(msg, (int)ent->fields.server->punchangle[i]);
2024 MSG_WriteAngle16i(msg, ent->fields.server->punchangle[i]);
2026 if (bits & (SU_PUNCHVEC1<<i))
2028 if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
2029 MSG_WriteCoord16i(msg, punchvector[i]);
2031 MSG_WriteCoord32f(msg, punchvector[i]);
2033 if (bits & (SU_VELOCITY1<<i))
2035 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
2036 MSG_WriteChar(msg, (int)(ent->fields.server->velocity[i] * (1.0f / 16.0f)));
2038 MSG_WriteCoord32f(msg, ent->fields.server->velocity[i]);
2042 if (bits & SU_ITEMS)
2043 MSG_WriteLong (msg, stats[STAT_ITEMS]);
2045 if (sv.protocol == PROTOCOL_DARKPLACES5)
2047 if (bits & SU_WEAPONFRAME)
2048 MSG_WriteShort (msg, stats[STAT_WEAPONFRAME]);
2049 if (bits & SU_ARMOR)
2050 MSG_WriteShort (msg, stats[STAT_ARMOR]);
2051 if (bits & SU_WEAPON)
2052 MSG_WriteShort (msg, stats[STAT_WEAPON]);
2053 MSG_WriteShort (msg, stats[STAT_HEALTH]);
2054 MSG_WriteShort (msg, stats[STAT_AMMO]);
2055 MSG_WriteShort (msg, stats[STAT_SHELLS]);
2056 MSG_WriteShort (msg, stats[STAT_NAILS]);
2057 MSG_WriteShort (msg, stats[STAT_ROCKETS]);
2058 MSG_WriteShort (msg, stats[STAT_CELLS]);
2059 MSG_WriteShort (msg, stats[STAT_ACTIVEWEAPON]);
2060 if (bits & SU_VIEWZOOM)
2061 MSG_WriteShort (msg, bound(0, stats[STAT_VIEWZOOM], 65535));
2063 else if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
2065 if (bits & SU_WEAPONFRAME)
2066 MSG_WriteByte (msg, stats[STAT_WEAPONFRAME]);
2067 if (bits & SU_ARMOR)
2068 MSG_WriteByte (msg, stats[STAT_ARMOR]);
2069 if (bits & SU_WEAPON)
2071 if (sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
2072 MSG_WriteShort (msg, stats[STAT_WEAPON]);
2074 MSG_WriteByte (msg, stats[STAT_WEAPON]);
2076 MSG_WriteShort (msg, stats[STAT_HEALTH]);
2077 MSG_WriteByte (msg, stats[STAT_AMMO]);
2078 MSG_WriteByte (msg, stats[STAT_SHELLS]);
2079 MSG_WriteByte (msg, stats[STAT_NAILS]);
2080 MSG_WriteByte (msg, stats[STAT_ROCKETS]);
2081 MSG_WriteByte (msg, stats[STAT_CELLS]);
2082 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE || gamemode == GAME_NEXUIZ)
2084 for (i = 0;i < 32;i++)
2085 if (stats[STAT_ACTIVEWEAPON] & (1<<i))
2087 MSG_WriteByte (msg, i);
2090 MSG_WriteByte (msg, stats[STAT_ACTIVEWEAPON]);
2091 if (bits & SU_VIEWZOOM)
2093 if (sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
2094 MSG_WriteByte (msg, bound(0, stats[STAT_VIEWZOOM], 255));
2096 MSG_WriteShort (msg, bound(0, stats[STAT_VIEWZOOM], 65535));
2101 void SV_FlushBroadcastMessages(void)
2105 if (sv.datagram.cursize <= 0)
2107 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
2109 if (!client->spawned || !client->netconnection || client->unreliablemsg.cursize + sv.datagram.cursize > client->unreliablemsg.maxsize || client->unreliablemsg_splitpoints >= (int)(sizeof(client->unreliablemsg_splitpoint)/sizeof(client->unreliablemsg_splitpoint[0])))
2111 SZ_Write(&client->unreliablemsg, sv.datagram.data, sv.datagram.cursize);
2112 client->unreliablemsg_splitpoint[client->unreliablemsg_splitpoints++] = client->unreliablemsg.cursize;
2114 SZ_Clear(&sv.datagram);
2117 static void SV_WriteUnreliableMessages(client_t *client, sizebuf_t *msg, int maxsize, int maxsize2)
2119 // scan the splitpoints to find out how many we can fit in
2120 int numsegments, j, split;
2121 if (!client->unreliablemsg_splitpoints)
2123 // always accept the first one if it's within 1024 bytes, this ensures
2124 // that very big datagrams which are over the rate limit still get
2125 // through, just to keep it working
2126 for (numsegments = 1;numsegments < client->unreliablemsg_splitpoints;numsegments++)
2127 if (msg->cursize + client->unreliablemsg_splitpoint[numsegments] > maxsize)
2129 // the first segment gets an exemption from the rate limiting, otherwise
2130 // it could get dropped consistently due to a low rate limit
2131 if (numsegments == 1)
2133 // some will fit, so add the ones that will fit
2134 split = client->unreliablemsg_splitpoint[numsegments-1];
2135 // note this discards ones that were accepted by the segments scan but
2136 // can not fit, such as a really huge first one that will never ever
2137 // fit in a packet...
2138 if (msg->cursize + split <= maxsize)
2139 SZ_Write(msg, client->unreliablemsg.data, split);
2140 // remove the part we sent, keeping any remaining data
2141 client->unreliablemsg.cursize -= split;
2142 if (client->unreliablemsg.cursize > 0)
2143 memmove(client->unreliablemsg.data, client->unreliablemsg.data + split, client->unreliablemsg.cursize);
2144 // adjust remaining splitpoints
2145 client->unreliablemsg_splitpoints -= numsegments;
2146 for (j = 0;j < client->unreliablemsg_splitpoints;j++)
2147 client->unreliablemsg_splitpoint[j] = client->unreliablemsg_splitpoint[numsegments + j] - split;
2151 =======================
2152 SV_SendClientDatagram
2153 =======================
2155 static void SV_SendClientDatagram (client_t *client)
2157 int clientrate, maxrate, maxsize, maxsize2, downloadsize;
2159 int stats[MAX_CL_STATS];
2160 static unsigned char sv_sendclientdatagram_buf[NET_MAXMESSAGE];
2162 // obey rate limit by limiting packet frequency if the packet size
2164 // (usually this is caused by reliable messages)
2165 if (!NetConn_CanSend(client->netconnection))
2168 // PROTOCOL_DARKPLACES5 and later support packet size limiting of updates
2169 maxrate = max(NET_MINRATE, sv_maxrate.integer);
2170 if (sv_maxrate.integer != maxrate)
2171 Cvar_SetValueQuick(&sv_maxrate, maxrate);
2173 // clientrate determines the 'cleartime' of a packet
2174 // (how long to wait before sending another, based on this packet's size)
2175 clientrate = bound(NET_MINRATE, client->rate, maxrate);
2177 switch (sv.protocol)
2179 case PROTOCOL_QUAKE:
2180 case PROTOCOL_QUAKEDP:
2181 case PROTOCOL_NEHAHRAMOVIE:
2182 case PROTOCOL_NEHAHRABJP:
2183 case PROTOCOL_NEHAHRABJP2:
2184 case PROTOCOL_NEHAHRABJP3:
2185 case PROTOCOL_QUAKEWORLD:
2186 // no packet size limit support on Quake protocols because it just
2187 // causes missing entities/effects
2188 // packets are simply sent less often to obey the rate limit
2192 case PROTOCOL_DARKPLACES1:
2193 case PROTOCOL_DARKPLACES2:
2194 case PROTOCOL_DARKPLACES3:
2195 case PROTOCOL_DARKPLACES4:
2196 // no packet size limit support on DP1-4 protocols because they kick
2197 // the client off if they overflow, and miss effects
2198 // packets are simply sent less often to obey the rate limit
2199 maxsize = sizeof(sv_sendclientdatagram_buf);
2200 maxsize2 = sizeof(sv_sendclientdatagram_buf);
2203 // DP5 and later protocols support packet size limiting which is a
2204 // better method than limiting packet frequency as QW does
2206 // at very low rates (or very small sys_ticrate) the packet size is
2207 // not reduced below 128, but packets may be sent less often
2208 maxsize = (int)(clientrate * sys_ticrate.value);
2209 maxsize = bound(128, maxsize, 1400);
2211 // csqc entities can easily exceed 128 bytes, so disable throttling in
2212 // mods that use csqc (they are likely to use less bandwidth anyway)
2213 if (sv.csqc_progsize > 0)
2218 if (LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) == LHNETADDRESSTYPE_LOOP && !sv_ratelimitlocalplayer.integer)
2220 // for good singleplayer, send huge packets
2221 maxsize = sizeof(sv_sendclientdatagram_buf);
2222 maxsize2 = sizeof(sv_sendclientdatagram_buf);
2223 // never limit frequency in singleplayer
2224 clientrate = 1000000000;
2227 // while downloading, limit entity updates to half the packet
2228 // (any leftover space will be used for downloading)
2229 if (host_client->download_file)
2232 msg.data = sv_sendclientdatagram_buf;
2233 msg.maxsize = sizeof(sv_sendclientdatagram_buf);
2235 msg.allowoverflow = false;
2237 if (host_client->spawned)
2239 // the player is in the game
2240 MSG_WriteByte (&msg, svc_time);
2241 MSG_WriteFloat (&msg, sv.time);
2243 // add the client specific data to the datagram
2244 SV_WriteClientdataToMessage (client, client->edict, &msg, stats);
2245 // now update the stats[] array using any registered custom fields
2246 VM_SV_UpdateCustomStats (client, client->edict, &msg, stats);
2247 // set host_client->statsdeltabits
2248 Protocol_UpdateClientStats (stats);
2250 // add as many queued unreliable messages (effects) as we can fit
2251 // limit effects to half of the remaining space
2252 if (client->unreliablemsg.cursize)
2253 SV_WriteUnreliableMessages (client, &msg, maxsize/2, maxsize2);
2255 // now write as many entities as we can fit, and also sends stats
2256 SV_WriteEntitiesToClient (client, client->edict, &msg, maxsize);
2258 else if (realtime > client->keepalivetime)
2260 // the player isn't totally in the game yet
2261 // send small keepalive messages if too much time has passed
2262 // (may also be sending downloads)
2263 client->keepalivetime = realtime + 5;
2264 MSG_WriteChar (&msg, svc_nop);
2267 // if a download is active, see if there is room to fit some download data
2269 downloadsize = min(maxsize*2,maxsize2) - msg.cursize - 7;
2270 if (host_client->download_file && host_client->download_started && downloadsize > 0)
2272 fs_offset_t downloadstart;
2273 unsigned char data[1400];
2274 downloadstart = FS_Tell(host_client->download_file);
2275 downloadsize = min(downloadsize, (int)sizeof(data));
2276 downloadsize = FS_Read(host_client->download_file, data, downloadsize);
2277 // note this sends empty messages if at the end of the file, which is
2278 // necessary to keep the packet loss logic working
2279 // (the last blocks may be lost and need to be re-sent, and that will
2280 // only occur if the client acks the empty end messages, revealing
2281 // a gap in the download progress, causing the last blocks to be
2283 MSG_WriteChar (&msg, svc_downloaddata);
2284 MSG_WriteLong (&msg, downloadstart);
2285 MSG_WriteShort (&msg, downloadsize);
2286 if (downloadsize > 0)
2287 SZ_Write (&msg, data, downloadsize);
2290 // reliable only if none is in progress
2291 if(client->sendsignon != 2 && !client->netconnection->sendMessageLength)
2292 SV_WriteDemoMessage(client, &(client->netconnection->message), false);
2294 SV_WriteDemoMessage(client, &msg, false);
2296 // send the datagram
2297 NetConn_SendUnreliableMessage (client->netconnection, &msg, sv.protocol, clientrate, client->sendsignon == 2);
2298 if (client->sendsignon == 1 && !client->netconnection->message.cursize)
2299 client->sendsignon = 2; // prevent reliable until client sends prespawn (this is the keepalive phase)
2303 =======================
2304 SV_UpdateToReliableMessages
2305 =======================
2307 static void SV_UpdateToReliableMessages (void)
2316 // check for changes to be sent over the reliable streams
2317 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2319 // update the host_client fields we care about according to the entity fields
2320 host_client->edict = PRVM_EDICT_NUM(i+1);
2323 name = PRVM_GetString(host_client->edict->fields.server->netname);
2326 // always point the string back at host_client->name to keep it safe
2327 strlcpy (host_client->name, name, sizeof (host_client->name));
2328 host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
2329 if (strcmp(host_client->old_name, host_client->name))
2331 if (host_client->spawned)
2332 SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
2333 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
2334 // send notification to all clients
2335 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
2336 MSG_WriteByte (&sv.reliable_datagram, i);
2337 MSG_WriteString (&sv.reliable_datagram, host_client->name);
2338 SV_WriteNetnameIntoDemo(host_client);
2341 // DP_SV_CLIENTCOLORS
2342 // this is always found (since it's added by the progs loader)
2343 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
2344 host_client->colors = (int)val->_float;
2345 if (host_client->old_colors != host_client->colors)
2347 host_client->old_colors = host_client->colors;
2348 // send notification to all clients
2349 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
2350 MSG_WriteByte (&sv.reliable_datagram, i);
2351 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
2354 // NEXUIZ_PLAYERMODEL
2355 if( prog->fieldoffsets.playermodel >= 0 ) {
2356 model = PRVM_GetString(PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string);
2359 // always point the string back at host_client->name to keep it safe
2360 strlcpy (host_client->playermodel, model, sizeof (host_client->playermodel));
2361 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
2364 // NEXUIZ_PLAYERSKIN
2365 if( prog->fieldoffsets.playerskin >= 0 ) {
2366 skin = PRVM_GetString(PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string);
2369 // always point the string back at host_client->name to keep it safe
2370 strlcpy (host_client->playerskin, skin, sizeof (host_client->playerskin));
2371 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
2374 // TODO: add an extension name for this [1/17/2008 Black]
2375 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcamera)) && val->edict > 0 ) {
2376 int oldclientcamera = host_client->clientcamera;
2377 if( val->edict >= prog->max_edicts || PRVM_EDICT_NUM( val->edict )->priv.required->free ) {
2378 val->edict = host_client->clientcamera = PRVM_NUM_FOR_EDICT( host_client->edict );
2380 host_client->clientcamera = val->edict;
2383 if( oldclientcamera != host_client->clientcamera ) {
2384 MSG_WriteByte (&host_client->netconnection->message, svc_setview );
2385 MSG_WriteShort (&host_client->netconnection->message, host_client->clientcamera);
2390 host_client->frags = (int)host_client->edict->fields.server->frags;
2391 if(gamemode == GAME_NEXUIZ)
2392 if(!host_client->spawned && host_client->netconnection)
2393 host_client->frags = -666;
2394 if (host_client->old_frags != host_client->frags)
2396 host_client->old_frags = host_client->frags;
2397 // send notification to all clients
2398 MSG_WriteByte (&sv.reliable_datagram, svc_updatefrags);
2399 MSG_WriteByte (&sv.reliable_datagram, i);
2400 MSG_WriteShort (&sv.reliable_datagram, host_client->frags);
2404 for (j = 0, client = svs.clients;j < svs.maxclients;j++, client++)
2405 if (client->netconnection && (client->spawned || client->clientconnectcalled)) // also send MSG_ALL to people who are past ClientConnect, but not spawned yet
2406 SZ_Write (&client->netconnection->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize);
2408 SZ_Clear (&sv.reliable_datagram);
2413 =======================
2414 SV_SendClientMessages
2415 =======================
2417 void SV_SendClientMessages (void)
2419 int i, prepared = false;
2421 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2422 Sys_Error("SV_SendClientMessages: no quakeworld support\n");
2424 SV_FlushBroadcastMessages();
2426 // update frags, names, etc
2427 SV_UpdateToReliableMessages();
2429 // build individual updates
2430 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2432 if (!host_client->active)
2434 if (!host_client->netconnection)
2437 if (host_client->netconnection->message.overflowed)
2439 SV_DropClient (true); // if the message couldn't send, kick off
2446 // only prepare entities once per frame
2447 SV_PrepareEntitiesForSending();
2449 SV_SendClientDatagram (host_client);
2452 // clear muzzle flashes
2456 static void SV_StartDownload_f(void)
2458 if (host_client->download_file)
2459 host_client->download_started = true;
2463 * Compression extension negotiation:
2466 * cl_serverextension_download 2
2469 * download <filename> <list of zero or more suppported compressions in order of preference>
2471 * download maps/map1.bsp lzo deflate huffman
2474 * cl_downloadbegin <compressed size> <filename> <compression method actually used>
2476 * cl_downloadbegin 123456 maps/map1.bsp deflate
2478 * The server may choose not to compress the file by sending no compression name, like:
2479 * cl_downloadbegin 345678 maps/map1.bsp
2481 * NOTE: the "download" command may only specify compression algorithms if
2482 * cl_serverextension_download is 2!
2483 * If cl_serverextension_download has a different value, the client must
2484 * assume this extension is not supported!
2487 static void Download_CheckExtensions(void)
2490 int argc = Cmd_Argc();
2492 // first reset them all
2493 host_client->download_deflate = false;
2495 for(i = 2; i < argc; ++i)
2497 if(!strcmp(Cmd_Argv(i), "deflate"))
2499 host_client->download_deflate = true;
2505 static void SV_Download_f(void)
2507 const char *whichpack, *whichpack2, *extension;
2508 qboolean is_csqc; // so we need to check only once
2512 SV_ClientPrintf("usage: download <filename> {<extensions>}*\n");
2513 SV_ClientPrintf(" supported extensions: deflate\n");
2517 if (FS_CheckNastyPath(Cmd_Argv(1), false))
2519 SV_ClientPrintf("Download rejected: nasty filename \"%s\"\n", Cmd_Argv(1));
2523 if (host_client->download_file)
2525 // at this point we'll assume the previous download should be aborted
2526 Con_DPrintf("Download of %s aborted by %s starting a new download\n", host_client->download_name, host_client->name);
2527 Host_ClientCommands("\nstopdownload\n");
2529 // close the file and reset variables
2530 FS_Close(host_client->download_file);
2531 host_client->download_file = NULL;
2532 host_client->download_name[0] = 0;
2533 host_client->download_expectedposition = 0;
2534 host_client->download_started = false;
2537 is_csqc = (sv.csqc_progname[0] && strcmp(Cmd_Argv(1), sv.csqc_progname) == 0);
2539 if (!sv_allowdownloads.integer && !is_csqc)
2541 SV_ClientPrintf("Downloads are disabled on this server\n");
2542 Host_ClientCommands("\nstopdownload\n");
2546 Download_CheckExtensions();
2548 strlcpy(host_client->download_name, Cmd_Argv(1), sizeof(host_client->download_name));
2549 extension = FS_FileExtension(host_client->download_name);
2551 // host_client is asking to download a specified file
2552 if (developer_extra.integer)
2553 Con_DPrintf("Download request for %s by %s\n", host_client->download_name, host_client->name);
2557 char extensions[MAX_QPATH]; // make sure this can hold all extensions
2558 extensions[0] = '\0';
2560 if(host_client->download_deflate)
2561 strlcat(extensions, " deflate", sizeof(extensions));
2563 Con_DPrintf("Downloading %s to %s\n", host_client->download_name, host_client->name);
2565 if(host_client->download_deflate && svs.csqc_progdata_deflated)
2566 host_client->download_file = FS_FileFromData(svs.csqc_progdata_deflated, svs.csqc_progsize_deflated, true);
2568 host_client->download_file = FS_FileFromData(svs.csqc_progdata, sv.csqc_progsize, true);
2570 // no, no space is needed between %s and %s :P
2571 Host_ClientCommands("\ncl_downloadbegin %i %s%s\n", (int)FS_FileSize(host_client->download_file), host_client->download_name, extensions);
2573 host_client->download_expectedposition = 0;
2574 host_client->download_started = false;
2575 host_client->sendsignon = true; // make sure this message is sent
2579 if (!FS_FileExists(host_client->download_name))
2581 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);
2582 Host_ClientCommands("\nstopdownload\n");
2586 // check if the user is trying to download part of registered Quake(r)
2587 whichpack = FS_WhichPack(host_client->download_name);
2588 whichpack2 = FS_WhichPack("gfx/pop.lmp");
2589 if ((whichpack && whichpack2 && !strcasecmp(whichpack, whichpack2)) || FS_IsRegisteredQuakePack(host_client->download_name))
2591 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);
2592 Host_ClientCommands("\nstopdownload\n");
2596 // check if the server has forbidden archive downloads entirely
2597 if (!sv_allowdownloads_inarchive.integer)
2599 whichpack = FS_WhichPack(host_client->download_name);
2602 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);
2603 Host_ClientCommands("\nstopdownload\n");
2608 if (!sv_allowdownloads_config.integer)
2610 if (!strcasecmp(extension, "cfg"))
2612 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);
2613 Host_ClientCommands("\nstopdownload\n");
2618 if (!sv_allowdownloads_dlcache.integer)
2620 if (!strncasecmp(host_client->download_name, "dlcache/", 8))
2622 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);
2623 Host_ClientCommands("\nstopdownload\n");
2628 if (!sv_allowdownloads_archive.integer)
2630 if (!strcasecmp(extension, "pak") || !strcasecmp(extension, "pk3"))
2632 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);
2633 Host_ClientCommands("\nstopdownload\n");
2638 host_client->download_file = FS_OpenVirtualFile(host_client->download_name, true);
2639 if (!host_client->download_file)
2641 SV_ClientPrintf("Download rejected: server could not open the file \"%s\"\n", host_client->download_name);
2642 Host_ClientCommands("\nstopdownload\n");
2646 if (FS_FileSize(host_client->download_file) > 1<<30)
2648 SV_ClientPrintf("Download rejected: file \"%s\" is very large\n", host_client->download_name);
2649 Host_ClientCommands("\nstopdownload\n");
2650 FS_Close(host_client->download_file);
2651 host_client->download_file = NULL;
2655 if (FS_FileSize(host_client->download_file) < 0)
2657 SV_ClientPrintf("Download rejected: file \"%s\" is not a regular file\n", host_client->download_name);
2658 Host_ClientCommands("\nstopdownload\n");
2659 FS_Close(host_client->download_file);
2660 host_client->download_file = NULL;
2664 Con_DPrintf("Downloading %s to %s\n", host_client->download_name, host_client->name);
2667 * we can only do this if we would actually deflate on the fly
2668 * which we do not (yet)!
2670 char extensions[MAX_QPATH]; // make sure this can hold all extensions
2671 extensions[0] = '\0';
2673 if(host_client->download_deflate)
2674 strlcat(extensions, " deflate", sizeof(extensions));
2676 // no, no space is needed between %s and %s :P
2677 Host_ClientCommands("\ncl_downloadbegin %i %s%s\n", (int)FS_FileSize(host_client->download_file), host_client->download_name, extensions);
2680 Host_ClientCommands("\ncl_downloadbegin %i %s\n", (int)FS_FileSize(host_client->download_file), host_client->download_name);
2682 host_client->download_expectedposition = 0;
2683 host_client->download_started = false;
2684 host_client->sendsignon = true; // make sure this message is sent
2686 // the rest of the download process is handled in SV_SendClientDatagram
2687 // and other code dealing with svc_downloaddata and clc_ackdownloaddata
2689 // no svc_downloaddata messages will be sent until sv_startdownload is
2690 // sent by the client
2694 ==============================================================================
2698 ==============================================================================
2707 int SV_ModelIndex(const char *s, int precachemode)
2709 int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3) ? 256 : MAX_MODELS);
2710 char filename[MAX_QPATH];
2714 //if (precachemode == 2)
2716 strlcpy(filename, s, sizeof(filename));
2717 for (i = 2;i < limit;i++)
2719 if (!sv.model_precache[i][0])
2723 if (sv.state != ss_loading && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5))
2725 Con_Printf("SV_ModelIndex(\"%s\"): precache_model can only be done in spawn functions\n", filename);
2728 if (precachemode == 1)
2729 Con_Printf("SV_ModelIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename);
2730 strlcpy(sv.model_precache[i], filename, sizeof(sv.model_precache[i]));
2731 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, s[0] == '*' ? sv.worldname : NULL);
2732 if (sv.state != ss_loading)
2734 MSG_WriteByte(&sv.reliable_datagram, svc_precache);
2735 MSG_WriteShort(&sv.reliable_datagram, i);
2736 MSG_WriteString(&sv.reliable_datagram, filename);
2740 Con_Printf("SV_ModelIndex(\"%s\"): not precached\n", filename);
2743 if (!strcmp(sv.model_precache[i], filename))
2746 Con_Printf("SV_ModelIndex(\"%s\"): i (%i) == MAX_MODELS (%i)\n", filename, i, MAX_MODELS);
2756 int SV_SoundIndex(const char *s, int precachemode)
2758 int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3) ? 256 : MAX_SOUNDS);
2759 char filename[MAX_QPATH];
2763 //if (precachemode == 2)
2765 strlcpy(filename, s, sizeof(filename));
2766 for (i = 1;i < limit;i++)
2768 if (!sv.sound_precache[i][0])
2772 if (sv.state != ss_loading && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5))
2774 Con_Printf("SV_SoundIndex(\"%s\"): precache_sound can only be done in spawn functions\n", filename);
2777 if (precachemode == 1)
2778 Con_Printf("SV_SoundIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename);
2779 strlcpy(sv.sound_precache[i], filename, sizeof(sv.sound_precache[i]));
2780 if (sv.state != ss_loading)
2782 MSG_WriteByte(&sv.reliable_datagram, svc_precache);
2783 MSG_WriteShort(&sv.reliable_datagram, i + 32768);
2784 MSG_WriteString(&sv.reliable_datagram, filename);
2788 Con_Printf("SV_SoundIndex(\"%s\"): not precached\n", filename);
2791 if (!strcmp(sv.sound_precache[i], filename))
2794 Con_Printf("SV_SoundIndex(\"%s\"): i (%i) == MAX_SOUNDS (%i)\n", filename, i, MAX_SOUNDS);
2800 SV_ParticleEffectIndex
2804 int SV_ParticleEffectIndex(const char *name)
2806 int i, argc, linenumber, effectnameindex;
2808 fs_offset_t filesize;
2809 unsigned char *filedata;
2811 const char *textstart;
2812 //const char *textend;
2813 char argv[16][1024];
2814 char filename[MAX_QPATH];
2815 if (!sv.particleeffectnamesloaded)
2817 sv.particleeffectnamesloaded = true;
2818 memset(sv.particleeffectname, 0, sizeof(sv.particleeffectname));
2819 for (i = 0;i < EFFECT_TOTAL;i++)
2820 strlcpy(sv.particleeffectname[i], standardeffectnames[i], sizeof(sv.particleeffectname[i]));
2821 for (filepass = 0;;filepass++)
2824 dpsnprintf(filename, sizeof(filename), "effectinfo.txt");
2825 else if (filepass == 1)
2826 dpsnprintf(filename, sizeof(filename), "%s_effectinfo.txt", sv.worldnamenoextension);
2829 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
2832 textstart = (const char *)filedata;
2833 //textend = (const char *)filedata + filesize;
2835 for (linenumber = 1;;linenumber++)
2840 if (!COM_ParseToken_Simple(&text, true, false) || !strcmp(com_token, "\n"))
2844 strlcpy(argv[argc], com_token, sizeof(argv[argc]));
2848 if (com_token[0] == 0)
2849 break; // if the loop exited and it's not a \n, it's EOF
2852 if (!strcmp(argv[0], "effect"))
2856 for (effectnameindex = 1;effectnameindex < SV_MAX_PARTICLEEFFECTNAME;effectnameindex++)
2858 if (sv.particleeffectname[effectnameindex][0])
2860 if (!strcmp(sv.particleeffectname[effectnameindex], argv[1]))
2865 strlcpy(sv.particleeffectname[effectnameindex], argv[1], sizeof(sv.particleeffectname[effectnameindex]));
2869 // if we run out of names, abort
2870 if (effectnameindex == SV_MAX_PARTICLEEFFECTNAME)
2872 Con_Printf("%s:%i: too many effects!\n", filename, linenumber);
2881 // search for the name
2882 for (effectnameindex = 1;effectnameindex < SV_MAX_PARTICLEEFFECTNAME && sv.particleeffectname[effectnameindex][0];effectnameindex++)
2883 if (!strcmp(sv.particleeffectname[effectnameindex], name))
2884 return effectnameindex;
2885 // return 0 if we couldn't find it
2889 dp_model_t *SV_GetModelByIndex(int modelindex)
2891 return (modelindex > 0 && modelindex < MAX_MODELS) ? sv.models[modelindex] : NULL;
2894 dp_model_t *SV_GetModelFromEdict(prvm_edict_t *ed)
2897 if (!ed || ed->priv.server->free)
2899 modelindex = (int)ed->fields.server->modelindex;
2900 return (modelindex > 0 && modelindex < MAX_MODELS) ? sv.models[modelindex] : NULL;
2909 static void SV_CreateBaseline (void)
2911 int i, entnum, large;
2912 prvm_edict_t *svent;
2914 // LordHavoc: clear *all* states (note just active ones)
2915 for (entnum = 0;entnum < prog->max_edicts;entnum++)
2917 // get the current server version
2918 svent = PRVM_EDICT_NUM(entnum);
2920 // LordHavoc: always clear state values, whether the entity is in use or not
2921 svent->priv.server->baseline = defaultstate;
2923 if (svent->priv.server->free)
2925 if (entnum > svs.maxclients && !svent->fields.server->modelindex)
2928 // create entity baseline
2929 VectorCopy (svent->fields.server->origin, svent->priv.server->baseline.origin);
2930 VectorCopy (svent->fields.server->angles, svent->priv.server->baseline.angles);
2931 svent->priv.server->baseline.frame = (int)svent->fields.server->frame;
2932 svent->priv.server->baseline.skin = (int)svent->fields.server->skin;
2933 if (entnum > 0 && entnum <= svs.maxclients)
2935 svent->priv.server->baseline.colormap = entnum;
2936 svent->priv.server->baseline.modelindex = SV_ModelIndex("progs/player.mdl", 1);
2940 svent->priv.server->baseline.colormap = 0;
2941 svent->priv.server->baseline.modelindex = (int)svent->fields.server->modelindex;
2945 if (svent->priv.server->baseline.modelindex & 0xFF00 || svent->priv.server->baseline.frame & 0xFF00)
2948 if (sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
2952 // add to the message
2954 MSG_WriteByte (&sv.signon, svc_spawnbaseline2);
2956 MSG_WriteByte (&sv.signon, svc_spawnbaseline);
2957 MSG_WriteShort (&sv.signon, entnum);
2961 MSG_WriteShort (&sv.signon, svent->priv.server->baseline.modelindex);
2962 MSG_WriteShort (&sv.signon, svent->priv.server->baseline.frame);
2964 else if (sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
2966 MSG_WriteShort (&sv.signon, svent->priv.server->baseline.modelindex);
2967 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.frame);
2971 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.modelindex);
2972 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.frame);
2974 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.colormap);
2975 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.skin);
2976 for (i=0 ; i<3 ; i++)
2978 MSG_WriteCoord(&sv.signon, svent->priv.server->baseline.origin[i], sv.protocol);
2979 MSG_WriteAngle(&sv.signon, svent->priv.server->baseline.angles[i], sv.protocol);
2988 Load csprogs.dat and comperss it so it doesn't need to be
2989 reloaded on request.
2992 void SV_Prepare_CSQC(void)
2994 fs_offset_t progsize;
2996 if(svs.csqc_progdata)
2998 Con_DPrintf("Unloading old CSQC data.\n");
2999 Mem_Free(svs.csqc_progdata);
3000 if(svs.csqc_progdata_deflated)
3001 Mem_Free(svs.csqc_progdata_deflated);
3004 svs.csqc_progdata = NULL;
3005 svs.csqc_progdata_deflated = NULL;
3007 sv.csqc_progname[0] = 0;
3008 svs.csqc_progdata = FS_LoadFile(csqc_progname.string, sv_mempool, false, &progsize);
3012 size_t deflated_size;
3014 sv.csqc_progsize = (int)progsize;
3015 sv.csqc_progcrc = CRC_Block(svs.csqc_progdata, progsize);
3016 strlcpy(sv.csqc_progname, csqc_progname.string, sizeof(sv.csqc_progname));
3017 Con_DPrintf("server detected csqc progs file \"%s\" with size %i and crc %i\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc);
3019 Con_DPrint("Compressing csprogs.dat\n");
3020 //unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflated_size, int level, mempool_t *mempool);
3021 svs.csqc_progdata_deflated = FS_Deflate(svs.csqc_progdata, progsize, &deflated_size, -1, sv_mempool);
3022 svs.csqc_progsize_deflated = (int)deflated_size;
3023 if(svs.csqc_progdata_deflated)
3025 Con_DPrintf("Deflated: %g%%\n", 100.0 - 100.0 * (deflated_size / (float)progsize));
3026 Con_DPrintf("Uncompressed: %u\nCompressed: %u\n", (unsigned)sv.csqc_progsize, (unsigned)svs.csqc_progsize_deflated);
3029 Con_DPrintf("Cannot compress - need zlib for this. Using uncompressed progs only.\n");
3037 Grabs the current state of each client for saving across the
3038 transition to another level
3041 void SV_SaveSpawnparms (void)
3045 svs.serverflags = (int)prog->globals.server->serverflags;
3047 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3049 if (!host_client->active)
3052 // call the progs to get default spawn parms for the new client
3053 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
3054 PRVM_ExecuteProgram (prog->globals.server->SetChangeParms, "QC function SetChangeParms is missing");
3055 for (j=0 ; j<NUM_SPAWN_PARMS ; j++)
3056 host_client->spawn_parms[j] = (&prog->globals.server->parm1)[j];
3064 This is called at the start of each level
3068 void SV_SpawnServer (const char *server)
3073 dp_model_t *worldmodel;
3074 char modelname[sizeof(sv.worldname)];
3076 Con_DPrintf("SpawnServer: %s\n", server);
3078 dpsnprintf (modelname, sizeof(modelname), "maps/%s.bsp", server);
3080 if (!FS_FileExists(modelname))
3082 dpsnprintf (modelname, sizeof(modelname), "maps/%s", server);
3083 if (!FS_FileExists(modelname))
3085 Con_Printf("SpawnServer: no map file named maps/%s.bsp\n", server);
3090 if (cls.state != ca_dedicated)
3092 SCR_BeginLoadingPlaque();
3099 World_End(&sv.world);
3100 if(prog->funcoffsets.SV_Shutdown)
3102 func_t s = prog->funcoffsets.SV_Shutdown;
3103 prog->funcoffsets.SV_Shutdown = 0; // prevent it from getting called again
3104 PRVM_ExecuteProgram(s,"SV_Shutdown() required");
3109 // free q3 shaders so that any newly downloaded shaders will be active
3110 Mod_FreeQ3Shaders();
3112 worldmodel = Mod_ForName(modelname, false, developer.integer > 0, NULL);
3113 if (!worldmodel || !worldmodel->TraceBox)
3115 Con_Printf("Couldn't load map %s\n", modelname);
3119 // let's not have any servers with no name
3120 if (hostname.string[0] == 0)
3121 Cvar_Set ("hostname", "UNNAMED");
3122 scr_centertime_off = 0;
3124 svs.changelevel_issued = false; // now safe to issue another
3126 // make the map a required file for clients
3127 Curl_ClearRequirements();
3128 Curl_RequireFile(modelname);
3131 // tell all connected clients that we are going to a new level
3136 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
3138 if (client->netconnection)
3140 MSG_WriteByte(&client->netconnection->message, svc_stufftext);
3141 MSG_WriteString(&client->netconnection->message, "reconnect\n");
3148 NetConn_OpenServerPorts(true);
3152 // make cvars consistant
3155 Cvar_SetValue ("deathmatch", 0);
3156 // LordHavoc: it can be useful to have skills outside the range 0-3...
3157 //current_skill = bound(0, (int)(skill.value + 0.5), 3);
3158 //Cvar_SetValue ("skill", (float)current_skill);
3159 current_skill = (int)(skill.value + 0.5);
3162 // set up the new server
3164 memset (&sv, 0, sizeof(sv));
3165 // if running a local client, make sure it doesn't try to access the last
3166 // level's data which is no longer valiud
3169 Cvar_SetValue("halflifebsp", worldmodel->brush.ishlbsp);
3171 if(*sv_random_seed.string)
3173 srand(sv_random_seed.integer);
3174 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);
3181 // set level base name variables for later use
3182 strlcpy (sv.name, server, sizeof (sv.name));
3183 strlcpy(sv.worldname, modelname, sizeof(sv.worldname));
3184 FS_StripExtension(sv.worldname, sv.worldnamenoextension, sizeof(sv.worldnamenoextension));
3185 strlcpy(sv.worldbasename, !strncmp(sv.worldnamenoextension, "maps/", 5) ? sv.worldnamenoextension + 5 : sv.worldnamenoextension, sizeof(sv.worldbasename));
3186 //Cvar_SetQuick(&sv_worldmessage, sv.worldmessage); // set later after QC is spawned
3187 Cvar_SetQuick(&sv_worldname, sv.worldname);
3188 Cvar_SetQuick(&sv_worldnamenoextension, sv.worldnamenoextension);
3189 Cvar_SetQuick(&sv_worldbasename, sv.worldbasename);
3191 sv.protocol = Protocol_EnumForName(sv_protocolname.string);
3192 if (sv.protocol == PROTOCOL_UNKNOWN)
3195 Protocol_Names(buffer, sizeof(buffer));
3196 Con_Printf("Unknown sv_protocolname \"%s\", valid values are:\n%s\n", sv_protocolname.string, buffer);
3197 sv.protocol = PROTOCOL_QUAKE;
3202 // load progs to get entity field count
3203 //PR_LoadProgs ( sv_progs.string );
3205 sv.datagram.maxsize = sizeof(sv.datagram_buf);
3206 sv.datagram.cursize = 0;
3207 sv.datagram.data = sv.datagram_buf;
3209 sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf);
3210 sv.reliable_datagram.cursize = 0;
3211 sv.reliable_datagram.data = sv.reliable_datagram_buf;
3213 sv.signon.maxsize = sizeof(sv.signon_buf);
3214 sv.signon.cursize = 0;
3215 sv.signon.data = sv.signon_buf;
3217 // leave slots at start for clients only
3218 //prog->num_edicts = svs.maxclients+1;
3220 sv.state = ss_loading;
3221 prog->allowworldwrites = true;
3224 prog->globals.server->time = sv.time = 1.0;
3227 worldmodel->used = true;
3229 sv.worldmodel = worldmodel;
3230 sv.models[1] = sv.worldmodel;
3233 // clear world interaction links
3235 World_SetSize(&sv.world, sv.worldname, sv.worldmodel->normalmins, sv.worldmodel->normalmaxs);
3236 World_Start(&sv.world);
3238 strlcpy(sv.sound_precache[0], "", sizeof(sv.sound_precache[0]));
3240 strlcpy(sv.model_precache[0], "", sizeof(sv.model_precache[0]));
3241 strlcpy(sv.model_precache[1], sv.worldname, sizeof(sv.model_precache[1]));
3242 for (i = 1;i < sv.worldmodel->brush.numsubmodels && i+1 < MAX_MODELS;i++)
3244 dpsnprintf(sv.model_precache[i+1], sizeof(sv.model_precache[i+1]), "*%i", i);
3245 sv.models[i+1] = Mod_ForName (sv.model_precache[i+1], false, false, sv.worldname);
3247 if(i < sv.worldmodel->brush.numsubmodels)
3248 Con_Printf("Too many submodels (MAX_MODELS is %i)\n", MAX_MODELS);
3251 // load the rest of the entities
3253 // AK possible hack since num_edicts is still 0
3254 ent = PRVM_EDICT_NUM(0);
3255 memset (ent->fields.server, 0, prog->progs->entityfields * 4);
3256 ent->priv.server->free = false;
3257 ent->fields.server->model = PRVM_SetEngineString(sv.worldname);
3258 ent->fields.server->modelindex = 1; // world model
3259 ent->fields.server->solid = SOLID_BSP;
3260 ent->fields.server->movetype = MOVETYPE_PUSH;
3261 VectorCopy(sv.world.mins, ent->fields.server->mins);
3262 VectorCopy(sv.world.maxs, ent->fields.server->maxs);
3263 VectorCopy(sv.world.mins, ent->fields.server->absmin);
3264 VectorCopy(sv.world.maxs, ent->fields.server->absmax);
3267 prog->globals.server->coop = coop.integer;
3269 prog->globals.server->deathmatch = deathmatch.integer;
3271 prog->globals.server->mapname = PRVM_SetEngineString(sv.name);
3273 // serverflags are for cross level information (sigils)
3274 prog->globals.server->serverflags = svs.serverflags;
3276 // we need to reset the spawned flag on all connected clients here so that
3277 // their thinks don't run during startup (before PutClientInServer)
3278 // we also need to set up the client entities now
3279 // and we need to set the ->edict pointers to point into the progs edicts
3280 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3282 host_client->spawned = false;
3283 host_client->edict = PRVM_EDICT_NUM(i + 1);
3284 PRVM_ED_ClearEdict(host_client->edict);
3287 // load replacement entity file if found
3288 if (sv_entpatch.integer && (entities = (char *)FS_LoadFile(va("%s.ent", sv.worldnamenoextension), tempmempool, true, NULL)))
3290 Con_Printf("Loaded %s.ent\n", sv.worldnamenoextension);
3291 PRVM_ED_LoadFromFile (entities);
3295 PRVM_ED_LoadFromFile (sv.worldmodel->brush.entities);
3298 // LordHavoc: clear world angles (to fix e3m3.bsp)
3299 VectorClear(prog->edicts->fields.server->angles);
3301 // all setup is completed, any further precache statements are errors
3302 // sv.state = ss_active; // LordHavoc: workaround for svc_precache bug
3303 prog->allowworldwrites = false;
3305 // run two frames to allow everything to settle
3306 prog->globals.server->time = sv.time = 1.0001;
3307 for (i = 0;i < 2;i++)
3313 if (cls.state == ca_dedicated)
3316 // create a baseline for more efficient communications
3317 if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
3318 SV_CreateBaseline ();
3320 sv.state = ss_active; // LordHavoc: workaround for svc_precache bug
3322 // to prevent network timeouts
3323 realtime = Sys_DoubleTime();
3325 // send serverinfo to all connected clients, and set up botclients coming back from a level change
3326 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3328 host_client->clientconnectcalled = false; // do NOT call ClientDisconnect if he drops before ClientConnect!
3329 if (!host_client->active)
3331 if (host_client->netconnection)
3332 SV_SendServerinfo(host_client);
3336 // if client is a botclient coming from a level change, we need to
3337 // set up client info that normally requires networking
3339 // copy spawn parms out of the client_t
3340 for (j=0 ; j< NUM_SPAWN_PARMS ; j++)
3341 (&prog->globals.server->parm1)[j] = host_client->spawn_parms[j];
3343 // call the spawn function
3344 host_client->clientconnectcalled = true;
3345 prog->globals.server->time = sv.time;
3346 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
3347 PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
3348 PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
3349 host_client->spawned = true;
3353 // update the map title cvar
3354 strlcpy(sv.worldmessage, PRVM_GetString(prog->edicts->fields.server->message), sizeof(sv.worldmessage)); // map title (not related to filename)
3355 Cvar_SetQuick(&sv_worldmessage, sv.worldmessage);
3357 Con_DPrint("Server spawned.\n");
3358 NetConn_Heartbeat (2);
3363 /////////////////////////////////////////////////////
3366 static void SV_VM_CB_BeginIncreaseEdicts(void)
3368 // links don't survive the transition, so unlink everything
3369 World_UnlinkAll(&sv.world);
3372 static void SV_VM_CB_EndIncreaseEdicts(void)
3377 // link every entity except world
3378 for (i = 1, ent = prog->edicts;i < prog->num_edicts;i++, ent++)
3379 if (!ent->priv.server->free)
3383 static void SV_VM_CB_InitEdict(prvm_edict_t *e)
3385 // LordHavoc: for consistency set these here
3386 int num = PRVM_NUM_FOR_EDICT(e) - 1;
3388 e->priv.server->move = false; // don't move on first frame
3390 if (num >= 0 && num < svs.maxclients)
3393 // set colormap and team on newly created player entity
3394 e->fields.server->colormap = num + 1;
3395 e->fields.server->team = (svs.clients[num].colors & 15) + 1;
3396 // set netname/clientcolors back to client values so that
3397 // DP_SV_CLIENTNAME and DP_SV_CLIENTCOLORS will not immediately
3399 e->fields.server->netname = PRVM_SetEngineString(svs.clients[num].name);
3400 if ((val = PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.clientcolors)))
3401 val->_float = svs.clients[num].colors;
3402 // NEXUIZ_PLAYERMODEL and NEXUIZ_PLAYERSKIN
3403 if( prog->fieldoffsets.playermodel >= 0 )
3404 PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(svs.clients[num].playermodel);
3405 if( prog->fieldoffsets.playerskin >= 0 )
3406 PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(svs.clients[num].playerskin);
3407 // Assign netaddress (IP Address, etc)
3408 if(prog->fieldoffsets.netaddress >= 0)
3409 { // Valid Field; Process
3410 if(svs.clients[num].netconnection != NULL)
3411 {// Valid Address; Assign
3412 // Acquire Readable Address
3413 LHNETADDRESS_ToString(&svs.clients[num].netconnection->peeraddress, svs.clients[num].netaddress, sizeof(svs.clients[num].netaddress), false);
3414 PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.netaddress)->string = PRVM_SetEngineString(svs.clients[num].netaddress);
3418 PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.netaddress)->string = PRVM_SetEngineString("null/botclient");
3423 static void SV_VM_CB_FreeEdict(prvm_edict_t *ed)
3428 World_UnlinkEdict(ed); // unlink from world bsp
3430 ed->fields.server->model = 0;
3431 ed->fields.server->takedamage = 0;
3432 ed->fields.server->modelindex = 0;
3433 ed->fields.server->colormap = 0;
3434 ed->fields.server->skin = 0;
3435 ed->fields.server->frame = 0;
3436 VectorClear(ed->fields.server->origin);
3437 VectorClear(ed->fields.server->angles);
3438 ed->fields.server->nextthink = -1;
3439 ed->fields.server->solid = 0;
3441 VM_RemoveEdictSkeleton(ed);
3442 World_Physics_RemoveFromEntity(&sv.world, ed);
3443 World_Physics_RemoveJointFromEntity(&sv.world, ed);
3445 // make sure csqc networking is aware of the removed entity
3446 e = PRVM_NUM_FOR_EDICT(ed);
3447 sv.csqcentityversion[e] = 0;
3448 for (i = 0;i < svs.maxclients;i++)
3450 if (svs.clients[i].csqcentityscope[e])
3451 svs.clients[i].csqcentityscope[e] = 1; // removed, awaiting send
3452 svs.clients[i].csqcentitysendflags[e] = 0xFFFFFF;
3456 static void SV_VM_CB_CountEdicts(void)
3460 int active, models, solid, step;
3462 active = models = solid = step = 0;
3463 for (i=0 ; i<prog->num_edicts ; i++)
3465 ent = PRVM_EDICT_NUM(i);
3466 if (ent->priv.server->free)
3469 if (ent->fields.server->solid)
3471 if (ent->fields.server->model)
3473 if (ent->fields.server->movetype == MOVETYPE_STEP)
3477 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
3478 Con_Printf("active :%3i\n", active);
3479 Con_Printf("view :%3i\n", models);
3480 Con_Printf("touch :%3i\n", solid);
3481 Con_Printf("step :%3i\n", step);
3484 static qboolean SV_VM_CB_LoadEdict(prvm_edict_t *ent)
3486 // remove things from different skill levels or deathmatch
3487 if (gamemode != GAME_TRANSFUSION) //Transfusion does this in QC
3489 if (deathmatch.integer)
3491 if (((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_DEATHMATCH))
3496 else if ((current_skill <= 0 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_EASY ))
3497 || (current_skill == 1 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_MEDIUM))
3498 || (current_skill >= 2 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_HARD )))
3506 static void SV_VM_Setup(void)
3509 PRVM_InitProg( PRVM_SERVERPROG );
3511 // allocate the mempools
3512 // TODO: move the magic numbers/constants into #defines [9/13/2006 Black]
3513 prog->progs_mempool = Mem_AllocPool("Server Progs", 0, NULL);
3514 prog->builtins = vm_sv_builtins;
3515 prog->numbuiltins = vm_sv_numbuiltins;
3516 prog->headercrc = PROGHEADER_CRC;
3517 prog->headercrc2 = PROGHEADER_CRC_TENEBRAE;
3518 prog->max_edicts = 512;
3519 if (sv.protocol == PROTOCOL_QUAKE)
3520 prog->limit_edicts = 640; // before quake mission pack 1 this was 512
3521 else if (sv.protocol == PROTOCOL_QUAKEDP)
3522 prog->limit_edicts = 2048; // guessing
3523 else if (sv.protocol == PROTOCOL_NEHAHRAMOVIE)
3524 prog->limit_edicts = 2048; // guessing!
3525 else if (sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
3526 prog->limit_edicts = 4096; // guessing!
3528 prog->limit_edicts = MAX_EDICTS;
3529 prog->reserved_edicts = svs.maxclients;
3530 prog->edictprivate_size = sizeof(edict_engineprivate_t);
3531 prog->name = "server";
3532 prog->extensionstring = vm_sv_extensions;
3533 prog->loadintoworld = true;
3535 prog->begin_increase_edicts = SV_VM_CB_BeginIncreaseEdicts;
3536 prog->end_increase_edicts = SV_VM_CB_EndIncreaseEdicts;
3537 prog->init_edict = SV_VM_CB_InitEdict;
3538 prog->free_edict = SV_VM_CB_FreeEdict;
3539 prog->count_edicts = SV_VM_CB_CountEdicts;
3540 prog->load_edict = SV_VM_CB_LoadEdict;
3541 prog->init_cmd = VM_SV_Cmd_Init;
3542 prog->reset_cmd = VM_SV_Cmd_Reset;
3543 prog->error_cmd = Host_Error;
3544 prog->ExecuteProgram = SVVM_ExecuteProgram;
3546 // TODO: add a requiredfuncs list (ask LH if this is necessary at all)
3547 PRVM_LoadProgs( sv_progs.string, 0, NULL, REQFIELDS, reqfields, 0, NULL );
3549 // some mods compiled with scrambling compilers lack certain critical
3550 // global names and field names such as "self" and "time" and "nextthink"
3551 // so we have to set these offsets manually, matching the entvars_t
3552 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, angles);
3553 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, chain);
3554 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, classname);
3555 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, frame);
3556 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, groundentity);
3557 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ideal_yaw);
3558 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, nextthink);
3559 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, think);
3560 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, yaw_speed);
3561 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, self);
3562 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, time);
3563 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_forward);
3564 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_right);
3565 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_up);
3566 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_allsolid);
3567 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_startsolid);
3568 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_fraction);
3569 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inwater);
3570 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inopen);
3571 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_endpos);
3572 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_normal);
3573 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_dist);
3574 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_ent);
3575 // OP_STATE is always supported on server (due to entvars_t)
3576 prog->flag |= PRVM_OP_STATE;
3578 VM_CustomStats_Clear();//[515]: csqc
3585 void SV_VM_Begin(void)
3588 PRVM_SetProg( PRVM_SERVERPROG );
3590 prog->globals.server->time = (float) sv.time;
3593 void SV_VM_End(void)