Add support for the .dpk package format used by Unvanquished
[xonotic/darkplaces.git] / sv_main.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
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.
8
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.
12
13 See the GNU General Public License for more details.
14
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.
18
19 */
20 // sv_main.c -- server main program
21
22 #include "quakedef.h"
23 #include "sv_demo.h"
24 #include "libcurl.h"
25 #include "csprogs.h"
26 #include "thread.h"
27
28 // current client
29 client_t *host_client;
30
31 static void SV_SaveEntFile_f(cmd_state_t *cmd);
32 static void SV_StartDownload_f(cmd_state_t *cmd);
33 static void SV_Download_f(cmd_state_t *cmd);
34 static void SV_VM_Setup(void);
35 extern cvar_t net_connecttimeout;
36
37 cvar_t sv_worldmessage = {CF_SERVER | CF_READONLY, "sv_worldmessage", "", "title of current level"};
38 cvar_t sv_worldname = {CF_SERVER | CF_READONLY, "sv_worldname", "", "name of current worldmodel"};
39 cvar_t sv_worldnamenoextension = {CF_SERVER | CF_READONLY, "sv_worldnamenoextension", "", "name of current worldmodel without extension"};
40 cvar_t sv_worldbasename = {CF_SERVER | CF_READONLY, "sv_worldbasename", "", "name of current worldmodel without maps/ prefix or extension"};
41
42 cvar_t sv_disablenotify = {CF_SERVER, "sv_disablenotify", "0", "suppress broadcast prints when certain cvars are changed (CF_NOTIFY flag in engine code)"};
43 cvar_t coop = {CF_SERVER, "coop","0", "coop mode, 0 = no coop, 1 = coop mode, multiple players playing through the singleplayer game (coop mode also shuts off deathmatch)"};
44 cvar_t deathmatch = {CF_SERVER, "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)"};
45 cvar_t fraglimit = {CF_SERVER | CF_NOTIFY, "fraglimit","0", "ends level if this many frags is reached by any player"};
46 cvar_t gamecfg = {CF_SERVER, "gamecfg", "0", "unused cvar in quake, can be used by mods"};
47 cvar_t noexit = {CF_SERVER | CF_NOTIFY, "noexit","0", "kills anyone attempting to use an exit"};
48 cvar_t nomonsters = {CF_SERVER, "nomonsters", "0", "unused cvar in quake, can be used by mods"};
49 cvar_t pausable = {CF_SERVER, "pausable","1", "allow players to pause or not (otherwise, only the server admin can)"};
50 cvar_t pr_checkextension = {CF_SERVER | CF_READONLY, "pr_checkextension", "1", "indicates to QuakeC that the standard quakec extensions system is available (if 0, quakec should not attempt to use extensions)"};
51 cvar_t samelevel = {CF_SERVER | CF_NOTIFY, "samelevel","0", "repeats same level if level ends (due to timelimit or someone hitting an exit)"};
52 cvar_t skill = {CF_SERVER, "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)"};
53 cvar_t host_timescale = {CF_CLIENT | CF_SERVER, "host_timescale", "1.0", "controls game speed, 0.5 is half speed, 2 is double speed"};
54
55 cvar_t sv_accelerate = {CF_SERVER, "sv_accelerate", "10", "rate at which a player accelerates to sv_maxspeed"};
56 cvar_t sv_aim = {CF_SERVER | CF_ARCHIVE, "sv_aim", "2", "maximum cosine angle for quake's vertical autoaim, a value above 1 completely disables the autoaim, quake used 0.93"};
57 cvar_t sv_airaccel_qw = {CF_SERVER, "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"};
58 cvar_t sv_airaccel_qw_stretchfactor = {CF_SERVER, "sv_airaccel_qw_stretchfactor", "0", "when set, the maximum acceleration increase the player may get compared to forward-acceleration when strafejumping"};
59 cvar_t sv_airaccel_sideways_friction = {CF_SERVER, "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"};
60 cvar_t sv_airaccelerate = {CF_SERVER, "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"};
61 cvar_t sv_airstopaccelerate = {CF_SERVER, "sv_airstopaccelerate", "0", "when set, replacement for sv_airaccelerate when moving backwards"};
62 cvar_t sv_airspeedlimit_nonqw = {CF_SERVER, "sv_airspeedlimit_nonqw", "0", "when set, this is a soft speed limit while in air when using airaccel_qw not equal to 1"};
63 cvar_t sv_airstrafeaccelerate = {CF_SERVER, "sv_airstrafeaccelerate", "0", "when set, replacement for sv_airaccelerate when just strafing"};
64 cvar_t sv_maxairstrafespeed = {CF_SERVER, "sv_maxairstrafespeed", "0", "when set, replacement for sv_maxairspeed when just strafing"};
65 cvar_t sv_airstrafeaccel_qw = {CF_SERVER, "sv_airstrafeaccel_qw", "0", "when set, replacement for sv_airaccel_qw when just strafing"};
66 cvar_t sv_aircontrol = {CF_SERVER, "sv_aircontrol", "0", "CPMA-style air control"};
67 cvar_t sv_aircontrol_power = {CF_SERVER, "sv_aircontrol_power", "2", "CPMA-style air control exponent"};
68 cvar_t sv_aircontrol_penalty = {CF_SERVER, "sv_aircontrol_penalty", "0", "deceleration while using CPMA-style air control"};
69 cvar_t sv_allowdownloads = {CF_SERVER, "sv_allowdownloads", "1", "whether to allow clients to download files from the server (does not affect http downloads)"};
70 cvar_t sv_allowdownloads_archive = {CF_SERVER, "sv_allowdownloads_archive", "0", "whether to allow downloads of archives (pak/pk3)"};
71 cvar_t sv_allowdownloads_config = {CF_SERVER, "sv_allowdownloads_config", "0", "whether to allow downloads of config files (cfg)"};
72 cvar_t sv_allowdownloads_dlcache = {CF_SERVER, "sv_allowdownloads_dlcache", "0", "whether to allow downloads of dlcache files (dlcache/)"};
73 cvar_t sv_allowdownloads_inarchive = {CF_SERVER, "sv_allowdownloads_inarchive", "0", "whether to allow downloads from archives (pak/pk3)"};
74 cvar_t sv_areagrid_mingridsize = {CF_SERVER | CF_NOTIFY, "sv_areagrid_mingridsize", "128", "minimum areagrid cell size, smaller values work better for lots of small objects, higher values for large objects"};
75 cvar_t sv_checkforpacketsduringsleep = {CF_SERVER, "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"};
76 cvar_t sv_clmovement_enable = {CF_SERVER, "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"};
77 cvar_t sv_clmovement_minping = {CF_SERVER, "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)"};
78 cvar_t sv_clmovement_minping_disabletime = {CF_SERVER, "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)"};
79 cvar_t sv_clmovement_inputtimeout = {CF_SERVER, "sv_clmovement_inputtimeout", "0.1", "when a client does not send input for this many seconds (max 0.1), force them to move anyway (unlike QuakeWorld)"};
80 cvar_t sv_cullentities_nevercullbmodels = {CF_SERVER, "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!)"};
81 cvar_t sv_cullentities_pvs = {CF_SERVER, "sv_cullentities_pvs", "1", "fast but loose culling of hidden entities"};
82 cvar_t sv_cullentities_stats = {CF_SERVER, "sv_cullentities_stats", "0", "displays stats on network entities culled by various methods for each client"};
83 cvar_t sv_cullentities_trace = {CF_SERVER, "sv_cullentities_trace", "0", "somewhat slow but very tight culling of hidden entities, minimizes network traffic and makes wallhack cheats useless"};
84 cvar_t sv_cullentities_trace_delay = {CF_SERVER, "sv_cullentities_trace_delay", "1", "number of seconds until the entity gets actually culled"};
85 cvar_t sv_cullentities_trace_delay_players = {CF_SERVER, "sv_cullentities_trace_delay_players", "0.2", "number of seconds until the entity gets actually culled if it is a player entity"};
86 cvar_t sv_cullentities_trace_enlarge = {CF_SERVER, "sv_cullentities_trace_enlarge", "0", "box enlargement for entity culling"};
87 cvar_t sv_cullentities_trace_expand = {CF_SERVER, "sv_cullentities_trace_expand", "0", "box is expanded by this many units for entity culling"};
88 cvar_t sv_cullentities_trace_eyejitter = {CF_SERVER, "sv_cullentities_trace_eyejitter", "16", "jitter the eye by this much for each trace"};
89 cvar_t sv_cullentities_trace_prediction = {CF_SERVER, "sv_cullentities_trace_prediction", "1", "also trace from the predicted player position"};
90 cvar_t sv_cullentities_trace_prediction_time = {CF_SERVER, "sv_cullentities_trace_prediction_time", "0.2", "how many seconds of prediction to use"};
91 cvar_t sv_cullentities_trace_entityocclusion = {CF_SERVER, "sv_cullentities_trace_entityocclusion", "0", "also check if doors and other bsp models are in the way"};
92 cvar_t sv_cullentities_trace_samples = {CF_SERVER, "sv_cullentities_trace_samples", "2", "number of samples to test for entity culling"};
93 cvar_t sv_cullentities_trace_samples_extra = {CF_SERVER, "sv_cullentities_trace_samples_extra", "2", "number of samples to test for entity culling when the entity affects its surroundings by e.g. dlight"};
94 cvar_t sv_cullentities_trace_samples_players = {CF_SERVER, "sv_cullentities_trace_samples_players", "8", "number of samples to test for entity culling when the entity is a player entity"};
95 cvar_t sv_debugmove = {CF_SERVER | CF_NOTIFY, "sv_debugmove", "0", "disables collision detection optimizations for debugging purposes"};
96 cvar_t sv_echobprint = {CF_SERVER | CF_ARCHIVE, "sv_echobprint", "1", "prints gamecode bprint() calls to server console"};
97 cvar_t sv_edgefriction = {CF_SERVER, "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)"};
98 cvar_t sv_entpatch = {CF_SERVER, "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)"};
99 cvar_t sv_freezenonclients = {CF_SERVER | CF_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
100 cvar_t sv_friction = {CF_SERVER | CF_NOTIFY, "sv_friction","4", "how fast you slow down"};
101 cvar_t sv_gameplayfix_blowupfallenzombies = {CF_SERVER, "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"};
102 cvar_t sv_gameplayfix_consistentplayerprethink = {CF_SERVER, "sv_gameplayfix_consistentplayerprethink", "0", "improves fairness in multiplayer by running all PlayerPreThink functions (which fire weapons) before performing physics, then running all PlayerPostThink functions"};
103 cvar_t sv_gameplayfix_delayprojectiles = {CF_SERVER, "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"};
104 cvar_t sv_gameplayfix_droptofloorstartsolid = {CF_SERVER, "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)"};
105 cvar_t sv_gameplayfix_droptofloorstartsolid_nudgetocorrect = {CF_SERVER, "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect", "1", "tries to nudge stuck items and monsters out of walls before droptofloor is performed"};
106 cvar_t sv_gameplayfix_easierwaterjump = {CF_SERVER, "sv_gameplayfix_easierwaterjump", "1", "changes water jumping to make it easier to get out of water (exactly like in QuakeWorld)"};
107 cvar_t sv_gameplayfix_findradiusdistancetobox = {CF_SERVER, "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"};
108 cvar_t sv_gameplayfix_gravityunaffectedbyticrate = {CF_SERVER, "sv_gameplayfix_gravityunaffectedbyticrate", "0", "fix some ticrate issues in physics."};
109 cvar_t sv_gameplayfix_grenadebouncedownslopes = {CF_SERVER, "sv_gameplayfix_grenadebouncedownslopes", "1", "prevents MOVETYPE_BOUNCE (grenades) from getting stuck when fired down a downward sloping surface"};
110 cvar_t sv_gameplayfix_multiplethinksperframe = {CF_SERVER, "sv_gameplayfix_multiplethinksperframe", "1", "allows entities to think more often than the server framerate, primarily useful for very high fire rate weapons"};
111 cvar_t sv_gameplayfix_noairborncorpse = {CF_SERVER, "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"};
112 cvar_t sv_gameplayfix_noairborncorpse_allowsuspendeditems = {CF_SERVER, "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)"};
113 cvar_t sv_gameplayfix_nudgeoutofsolid = {CF_SERVER, "sv_gameplayfix_nudgeoutofsolid", "0", "attempts to fix physics errors (where an object ended up in solid for some reason)"};
114 cvar_t sv_gameplayfix_nudgeoutofsolid_separation = {CF_SERVER, "sv_gameplayfix_nudgeoutofsolid_separation", "0.03125", "keep objects this distance apart to prevent collision issues on seams"};
115 cvar_t sv_gameplayfix_q2airaccelerate = {CF_SERVER, "sv_gameplayfix_q2airaccelerate", "0", "Quake2-style air acceleration"};
116 cvar_t sv_gameplayfix_nogravityonground = {CF_SERVER, "sv_gameplayfix_nogravityonground", "0", "turn off gravity when on ground (to get rid of sliding)"};
117 cvar_t sv_gameplayfix_setmodelrealbox = {CF_SERVER, "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"};
118 cvar_t sv_gameplayfix_slidemoveprojectiles = {CF_SERVER, "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"};
119 cvar_t sv_gameplayfix_stepdown = {CF_SERVER, "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)"};
120 cvar_t sv_gameplayfix_stepmultipletimes = {CF_SERVER, "sv_gameplayfix_stepmultipletimes", "0", "applies step-up onto a ledge more than once in a single frame, when running quickly up stairs"};
121 cvar_t sv_gameplayfix_nostepmoveonsteepslopes = {CF_SERVER, "sv_gameplayfix_nostepmoveonsteepslopes", "0", "crude fix which prevents MOVETYPE_STEP (not swimming or flying) to move on slopes whose angle is bigger than 45 degree"};
122 cvar_t sv_gameplayfix_swiminbmodels = {CF_SERVER, "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"};
123 cvar_t sv_gameplayfix_upwardvelocityclearsongroundflag = {CF_SERVER, "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"};
124 cvar_t sv_gameplayfix_downtracesupportsongroundflag = {CF_SERVER, "sv_gameplayfix_downtracesupportsongroundflag", "1", "prevents very short moves from clearing onground (which may make the player stick to the floor at high netfps)"};
125 cvar_t sv_gameplayfix_q1bsptracelinereportstexture = {CF_SERVER, "sv_gameplayfix_q1bsptracelinereportstexture", "1", "enables mods to get accurate trace_texture results on q1bsp by using a surface-hitting traceline implementation rather than the standard solidbsp method, q3bsp always reports texture accurately"};
126 cvar_t sv_gameplayfix_unstickplayers = {CF_SERVER, "sv_gameplayfix_unstickplayers", "1", "big hack to try and fix the rare case of MOVETYPE_WALK entities getting stuck in the world clipping hull."};
127 cvar_t sv_gameplayfix_unstickentities = {CF_SERVER, "sv_gameplayfix_unstickentities", "1", "hack to check if entities are crossing world collision hull and try to move them to the right position"};
128 cvar_t sv_gameplayfix_fixedcheckwatertransition = {CF_SERVER, "sv_gameplayfix_fixedcheckwatertransition", "1", "fix two very stupid bugs in SV_CheckWaterTransition when watertype is CONTENTS_EMPTY (the bugs causes waterlevel to be 1 on first frame, -1 on second frame - the fix makes it 0 on both frames)"};
129 cvar_t sv_gameplayfix_customstats = {CF_SERVER, "sv_gameplayfix_customstats", "0", "Disable stats higher than 220, for use by certain games such as Xonotic"};
130 cvar_t sv_gravity = {CF_SERVER | CF_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
131 cvar_t sv_init_frame_count = {CF_SERVER, "sv_init_frame_count", "2", "number of frames to run to allow everything to settle before letting clients connect"};
132 cvar_t sv_idealpitchscale = {CF_SERVER, "sv_idealpitchscale","0.8", "how much to look up/down slopes and stairs when not using freelook"};
133 cvar_t sv_jumpstep = {CF_SERVER | CF_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping"};
134 cvar_t sv_jumpvelocity = {CF_SERVER, "sv_jumpvelocity", "270", "cvar that can be used by QuakeC code for jump velocity"};
135 cvar_t sv_maxairspeed = {CF_SERVER, "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)"};
136 cvar_t sv_maxrate = {CF_SERVER | CF_ARCHIVE | CF_NOTIFY, "sv_maxrate", "1000000", "upper limit on client rate cvar, should reflect your network connection quality"};
137 cvar_t sv_maxspeed = {CF_SERVER | CF_NOTIFY, "sv_maxspeed", "320", "maximum speed a player can accelerate to when on ground (can be exceeded by tricks)"};
138 cvar_t sv_maxvelocity = {CF_SERVER | CF_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
139 cvar_t sv_nostep = {CF_SERVER | CF_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
140 cvar_t sv_playerphysicsqc = {CF_SERVER | CF_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
141 cvar_t sv_progs = {CF_SERVER, "sv_progs", "progs.dat", "selects which quakec progs.dat file to run" };
142 cvar_t sv_protocolname = {CF_SERVER, "sv_protocolname", "DP7", "selects network protocol to host for (values include QUAKE, QUAKEDP, NEHAHRAMOVIE, DP1 and up)"};
143 cvar_t sv_random_seed = {CF_SERVER, "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"};
144 cvar_t host_limitlocal = {CF_SERVER, "host_limitlocal", "0", "whether to apply rate limiting to the local player in a listen server (only useful for testing)"};
145 cvar_t sv_sound_land = {CF_SERVER, "sv_sound_land", "demon/dland2.wav", "sound to play when MOVETYPE_STEP entity hits the ground at high speed (empty cvar disables the sound)"};
146 cvar_t sv_sound_watersplash = {CF_SERVER, "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)"};
147 cvar_t sv_stepheight = {CF_SERVER | CF_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
148 cvar_t sv_stopspeed = {CF_SERVER | CF_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
149 cvar_t sv_wallfriction = {CF_SERVER | CF_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
150 cvar_t sv_wateraccelerate = {CF_SERVER, "sv_wateraccelerate", "-1", "rate at which a player accelerates to sv_maxspeed while in water, if less than 0 the sv_accelerate variable is used instead"};
151 cvar_t sv_waterfriction = {CF_SERVER | CF_NOTIFY, "sv_waterfriction","-1", "how fast you slow down in water, if less than 0 the sv_friction variable is used instead"};
152 cvar_t sv_warsowbunny_airforwardaccel = {CF_SERVER, "sv_warsowbunny_airforwardaccel", "1.00001", "how fast you accelerate until you reach sv_maxspeed"};
153 cvar_t sv_warsowbunny_accel = {CF_SERVER, "sv_warsowbunny_accel", "0.1585", "how fast you accelerate until after reaching sv_maxspeed (it gets harder as you near sv_warsowbunny_topspeed)"};
154 cvar_t sv_warsowbunny_topspeed = {CF_SERVER, "sv_warsowbunny_topspeed", "925", "soft speed limit (can get faster with rjs and on ramps)"};
155 cvar_t sv_warsowbunny_turnaccel = {CF_SERVER, "sv_warsowbunny_turnaccel", "0", "max sharpness of turns (also master switch for the sv_warsowbunny_* mode; set this to 9 to enable)"};
156 cvar_t sv_warsowbunny_backtosideratio = {CF_SERVER, "sv_warsowbunny_backtosideratio", "0.8", "lower values make it easier to change direction without losing speed; the drawback is \"understeering\" in sharp turns"};
157 cvar_t sv_onlycsqcnetworking = {CF_SERVER, "sv_onlycsqcnetworking", "0", "disables legacy entity networking code for higher performance (except on clients, which can still be legacy)"};
158 cvar_t sv_areadebug = {CF_SERVER, "sv_areadebug", "0", "disables physics culling for debugging purposes (only for development)"};
159 cvar_t sys_ticrate = {CF_SERVER | CF_ARCHIVE, "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"};
160 cvar_t teamplay = {CF_SERVER | CF_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"};
161 cvar_t timelimit = {CF_SERVER | CF_NOTIFY, "timelimit","0", "ends level at this time (in minutes)"};
162 cvar_t sv_threaded = {CF_SERVER, "sv_threaded", "0", "enables a separate thread for server code, improving performance, especially when hosting a game while playing, EXPERIMENTAL, may be crashy"};
163
164 cvar_t sv_rollspeed = {CF_CLIENT, "sv_rollspeed", "200", "how much strafing is necessary to tilt the view"};
165 cvar_t sv_rollangle = {CF_CLIENT, "sv_rollangle", "2.0", "how much to tilt the view when strafing"};
166
167 cvar_t saved1 = {CF_SERVER | CF_ARCHIVE, "saved1", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
168 cvar_t saved2 = {CF_SERVER | CF_ARCHIVE, "saved2", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
169 cvar_t saved3 = {CF_SERVER | CF_ARCHIVE, "saved3", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
170 cvar_t saved4 = {CF_SERVER | CF_ARCHIVE, "saved4", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
171 cvar_t savedgamecfg = {CF_SERVER | CF_ARCHIVE, "savedgamecfg", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"};
172 cvar_t scratch1 = {CF_SERVER, "scratch1", "0", "unused cvar in quake, can be used by mods"};
173 cvar_t scratch2 = {CF_SERVER,"scratch2", "0", "unused cvar in quake, can be used by mods"};
174 cvar_t scratch3 = {CF_SERVER, "scratch3", "0", "unused cvar in quake, can be used by mods"};
175 cvar_t scratch4 = {CF_SERVER, "scratch4", "0", "unused cvar in quake, can be used by mods"};
176 cvar_t temp1 = {CF_SERVER, "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)"};
177
178 cvar_t nehx00 = {CF_SERVER, "nehx00", "0", "nehahra data storage cvar (used in singleplayer)"};
179 cvar_t nehx01 = {CF_SERVER, "nehx01", "0", "nehahra data storage cvar (used in singleplayer)"};
180 cvar_t nehx02 = {CF_SERVER, "nehx02", "0", "nehahra data storage cvar (used in singleplayer)"};
181 cvar_t nehx03 = {CF_SERVER, "nehx03", "0", "nehahra data storage cvar (used in singleplayer)"};
182 cvar_t nehx04 = {CF_SERVER, "nehx04", "0", "nehahra data storage cvar (used in singleplayer)"};
183 cvar_t nehx05 = {CF_SERVER, "nehx05", "0", "nehahra data storage cvar (used in singleplayer)"};
184 cvar_t nehx06 = {CF_SERVER, "nehx06", "0", "nehahra data storage cvar (used in singleplayer)"};
185 cvar_t nehx07 = {CF_SERVER, "nehx07", "0", "nehahra data storage cvar (used in singleplayer)"};
186 cvar_t nehx08 = {CF_SERVER, "nehx08", "0", "nehahra data storage cvar (used in singleplayer)"};
187 cvar_t nehx09 = {CF_SERVER, "nehx09", "0", "nehahra data storage cvar (used in singleplayer)"};
188 cvar_t nehx10 = {CF_SERVER, "nehx10", "0", "nehahra data storage cvar (used in singleplayer)"};
189 cvar_t nehx11 = {CF_SERVER, "nehx11", "0", "nehahra data storage cvar (used in singleplayer)"};
190 cvar_t nehx12 = {CF_SERVER, "nehx12", "0", "nehahra data storage cvar (used in singleplayer)"};
191 cvar_t nehx13 = {CF_SERVER, "nehx13", "0", "nehahra data storage cvar (used in singleplayer)"};
192 cvar_t nehx14 = {CF_SERVER, "nehx14", "0", "nehahra data storage cvar (used in singleplayer)"};
193 cvar_t nehx15 = {CF_SERVER, "nehx15", "0", "nehahra data storage cvar (used in singleplayer)"};
194 cvar_t nehx16 = {CF_SERVER, "nehx16", "0", "nehahra data storage cvar (used in singleplayer)"};
195 cvar_t nehx17 = {CF_SERVER, "nehx17", "0", "nehahra data storage cvar (used in singleplayer)"};
196 cvar_t nehx18 = {CF_SERVER, "nehx18", "0", "nehahra data storage cvar (used in singleplayer)"};
197 cvar_t nehx19 = {CF_SERVER, "nehx19", "0", "nehahra data storage cvar (used in singleplayer)"};
198 cvar_t cutscene = {CF_SERVER, "cutscene", "1", "enables cutscenes in nehahra, can be used by other mods"};
199
200 cvar_t sv_autodemo_perclient = {CF_SERVER | CF_ARCHIVE, "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)"};
201 cvar_t sv_autodemo_perclient_nameformat = {CF_SERVER | CF_ARCHIVE, "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)" };
202 cvar_t sv_autodemo_perclient_discardable = {CF_SERVER | CF_ARCHIVE, "sv_autodemo_perclient_discardable", "0", "Allow game code to decide whether a demo should be kept or discarded."};
203
204 cvar_t halflifebsp = {CF_SERVER, "halflifebsp", "0", "indicates the current map is hlbsp format (useful to know because of different bounding box sizes)"};
205 cvar_t sv_mapformat_is_quake2 = {CF_SERVER, "sv_mapformat_is_quake2", "0", "indicates the current map is q2bsp format (useful to know because of different entity behaviors, .frame on submodels and other things)"};
206 cvar_t sv_mapformat_is_quake3 = {CF_SERVER, "sv_mapformat_is_quake3", "0", "indicates the current map is q2bsp format (useful to know because of different entity behaviors)"};
207
208 cvar_t sv_writepicture_quality = {CF_SERVER | CF_ARCHIVE, "sv_writepicture_quality", "10", "WritePicture quality offset (higher means better quality, but slower)"};
209
210 server_t sv;
211 server_static_t svs;
212
213 mempool_t *sv_mempool = NULL;
214
215 extern cvar_t host_timescale;
216 extern float            scr_centertime_off;
217
218 // MUST match effectnameindex_t in client.h
219 static const char *standardeffectnames[EFFECT_TOTAL] =
220 {
221         "",
222         "TE_GUNSHOT",
223         "TE_GUNSHOTQUAD",
224         "TE_SPIKE",
225         "TE_SPIKEQUAD",
226         "TE_SUPERSPIKE",
227         "TE_SUPERSPIKEQUAD",
228         "TE_WIZSPIKE",
229         "TE_KNIGHTSPIKE",
230         "TE_EXPLOSION",
231         "TE_EXPLOSIONQUAD",
232         "TE_TAREXPLOSION",
233         "TE_TELEPORT",
234         "TE_LAVASPLASH",
235         "TE_SMALLFLASH",
236         "TE_FLAMEJET",
237         "EF_FLAME",
238         "TE_BLOOD",
239         "TE_SPARK",
240         "TE_PLASMABURN",
241         "TE_TEI_G3",
242         "TE_TEI_SMOKE",
243         "TE_TEI_BIGEXPLOSION",
244         "TE_TEI_PLASMAHIT",
245         "EF_STARDUST",
246         "TR_ROCKET",
247         "TR_GRENADE",
248         "TR_BLOOD",
249         "TR_WIZSPIKE",
250         "TR_SLIGHTBLOOD",
251         "TR_KNIGHTSPIKE",
252         "TR_VORESPIKE",
253         "TR_NEHAHRASMOKE",
254         "TR_NEXUIZPLASMA",
255         "TR_GLOWTRAIL",
256         "SVC_PARTICLE"
257 };
258
259 #define SV_REQFUNCS 0
260 #define sv_reqfuncs NULL
261
262 //#define SV_REQFUNCS (sizeof(sv_reqfuncs) / sizeof(const char *))
263 //static const char *sv_reqfuncs[] = {
264 //};
265
266 #define SV_REQFIELDS (sizeof(sv_reqfields) / sizeof(prvm_required_field_t))
267
268 prvm_required_field_t sv_reqfields[] =
269 {
270 #define PRVM_DECLARE_serverglobalfloat(x)
271 #define PRVM_DECLARE_serverglobalvector(x)
272 #define PRVM_DECLARE_serverglobalstring(x)
273 #define PRVM_DECLARE_serverglobaledict(x)
274 #define PRVM_DECLARE_serverglobalfunction(x)
275 #define PRVM_DECLARE_clientglobalfloat(x)
276 #define PRVM_DECLARE_clientglobalvector(x)
277 #define PRVM_DECLARE_clientglobalstring(x)
278 #define PRVM_DECLARE_clientglobaledict(x)
279 #define PRVM_DECLARE_clientglobalfunction(x)
280 #define PRVM_DECLARE_menuglobalfloat(x)
281 #define PRVM_DECLARE_menuglobalvector(x)
282 #define PRVM_DECLARE_menuglobalstring(x)
283 #define PRVM_DECLARE_menuglobaledict(x)
284 #define PRVM_DECLARE_menuglobalfunction(x)
285 #define PRVM_DECLARE_serverfieldfloat(x) {ev_float, #x},
286 #define PRVM_DECLARE_serverfieldvector(x) {ev_vector, #x},
287 #define PRVM_DECLARE_serverfieldstring(x) {ev_string, #x},
288 #define PRVM_DECLARE_serverfieldedict(x) {ev_entity, #x},
289 #define PRVM_DECLARE_serverfieldfunction(x) {ev_function, #x},
290 #define PRVM_DECLARE_clientfieldfloat(x)
291 #define PRVM_DECLARE_clientfieldvector(x)
292 #define PRVM_DECLARE_clientfieldstring(x)
293 #define PRVM_DECLARE_clientfieldedict(x)
294 #define PRVM_DECLARE_clientfieldfunction(x)
295 #define PRVM_DECLARE_menufieldfloat(x)
296 #define PRVM_DECLARE_menufieldvector(x)
297 #define PRVM_DECLARE_menufieldstring(x)
298 #define PRVM_DECLARE_menufieldedict(x)
299 #define PRVM_DECLARE_menufieldfunction(x)
300 #define PRVM_DECLARE_serverfunction(x)
301 #define PRVM_DECLARE_clientfunction(x)
302 #define PRVM_DECLARE_menufunction(x)
303 #define PRVM_DECLARE_field(x)
304 #define PRVM_DECLARE_global(x)
305 #define PRVM_DECLARE_function(x)
306 #include "prvm_offsets.h"
307 #undef PRVM_DECLARE_serverglobalfloat
308 #undef PRVM_DECLARE_serverglobalvector
309 #undef PRVM_DECLARE_serverglobalstring
310 #undef PRVM_DECLARE_serverglobaledict
311 #undef PRVM_DECLARE_serverglobalfunction
312 #undef PRVM_DECLARE_clientglobalfloat
313 #undef PRVM_DECLARE_clientglobalvector
314 #undef PRVM_DECLARE_clientglobalstring
315 #undef PRVM_DECLARE_clientglobaledict
316 #undef PRVM_DECLARE_clientglobalfunction
317 #undef PRVM_DECLARE_menuglobalfloat
318 #undef PRVM_DECLARE_menuglobalvector
319 #undef PRVM_DECLARE_menuglobalstring
320 #undef PRVM_DECLARE_menuglobaledict
321 #undef PRVM_DECLARE_menuglobalfunction
322 #undef PRVM_DECLARE_serverfieldfloat
323 #undef PRVM_DECLARE_serverfieldvector
324 #undef PRVM_DECLARE_serverfieldstring
325 #undef PRVM_DECLARE_serverfieldedict
326 #undef PRVM_DECLARE_serverfieldfunction
327 #undef PRVM_DECLARE_clientfieldfloat
328 #undef PRVM_DECLARE_clientfieldvector
329 #undef PRVM_DECLARE_clientfieldstring
330 #undef PRVM_DECLARE_clientfieldedict
331 #undef PRVM_DECLARE_clientfieldfunction
332 #undef PRVM_DECLARE_menufieldfloat
333 #undef PRVM_DECLARE_menufieldvector
334 #undef PRVM_DECLARE_menufieldstring
335 #undef PRVM_DECLARE_menufieldedict
336 #undef PRVM_DECLARE_menufieldfunction
337 #undef PRVM_DECLARE_serverfunction
338 #undef PRVM_DECLARE_clientfunction
339 #undef PRVM_DECLARE_menufunction
340 #undef PRVM_DECLARE_field
341 #undef PRVM_DECLARE_global
342 #undef PRVM_DECLARE_function
343 };
344
345 #define SV_REQGLOBALS (sizeof(sv_reqglobals) / sizeof(prvm_required_field_t))
346
347 prvm_required_field_t sv_reqglobals[] =
348 {
349 #define PRVM_DECLARE_serverglobalfloat(x) {ev_float, #x},
350 #define PRVM_DECLARE_serverglobalvector(x) {ev_vector, #x},
351 #define PRVM_DECLARE_serverglobalstring(x) {ev_string, #x},
352 #define PRVM_DECLARE_serverglobaledict(x) {ev_entity, #x},
353 #define PRVM_DECLARE_serverglobalfunction(x) {ev_function, #x},
354 #define PRVM_DECLARE_clientglobalfloat(x)
355 #define PRVM_DECLARE_clientglobalvector(x)
356 #define PRVM_DECLARE_clientglobalstring(x)
357 #define PRVM_DECLARE_clientglobaledict(x)
358 #define PRVM_DECLARE_clientglobalfunction(x)
359 #define PRVM_DECLARE_menuglobalfloat(x)
360 #define PRVM_DECLARE_menuglobalvector(x)
361 #define PRVM_DECLARE_menuglobalstring(x)
362 #define PRVM_DECLARE_menuglobaledict(x)
363 #define PRVM_DECLARE_menuglobalfunction(x)
364 #define PRVM_DECLARE_serverfieldfloat(x)
365 #define PRVM_DECLARE_serverfieldvector(x)
366 #define PRVM_DECLARE_serverfieldstring(x)
367 #define PRVM_DECLARE_serverfieldedict(x)
368 #define PRVM_DECLARE_serverfieldfunction(x)
369 #define PRVM_DECLARE_clientfieldfloat(x)
370 #define PRVM_DECLARE_clientfieldvector(x)
371 #define PRVM_DECLARE_clientfieldstring(x)
372 #define PRVM_DECLARE_clientfieldedict(x)
373 #define PRVM_DECLARE_clientfieldfunction(x)
374 #define PRVM_DECLARE_menufieldfloat(x)
375 #define PRVM_DECLARE_menufieldvector(x)
376 #define PRVM_DECLARE_menufieldstring(x)
377 #define PRVM_DECLARE_menufieldedict(x)
378 #define PRVM_DECLARE_menufieldfunction(x)
379 #define PRVM_DECLARE_serverfunction(x)
380 #define PRVM_DECLARE_clientfunction(x)
381 #define PRVM_DECLARE_menufunction(x)
382 #define PRVM_DECLARE_field(x)
383 #define PRVM_DECLARE_global(x)
384 #define PRVM_DECLARE_function(x)
385 #include "prvm_offsets.h"
386 #undef PRVM_DECLARE_serverglobalfloat
387 #undef PRVM_DECLARE_serverglobalvector
388 #undef PRVM_DECLARE_serverglobalstring
389 #undef PRVM_DECLARE_serverglobaledict
390 #undef PRVM_DECLARE_serverglobalfunction
391 #undef PRVM_DECLARE_clientglobalfloat
392 #undef PRVM_DECLARE_clientglobalvector
393 #undef PRVM_DECLARE_clientglobalstring
394 #undef PRVM_DECLARE_clientglobaledict
395 #undef PRVM_DECLARE_clientglobalfunction
396 #undef PRVM_DECLARE_menuglobalfloat
397 #undef PRVM_DECLARE_menuglobalvector
398 #undef PRVM_DECLARE_menuglobalstring
399 #undef PRVM_DECLARE_menuglobaledict
400 #undef PRVM_DECLARE_menuglobalfunction
401 #undef PRVM_DECLARE_serverfieldfloat
402 #undef PRVM_DECLARE_serverfieldvector
403 #undef PRVM_DECLARE_serverfieldstring
404 #undef PRVM_DECLARE_serverfieldedict
405 #undef PRVM_DECLARE_serverfieldfunction
406 #undef PRVM_DECLARE_clientfieldfloat
407 #undef PRVM_DECLARE_clientfieldvector
408 #undef PRVM_DECLARE_clientfieldstring
409 #undef PRVM_DECLARE_clientfieldedict
410 #undef PRVM_DECLARE_clientfieldfunction
411 #undef PRVM_DECLARE_menufieldfloat
412 #undef PRVM_DECLARE_menufieldvector
413 #undef PRVM_DECLARE_menufieldstring
414 #undef PRVM_DECLARE_menufieldedict
415 #undef PRVM_DECLARE_menufieldfunction
416 #undef PRVM_DECLARE_serverfunction
417 #undef PRVM_DECLARE_clientfunction
418 #undef PRVM_DECLARE_menufunction
419 #undef PRVM_DECLARE_field
420 #undef PRVM_DECLARE_global
421 #undef PRVM_DECLARE_function
422 };
423
424 static void Host_Timescale_c(cvar_t *var)
425 {
426         if(var->value < 0.00001 && var->value != 0)
427                 Cvar_SetValueQuick(var, 0);
428 }
429
430 //============================================================================
431
432 static void SV_AreaStats_f(cmd_state_t *cmd)
433 {
434         World_PrintAreaStats(&sv.world, "server");
435 }
436
437 static void SV_ServerOptions (void)
438 {
439         int i;
440
441         // general default
442         svs.maxclients = 8;
443
444 // COMMANDLINEOPTION: Server: -dedicated [playerlimit] starts a dedicated server (with a command console), default playerlimit is 8
445 // COMMANDLINEOPTION: Server: -listen [playerlimit] starts a multiplayer server with graphical client, like singleplayer but other players can connect, default playerlimit is 8
446         // if no client is in the executable or -dedicated is specified on
447         // commandline, start a dedicated server
448         i = Sys_CheckParm ("-dedicated");
449         if (i || !cl_available)
450         {
451                 cls.state = ca_dedicated;
452                 // check for -dedicated specifying how many players
453                 if (i && i + 1 < sys.argc && atoi (sys.argv[i+1]) >= 1)
454                         svs.maxclients = atoi (sys.argv[i+1]);
455                 if (Sys_CheckParm ("-listen"))
456                         Con_Printf ("Only one of -dedicated or -listen can be specified\n");
457                 // default sv_public on for dedicated servers (often hosted by serious administrators), off for listen servers (often hosted by clueless users)
458                 Cvar_SetValue(&cvars_all, "sv_public", 1);
459         }
460         else if (cl_available)
461         {
462                 // client exists and not dedicated, check if -listen is specified
463                 cls.state = ca_disconnected;
464                 i = Sys_CheckParm ("-listen");
465                 if (i)
466                 {
467                         // default players unless specified
468                         if (i + 1 < sys.argc && atoi (sys.argv[i+1]) >= 1)
469                                 svs.maxclients = atoi (sys.argv[i+1]);
470                 }
471                 else
472                 {
473                         // default players in some games, singleplayer in most
474                         if (gamemode != GAME_GOODVSBAD2 && !IS_NEXUIZ_DERIVED(gamemode) && gamemode != GAME_BATTLEMECH)
475                                 svs.maxclients = 1;
476                 }
477         }
478
479         svs.maxclients = svs.maxclients_next = bound(1, svs.maxclients, MAX_SCOREBOARD);
480
481         svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
482
483         if (svs.maxclients > 1 && !deathmatch.integer && !coop.integer)
484                 Cvar_SetValueQuick(&deathmatch, 1);
485 }
486
487 /*
488 ===============
489 SV_Init
490 ===============
491 */
492 void SV_Init (void)
493 {
494         // init the csqc progs cvars, since they are updated/used by the server code
495         // TODO: fix this since this is a quick hack to make some of [515]'s broken code run ;) [9/13/2006 Black]
496         extern cvar_t csqc_progname;    //[515]: csqc crc check and right csprogs name according to progs.dat
497         extern cvar_t csqc_progcrc;
498         extern cvar_t csqc_progsize;
499         extern cvar_t csqc_usedemoprogs;
500
501         Cvar_RegisterVariable(&sv_worldmessage);
502         Cvar_RegisterVariable(&sv_worldname);
503         Cvar_RegisterVariable(&sv_worldnamenoextension);
504         Cvar_RegisterVariable(&sv_worldbasename);
505
506         Cvar_RegisterVariable (&csqc_progname);
507         Cvar_RegisterVariable (&csqc_progcrc);
508         Cvar_RegisterVariable (&csqc_progsize);
509         Cvar_RegisterVariable (&csqc_usedemoprogs);
510
511         Cmd_AddCommand(CF_SHARED, "sv_saveentfile", SV_SaveEntFile_f, "save map entities to .ent file (to allow external editing)");
512         Cmd_AddCommand(CF_SHARED, "sv_areastats", SV_AreaStats_f, "prints statistics on entity culling during collision traces");
513         Cmd_AddCommand(CF_CLIENT | CF_SERVER_FROM_CLIENT, "sv_startdownload", SV_StartDownload_f, "begins sending a file to the client (network protocol use only)");
514         Cmd_AddCommand(CF_CLIENT | CF_SERVER_FROM_CLIENT, "download", SV_Download_f, "downloads a specified file from the server");
515
516         Cvar_RegisterVariable (&sv_disablenotify);
517         Cvar_RegisterVariable (&coop);
518         Cvar_RegisterVariable (&deathmatch);
519         Cvar_RegisterVariable (&fraglimit);
520         Cvar_RegisterVariable (&gamecfg);
521         Cvar_RegisterVariable (&noexit);
522         Cvar_RegisterVariable (&nomonsters);
523         Cvar_RegisterVariable (&pausable);
524         Cvar_RegisterVariable (&pr_checkextension);
525         Cvar_RegisterVariable (&samelevel);
526         Cvar_RegisterVariable (&skill);
527         Cvar_RegisterVariable (&host_timescale);
528         Cvar_RegisterCallback (&host_timescale, Host_Timescale_c);
529         Cvar_RegisterAlias (&host_timescale, "slowmo");
530         Cvar_RegisterAlias (&host_timescale, "timescale");
531         Cvar_RegisterVariable (&sv_accelerate);
532         Cvar_RegisterVariable (&sv_aim);
533         Cvar_RegisterVariable (&sv_airaccel_qw);
534         Cvar_RegisterVariable (&sv_airaccel_qw_stretchfactor);
535         Cvar_RegisterVariable (&sv_airaccel_sideways_friction);
536         Cvar_RegisterVariable (&sv_airaccelerate);
537         Cvar_RegisterVariable (&sv_airstopaccelerate);
538         Cvar_RegisterVariable (&sv_airstrafeaccelerate);
539         Cvar_RegisterVariable (&sv_maxairstrafespeed);
540         Cvar_RegisterVariable (&sv_airstrafeaccel_qw);
541         Cvar_RegisterVariable (&sv_airspeedlimit_nonqw);
542         Cvar_RegisterVariable (&sv_aircontrol);
543         Cvar_RegisterVariable (&sv_aircontrol_power);
544         Cvar_RegisterVariable (&sv_aircontrol_penalty);
545         Cvar_RegisterVariable (&sv_allowdownloads);
546         Cvar_RegisterVariable (&sv_allowdownloads_archive);
547         Cvar_RegisterVariable (&sv_allowdownloads_config);
548         Cvar_RegisterVariable (&sv_allowdownloads_dlcache);
549         Cvar_RegisterVariable (&sv_allowdownloads_inarchive);
550         Cvar_RegisterVariable (&sv_areagrid_mingridsize);
551         Cvar_RegisterVariable (&sv_checkforpacketsduringsleep);
552         Cvar_RegisterVariable (&sv_clmovement_enable);
553         Cvar_RegisterVariable (&sv_clmovement_minping);
554         Cvar_RegisterVariable (&sv_clmovement_minping_disabletime);
555         Cvar_RegisterVariable (&sv_clmovement_inputtimeout);
556         Cvar_RegisterVariable (&sv_cullentities_nevercullbmodels);
557         Cvar_RegisterVariable (&sv_cullentities_pvs);
558         Cvar_RegisterVariable (&sv_cullentities_stats);
559         Cvar_RegisterVariable (&sv_cullentities_trace);
560         Cvar_RegisterVariable (&sv_cullentities_trace_delay);
561         Cvar_RegisterVariable (&sv_cullentities_trace_delay_players);
562         Cvar_RegisterVariable (&sv_cullentities_trace_enlarge);
563         Cvar_RegisterVariable (&sv_cullentities_trace_expand);
564         Cvar_RegisterVariable (&sv_cullentities_trace_eyejitter);
565         Cvar_RegisterVariable (&sv_cullentities_trace_entityocclusion);
566         Cvar_RegisterVariable (&sv_cullentities_trace_prediction);
567         Cvar_RegisterVariable (&sv_cullentities_trace_prediction_time);
568         Cvar_RegisterVariable (&sv_cullentities_trace_samples);
569         Cvar_RegisterVariable (&sv_cullentities_trace_samples_extra);
570         Cvar_RegisterVariable (&sv_cullentities_trace_samples_players);
571         Cvar_RegisterVariable (&sv_debugmove);
572         Cvar_RegisterVariable (&sv_echobprint);
573         Cvar_RegisterVariable (&sv_edgefriction);
574         Cvar_RegisterVariable (&sv_entpatch);
575         Cvar_RegisterVariable (&sv_freezenonclients);
576         Cvar_RegisterVariable (&sv_friction);
577         Cvar_RegisterVariable (&sv_gameplayfix_blowupfallenzombies);
578         Cvar_RegisterVariable (&sv_gameplayfix_consistentplayerprethink);
579         Cvar_RegisterVariable (&sv_gameplayfix_delayprojectiles);
580         Cvar_RegisterVariable (&sv_gameplayfix_droptofloorstartsolid);
581         Cvar_RegisterVariable (&sv_gameplayfix_droptofloorstartsolid_nudgetocorrect);
582         Cvar_RegisterVariable (&sv_gameplayfix_easierwaterjump);
583         Cvar_RegisterVariable (&sv_gameplayfix_findradiusdistancetobox);
584         Cvar_RegisterVariable (&sv_gameplayfix_gravityunaffectedbyticrate);
585         Cvar_RegisterVariable (&sv_gameplayfix_grenadebouncedownslopes);
586         Cvar_RegisterVariable (&sv_gameplayfix_multiplethinksperframe);
587         Cvar_RegisterVariable (&sv_gameplayfix_noairborncorpse);
588         Cvar_RegisterVariable (&sv_gameplayfix_noairborncorpse_allowsuspendeditems);
589         Cvar_RegisterVariable (&sv_gameplayfix_nudgeoutofsolid);
590         Cvar_RegisterVariable (&sv_gameplayfix_nudgeoutofsolid_separation);
591         Cvar_RegisterVariable (&sv_gameplayfix_q2airaccelerate);
592         Cvar_RegisterVariable (&sv_gameplayfix_nogravityonground);
593         Cvar_RegisterVariable (&sv_gameplayfix_setmodelrealbox);
594         Cvar_RegisterVariable (&sv_gameplayfix_slidemoveprojectiles);
595         Cvar_RegisterVariable (&sv_gameplayfix_stepdown);
596         Cvar_RegisterVariable (&sv_gameplayfix_stepmultipletimes);
597         Cvar_RegisterVariable (&sv_gameplayfix_nostepmoveonsteepslopes);
598         Cvar_RegisterVariable (&sv_gameplayfix_swiminbmodels);
599         Cvar_RegisterVariable (&sv_gameplayfix_upwardvelocityclearsongroundflag);
600         Cvar_RegisterVariable (&sv_gameplayfix_downtracesupportsongroundflag);
601         Cvar_RegisterVariable (&sv_gameplayfix_q1bsptracelinereportstexture);
602         Cvar_RegisterVariable (&sv_gameplayfix_unstickplayers);
603         Cvar_RegisterVariable (&sv_gameplayfix_unstickentities);
604         Cvar_RegisterVariable (&sv_gameplayfix_fixedcheckwatertransition);
605         Cvar_RegisterVariable (&sv_gameplayfix_customstats);
606         Cvar_RegisterVariable (&sv_gravity);
607         Cvar_RegisterVariable (&sv_init_frame_count);
608         Cvar_RegisterVariable (&sv_idealpitchscale);
609         Cvar_RegisterVariable (&sv_jumpstep);
610         Cvar_RegisterVariable (&sv_jumpvelocity);
611         Cvar_RegisterVariable (&sv_maxairspeed);
612         Cvar_RegisterVariable (&sv_maxrate);
613         Cvar_RegisterVariable (&sv_maxspeed);
614         Cvar_RegisterVariable (&sv_maxvelocity);
615         Cvar_RegisterVariable (&sv_nostep);
616         Cvar_RegisterVariable (&sv_playerphysicsqc);
617         Cvar_RegisterVariable (&sv_progs);
618         Cvar_RegisterVariable (&sv_protocolname);
619         Cvar_RegisterVariable (&sv_random_seed);
620         Cvar_RegisterVariable (&host_limitlocal);
621         Cvar_RegisterAlias(&host_limitlocal, "sv_ratelimitlocalplayer");
622         Cvar_RegisterVariable (&sv_sound_land);
623         Cvar_RegisterVariable (&sv_sound_watersplash);
624         Cvar_RegisterVariable (&sv_stepheight);
625         Cvar_RegisterVariable (&sv_stopspeed);
626         Cvar_RegisterVariable (&sv_wallfriction);
627         Cvar_RegisterVariable (&sv_wateraccelerate);
628         Cvar_RegisterVariable (&sv_waterfriction);
629         Cvar_RegisterVariable (&sv_warsowbunny_airforwardaccel);
630         Cvar_RegisterVariable (&sv_warsowbunny_accel);
631         Cvar_RegisterVariable (&sv_warsowbunny_topspeed);
632         Cvar_RegisterVariable (&sv_warsowbunny_turnaccel);
633         Cvar_RegisterVariable (&sv_warsowbunny_backtosideratio);
634         Cvar_RegisterVariable (&sv_onlycsqcnetworking);
635         Cvar_RegisterVariable (&sv_areadebug);
636         Cvar_RegisterVariable (&sys_ticrate);
637         Cvar_RegisterVariable (&teamplay);
638         Cvar_RegisterVariable (&timelimit);
639         Cvar_RegisterVariable (&sv_threaded);
640
641         Cvar_RegisterVariable (&sv_rollangle);
642         Cvar_RegisterVariable (&sv_rollspeed);
643
644         Cvar_RegisterVariable (&saved1);
645         Cvar_RegisterVariable (&saved2);
646         Cvar_RegisterVariable (&saved3);
647         Cvar_RegisterVariable (&saved4);
648         Cvar_RegisterVariable (&savedgamecfg);
649         Cvar_RegisterVariable (&scratch1);
650         Cvar_RegisterVariable (&scratch2);
651         Cvar_RegisterVariable (&scratch3);
652         Cvar_RegisterVariable (&scratch4);
653         Cvar_RegisterVariable (&temp1);
654
655         // LadyHavoc: Nehahra uses these to pass data around cutscene demos
656         Cvar_RegisterVariable (&nehx00);
657         Cvar_RegisterVariable (&nehx01);
658         Cvar_RegisterVariable (&nehx02);
659         Cvar_RegisterVariable (&nehx03);
660         Cvar_RegisterVariable (&nehx04);
661         Cvar_RegisterVariable (&nehx05);
662         Cvar_RegisterVariable (&nehx06);
663         Cvar_RegisterVariable (&nehx07);
664         Cvar_RegisterVariable (&nehx08);
665         Cvar_RegisterVariable (&nehx09);
666         Cvar_RegisterVariable (&nehx10);
667         Cvar_RegisterVariable (&nehx11);
668         Cvar_RegisterVariable (&nehx12);
669         Cvar_RegisterVariable (&nehx13);
670         Cvar_RegisterVariable (&nehx14);
671         Cvar_RegisterVariable (&nehx15);
672         Cvar_RegisterVariable (&nehx16);
673         Cvar_RegisterVariable (&nehx17);
674         Cvar_RegisterVariable (&nehx18);
675         Cvar_RegisterVariable (&nehx19);
676         Cvar_RegisterVariable (&cutscene); // for Nehahra but useful to other mods as well
677
678         Cvar_RegisterVariable (&sv_autodemo_perclient);
679         Cvar_RegisterVariable (&sv_autodemo_perclient_nameformat);
680         Cvar_RegisterVariable (&sv_autodemo_perclient_discardable);
681
682         Cvar_RegisterVariable (&halflifebsp);
683         Cvar_RegisterVariable (&sv_mapformat_is_quake2);
684         Cvar_RegisterVariable (&sv_mapformat_is_quake3);
685
686         Cvar_RegisterVariable (&sv_writepicture_quality);
687
688         SV_InitOperatorCommands();
689         host.hook.SV_Shutdown = SV_Shutdown;
690
691         sv_mempool = Mem_AllocPool("server", 0, NULL);
692
693         SV_ServerOptions();
694         Cvar_Callback(&sv_netport);
695 }
696
697 static void SV_SaveEntFile_f(cmd_state_t *cmd)
698 {
699         char vabuf[1024];
700         if (!sv.active || !sv.worldmodel)
701         {
702                 Con_Print("Not running a server\n");
703                 return;
704         }
705         FS_WriteFile(va(vabuf, sizeof(vabuf), "%s.ent", sv.worldnamenoextension), sv.worldmodel->brush.entities, (fs_offset_t)strlen(sv.worldmodel->brush.entities));
706 }
707
708 /*
709 ==============================================================================
710
711 CLIENT SPAWNING
712
713 ==============================================================================
714 */
715
716 /*
717 ================
718 SV_SendServerinfo
719
720 Sends the first message from the server to a connected client.
721 This will be sent on the initial connection and upon each server load.
722 ================
723 */
724 void SV_SendServerinfo (client_t *client)
725 {
726         prvm_prog_t *prog = SVVM_prog;
727         int i;
728         char message[128];
729         char vabuf[1024];
730
731         // we know that this client has a netconnection and thus is not a bot
732
733         // edicts get reallocated on level changes, so we need to update it here
734         client->edict = PRVM_EDICT_NUM((client - svs.clients) + 1);
735
736         // clear cached stuff that depends on the level
737         client->weaponmodel[0] = 0;
738         client->weaponmodelindex = 0;
739
740         // LadyHavoc: clear entityframe tracking
741         client->latestframenum = 0;
742
743         // initialize the movetime, so a speedhack can't make use of the time before this client joined
744         client->cmd.time = sv.time;
745
746         if (client->entitydatabase)
747                 EntityFrame_FreeDatabase(client->entitydatabase);
748         if (client->entitydatabase4)
749                 EntityFrame4_FreeDatabase(client->entitydatabase4);
750         if (client->entitydatabase5)
751                 EntityFrame5_FreeDatabase(client->entitydatabase5);
752
753         memset(client->stats, 0, sizeof(client->stats));
754         memset(client->statsdeltabits, 0, sizeof(client->statsdeltabits));
755
756         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)
757         {
758                 if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3)
759                         client->entitydatabase = EntityFrame_AllocDatabase(sv_mempool);
760                 else if (sv.protocol == PROTOCOL_DARKPLACES4)
761                         client->entitydatabase4 = EntityFrame4_AllocDatabase(sv_mempool);
762                 else
763                         client->entitydatabase5 = EntityFrame5_AllocDatabase(sv_mempool);
764         }
765
766         // reset csqc entity versions
767         for (i = 0;i < prog->max_edicts;i++)
768         {
769                 client->csqcentityscope[i] = 0;
770                 client->csqcentitysendflags[i] = 0xFFFFFF;
771         }
772         for (i = 0;i < NUM_CSQCENTITYDB_FRAMES;i++)
773         {
774                 client->csqcentityframehistory[i].num = 0;
775                 client->csqcentityframehistory[i].framenum = -1;
776         }
777         client->csqcnumedicts = 0;
778         client->csqcentityframehistory_next = 0;
779
780         SZ_Clear (&client->netconnection->message);
781         MSG_WriteByte (&client->netconnection->message, svc_print);
782         dpsnprintf (message, sizeof (message), "\nServer: %s build %s (progs %i crc)\n", gamename, buildstring, prog->filecrc);
783         MSG_WriteString (&client->netconnection->message,message);
784
785         SV_StopDemoRecording(client); // to split up demos into different files
786         if(sv_autodemo_perclient.integer)
787         {
788                 char demofile[MAX_OSPATH];
789                 char ipaddress[MAX_QPATH];
790                 size_t j;
791
792                 // start a new demo file
793                 LHNETADDRESS_ToString(&(client->netconnection->peeraddress), ipaddress, sizeof(ipaddress), true);
794                 for(j = 0; ipaddress[j]; ++j)
795                         if(!isalnum(ipaddress[j]))
796                                 ipaddress[j] = '-';
797                 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);
798
799                 SV_StartDemoRecording(client, demofile, -1);
800         }
801
802         //[515]: init csprogs according to version of svprogs, check the crc, etc.
803         if (sv.csqc_progname[0])
804         {
805                 Con_DPrintf("sending csqc info to client (\"%s\" with size %i and crc %i)\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc);
806                 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
807                 MSG_WriteString (&client->netconnection->message, va(vabuf, sizeof(vabuf), "csqc_progname %s\n", sv.csqc_progname));
808                 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
809                 MSG_WriteString (&client->netconnection->message, va(vabuf, sizeof(vabuf), "csqc_progsize %i\n", sv.csqc_progsize));
810                 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
811                 MSG_WriteString (&client->netconnection->message, va(vabuf, sizeof(vabuf), "csqc_progcrc %i\n", sv.csqc_progcrc));
812
813                 if(client->sv_demo_file != NULL)
814                 {
815                         int k;
816                         static char buf[NET_MAXMESSAGE];
817                         sizebuf_t sb;
818
819                         sb.data = (unsigned char *) buf;
820                         sb.maxsize = sizeof(buf);
821                         k = 0;
822                         while(MakeDownloadPacket(sv.csqc_progname, svs.csqc_progdata, sv.csqc_progsize, sv.csqc_progcrc, k++, &sb, sv.protocol))
823                                 SV_WriteDemoMessage(client, &sb, false);
824                 }
825
826                 //[515]: init stufftext string (it is sent before svc_serverinfo)
827                 if (PRVM_GetString(prog, PRVM_serverglobalstring(SV_InitCmd)))
828                 {
829                         MSG_WriteByte (&client->netconnection->message, svc_stufftext);
830                         MSG_WriteString (&client->netconnection->message, va(vabuf, sizeof(vabuf), "%s\n", PRVM_GetString(prog, PRVM_serverglobalstring(SV_InitCmd))));
831                 }
832         }
833
834         //if (sv_allowdownloads.integer)
835         // always send the info that the server supports the protocol, even if downloads are forbidden
836         // only because of that, the CSQC exception can work
837         {
838                 MSG_WriteByte (&client->netconnection->message, svc_stufftext);
839                 MSG_WriteString (&client->netconnection->message, "cl_serverextension_download 2\n");
840         }
841
842         // send at this time so it's guaranteed to get executed at the right time
843         {
844                 client_t *save;
845                 save = host_client;
846                 host_client = client;
847                 Curl_SendRequirements();
848                 host_client = save;
849         }
850
851         MSG_WriteByte (&client->netconnection->message, svc_serverinfo);
852         MSG_WriteLong (&client->netconnection->message, Protocol_NumberForEnum(sv.protocol));
853         MSG_WriteByte (&client->netconnection->message, svs.maxclients);
854
855         if (!coop.integer && deathmatch.integer)
856                 MSG_WriteByte (&client->netconnection->message, GAME_DEATHMATCH);
857         else
858                 MSG_WriteByte (&client->netconnection->message, GAME_COOP);
859
860         MSG_WriteString (&client->netconnection->message,PRVM_GetString(prog, PRVM_serveredictstring(prog->edicts, message)));
861
862         for (i = 1;i < MAX_MODELS && sv.model_precache[i][0];i++)
863                 MSG_WriteString (&client->netconnection->message, sv.model_precache[i]);
864         MSG_WriteByte (&client->netconnection->message, 0);
865
866         for (i = 1;i < MAX_SOUNDS && sv.sound_precache[i][0];i++)
867                 MSG_WriteString (&client->netconnection->message, sv.sound_precache[i]);
868         MSG_WriteByte (&client->netconnection->message, 0);
869
870 // send music
871         MSG_WriteByte (&client->netconnection->message, svc_cdtrack);
872         MSG_WriteByte (&client->netconnection->message, (int)PRVM_serveredictfloat(prog->edicts, sounds));
873         MSG_WriteByte (&client->netconnection->message, (int)PRVM_serveredictfloat(prog->edicts, sounds));
874
875 // set view
876 // store this in clientcamera, too
877         client->clientcamera = PRVM_NUM_FOR_EDICT(client->edict);
878         MSG_WriteByte (&client->netconnection->message, svc_setview);
879         MSG_WriteShort (&client->netconnection->message, client->clientcamera);
880
881         MSG_WriteByte (&client->netconnection->message, svc_signonnum);
882         MSG_WriteByte (&client->netconnection->message, 1);
883
884         client->prespawned = false;             // need prespawn, spawn, etc
885         client->spawned = false;                // need prespawn, spawn, etc
886         client->begun = false;                  // need prespawn, spawn, etc
887         client->sendsignon = 1;                 // send this message, and increment to 2, 2 will be set to 0 by the prespawn command
888
889         // clear movement info until client enters the new level properly
890         memset(&client->cmd, 0, sizeof(client->cmd));
891         client->movesequence = 0;
892         client->movement_highestsequence_seen = 0;
893         memset(&client->movement_count, 0, sizeof(client->movement_count));
894         client->ping = 0;
895
896         // allow the client some time to send his keepalives, even if map loading took ages
897         client->netconnection->timeout = host.realtime + net_connecttimeout.value;
898 }
899
900 /*
901 ================
902 SV_ConnectClient
903
904 Initializes a client_t for a new net connection.  This will only be called
905 once for a player each game, not once for each level change.
906 ================
907 */
908 void SV_ConnectClient (int clientnum, netconn_t *netconnection)
909 {
910         prvm_prog_t *prog = SVVM_prog;
911         client_t                *client;
912         int                             i;
913
914         client = svs.clients + clientnum;
915
916 // set up the client_t
917         if (sv.loadgame)
918         {
919                 float backupparms[NUM_SPAWN_PARMS];
920                 memcpy(backupparms, client->spawn_parms, sizeof(backupparms));
921                 memset(client, 0, sizeof(*client));
922                 memcpy(client->spawn_parms, backupparms, sizeof(backupparms));
923         }
924         else
925                 memset(client, 0, sizeof(*client));
926         client->active = true;
927         client->netconnection = netconnection;
928
929         Con_DPrintf("Client %s connected\n", client->netconnection ? client->netconnection->address : "botclient");
930
931         if(client->netconnection && client->netconnection->crypto.authenticated)
932         {
933                 Con_Printf("%s connection to %s has been established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
934                                 client->netconnection->crypto.use_aes ? "Encrypted" : "Authenticated",
935                                 client->netconnection->address,
936                                 client->netconnection->crypto.client_idfp[0] ? client->netconnection->crypto.client_idfp : "-",
937                                 (client->netconnection->crypto.client_issigned || !client->netconnection->crypto.client_keyfp[0]) ? "" : "~",
938                                 crypto_keyfp_recommended_length, client->netconnection->crypto.client_keyfp[0] ? client->netconnection->crypto.client_keyfp : "-",
939                                 crypto_keyfp_recommended_length, client->netconnection->crypto.server_idfp[0] ? client->netconnection->crypto.server_idfp : "-",
940                                 (client->netconnection->crypto.server_issigned || !client->netconnection->crypto.server_keyfp[0]) ? "" : "~",
941                                 crypto_keyfp_recommended_length, client->netconnection->crypto.server_keyfp[0] ? client->netconnection->crypto.server_keyfp : "-"
942                                 );
943         }
944
945         strlcpy(client->name, "unconnected", sizeof(client->name));
946         strlcpy(client->old_name, "unconnected", sizeof(client->old_name));
947         client->prespawned = false;
948         client->spawned = false;
949         client->begun = false;
950         client->edict = PRVM_EDICT_NUM(clientnum+1);
951         if (client->netconnection)
952                 client->netconnection->message.allowoverflow = true;            // we can catch it
953         // prepare the unreliable message buffer
954         client->unreliablemsg.data = client->unreliablemsg_data;
955         client->unreliablemsg.maxsize = sizeof(client->unreliablemsg_data);
956         // updated by receiving "rate" command from client, this is also the default if not using a DP client
957         client->rate = 1000000000;
958         client->connecttime = host.realtime;
959
960         if (!sv.loadgame)
961         {
962                 // call the progs to get default spawn parms for the new client
963                 // set self to world to intentionally cause errors with broken SetNewParms code in some mods
964                 PRVM_serverglobalfloat(time) = sv.time;
965                 PRVM_serverglobaledict(self) = 0;
966                 prog->ExecuteProgram(prog, PRVM_serverfunction(SetNewParms), "QC function SetNewParms is missing");
967                 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
968                         client->spawn_parms[i] = (&PRVM_serverglobalfloat(parm1))[i];
969
970                 // set up the entity for this client (including .colormap, .team, etc)
971                 PRVM_ED_ClearEdict(prog, client->edict);
972         }
973
974         // don't call SendServerinfo for a fresh botclient because its fields have
975         // not been set up by the qc yet
976         if (client->netconnection)
977                 SV_SendServerinfo (client);
978         else
979                 client->prespawned = client->spawned = client->begun = true;
980 }
981
982 /*
983 =====================
984 SV_DropClient
985
986 Called when the player is getting totally kicked off the host
987 if (crash = true), don't bother sending signofs
988 =====================
989 */
990 void SV_DropClient(qbool crash)
991 {
992         prvm_prog_t *prog = SVVM_prog;
993         int i;
994         Con_Printf("Client \"%s\" dropped\n", host_client->name);
995
996         SV_StopDemoRecording(host_client);
997
998         // make sure edict is not corrupt (from a level change for example)
999         host_client->edict = PRVM_EDICT_NUM(host_client - svs.clients + 1);
1000
1001         if (host_client->netconnection)
1002         {
1003                 // tell the client to be gone
1004                 if (!crash)
1005                 {
1006                         // LadyHavoc: no opportunity for resending, so use unreliable 3 times
1007                         unsigned char bufdata[8];
1008                         sizebuf_t buf;
1009                         memset(&buf, 0, sizeof(buf));
1010                         buf.data = bufdata;
1011                         buf.maxsize = sizeof(bufdata);
1012                         MSG_WriteByte(&buf, svc_disconnect);
1013                         NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol, 10000, 0, false);
1014                         NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol, 10000, 0, false);
1015                         NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol, 10000, 0, false);
1016                 }
1017         }
1018
1019         // call qc ClientDisconnect function
1020         // LadyHavoc: don't call QC if server is dead (avoids recursive
1021         // Host_Error in some mods when they run out of edicts)
1022         if (host_client->clientconnectcalled && sv.active && host_client->edict)
1023         {
1024                 // call the prog function for removing a client
1025                 // this will set the body to a dead frame, among other things
1026                 int saveSelf = PRVM_serverglobaledict(self);
1027                 host_client->clientconnectcalled = false;
1028                 PRVM_serverglobalfloat(time) = sv.time;
1029                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1030                 prog->ExecuteProgram(prog, PRVM_serverfunction(ClientDisconnect), "QC function ClientDisconnect is missing");
1031                 PRVM_serverglobaledict(self) = saveSelf;
1032         }
1033
1034         if (host_client->netconnection)
1035         {
1036                 // break the net connection
1037                 NetConn_Close(host_client->netconnection);
1038                 host_client->netconnection = NULL;
1039         }
1040
1041         // if a download is active, close it
1042         if (host_client->download_file)
1043         {
1044                 Con_DPrintf("Download of %s aborted when %s dropped\n", host_client->download_name, host_client->name);
1045                 FS_Close(host_client->download_file);
1046                 host_client->download_file = NULL;
1047                 host_client->download_name[0] = 0;
1048                 host_client->download_expectedposition = 0;
1049                 host_client->download_started = false;
1050         }
1051
1052         // remove leaving player from scoreboard
1053         host_client->name[0] = 0;
1054         host_client->colors = 0;
1055         host_client->frags = 0;
1056         // send notification to all clients
1057         // get number of client manually just to make sure we get it right...
1058         i = host_client - svs.clients;
1059         MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1060         MSG_WriteByte (&sv.reliable_datagram, i);
1061         MSG_WriteString (&sv.reliable_datagram, host_client->name);
1062         MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1063         MSG_WriteByte (&sv.reliable_datagram, i);
1064         MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1065         MSG_WriteByte (&sv.reliable_datagram, svc_updatefrags);
1066         MSG_WriteByte (&sv.reliable_datagram, i);
1067         MSG_WriteShort (&sv.reliable_datagram, host_client->frags);
1068
1069         // free the client now
1070         if (host_client->entitydatabase)
1071                 EntityFrame_FreeDatabase(host_client->entitydatabase);
1072         if (host_client->entitydatabase4)
1073                 EntityFrame4_FreeDatabase(host_client->entitydatabase4);
1074         if (host_client->entitydatabase5)
1075                 EntityFrame5_FreeDatabase(host_client->entitydatabase5);
1076
1077         if (sv.active)
1078         {
1079                 // clear a fields that matter to DP_SV_CLIENTNAME and DP_SV_CLIENTCOLORS, and also frags
1080                 PRVM_ED_ClearEdict(prog, host_client->edict);
1081         }
1082
1083         // clear the client struct (this sets active to false)
1084         memset(host_client, 0, sizeof(*host_client));
1085
1086         // update server listing on the master because player count changed
1087         // (which the master uses for filtering empty/full servers)
1088         NetConn_Heartbeat(1);
1089
1090         if (sv.loadgame)
1091         {
1092                 for (i = 0;i < svs.maxclients;i++)
1093                         if (svs.clients[i].active && !svs.clients[i].spawned)
1094                                 break;
1095                 if (i == svs.maxclients)
1096                 {
1097                         Con_Printf("Loaded game, everyone rejoined - unpausing\n");
1098                         sv.paused = sv.loadgame = false; // we're basically done with loading now
1099                 }
1100         }
1101 }
1102
1103 static void SV_StartDownload_f(cmd_state_t *cmd)
1104 {
1105         if (host_client->download_file)
1106                 host_client->download_started = true;
1107 }
1108
1109 /*
1110  * Compression extension negotiation:
1111  *
1112  * Server to client:
1113  *   cl_serverextension_download 2
1114  *
1115  * Client to server:
1116  *   download <filename> <list of zero or more suppported compressions in order of preference>
1117  * e.g.
1118  *   download maps/map1.bsp lzo deflate huffman
1119  *
1120  * Server to client:
1121  *   cl_downloadbegin <compressed size> <filename> <compression method actually used>
1122  * e.g.
1123  *   cl_downloadbegin 123456 maps/map1.bsp deflate
1124  *
1125  * The server may choose not to compress the file by sending no compression name, like:
1126  *   cl_downloadbegin 345678 maps/map1.bsp
1127  *
1128  * NOTE: the "download" command may only specify compression algorithms if
1129  *       cl_serverextension_download is 2!
1130  *       If cl_serverextension_download has a different value, the client must
1131  *       assume this extension is not supported!
1132  */
1133
1134 static void Download_CheckExtensions(cmd_state_t *cmd)
1135 {
1136         int i;
1137         int argc = Cmd_Argc(cmd);
1138
1139         // first reset them all
1140         host_client->download_deflate = false;
1141         
1142         for(i = 2; i < argc; ++i)
1143         {
1144                 if(!strcmp(Cmd_Argv(cmd, i), "deflate"))
1145                 {
1146                         host_client->download_deflate = true;
1147                         break;
1148                 }
1149         }
1150 }
1151
1152 static void SV_Download_f(cmd_state_t *cmd)
1153 {
1154         const char *whichpack, *whichpack2, *extension;
1155         qbool is_csqc; // so we need to check only once
1156
1157         if (Cmd_Argc(cmd) < 2)
1158         {
1159                 SV_ClientPrintf("usage: download <filename> {<extensions>}*\n");
1160                 SV_ClientPrintf("       supported extensions: deflate\n");
1161                 return;
1162         }
1163
1164         if (FS_CheckNastyPath(Cmd_Argv(cmd, 1), false))
1165         {
1166                 SV_ClientPrintf("Download rejected: nasty filename \"%s\"\n", Cmd_Argv(cmd, 1));
1167                 return;
1168         }
1169
1170         if (host_client->download_file)
1171         {
1172                 // at this point we'll assume the previous download should be aborted
1173                 Con_DPrintf("Download of %s aborted by %s starting a new download\n", host_client->download_name, host_client->name);
1174                 SV_ClientCommands("\nstopdownload\n");
1175
1176                 // close the file and reset variables
1177                 FS_Close(host_client->download_file);
1178                 host_client->download_file = NULL;
1179                 host_client->download_name[0] = 0;
1180                 host_client->download_expectedposition = 0;
1181                 host_client->download_started = false;
1182         }
1183
1184         is_csqc = (sv.csqc_progname[0] && strcmp(Cmd_Argv(cmd, 1), sv.csqc_progname) == 0);
1185         
1186         if (!sv_allowdownloads.integer && !is_csqc)
1187         {
1188                 SV_ClientPrintf("Downloads are disabled on this server\n");
1189                 SV_ClientCommands("\nstopdownload\n");
1190                 return;
1191         }
1192
1193         Download_CheckExtensions(cmd);
1194
1195         strlcpy(host_client->download_name, Cmd_Argv(cmd, 1), sizeof(host_client->download_name));
1196         extension = FS_FileExtension(host_client->download_name);
1197
1198         // host_client is asking to download a specified file
1199         if (developer_extra.integer)
1200                 Con_DPrintf("Download request for %s by %s\n", host_client->download_name, host_client->name);
1201
1202         if(is_csqc)
1203         {
1204                 char extensions[MAX_QPATH]; // make sure this can hold all extensions
1205                 extensions[0] = '\0';
1206                 
1207                 if(host_client->download_deflate)
1208                         strlcat(extensions, " deflate", sizeof(extensions));
1209                 
1210                 Con_DPrintf("Downloading %s to %s\n", host_client->download_name, host_client->name);
1211
1212                 if(host_client->download_deflate && svs.csqc_progdata_deflated)
1213                         host_client->download_file = FS_FileFromData(svs.csqc_progdata_deflated, svs.csqc_progsize_deflated, true);
1214                 else
1215                         host_client->download_file = FS_FileFromData(svs.csqc_progdata, sv.csqc_progsize, true);
1216                 
1217                 // no, no space is needed between %s and %s :P
1218                 SV_ClientCommands("\ncl_downloadbegin %i %s%s\n", (int)FS_FileSize(host_client->download_file), host_client->download_name, extensions);
1219
1220                 host_client->download_expectedposition = 0;
1221                 host_client->download_started = false;
1222                 host_client->sendsignon = true; // make sure this message is sent
1223                 return;
1224         }
1225
1226         if (!FS_FileExists(host_client->download_name))
1227         {
1228                 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);
1229                 SV_ClientCommands("\nstopdownload\n");
1230                 return;
1231         }
1232
1233         // check if the user is trying to download part of registered Quake(r)
1234         whichpack = FS_WhichPack(host_client->download_name);
1235         whichpack2 = FS_WhichPack("gfx/pop.lmp");
1236         if ((whichpack && whichpack2 && !strcasecmp(whichpack, whichpack2)) || FS_IsRegisteredQuakePack(host_client->download_name))
1237         {
1238                 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);
1239                 SV_ClientCommands("\nstopdownload\n");
1240                 return;
1241         }
1242
1243         // check if the server has forbidden archive downloads entirely
1244         if (!sv_allowdownloads_inarchive.integer)
1245         {
1246                 whichpack = FS_WhichPack(host_client->download_name);
1247                 if (whichpack)
1248                 {
1249                         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);
1250                         SV_ClientCommands("\nstopdownload\n");
1251                         return;
1252                 }
1253         }
1254
1255         if (!sv_allowdownloads_config.integer)
1256         {
1257                 if (!strcasecmp(extension, "cfg"))
1258                 {
1259                         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);
1260                         SV_ClientCommands("\nstopdownload\n");
1261                         return;
1262                 }
1263         }
1264
1265         if (!sv_allowdownloads_dlcache.integer)
1266         {
1267                 if (!strncasecmp(host_client->download_name, "dlcache/", 8))
1268                 {
1269                         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);
1270                         SV_ClientCommands("\nstopdownload\n");
1271                         return;
1272                 }
1273         }
1274
1275         if (!sv_allowdownloads_archive.integer)
1276         {
1277                 if (!strcasecmp(extension, "pak") || !strcasecmp(extension, "pk3") || !strcasecmp(extension, "dpk"))
1278                 {
1279                         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);
1280                         SV_ClientCommands("\nstopdownload\n");
1281                         return;
1282                 }
1283         }
1284
1285         host_client->download_file = FS_OpenVirtualFile(host_client->download_name, true);
1286         if (!host_client->download_file)
1287         {
1288                 SV_ClientPrintf("Download rejected: server could not open the file \"%s\"\n", host_client->download_name);
1289                 SV_ClientCommands("\nstopdownload\n");
1290                 return;
1291         }
1292
1293         if (FS_FileSize(host_client->download_file) > 1<<30)
1294         {
1295                 SV_ClientPrintf("Download rejected: file \"%s\" is very large\n", host_client->download_name);
1296                 SV_ClientCommands("\nstopdownload\n");
1297                 FS_Close(host_client->download_file);
1298                 host_client->download_file = NULL;
1299                 return;
1300         }
1301
1302         if (FS_FileSize(host_client->download_file) < 0)
1303         {
1304                 SV_ClientPrintf("Download rejected: file \"%s\" is not a regular file\n", host_client->download_name);
1305                 SV_ClientCommands("\nstopdownload\n");
1306                 FS_Close(host_client->download_file);
1307                 host_client->download_file = NULL;
1308                 return;
1309         }
1310
1311         Con_DPrintf("Downloading %s to %s\n", host_client->download_name, host_client->name);
1312
1313         /*
1314          * we can only do this if we would actually deflate on the fly
1315          * which we do not (yet)!
1316         {
1317                 char extensions[MAX_QPATH]; // make sure this can hold all extensions
1318                 extensions[0] = '\0';
1319                 
1320                 if(host_client->download_deflate)
1321                         strlcat(extensions, " deflate", sizeof(extensions));
1322
1323                 // no, no space is needed between %s and %s :P
1324                 SV_ClientCommands("\ncl_downloadbegin %i %s%s\n", (int)FS_FileSize(host_client->download_file), host_client->download_name, extensions);
1325         }
1326         */
1327         SV_ClientCommands("\ncl_downloadbegin %i %s\n", (int)FS_FileSize(host_client->download_file), host_client->download_name);
1328
1329         host_client->download_expectedposition = 0;
1330         host_client->download_started = false;
1331         host_client->sendsignon = true; // make sure this message is sent
1332
1333         // the rest of the download process is handled in SV_SendClientDatagram
1334         // and other code dealing with svc_downloaddata and clc_ackdownloaddata
1335         //
1336         // no svc_downloaddata messages will be sent until sv_startdownload is
1337         // sent by the client
1338 }
1339
1340 /*
1341 ==============================================================================
1342
1343 SERVER SPAWNING
1344
1345 ==============================================================================
1346 */
1347
1348 /*
1349 ================
1350 SV_ModelIndex
1351
1352 ================
1353 */
1354 int SV_ModelIndex(const char *s, int precachemode)
1355 {
1356         int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) ? 256 : MAX_MODELS);
1357         char filename[MAX_QPATH];
1358         if (!s || !*s)
1359                 return 0;
1360         // testing
1361         //if (precachemode == 2)
1362         //      return 0;
1363         strlcpy(filename, s, sizeof(filename));
1364         for (i = 2;i < limit;i++)
1365         {
1366                 if (!sv.model_precache[i][0])
1367                 {
1368                         if (precachemode)
1369                         {
1370                                 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))
1371                                 {
1372                                         Con_Printf("SV_ModelIndex(\"%s\"): precache_model can only be done in spawn functions\n", filename);
1373                                         return 0;
1374                                 }
1375                                 if (precachemode == 1)
1376                                         Con_Printf("SV_ModelIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename);
1377                                 strlcpy(sv.model_precache[i], filename, sizeof(sv.model_precache[i]));
1378                                 if (sv.state == ss_loading)
1379                                 {
1380                                         // running from SV_SpawnServer which is launched from the client console command interpreter
1381                                         sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, s[0] == '*' ? sv.worldname : NULL);
1382                                 }
1383                                 else
1384                                 {
1385                                         if (svs.threaded)
1386                                         {
1387                                                 // this is running on the server thread, we can't load a model here (it would crash on renderer calls), so only look it up, the svc_precache will cause it to be loaded when it reaches the client
1388                                                 sv.models[i] = Mod_FindName (sv.model_precache[i], s[0] == '*' ? sv.worldname : NULL);
1389                                         }
1390                                         else
1391                                         {
1392                                                 // running single threaded, so we can load the model here
1393                                                 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, s[0] == '*' ? sv.worldname : NULL);
1394                                         }
1395                                         MSG_WriteByte(&sv.reliable_datagram, svc_precache);
1396                                         MSG_WriteShort(&sv.reliable_datagram, i);
1397                                         MSG_WriteString(&sv.reliable_datagram, filename);
1398                                 }
1399                                 return i;
1400                         }
1401                         Con_Printf("SV_ModelIndex(\"%s\"): not precached\n", filename);
1402                         return 0;
1403                 }
1404                 if (!strcmp(sv.model_precache[i], filename))
1405                         return i;
1406         }
1407         Con_Printf("SV_ModelIndex(\"%s\"): i (%i) == MAX_MODELS (%i)\n", filename, i, MAX_MODELS);
1408         return 0;
1409 }
1410
1411 /*
1412 ================
1413 SV_SoundIndex
1414
1415 ================
1416 */
1417 int SV_SoundIndex(const char *s, int precachemode)
1418 {
1419         int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP) ? 256 : MAX_SOUNDS);
1420         char filename[MAX_QPATH];
1421         if (!s || !*s)
1422                 return 0;
1423         // testing
1424         //if (precachemode == 2)
1425         //      return 0;
1426         strlcpy(filename, s, sizeof(filename));
1427         for (i = 1;i < limit;i++)
1428         {
1429                 if (!sv.sound_precache[i][0])
1430                 {
1431                         if (precachemode)
1432                         {
1433                                 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))
1434                                 {
1435                                         Con_Printf("SV_SoundIndex(\"%s\"): precache_sound can only be done in spawn functions\n", filename);
1436                                         return 0;
1437                                 }
1438                                 if (precachemode == 1)
1439                                         Con_Printf("SV_SoundIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename);
1440                                 strlcpy(sv.sound_precache[i], filename, sizeof(sv.sound_precache[i]));
1441                                 if (sv.state != ss_loading)
1442                                 {
1443                                         MSG_WriteByte(&sv.reliable_datagram, svc_precache);
1444                                         MSG_WriteShort(&sv.reliable_datagram, i + 32768);
1445                                         MSG_WriteString(&sv.reliable_datagram, filename);
1446                                 }
1447                                 return i;
1448                         }
1449                         Con_Printf("SV_SoundIndex(\"%s\"): not precached\n", filename);
1450                         return 0;
1451                 }
1452                 if (!strcmp(sv.sound_precache[i], filename))
1453                         return i;
1454         }
1455         Con_Printf("SV_SoundIndex(\"%s\"): i (%i) == MAX_SOUNDS (%i)\n", filename, i, MAX_SOUNDS);
1456         return 0;
1457 }
1458
1459 /*
1460 ================
1461 SV_ParticleEffectIndex
1462
1463 ================
1464 */
1465 int SV_ParticleEffectIndex(const char *name)
1466 {
1467         int i, argc, linenumber, effectnameindex;
1468         int filepass;
1469         fs_offset_t filesize;
1470         unsigned char *filedata;
1471         const char *text;
1472         const char *textstart;
1473         //const char *textend;
1474         char argv[16][1024];
1475         char filename[MAX_QPATH];
1476         if (!sv.particleeffectnamesloaded)
1477         {
1478                 sv.particleeffectnamesloaded = true;
1479                 memset(sv.particleeffectname, 0, sizeof(sv.particleeffectname));
1480                 for (i = 0;i < EFFECT_TOTAL;i++)
1481                         strlcpy(sv.particleeffectname[i], standardeffectnames[i], sizeof(sv.particleeffectname[i]));
1482                 for (filepass = 0;;filepass++)
1483                 {
1484                         if (filepass == 0)
1485                                 dpsnprintf(filename, sizeof(filename), "effectinfo.txt");
1486                         else if (filepass == 1)
1487                                 dpsnprintf(filename, sizeof(filename), "%s_effectinfo.txt", sv.worldnamenoextension);
1488                         else
1489                                 break;
1490                         filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
1491                         if (!filedata)
1492                                 continue;
1493                         textstart = (const char *)filedata;
1494                         //textend = (const char *)filedata + filesize;
1495                         text = textstart;
1496                         for (linenumber = 1;;linenumber++)
1497                         {
1498                                 argc = 0;
1499                                 for (;;)
1500                                 {
1501                                         if (!COM_ParseToken_Simple(&text, true, false, true) || !strcmp(com_token, "\n"))
1502                                                 break;
1503                                         if (argc < 16)
1504                                         {
1505                                                 strlcpy(argv[argc], com_token, sizeof(argv[argc]));
1506                                                 argc++;
1507                                         }
1508                                 }
1509                                 if (com_token[0] == 0)
1510                                         break; // if the loop exited and it's not a \n, it's EOF
1511                                 if (argc < 1)
1512                                         continue;
1513                                 if (!strcmp(argv[0], "effect"))
1514                                 {
1515                                         if (argc == 2)
1516                                         {
1517                                                 for (effectnameindex = 1;effectnameindex < MAX_PARTICLEEFFECTNAME;effectnameindex++)
1518                                                 {
1519                                                         if (sv.particleeffectname[effectnameindex][0])
1520                                                         {
1521                                                                 if (!strcmp(sv.particleeffectname[effectnameindex], argv[1]))
1522                                                                         break;
1523                                                         }
1524                                                         else
1525                                                         {
1526                                                                 strlcpy(sv.particleeffectname[effectnameindex], argv[1], sizeof(sv.particleeffectname[effectnameindex]));
1527                                                                 break;
1528                                                         }
1529                                                 }
1530                                                 // if we run out of names, abort
1531                                                 if (effectnameindex == MAX_PARTICLEEFFECTNAME)
1532                                                 {
1533                                                         Con_Printf("%s:%i: too many effects!\n", filename, linenumber);
1534                                                         break;
1535                                                 }
1536                                         }
1537                                 }
1538                         }
1539                         Mem_Free(filedata);
1540                 }
1541         }
1542         // search for the name
1543         for (effectnameindex = 1;effectnameindex < MAX_PARTICLEEFFECTNAME && sv.particleeffectname[effectnameindex][0];effectnameindex++)
1544                 if (!strcmp(sv.particleeffectname[effectnameindex], name))
1545                         return effectnameindex;
1546         // return 0 if we couldn't find it
1547         return 0;
1548 }
1549
1550 model_t *SV_GetModelByIndex(int modelindex)
1551 {
1552         return (modelindex > 0 && modelindex < MAX_MODELS) ? sv.models[modelindex] : NULL;
1553 }
1554
1555 model_t *SV_GetModelFromEdict(prvm_edict_t *ed)
1556 {
1557         prvm_prog_t *prog = SVVM_prog;
1558         int modelindex;
1559         if (!ed || ed->priv.server->free)
1560                 return NULL;
1561         modelindex = (int)PRVM_serveredictfloat(ed, modelindex);
1562         return (modelindex > 0 && modelindex < MAX_MODELS) ? sv.models[modelindex] : NULL;
1563 }
1564
1565 /*
1566 ================
1567 SV_CreateBaseline
1568
1569 ================
1570 */
1571 static void SV_CreateBaseline (void)
1572 {
1573         prvm_prog_t *prog = SVVM_prog;
1574         int i, entnum, large;
1575         prvm_edict_t *svent;
1576
1577         // LadyHavoc: clear *all* baselines (not just active ones)
1578         for (entnum = 0;entnum < prog->max_edicts;entnum++)
1579         {
1580                 // get the current server version
1581                 svent = PRVM_EDICT_NUM(entnum);
1582
1583                 // LadyHavoc: always clear state values, whether the entity is in use or not
1584                 svent->priv.server->baseline = defaultstate;
1585
1586                 if (svent->priv.server->free)
1587                         continue;
1588                 if (entnum > svs.maxclients && !PRVM_serveredictfloat(svent, modelindex))
1589                         continue;
1590
1591                 // create entity baseline
1592                 VectorCopy (PRVM_serveredictvector(svent, origin), svent->priv.server->baseline.origin);
1593                 VectorCopy (PRVM_serveredictvector(svent, angles), svent->priv.server->baseline.angles);
1594                 svent->priv.server->baseline.frame = (int)PRVM_serveredictfloat(svent, frame);
1595                 svent->priv.server->baseline.skin = (int)PRVM_serveredictfloat(svent, skin);
1596                 if (entnum > 0 && entnum <= svs.maxclients)
1597                 {
1598                         svent->priv.server->baseline.colormap = entnum;
1599                         svent->priv.server->baseline.modelindex = SV_ModelIndex("progs/player.mdl", 1);
1600                 }
1601                 else
1602                 {
1603                         svent->priv.server->baseline.colormap = 0;
1604                         svent->priv.server->baseline.modelindex = (int)PRVM_serveredictfloat(svent, modelindex);
1605                 }
1606
1607                 large = false;
1608                 if (svent->priv.server->baseline.modelindex & 0xFF00 || svent->priv.server->baseline.frame & 0xFF00)
1609                 {
1610                         large = true;
1611                         if (sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
1612                                 large = false;
1613                 }
1614
1615                 // add to the message
1616                 if (large)
1617                         MSG_WriteByte (&sv.signon, svc_spawnbaseline2);
1618                 else
1619                         MSG_WriteByte (&sv.signon, svc_spawnbaseline);
1620                 MSG_WriteShort (&sv.signon, entnum);
1621
1622                 if (large)
1623                 {
1624                         MSG_WriteShort (&sv.signon, svent->priv.server->baseline.modelindex);
1625                         MSG_WriteShort (&sv.signon, svent->priv.server->baseline.frame);
1626                 }
1627                 else if (sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
1628                 {
1629                         MSG_WriteShort (&sv.signon, svent->priv.server->baseline.modelindex);
1630                         MSG_WriteByte (&sv.signon, svent->priv.server->baseline.frame);
1631                 }
1632                 else
1633                 {
1634                         MSG_WriteByte (&sv.signon, svent->priv.server->baseline.modelindex);
1635                         MSG_WriteByte (&sv.signon, svent->priv.server->baseline.frame);
1636                 }
1637                 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.colormap);
1638                 MSG_WriteByte (&sv.signon, svent->priv.server->baseline.skin);
1639                 for (i=0 ; i<3 ; i++)
1640                 {
1641                         MSG_WriteCoord(&sv.signon, svent->priv.server->baseline.origin[i], sv.protocol);
1642                         MSG_WriteAngle(&sv.signon, svent->priv.server->baseline.angles[i], sv.protocol);
1643                 }
1644         }
1645 }
1646
1647 /*
1648 ================
1649 SV_Prepare_CSQC
1650
1651 Load csprogs.dat and comperss it so it doesn't need to be
1652 reloaded on request.
1653 ================
1654 */
1655 static void SV_Prepare_CSQC(void)
1656 {
1657         fs_offset_t progsize;
1658
1659         if(svs.csqc_progdata)
1660         {
1661                 Con_DPrintf("Unloading old CSQC data.\n");
1662                 Mem_Free(svs.csqc_progdata);
1663                 if(svs.csqc_progdata_deflated)
1664                         Mem_Free(svs.csqc_progdata_deflated);
1665         }
1666
1667         svs.csqc_progdata = NULL;
1668         svs.csqc_progdata_deflated = NULL;
1669         
1670         sv.csqc_progname[0] = 0;
1671         svs.csqc_progdata = FS_LoadFile(csqc_progname.string, sv_mempool, false, &progsize);
1672
1673         if(progsize > 0)
1674         {
1675                 size_t deflated_size;
1676
1677                 sv.csqc_progsize = (int)progsize;
1678                 sv.csqc_progcrc = CRC_Block(svs.csqc_progdata, progsize);
1679                 strlcpy(sv.csqc_progname, csqc_progname.string, sizeof(sv.csqc_progname));
1680                 Con_DPrintf("server detected csqc progs file \"%s\" with size %i and crc %i\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc);
1681
1682                 Con_DPrint("Compressing csprogs.dat\n");
1683                 //unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflated_size, int level, mempool_t *mempool);
1684                 svs.csqc_progdata_deflated = FS_Deflate(svs.csqc_progdata, progsize, &deflated_size, -1, sv_mempool);
1685                 svs.csqc_progsize_deflated = (int)deflated_size;
1686                 if(svs.csqc_progdata_deflated)
1687                 {
1688                         Con_DPrintf("Deflated: %g%%\n", 100.0 - 100.0 * (deflated_size / (float)progsize));
1689                         Con_DPrintf("Uncompressed: %u\nCompressed:   %u\n", (unsigned)sv.csqc_progsize, (unsigned)svs.csqc_progsize_deflated);
1690                 }
1691                 else
1692                         Con_DPrintf("Cannot compress - need zlib for this. Using uncompressed progs only.\n");
1693         }
1694 }
1695
1696 /*
1697 ================
1698 SV_SaveSpawnparms
1699
1700 Grabs the current state of each client for saving across the
1701 transition to another level
1702 ================
1703 */
1704 void SV_SaveSpawnparms (void)
1705 {
1706         prvm_prog_t *prog = SVVM_prog;
1707         int             i, j;
1708
1709         svs.serverflags = (int)PRVM_serverglobalfloat(serverflags);
1710
1711         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1712         {
1713                 if (!host_client->active)
1714                         continue;
1715
1716         // call the progs to get default spawn parms for the new client
1717                 PRVM_serverglobalfloat(time) = sv.time;
1718                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1719                 prog->ExecuteProgram(prog, PRVM_serverfunction(SetChangeParms), "QC function SetChangeParms is missing");
1720                 for (j=0 ; j<NUM_SPAWN_PARMS ; j++)
1721                         host_client->spawn_parms[j] = (&PRVM_serverglobalfloat(parm1))[j];
1722         }
1723 }
1724
1725 // Returns 1 if we're singleplayer, > 1 if we're a listen server
1726 int SV_IsLocalServer(void)
1727 {
1728         return (host_isclient.integer && sv.active ? svs.maxclients : 0);
1729 }
1730
1731 /*
1732 ================
1733 SV_SpawnServer
1734
1735 This is called at the start of each level
1736 ================
1737 */
1738
1739 void SV_SpawnServer (const char *map)
1740 {
1741         prvm_prog_t *prog = SVVM_prog;
1742         prvm_edict_t *ent;
1743         int i;
1744         char *entities;
1745         model_t *worldmodel;
1746         char modelname[sizeof(sv.worldname)];
1747         char vabuf[1024];
1748
1749         Con_Printf("SpawnServer: %s\n", map);
1750
1751         dpsnprintf (modelname, sizeof(modelname), "maps/%s.bsp", map);
1752
1753         if (!FS_FileExists(modelname))
1754         {
1755                 dpsnprintf (modelname, sizeof(modelname), "maps/%s", map);
1756                 if (!FS_FileExists(modelname))
1757                 {
1758                         Con_Printf("SpawnServer: no map file named %s\n", modelname);
1759                         return;
1760                 }
1761         }
1762
1763 //      SV_LockThreadMutex();
1764
1765         if(!host_isclient.integer)
1766                 Sys_MakeProcessNice();
1767         else
1768         {
1769                 SCR_BeginLoadingPlaque(false);
1770                 S_StopAllSounds();
1771         }
1772
1773         if(sv.active)
1774         {
1775                 World_End(&sv.world);
1776                 if(PRVM_serverfunction(SV_Shutdown))
1777                 {
1778                         func_t s = PRVM_serverfunction(SV_Shutdown);
1779                         PRVM_serverglobalfloat(time) = sv.time;
1780                         PRVM_serverfunction(SV_Shutdown) = 0; // prevent it from getting called again
1781                         prog->ExecuteProgram(prog, s,"SV_Shutdown() required");
1782                 }
1783         }
1784
1785         // free q3 shaders so that any newly downloaded shaders will be active
1786         Mod_FreeQ3Shaders();
1787
1788         worldmodel = Mod_ForName(modelname, false, developer.integer > 0, NULL);
1789         if (!worldmodel || !worldmodel->TraceBox)
1790         {
1791                 Con_Printf("Couldn't load map %s\n", modelname);
1792
1793                 if(!host_isclient.integer)
1794                         Sys_MakeProcessMean();
1795
1796 //              SV_UnlockThreadMutex();
1797
1798                 return;
1799         }
1800
1801         Collision_Cache_Reset(true);
1802
1803         // let's not have any servers with no name
1804         if (hostname.string[0] == 0)
1805                 Cvar_SetQuick(&hostname, "UNNAMED");
1806         scr_centertime_off = 0;
1807
1808         svs.changelevel_issued = false;         // now safe to issue another
1809
1810         // make the map a required file for clients
1811         Curl_ClearRequirements();
1812         Curl_RequireFile(modelname);
1813
1814 //
1815 // tell all connected clients that we are going to a new level
1816 //
1817         if (sv.active)
1818         {
1819                 client_t *client;
1820                 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1821                 {
1822                         if (client->netconnection)
1823                         {
1824                                 MSG_WriteByte(&client->netconnection->message, svc_stufftext);
1825                                 MSG_WriteString(&client->netconnection->message, "reconnect\n");
1826                         }
1827                 }
1828         }
1829         else
1830         {
1831                 // open server port
1832                 NetConn_OpenServerPorts(true);
1833         }
1834
1835 //
1836 // make cvars consistant
1837 //
1838         if (coop.integer)
1839                 Cvar_SetValueQuick(&deathmatch, 0);
1840         // LadyHavoc: it can be useful to have skills outside the range 0-3...
1841         //current_skill = bound(0, (int)(skill.value + 0.5), 3);
1842         //Cvar_SetValue ("skill", (float)current_skill);
1843         current_skill = (int)(skill.value + 0.5);
1844
1845 //
1846 // set up the new server
1847 //
1848         memset (&sv, 0, sizeof(sv));
1849         // if running a local client, make sure it doesn't try to access the last
1850         // level's data which is no longer valiud
1851         cls.signon = 0;
1852
1853         Cvar_SetValueQuick(&halflifebsp, worldmodel->brush.ishlbsp);
1854         Cvar_SetValueQuick(&sv_mapformat_is_quake2, worldmodel->brush.isq2bsp);
1855         Cvar_SetValueQuick(&sv_mapformat_is_quake3, worldmodel->brush.isq3bsp);
1856
1857         if(*sv_random_seed.string)
1858         {
1859                 srand(sv_random_seed.integer);
1860                 Con_Printf(CON_WARN "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);
1861         }
1862
1863         SV_VM_Setup();
1864
1865         sv.active = true;
1866
1867         // set level base name variables for later use
1868         strlcpy (sv.name, map, sizeof (sv.name));
1869         strlcpy(sv.worldname, modelname, sizeof(sv.worldname));
1870         FS_StripExtension(sv.worldname, sv.worldnamenoextension, sizeof(sv.worldnamenoextension));
1871         strlcpy(sv.worldbasename, !strncmp(sv.worldnamenoextension, "maps/", 5) ? sv.worldnamenoextension + 5 : sv.worldnamenoextension, sizeof(sv.worldbasename));
1872         //Cvar_SetQuick(&sv_worldmessage, sv.worldmessage); // set later after QC is spawned
1873         Cvar_SetQuick(&sv_worldname, sv.worldname);
1874         Cvar_SetQuick(&sv_worldnamenoextension, sv.worldnamenoextension);
1875         Cvar_SetQuick(&sv_worldbasename, sv.worldbasename);
1876
1877         sv.protocol = Protocol_EnumForName(sv_protocolname.string);
1878         if (sv.protocol == PROTOCOL_UNKNOWN)
1879         {
1880                 char buffer[1024];
1881                 Protocol_Names(buffer, sizeof(buffer));
1882                 Con_Printf(CON_ERROR "Unknown sv_protocolname \"%s\", valid values are:\n%s\n", sv_protocolname.string, buffer);
1883                 sv.protocol = PROTOCOL_QUAKE;
1884         }
1885
1886 // load progs to get entity field count
1887         //PR_LoadProgs ( sv_progs.string );
1888
1889         sv.datagram.maxsize = sizeof(sv.datagram_buf);
1890         sv.datagram.cursize = 0;
1891         sv.datagram.data = sv.datagram_buf;
1892
1893         sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf);
1894         sv.reliable_datagram.cursize = 0;
1895         sv.reliable_datagram.data = sv.reliable_datagram_buf;
1896
1897         sv.signon.maxsize = sizeof(sv.signon_buf);
1898         sv.signon.cursize = 0;
1899         sv.signon.data = sv.signon_buf;
1900
1901 // leave slots at start for clients only
1902         //prog->num_edicts = svs.maxclients+1;
1903
1904         sv.state = ss_loading;
1905         prog->allowworldwrites = true;
1906         sv.paused = false;
1907
1908         sv.time = 1.0;
1909
1910         Mod_ClearUsed();
1911         worldmodel->used = true;
1912
1913         sv.worldmodel = worldmodel;
1914         sv.models[1] = sv.worldmodel;
1915
1916 //
1917 // clear world interaction links
1918 //
1919         World_SetSize(&sv.world, sv.worldname, sv.worldmodel->normalmins, sv.worldmodel->normalmaxs, prog);
1920         World_Start(&sv.world);
1921
1922         strlcpy(sv.sound_precache[0], "", sizeof(sv.sound_precache[0]));
1923
1924         strlcpy(sv.model_precache[0], "", sizeof(sv.model_precache[0]));
1925         strlcpy(sv.model_precache[1], sv.worldname, sizeof(sv.model_precache[1]));
1926         for (i = 1;i < sv.worldmodel->brush.numsubmodels && i+1 < MAX_MODELS;i++)
1927         {
1928                 dpsnprintf(sv.model_precache[i+1], sizeof(sv.model_precache[i+1]), "*%i", i);
1929                 sv.models[i+1] = Mod_ForName (sv.model_precache[i+1], false, false, sv.worldname);
1930         }
1931         if(i < sv.worldmodel->brush.numsubmodels)
1932                 Con_Printf("Too many submodels (MAX_MODELS is %i)\n", MAX_MODELS);
1933
1934 //
1935 // load the rest of the entities
1936 //
1937         // AK possible hack since num_edicts is still 0
1938         ent = PRVM_EDICT_NUM(0);
1939         memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1940         ent->priv.server->free = false;
1941         PRVM_serveredictstring(ent, model) = PRVM_SetEngineString(prog, sv.worldname);
1942         PRVM_serveredictfloat(ent, modelindex) = 1;             // world model
1943         PRVM_serveredictfloat(ent, solid) = SOLID_BSP;
1944         PRVM_serveredictfloat(ent, movetype) = MOVETYPE_PUSH;
1945         VectorCopy(sv.world.mins, PRVM_serveredictvector(ent, mins));
1946         VectorCopy(sv.world.maxs, PRVM_serveredictvector(ent, maxs));
1947         VectorCopy(sv.world.mins, PRVM_serveredictvector(ent, absmin));
1948         VectorCopy(sv.world.maxs, PRVM_serveredictvector(ent, absmax));
1949
1950         if (coop.value)
1951                 PRVM_serverglobalfloat(coop) = coop.integer;
1952         else
1953                 PRVM_serverglobalfloat(deathmatch) = deathmatch.integer;
1954
1955         PRVM_serverglobalstring(mapname) = PRVM_SetEngineString(prog, sv.name);
1956
1957 // serverflags are for cross level information (sigils)
1958         PRVM_serverglobalfloat(serverflags) = svs.serverflags;
1959
1960         // we need to reset the spawned flag on all connected clients here so that
1961         // their thinks don't run during startup (before PutClientInServer)
1962         // we also need to set up the client entities now
1963         // and we need to set the ->edict pointers to point into the progs edicts
1964         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1965         {
1966                 host_client->begun = false;
1967                 host_client->edict = PRVM_EDICT_NUM(i + 1);
1968                 PRVM_ED_ClearEdict(prog, host_client->edict);
1969         }
1970
1971         // load replacement entity file if found
1972         if (sv_entpatch.integer && (entities = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s.ent", sv.worldnamenoextension), tempmempool, true, NULL)))
1973         {
1974                 Con_Printf("Loaded %s.ent\n", sv.worldnamenoextension);
1975                 PRVM_ED_LoadFromFile(prog, entities);
1976                 Mem_Free(entities);
1977         }
1978         else
1979                 PRVM_ED_LoadFromFile(prog, sv.worldmodel->brush.entities);
1980
1981
1982         // LadyHavoc: clear world angles (to fix e3m3.bsp)
1983         VectorClear(PRVM_serveredictvector(prog->edicts, angles));
1984
1985 // all setup is completed, any further precache statements are errors
1986 //      sv.state = ss_active; // LadyHavoc: workaround for svc_precache bug
1987         prog->allowworldwrites = false;
1988
1989 // run two frames to allow everything to settle
1990         sv.time = 1.0001;
1991         for (i = 0;i < sv_init_frame_count.integer;i++)
1992         {
1993                 sv.frametime = 0.1;
1994                 SV_Physics ();
1995         }
1996
1997         // Once all init frames have been run, we consider svqc code fully initialized.
1998         prog->inittime = host.realtime;
1999
2000         if(!host_isclient.integer)
2001                 Mod_PurgeUnused();
2002
2003 // create a baseline for more efficient communications
2004         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)
2005                 SV_CreateBaseline ();
2006
2007         sv.state = ss_active; // LadyHavoc: workaround for svc_precache bug
2008
2009 // send serverinfo to all connected clients, and set up botclients coming back from a level change
2010         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2011         {
2012                 host_client->clientconnectcalled = false; // do NOT call ClientDisconnect if he drops before ClientConnect!
2013                 if (!host_client->active)
2014                         continue;
2015                 if (host_client->netconnection)
2016                         SV_SendServerinfo(host_client);
2017                 else
2018                 {
2019                         int j;
2020                         // if client is a botclient coming from a level change, we need to
2021                         // set up client info that normally requires networking
2022
2023                         // copy spawn parms out of the client_t
2024                         for (j=0 ; j< NUM_SPAWN_PARMS ; j++)
2025                                 (&PRVM_serverglobalfloat(parm1))[j] = host_client->spawn_parms[j];
2026
2027                         // call the spawn function
2028                         host_client->clientconnectcalled = true;
2029                         PRVM_serverglobalfloat(time) = sv.time;
2030                         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
2031                         prog->ExecuteProgram(prog, PRVM_serverfunction(ClientConnect), "QC function ClientConnect is missing");
2032                         prog->ExecuteProgram(prog, PRVM_serverfunction(PutClientInServer), "QC function PutClientInServer is missing");
2033                         host_client->begun = true;
2034                 }
2035         }
2036
2037         // update the map title cvar
2038         strlcpy(sv.worldmessage, PRVM_GetString(prog, PRVM_serveredictstring(prog->edicts, message)), sizeof(sv.worldmessage)); // map title (not related to filename)
2039         Cvar_SetQuick(&sv_worldmessage, sv.worldmessage);
2040
2041         Con_Printf("Server spawned.\n");
2042         NetConn_Heartbeat (2);
2043
2044         if(!host_isclient.integer)
2045                 Sys_MakeProcessMean();
2046
2047 //      SV_UnlockThreadMutex();
2048 }
2049
2050 /*
2051 ==================
2052 SV_Shutdown
2053
2054 This only happens at the end of a game, not between levels
2055 ==================
2056 */
2057 void SV_Shutdown(void)
2058 {
2059         prvm_prog_t *prog = SVVM_prog;
2060         int i;
2061
2062         SV_LockThreadMutex();
2063
2064         if (!sv.active)
2065                 goto end;
2066
2067         Con_DPrintf("SV_Shutdown\n");
2068
2069         NetConn_Heartbeat(2);
2070         NetConn_Heartbeat(2);
2071
2072 // make sure all the clients know we're disconnecting
2073         World_End(&sv.world);
2074         if(prog->loaded)
2075         {
2076                 if(PRVM_serverfunction(SV_Shutdown))
2077                 {
2078                         func_t s = PRVM_serverfunction(SV_Shutdown);
2079                         PRVM_serverglobalfloat(time) = sv.time;
2080                         PRVM_serverfunction(SV_Shutdown) = 0; // prevent it from getting called again
2081                         prog->ExecuteProgram(prog, s,"SV_Shutdown() required");
2082                 }
2083         }
2084         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2085                 if (host_client->active)
2086                         SV_DropClient(false); // server shutdown
2087
2088         NetConn_CloseServerPorts();
2089
2090         sv.active = false;
2091 //
2092 // clear structures
2093 //
2094         memset(&sv, 0, sizeof(sv));
2095         memset(svs.clients, 0, svs.maxclients*sizeof(client_t));
2096 end:
2097         SV_UnlockThreadMutex();
2098 }
2099
2100 /////////////////////////////////////////////////////
2101 // SV VM stuff
2102
2103 static void SVVM_begin_increase_edicts(prvm_prog_t *prog)
2104 {
2105         // links don't survive the transition, so unlink everything
2106         World_UnlinkAll(&sv.world);
2107 }
2108
2109 static void SVVM_end_increase_edicts(prvm_prog_t *prog)
2110 {
2111         int i;
2112         prvm_edict_t *ent;
2113
2114         // link every entity except world
2115         for (i = 1, ent = prog->edicts;i < prog->num_edicts;i++, ent++)
2116                 if (!ent->priv.server->free && !VectorCompare(PRVM_serveredictvector(ent, absmin), PRVM_serveredictvector(ent, absmax)))
2117                         SV_LinkEdict(ent);
2118 }
2119
2120 static void SVVM_init_edict(prvm_prog_t *prog, prvm_edict_t *e)
2121 {
2122         // LadyHavoc: for consistency set these here
2123         int num = PRVM_NUM_FOR_EDICT(e) - 1;
2124
2125         e->priv.server->move = false; // don't move on first frame
2126
2127         if (num >= 0 && num < svs.maxclients)
2128         {
2129                 // set colormap and team on newly created player entity
2130                 PRVM_serveredictfloat(e, colormap) = num + 1;
2131                 PRVM_serveredictfloat(e, team) = (svs.clients[num].colors & 15) + 1;
2132                 // set netname/clientcolors back to client values so that
2133                 // DP_SV_CLIENTNAME and DP_SV_CLIENTCOLORS will not immediately
2134                 // reset them
2135                 PRVM_serveredictstring(e, netname) = PRVM_SetEngineString(prog, svs.clients[num].name);
2136                 PRVM_serveredictfloat(e, clientcolors) = svs.clients[num].colors;
2137                 // NEXUIZ_PLAYERMODEL and NEXUIZ_PLAYERSKIN
2138                 PRVM_serveredictstring(e, playermodel) = PRVM_SetEngineString(prog, svs.clients[num].playermodel);
2139                 PRVM_serveredictstring(e, playerskin) = PRVM_SetEngineString(prog, svs.clients[num].playerskin);
2140                 // Assign netaddress (IP Address, etc)
2141                 if(svs.clients[num].netconnection != NULL)
2142                 {
2143                         // Acquire Readable Address
2144                         LHNETADDRESS_ToString(&svs.clients[num].netconnection->peeraddress, svs.clients[num].netaddress, sizeof(svs.clients[num].netaddress), false);
2145                         PRVM_serveredictstring(e, netaddress) = PRVM_SetEngineString(prog, svs.clients[num].netaddress);
2146                 }
2147                 else
2148                         PRVM_serveredictstring(e, netaddress) = PRVM_SetEngineString(prog, "null/botclient");
2149                 if(svs.clients[num].netconnection != NULL && svs.clients[num].netconnection->crypto.authenticated && svs.clients[num].netconnection->crypto.client_idfp[0])
2150                         PRVM_serveredictstring(e, crypto_idfp) = PRVM_SetEngineString(prog, svs.clients[num].netconnection->crypto.client_idfp);
2151                 else
2152                         PRVM_serveredictstring(e, crypto_idfp) = 0;
2153                 PRVM_serveredictfloat(e, crypto_idfp_signed) = (svs.clients[num].netconnection != NULL && svs.clients[num].netconnection->crypto.authenticated && svs.clients[num].netconnection->crypto.client_issigned);
2154                 if(svs.clients[num].netconnection != NULL && svs.clients[num].netconnection->crypto.authenticated && svs.clients[num].netconnection->crypto.client_keyfp[0])
2155                         PRVM_serveredictstring(e, crypto_keyfp) = PRVM_SetEngineString(prog, svs.clients[num].netconnection->crypto.client_keyfp);
2156                 else
2157                         PRVM_serveredictstring(e, crypto_keyfp) = 0;
2158                 if(svs.clients[num].netconnection != NULL && svs.clients[num].netconnection->crypto.authenticated && svs.clients[num].netconnection->crypto.server_keyfp[0])
2159                         PRVM_serveredictstring(e, crypto_mykeyfp) = PRVM_SetEngineString(prog, svs.clients[num].netconnection->crypto.server_keyfp);
2160                 else
2161                         PRVM_serveredictstring(e, crypto_mykeyfp) = 0;
2162                 if(svs.clients[num].netconnection != NULL && svs.clients[num].netconnection->crypto.authenticated && svs.clients[num].netconnection->crypto.use_aes)
2163                         PRVM_serveredictstring(e, crypto_encryptmethod) = PRVM_SetEngineString(prog, "AES128");
2164                 else
2165                         PRVM_serveredictstring(e, crypto_encryptmethod) = 0;
2166                 if(svs.clients[num].netconnection != NULL && svs.clients[num].netconnection->crypto.authenticated)
2167                         PRVM_serveredictstring(e, crypto_signmethod) = PRVM_SetEngineString(prog, "HMAC-SHA256");
2168                 else
2169                         PRVM_serveredictstring(e, crypto_signmethod) = 0;
2170         }
2171 }
2172
2173 static void SVVM_free_edict(prvm_prog_t *prog, prvm_edict_t *ed)
2174 {
2175         int i;
2176         int e;
2177
2178         World_UnlinkEdict(ed);          // unlink from world bsp
2179
2180         PRVM_serveredictstring(ed, model) = 0;
2181         PRVM_serveredictfloat(ed, takedamage) = 0;
2182         PRVM_serveredictfloat(ed, modelindex) = 0;
2183         PRVM_serveredictfloat(ed, colormap) = 0;
2184         PRVM_serveredictfloat(ed, skin) = 0;
2185         PRVM_serveredictfloat(ed, frame) = 0;
2186         VectorClear(PRVM_serveredictvector(ed, origin));
2187         VectorClear(PRVM_serveredictvector(ed, angles));
2188         PRVM_serveredictfloat(ed, nextthink) = -1;
2189         PRVM_serveredictfloat(ed, solid) = 0;
2190
2191         VM_RemoveEdictSkeleton(prog, ed);
2192         World_Physics_RemoveFromEntity(&sv.world, ed);
2193         World_Physics_RemoveJointFromEntity(&sv.world, ed);
2194
2195         // make sure csqc networking is aware of the removed entity
2196         e = PRVM_NUM_FOR_EDICT(ed);
2197         sv.csqcentityversion[e] = 0;
2198         for (i = 0;i < svs.maxclients;i++)
2199                 svs.clients[i].csqcentitysendflags[e] = 0xFFFFFF;
2200 }
2201
2202 static void SVVM_count_edicts(prvm_prog_t *prog)
2203 {
2204         int             i;
2205         prvm_edict_t    *ent;
2206         int             active, models, solid, step;
2207
2208         active = models = solid = step = 0;
2209         for (i=0 ; i<prog->num_edicts ; i++)
2210         {
2211                 ent = PRVM_EDICT_NUM(i);
2212                 if (ent->priv.server->free)
2213                         continue;
2214                 active++;
2215                 if (PRVM_serveredictfloat(ent, solid))
2216                         solid++;
2217                 if (PRVM_serveredictstring(ent, model))
2218                         models++;
2219                 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_STEP)
2220                         step++;
2221         }
2222
2223         Con_Printf("num_edicts:%3i\n", prog->num_edicts);
2224         Con_Printf("active    :%3i\n", active);
2225         Con_Printf("view      :%3i\n", models);
2226         Con_Printf("touch     :%3i\n", solid);
2227         Con_Printf("step      :%3i\n", step);
2228 }
2229
2230 static qbool SVVM_load_edict(prvm_prog_t *prog, prvm_edict_t *ent)
2231 {
2232         // remove things from different skill levels or deathmatch
2233         if (gamemode != GAME_TRANSFUSION) //Transfusion does this in QC
2234         {
2235                 if (deathmatch.integer)
2236                 {
2237                         if (((int)PRVM_serveredictfloat(ent, spawnflags) & SPAWNFLAG_NOT_DEATHMATCH))
2238                         {
2239                                 return false;
2240                         }
2241                 }
2242                 else if ((current_skill <= 0 && ((int)PRVM_serveredictfloat(ent, spawnflags) & SPAWNFLAG_NOT_EASY  ))
2243                         || (current_skill == 1 && ((int)PRVM_serveredictfloat(ent, spawnflags) & SPAWNFLAG_NOT_MEDIUM))
2244                         || (current_skill >= 2 && ((int)PRVM_serveredictfloat(ent, spawnflags) & SPAWNFLAG_NOT_HARD  )))
2245                 {
2246                         return false;
2247                 }
2248         }
2249         return true;
2250 }
2251
2252 static void SV_VM_Setup(void)
2253 {
2254         prvm_prog_t *prog = SVVM_prog;
2255         PRVM_Prog_Init(prog, cmd_local);
2256
2257         // allocate the mempools
2258         // TODO: move the magic numbers/constants into #defines [9/13/2006 Black]
2259         prog->progs_mempool = Mem_AllocPool("Server Progs", 0, NULL);
2260         prog->builtins = vm_sv_builtins;
2261         prog->numbuiltins = vm_sv_numbuiltins;
2262         prog->max_edicts = 512;
2263         if (sv.protocol == PROTOCOL_QUAKE)
2264                 prog->limit_edicts = 640; // before quake mission pack 1 this was 512
2265         else if (sv.protocol == PROTOCOL_QUAKEDP)
2266                 prog->limit_edicts = 2048; // guessing
2267         else if (sv.protocol == PROTOCOL_NEHAHRAMOVIE)
2268                 prog->limit_edicts = 2048; // guessing!
2269         else if (sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
2270                 prog->limit_edicts = 4096; // guessing!
2271         else
2272                 prog->limit_edicts = MAX_EDICTS;
2273         prog->reserved_edicts = svs.maxclients;
2274         prog->edictprivate_size = sizeof(edict_engineprivate_t);
2275         prog->name = "server";
2276         prog->extensionstring = vm_sv_extensions;
2277         prog->loadintoworld = true;
2278
2279         // all callbacks must be defined (pointers are not checked before calling)
2280         prog->begin_increase_edicts = SVVM_begin_increase_edicts;
2281         prog->end_increase_edicts   = SVVM_end_increase_edicts;
2282         prog->init_edict            = SVVM_init_edict;
2283         prog->free_edict            = SVVM_free_edict;
2284         prog->count_edicts          = SVVM_count_edicts;
2285         prog->load_edict            = SVVM_load_edict;
2286         prog->init_cmd              = SVVM_init_cmd;
2287         prog->reset_cmd             = SVVM_reset_cmd;
2288         prog->error_cmd             = Host_Error;
2289         prog->ExecuteProgram        = SVVM_ExecuteProgram;
2290
2291         PRVM_Prog_Load(prog, sv_progs.string, NULL, 0, SV_REQFUNCS, sv_reqfuncs, SV_REQFIELDS, sv_reqfields, SV_REQGLOBALS, sv_reqglobals);
2292
2293         // some mods compiled with scrambling compilers lack certain critical
2294         // global names and field names such as "self" and "time" and "nextthink"
2295         // so we have to set these offsets manually, matching the entvars_t
2296         // but we only do this if the prog header crc matches, otherwise it's totally freeform
2297         if (prog->progs_crc == PROGHEADER_CRC || prog->progs_crc == PROGHEADER_CRC_TENEBRAE)
2298         {
2299                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, modelindex);
2300                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, absmin);
2301                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, absmax);
2302                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ltime);
2303                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, movetype);
2304                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, solid);
2305                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, origin);
2306                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, oldorigin);
2307                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, velocity);
2308                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, angles);
2309                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, avelocity);
2310                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, punchangle);
2311                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, classname);
2312                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, model);
2313                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, frame);
2314                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, skin);
2315                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, effects);
2316                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, mins);
2317                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, maxs);
2318                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, size);
2319                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, touch);
2320                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, use);
2321                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, think);
2322                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, blocked);
2323                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, nextthink);
2324                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, groundentity);
2325                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, health);
2326                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, frags);
2327                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, weapon);
2328                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, weaponmodel);
2329                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, weaponframe);
2330                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, currentammo);
2331                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ammo_shells);
2332                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ammo_nails);
2333                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ammo_rockets);
2334                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ammo_cells);
2335                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, items);
2336                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, takedamage);
2337                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, chain);
2338                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, deadflag);
2339                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, view_ofs);
2340                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, button0);
2341                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, button1);
2342                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, button2);
2343                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, impulse);
2344                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, fixangle);
2345                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, v_angle);
2346                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, idealpitch);
2347                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, netname);
2348                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, enemy);
2349                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, flags);
2350                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, colormap);
2351                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, team);
2352                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, max_health);
2353                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, teleport_time);
2354                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, armortype);
2355                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, armorvalue);
2356                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, waterlevel);
2357                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, watertype);
2358                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ideal_yaw);
2359                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, yaw_speed);
2360                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, aiment);
2361                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, goalentity);
2362                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, spawnflags);
2363                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, target);
2364                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, targetname);
2365                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, dmg_take);
2366                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, dmg_save);
2367                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, dmg_inflictor);
2368                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, owner);
2369                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, movedir);
2370                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, message);
2371                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, sounds);
2372                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, noise);
2373                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, noise1);
2374                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, noise2);
2375                 PRVM_ED_FindFieldOffset_FromStruct(entvars_t, noise3);
2376                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, self);
2377                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, other);
2378                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, world);
2379                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, time);
2380                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, frametime);
2381                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, force_retouch);
2382                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, mapname);
2383                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, deathmatch);
2384                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, coop);
2385                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, teamplay);
2386                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, serverflags);
2387                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, total_secrets);
2388                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, total_monsters);
2389                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, found_secrets);
2390                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, killed_monsters);
2391                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm1);
2392                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm2);
2393                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm3);
2394                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm4);
2395                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm5);
2396                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm6);
2397                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm7);
2398                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm8);
2399                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm9);
2400                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm10);
2401                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm11);
2402                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm12);
2403                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm13);
2404                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm14);
2405                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm15);
2406                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm16);
2407                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_forward);
2408                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_up);
2409                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_right);
2410                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_allsolid);
2411                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_startsolid);
2412                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_fraction);
2413                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_endpos);
2414                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_normal);
2415                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_dist);
2416                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_ent);
2417                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inopen);
2418                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inwater);
2419                 PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, msg_entity);
2420 //              PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, main);
2421 //              PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, StartFrame);
2422 //              PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, PlayerPreThink);
2423 //              PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, PlayerPostThink);
2424 //              PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, ClientKill);
2425 //              PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, ClientConnect);
2426 //              PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, PutClientInServer);
2427 //              PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, ClientDisconnect);
2428 //              PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, SetNewParms);
2429 //              PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, SetChangeParms);
2430         }
2431         else
2432                 Con_DPrintf("%s: %s system vars have been modified (CRC %i != engine %i), will not load in other engines\n", prog->name, sv_progs.string, prog->progs_crc, PROGHEADER_CRC);
2433
2434         // OP_STATE is always supported on server because we add fields/globals for it
2435         prog->flag |= PRVM_OP_STATE;
2436
2437         VM_CustomStats_Clear();//[515]: csqc
2438
2439         SV_Prepare_CSQC();
2440 }
2441
2442 static void SV_CheckTimeouts(void)
2443 {
2444         int i;
2445
2446         // never timeout loopback connections
2447         for (i = (host_isclient.integer ? 1 : 0), host_client = &svs.clients[i]; i < svs.maxclients; i++, host_client++)
2448         {
2449                 if (host_client->netconnection && host.realtime > host_client->netconnection->timeout)
2450                 {
2451                         if (host_client->begun)
2452                                 SV_BroadcastPrintf("Client \"%s\" connection timed out\n", host_client->name);
2453                         else
2454                                 Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
2455
2456                         SV_DropClient(false);
2457                 }
2458         }
2459 }
2460
2461 /*
2462 ==================
2463 SV_TimeReport
2464
2465 Returns a time report string, for example for
2466 ==================
2467 */
2468 const char *SV_TimingReport(char *buf, size_t buflen)
2469 {
2470         return va(buf, buflen, "%.1f%% CPU, %.2f%% lost, offset avg %.1fms, max %.1fms, sdev %.1fms", svs.perf_cpuload * 100, svs.perf_lost * 100, svs.perf_offset_avg * 1000, svs.perf_offset_max * 1000, svs.perf_offset_sdev * 1000);
2471 }
2472
2473 extern cvar_t host_maxwait;
2474 extern cvar_t host_framerate;
2475 extern cvar_t cl_maxphysicsframesperserverframe;
2476 double SV_Frame(double time)
2477 {
2478         static double sv_timer;
2479         int i;
2480         char vabuf[1024];
2481         qbool playing = false;
2482
2483         if (!svs.threaded)
2484         {
2485                 svs.perf_acc_sleeptime = host.sleeptime;
2486                 svs.perf_acc_realtime += time;
2487
2488                 // Look for clients who have spawned
2489                 for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
2490                         if(host_client->begun && host_client->netconnection)
2491                                 playing = true;
2492
2493                 if(svs.perf_acc_realtime > 5)
2494                 {
2495                         svs.perf_cpuload = 1 - svs.perf_acc_sleeptime / svs.perf_acc_realtime;
2496                         svs.perf_lost = svs.perf_acc_lost / svs.perf_acc_realtime;
2497
2498                         if(svs.perf_acc_offset_samples > 0)
2499                         {
2500                                 svs.perf_offset_max = svs.perf_acc_offset_max;
2501                                 svs.perf_offset_avg = svs.perf_acc_offset / svs.perf_acc_offset_samples;
2502                                 svs.perf_offset_sdev = sqrt(svs.perf_acc_offset_squared / svs.perf_acc_offset_samples - svs.perf_offset_avg * svs.perf_offset_avg);
2503                         }
2504
2505                         if(svs.perf_lost > 0 && developer_extra.integer && playing) // only complain if anyone is looking
2506                                 Con_DPrintf("Server can't keep up: %s\n", SV_TimingReport(vabuf, sizeof(vabuf)));
2507                 }
2508
2509                 if(svs.perf_acc_realtime > 5 || sv.time < 10)
2510                 {
2511                         /*
2512                          * Don't accumulate time for the first 10 seconds of a match
2513                          * so things can settle
2514                          */
2515                         svs.perf_acc_realtime = svs.perf_acc_sleeptime =
2516                         svs.perf_acc_lost = svs.perf_acc_offset =
2517                         svs.perf_acc_offset_squared = svs.perf_acc_offset_max =
2518                         svs.perf_acc_offset_samples = host.sleeptime = 0;
2519                 }
2520
2521                 /*
2522                  * Receive packets on each main loop iteration, as the main loop may
2523                  * be undersleeping due to select() detecting a new packet
2524                  */
2525                 if (sv.active)
2526                 {
2527                         NetConn_ServerFrame();
2528                         SV_CheckTimeouts();
2529                 }
2530         }
2531
2532         /*
2533          * If the accumulator hasn't become positive, don't
2534          * run the frame. Everything that happens before this
2535          * point will happen even if we're sleeping this frame.
2536          */
2537         if((sv_timer += time) < 0)
2538                 return sv_timer;
2539
2540         // limit the frametime steps to no more than 100ms each
2541         if (sv_timer > 0.1)
2542         {
2543                 if (!svs.threaded)
2544                         svs.perf_acc_lost += (sv_timer - 0.1);
2545                 sv_timer = 0.1;
2546         }
2547
2548         if (sv.active && sv_timer > 0 && !svs.threaded)
2549         {
2550                 /*
2551                  * Execute one or more server frames, with an upper limit on how much
2552                  * execution time to spend on server frames to avoid freezing the game if
2553                  * the server is overloaded. This execution time limit means the game will
2554                  * slow down if the server is taking too long.
2555                  */
2556                 int framecount, framelimit = 1;
2557                 double advancetime, aborttime = 0;
2558                 float offset;
2559                 prvm_prog_t *prog = SVVM_prog;
2560
2561                 // run the world state
2562                 // don't allow simulation to run too fast or too slow or logic glitches can occur
2563
2564                 // stop running server frames if the wall time reaches this value
2565                 if (sys_ticrate.value <= 0)
2566                         advancetime = sv_timer;
2567                 else
2568                 {
2569                         advancetime = sys_ticrate.value;
2570                         // listen servers can run multiple server frames per client frame
2571                         framelimit = cl_maxphysicsframesperserverframe.integer;
2572                         aborttime = Sys_DirtyTime() + 0.1;
2573                 }
2574
2575                 if(host_timescale.value > 0 && host_timescale.value < 1)
2576                         advancetime = min(advancetime, 0.1 / host_timescale.value);
2577                 else
2578                         advancetime = min(advancetime, 0.1);
2579
2580                 if(advancetime > 0)
2581                 {
2582                         offset = Sys_DirtyTime() - host.dirtytime;
2583                         if (offset < 0 || offset >= 1800)
2584                                 offset = 0;
2585
2586                         offset += sv_timer;
2587                         ++svs.perf_acc_offset_samples;
2588                         svs.perf_acc_offset += offset;
2589                         svs.perf_acc_offset_squared += offset * offset;
2590                         
2591                         if(svs.perf_acc_offset_max < offset)
2592                                 svs.perf_acc_offset_max = offset;
2593                 }
2594
2595                 // only advance time if not paused
2596                 // the game also pauses in singleplayer when menu or console is used
2597                 sv.frametime = advancetime * host_timescale.value;
2598                 if (host_framerate.value)
2599                         sv.frametime = host_framerate.value;
2600                 if (sv.paused || host.paused)
2601                         sv.frametime = 0;
2602
2603                 for (framecount = 0; framecount < framelimit && sv_timer > 0; framecount++)
2604                 {
2605                         sv_timer -= advancetime;
2606
2607                         // move things around and think unless paused
2608                         if (sv.frametime)
2609                                 SV_Physics();
2610
2611                         // if this server frame took too long, break out of the loop
2612                         if (framelimit > 1 && Sys_DirtyTime() >= aborttime)
2613                                 break;
2614                 }
2615
2616                 R_TimeReport("serverphysics");
2617
2618                 // send all messages to the clients
2619                 SV_SendClientMessages();
2620
2621                 if (sv.paused == 1 && host.realtime > sv.pausedstart && sv.pausedstart > 0) {
2622                         prog->globals.fp[OFS_PARM0] = host.realtime - sv.pausedstart;
2623                         PRVM_serverglobalfloat(time) = sv.time;
2624                         prog->ExecuteProgram(prog, PRVM_serverfunction(SV_PausedTic), "QC function SV_PausedTic is missing");
2625                 }
2626
2627                 // send an heartbeat if enough time has passed since the last one
2628                 NetConn_Heartbeat(0);
2629                 R_TimeReport("servernetwork");
2630         }
2631         else
2632         {
2633                 // don't let r_speeds display jump around
2634                 R_TimeReport("serverphysics");
2635                 R_TimeReport("servernetwork");
2636         }
2637
2638         // if there is some time remaining from this frame, reset the timer
2639         if (sv_timer >= 0)
2640         {
2641                 if (!svs.threaded)
2642                         svs.perf_acc_lost += sv_timer;
2643                 sv_timer = 0;
2644         }
2645
2646         return sv_timer;
2647 }
2648
2649 static int SV_ThreadFunc(void *voiddata)
2650 {
2651         prvm_prog_t *prog = SVVM_prog;
2652         qbool playing = false;
2653         double sv_timer = 0;
2654         double sv_deltarealtime, sv_oldrealtime, sv_realtime;
2655         double wait;
2656         int i;
2657         char vabuf[1024];
2658         sv_realtime = Sys_DirtyTime();
2659         while (!svs.threadstop)
2660         {
2661                 // FIXME: we need to handle Host_Error in the server thread somehow
2662 //              if (setjmp(sv_abortframe))
2663 //                      continue;                       // something bad happened in the server game
2664
2665                 sv_oldrealtime = sv_realtime;
2666                 sv_realtime = Sys_DirtyTime();
2667                 sv_deltarealtime = sv_realtime - sv_oldrealtime;
2668                 if (sv_deltarealtime < 0 || sv_deltarealtime >= 1800) sv_deltarealtime = 0;
2669
2670                 sv_timer += sv_deltarealtime;
2671
2672                 svs.perf_acc_realtime += sv_deltarealtime;
2673
2674                 // at this point we start doing real server work, and must block on any client activity pertaining to the server (such as executing SV_SpawnServer)
2675                 SV_LockThreadMutex();
2676
2677                 // Look for clients who have spawned
2678                 playing = false;
2679                 if (sv.active)
2680                         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2681                                 if(host_client->begun)
2682                                         if(host_client->netconnection)
2683                                                 playing = true;
2684                 if(sv.time < 10)
2685                 {
2686                         // don't accumulate time for the first 10 seconds of a match
2687                         // so things can settle
2688                         svs.perf_acc_realtime = svs.perf_acc_sleeptime = svs.perf_acc_lost = svs.perf_acc_offset = svs.perf_acc_offset_squared = svs.perf_acc_offset_max = svs.perf_acc_offset_samples = 0;
2689                 }
2690                 else if(svs.perf_acc_realtime > 5)
2691                 {
2692                         svs.perf_cpuload = 1 - svs.perf_acc_sleeptime / svs.perf_acc_realtime;
2693                         svs.perf_lost = svs.perf_acc_lost / svs.perf_acc_realtime;
2694                         if(svs.perf_acc_offset_samples > 0)
2695                         {
2696                                 svs.perf_offset_max = svs.perf_acc_offset_max;
2697                                 svs.perf_offset_avg = svs.perf_acc_offset / svs.perf_acc_offset_samples;
2698                                 svs.perf_offset_sdev = sqrt(svs.perf_acc_offset_squared / svs.perf_acc_offset_samples - svs.perf_offset_avg * svs.perf_offset_avg);
2699                         }
2700                         if(svs.perf_lost > 0 && developer_extra.integer)
2701                                 if(playing)
2702                                         Con_DPrintf("Server can't keep up: %s\n", SV_TimingReport(vabuf, sizeof(vabuf)));
2703                         svs.perf_acc_realtime = svs.perf_acc_sleeptime = svs.perf_acc_lost = svs.perf_acc_offset = svs.perf_acc_offset_squared = svs.perf_acc_offset_max = svs.perf_acc_offset_samples = 0;
2704                 }
2705
2706                 // get new packets
2707                 if (sv.active)
2708                 {
2709                         NetConn_ServerFrame();
2710                         SV_CheckTimeouts();
2711                 }
2712
2713                 // if the accumulators haven't become positive yet, wait a while
2714                 wait = sv_timer * -1000000.0;
2715                 if (wait >= 1)
2716                 {
2717                         double time0, delta;
2718                         SV_UnlockThreadMutex(); // don't keep mutex locked while sleeping
2719                         if (host_maxwait.value <= 0)
2720                                 wait = min(wait, 1000000.0);
2721                         else
2722                                 wait = min(wait, host_maxwait.value * 1000.0);
2723                         if(wait < 1)
2724                                 wait = 1; // because we cast to int
2725                         time0 = Sys_DirtyTime();
2726                         Sys_Sleep((int)wait);
2727                         delta = Sys_DirtyTime() - time0;if (delta < 0 || delta >= 1800) delta = 0;
2728                         svs.perf_acc_sleeptime += delta;
2729                         continue;
2730                 }
2731
2732                 if (sv.active && sv_timer > 0)
2733                 {
2734                         // execute one server frame
2735                         double advancetime;
2736                         float offset;
2737
2738                         if (sys_ticrate.value <= 0)
2739                                 advancetime = min(sv_timer, 0.1); // don't step more than 100ms
2740                         else
2741                                 advancetime = sys_ticrate.value;
2742
2743                         if(advancetime > 0)
2744                         {
2745                                 offset = sv_timer + (Sys_DirtyTime() - sv_realtime); // LadyHavoc: FIXME: I don't understand this line
2746                                 ++svs.perf_acc_offset_samples;
2747                                 svs.perf_acc_offset += offset;
2748                                 svs.perf_acc_offset_squared += offset * offset;
2749                                 if(svs.perf_acc_offset_max < offset)
2750                                         svs.perf_acc_offset_max = offset;
2751                         }
2752
2753                         // only advance time if not paused
2754                         // the game also pauses in singleplayer when menu or console is used
2755                         sv.frametime = advancetime * host_timescale.value;
2756                         if (host_framerate.value)
2757                                 sv.frametime = host_framerate.value;
2758                         if (sv.paused || host.paused)
2759                                 sv.frametime = 0;
2760
2761                         sv_timer -= advancetime;
2762
2763                         // move things around and think unless paused
2764                         if (sv.frametime)
2765                                 SV_Physics();
2766
2767                         // send all messages to the clients
2768                         SV_SendClientMessages();
2769
2770                         if (sv.paused == 1 && sv_realtime > sv.pausedstart && sv.pausedstart > 0)
2771                         {
2772                                 PRVM_serverglobalfloat(time) = sv.time;
2773                                 prog->globals.fp[OFS_PARM0] = sv_realtime - sv.pausedstart;
2774                                 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_PausedTic), "QC function SV_PausedTic is missing");
2775                         }
2776
2777                         // send an heartbeat if enough time has passed since the last one
2778                         NetConn_Heartbeat(0);
2779
2780                 }
2781
2782                 // we're back to safe code now
2783                 SV_UnlockThreadMutex();
2784
2785                 // if there is some time remaining from this frame, reset the timers
2786                 if (sv_timer >= 0)
2787                 {
2788                         svs.perf_acc_lost += sv_timer;
2789                         sv_timer = 0;
2790                 }
2791         }
2792         return 0;
2793 }
2794
2795 void SV_StartThread(void)
2796 {
2797         if (!sv_threaded.integer || !Thread_HasThreads())
2798                 return;
2799         svs.threaded = true;
2800         svs.threadstop = false;
2801         svs.threadmutex = Thread_CreateMutex();
2802         svs.thread = Thread_CreateThread(SV_ThreadFunc, NULL);
2803 }
2804
2805 void SV_StopThread(void)
2806 {
2807         if (!svs.threaded)
2808                 return;
2809         svs.threadstop = true;
2810         Thread_WaitThread(svs.thread, 0);
2811         Thread_DestroyMutex(svs.threadmutex);
2812         svs.threaded = false;
2813 }