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