X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=sv_main.c;h=8529655eb6d668bc1a3909a11ab2386bdc2d9fcc;hp=4696c3ddbcaf99da1cb8219a43bdefb34f429e2d;hb=8e4d41ab346185f7e878e4eaf97c47dc70321e9d;hpb=9e0b57e36ca8bb835c2f49901b4493b1f3b5625a diff --git a/sv_main.c b/sv_main.c index 4696c3dd..8529655e 100644 --- a/sv_main.c +++ b/sv_main.c @@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "sv_demo.h" #include "libcurl.h" #include "csprogs.h" +#include "thread.h" static void SV_SaveEntFile_f(void); static void SV_StartDownload_f(void); @@ -30,21 +31,19 @@ static void SV_Download_f(void); static void SV_VM_Setup(void); extern cvar_t net_connecttimeout; -void VM_CustomStats_Clear (void); -void VM_SV_UpdateCustomStats (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats); - cvar_t sv_worldmessage = {CVAR_READONLY, "sv_worldmessage", "", "title of current level"}; cvar_t sv_worldname = {CVAR_READONLY, "sv_worldname", "", "name of current worldmodel"}; cvar_t sv_worldnamenoextension = {CVAR_READONLY, "sv_worldnamenoextension", "", "name of current worldmodel without extension"}; cvar_t sv_worldbasename = {CVAR_READONLY, "sv_worldbasename", "", "name of current worldmodel without maps/ prefix or extension"}; +cvar_t sv_disablenotify = {0, "sv_disablenotify", "1", "suppress broadcast prints when certain cvars are changed (CVAR_NOTIFY flag in engine code)"}; cvar_t coop = {0, "coop","0", "coop mode, 0 = no coop, 1 = coop mode, multiple players playing through the singleplayer game (coop mode also shuts off deathmatch)"}; cvar_t deathmatch = {0, "deathmatch","0", "deathmatch mode, values depend on mod but typically 0 = no deathmatch, 1 = normal deathmatch with respawning weapons, 2 = weapons stay (players can only pick up new weapons)"}; cvar_t fraglimit = {CVAR_NOTIFY, "fraglimit","0", "ends level if this many frags is reached by any player"}; cvar_t gamecfg = {0, "gamecfg", "0", "unused cvar in quake, can be used by mods"}; cvar_t noexit = {CVAR_NOTIFY, "noexit","0", "kills anyone attempting to use an exit"}; cvar_t nomonsters = {0, "nomonsters", "0", "unused cvar in quake, can be used by mods"}; -cvar_t pausable = {0, "pausable","1", "allow players to pause or not"}; +cvar_t pausable = {0, "pausable","1", "allow players to pause or not (otherwise, only the server admin can)"}; cvar_t pr_checkextension = {CVAR_READONLY, "pr_checkextension", "1", "indicates to QuakeC that the standard quakec extensions system is available (if 0, quakec should not attempt to use extensions)"}; cvar_t samelevel = {CVAR_NOTIFY, "samelevel","0", "repeats same level if level ends (due to timelimit or someone hitting an exit)"}; cvar_t skill = {0, "skill","1", "difficulty level of game, affects monster layouts in levels, 0 = easy, 1 = normal, 2 = hard, 3 = nightmare (same layout as hard but monsters fire twice)"}; @@ -53,6 +52,7 @@ cvar_t slowmo = {0, "slowmo", "1.0", "controls game speed, 0.5 is half speed, 2 cvar_t sv_accelerate = {0, "sv_accelerate", "10", "rate at which a player accelerates to sv_maxspeed"}; cvar_t sv_aim = {CVAR_SAVE, "sv_aim", "2", "maximum cosine angle for quake's vertical autoaim, a value above 1 completely disables the autoaim, quake used 0.93"}; cvar_t sv_airaccel_qw = {0, "sv_airaccel_qw", "1", "ratio of QW-style air control as opposed to simple acceleration; when < 0, the speed is clamped against the maximum allowed forward speed after the move"}; +cvar_t sv_airaccel_qw_stretchfactor = {0, "sv_airaccel_qw_stretchfactor", "0", "when set, the maximum acceleration increase the player may get compared to forward-acceleration when strafejumping"}; cvar_t sv_airaccel_sideways_friction = {0, "sv_airaccel_sideways_friction", "", "anti-sideways movement stabilization (reduces speed gain when zigzagging); when < 0, only so much friction is applied that braking (by accelerating backwards) cannot be stronger"}; cvar_t sv_airaccelerate = {0, "sv_airaccelerate", "-1", "rate at which a player accelerates to sv_maxairspeed while in the air, if less than 0 the sv_accelerate variable is used instead"}; cvar_t sv_airstopaccelerate = {0, "sv_airstopaccelerate", "0", "when set, replacement for sv_airaccelerate when moving backwards"}; @@ -106,8 +106,8 @@ cvar_t sv_gameplayfix_grenadebouncedownslopes = {0, "sv_gameplayfix_grenadebounc cvar_t sv_gameplayfix_multiplethinksperframe = {0, "sv_gameplayfix_multiplethinksperframe", "1", "allows entities to think more often than the server framerate, primarily useful for very high fire rate weapons"}; cvar_t sv_gameplayfix_noairborncorpse = {0, "sv_gameplayfix_noairborncorpse", "1", "causes entities (corpses, items, etc) sitting ontop of moving entities (players) to fall when the moving entity (player) is no longer supporting them"}; cvar_t sv_gameplayfix_noairborncorpse_allowsuspendeditems = {0, "sv_gameplayfix_noairborncorpse_allowsuspendeditems", "1", "causes entities sitting ontop of objects that are instantaneously remove to float in midair (special hack to allow a common level design trick for floating items)"}; -cvar_t sv_gameplayfix_nudgeoutofsolid = {0, "sv_gameplayfix_nudgeoutofsolid", "1", "attempts to fix physics errors (where an object ended up in solid for some reason)"}; -cvar_t sv_gameplayfix_nudgeoutofsolid_bias = {0, "sv_gameplayfix_nudgeoutofsolid_bias", "0", "over-correction on nudgeoutofsolid logic, to prevent constant contact"}; +cvar_t sv_gameplayfix_nudgeoutofsolid = {0, "sv_gameplayfix_nudgeoutofsolid", "0", "attempts to fix physics errors (where an object ended up in solid for some reason)"}; +cvar_t sv_gameplayfix_nudgeoutofsolid_separation = {0, "sv_gameplayfix_nudgeoutofsolid_separation", "0.03125", "keep objects this distance apart to prevent collision issues on seams"}; cvar_t sv_gameplayfix_q2airaccelerate = {0, "sv_gameplayfix_q2airaccelerate", "0", "Quake2-style air acceleration"}; cvar_t sv_gameplayfix_nogravityonground = {0, "sv_gameplayfix_nogravityonground", "0", "turn off gravity when on ground (to get rid of sliding)"}; cvar_t sv_gameplayfix_setmodelrealbox = {0, "sv_gameplayfix_setmodelrealbox", "1", "fixes a bug in Quake that made setmodel always set the entity box to ('-16 -16 -16', '16 16 16') rather than properly checking the model box, breaks some poorly coded mods"}; @@ -119,6 +119,10 @@ cvar_t sv_gameplayfix_nostepmoveonsteepslopes = {0, "sv_gameplayfix_nostepmoveon cvar_t sv_gameplayfix_swiminbmodels = {0, "sv_gameplayfix_swiminbmodels", "1", "causes pointcontents (used to determine if you are in a liquid) to check bmodel entities as well as the world model, so you can swim around in (possibly moving) water bmodel entities"}; cvar_t sv_gameplayfix_upwardvelocityclearsongroundflag = {0, "sv_gameplayfix_upwardvelocityclearsongroundflag", "1", "prevents monsters, items, and most other objects from being stuck to the floor when pushed around by damage, and other situations in mods"}; cvar_t sv_gameplayfix_downtracesupportsongroundflag = {0, "sv_gameplayfix_downtracesupportsongroundflag", "1", "prevents very short moves from clearing onground (which may make the player stick to the floor at high netfps)"}; +cvar_t sv_gameplayfix_q1bsptracelinereportstexture = {0, "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"}; +cvar_t sv_gameplayfix_unstickplayers = {0, "sv_gameplayfix_unstickplayers", "1", "big hack to try and fix the rare case of MOVETYPE_WALK entities getting stuck in the world clipping hull."}; +cvar_t sv_gameplayfix_unstickentities = {0, "sv_gameplayfix_unstickentities", "1", "hack to check if entities are crossing world collision hull and try to move them to the right position"}; +cvar_t sv_gameplayfix_fixedcheckwatertransition = {0, "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)"}; cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"}; cvar_t sv_idealpitchscale = {0, "sv_idealpitchscale","0.8", "how much to look up/down slopes and stairs when not using freelook"}; cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"}; @@ -146,9 +150,11 @@ cvar_t sv_warsowbunny_topspeed = {0, "sv_warsowbunny_topspeed", "925", "soft spe cvar_t sv_warsowbunny_turnaccel = {0, "sv_warsowbunny_turnaccel", "0", "max sharpness of turns (also master switch for the sv_warsowbunny_* mode; set this to 9 to enable)"}; cvar_t sv_warsowbunny_backtosideratio = {0, "sv_warsowbunny_backtosideratio", "0.8", "lower values make it easier to change direction without losing speed; the drawback is \"understeering\" in sharp turns"}; cvar_t sv_onlycsqcnetworking = {0, "sv_onlycsqcnetworking", "0", "disables legacy entity networking code for higher performance (except on clients, which can still be legacy)"}; +cvar_t sv_areadebug = {0, "sv_areadebug", "0", "disables physics culling for debugging purposes (only for development)"}; cvar_t sys_ticrate = {CVAR_SAVE, "sys_ticrate","0.0138889", "how long a server frame is in seconds, 0.05 is 20fps server rate, 0.1 is 10fps (can not be set higher than 0.1), 0 runs as many server frames as possible (makes games against bots a little smoother, overwhelms network players), 0.0138889 matches QuakeWorld physics"}; cvar_t teamplay = {CVAR_NOTIFY, "teamplay","0", "teamplay mode, values depend on mod but typically 0 = no teams, 1 = no team damage no self damage, 2 = team damage and self damage, some mods support 3 = no team damage but can damage self"}; cvar_t timelimit = {CVAR_NOTIFY, "timelimit","0", "ends level at this time (in minutes)"}; +cvar_t sv_threaded = {0, "sv_threaded", "0", "enables a separate thread for server code, improving performance, especially when hosting a game while playing, EXPERIMENTAL, may be crashy"}; cvar_t saved1 = {CVAR_SAVE, "saved1", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"}; cvar_t saved2 = {CVAR_SAVE, "saved2", "0", "unused cvar in quake that is saved to config.cfg on exit, can be used by mods"}; @@ -238,9 +244,10 @@ static const char *standardeffectnames[EFFECT_TOTAL] = "SVC_PARTICLE" }; -#define SV_REQFUNCS (sizeof(sv_reqfuncs) / sizeof(const char *)) - +#define SV_REQFUNCS 0 #define sv_reqfuncs NULL + +//#define SV_REQFUNCS (sizeof(sv_reqfuncs) / sizeof(const char *)) //static const char *sv_reqfuncs[] = { //}; @@ -248,202 +255,165 @@ static const char *standardeffectnames[EFFECT_TOTAL] = prvm_required_field_t sv_reqfields[] = { - {ev_entity, "aiment"}, - {ev_entity, "chain"}, - {ev_entity, "clientcamera"}, - {ev_entity, "cursor_trace_ent"}, - {ev_entity, "drawonlytoclient"}, - {ev_entity, "enemy"}, - {ev_entity, "exteriormodeltoclient"}, - {ev_entity, "groundentity"}, - {ev_entity, "nodrawtoclient"}, - {ev_entity, "tag_entity"}, - {ev_entity, "viewmodelforclient"}, - {ev_float, "SendFlags"}, - {ev_float, "Version"}, - {ev_float, "alpha"}, - {ev_float, "ammo_cells1"}, - {ev_float, "ammo_lava_nails"}, - {ev_float, "ammo_multi_rockets"}, - {ev_float, "ammo_nails1"}, - {ev_float, "ammo_plasma"}, - {ev_float, "ammo_rockets1"}, - {ev_float, "ammo_shells1"}, - {ev_float, "bouncefactor"}, - {ev_float, "bouncestop"}, - {ev_float, "button3"}, - {ev_float, "button4"}, - {ev_float, "button5"}, - {ev_float, "button6"}, - {ev_float, "button7"}, - {ev_float, "button8"}, - {ev_float, "button9"}, - {ev_float, "button10"}, - {ev_float, "button11"}, - {ev_float, "button12"}, - {ev_float, "button13"}, - {ev_float, "button14"}, - {ev_float, "button15"}, - {ev_float, "button16"}, - {ev_float, "buttonchat"}, - {ev_float, "buttonuse"}, - {ev_float, "clientcolors"}, - {ev_float, "color"}, - {ev_float, "colormod"}, - {ev_float, "cursor_active"}, - {ev_float, "cursor_screen"}, - {ev_float, "cursor_trace_endpos"}, - {ev_float, "cursor_trace_start"}, - {ev_float, "dimension_hit"}, - {ev_float, "dimension_solid"}, - {ev_float, "disableclientprediction"}, - {ev_float, "discardabledemo"}, - {ev_float, "dphitcontentsmask"}, - {ev_float, "fatness"}, - {ev_float, "forceshader"}, - {ev_float, "frame"}, - {ev_float, "frame1time"}, - {ev_float, "frame2"}, - {ev_float, "frame2time"}, - {ev_float, "frame3"}, - {ev_float, "frame3time"}, - {ev_float, "frame4"}, - {ev_float, "frame4time"}, - {ev_float, "fullbright"}, - {ev_float, "glow_color"}, - {ev_float, "glow_size"}, - {ev_float, "glow_trail"}, - {ev_float, "gravity"}, - {ev_float, "hull"}, - {ev_float, "ideal_yaw"}, - {ev_float, "idealpitch"}, - {ev_float, "items2"}, - {ev_float, "jointtype"}, - {ev_float, "lerpfrac"}, - {ev_float, "lerpfrac3"}, - {ev_float, "lerpfrac4"}, - {ev_float, "light_lev"}, - {ev_float, "mass"}, - {ev_float, "modelflags"}, - {ev_float, "modelindex"}, - {ev_float, "movetype"}, - {ev_float, "nextthink"}, - {ev_float, "packetloss"}, - {ev_float, "pflags"}, - {ev_float, "ping"}, - {ev_float, "ping_packetloss"}, - {ev_float, "ping_movementloss"}, - {ev_float, "pitch_speed"}, - {ev_float, "pmodel"}, - {ev_float, "renderamt"}, - {ev_float, "renderflags"}, - {ev_float, "rendermode"}, - {ev_float, "scale"}, - {ev_float, "sendcomplexanimation"}, - {ev_float, "shadertime"}, - {ev_float, "skeletonindex"}, - {ev_float, "solid"}, - {ev_float, "style"}, - {ev_float, "tag_index"}, - {ev_float, "think"}, - {ev_float, "userwavefunc_param0"}, - {ev_float, "userwavefunc_param1"}, - {ev_float, "userwavefunc_param2"}, - {ev_float, "userwavefunc_param3"}, - {ev_float, "viewzoom"}, - {ev_float, "yaw_speed"}, - {ev_function, "SendEntity"}, - {ev_function, "camera_transform"}, - {ev_function, "contentstransition"}, - {ev_function, "customizeentityforclient"}, - {ev_function, "movetypesteplandevent"}, - {ev_string, "classname"}, - {ev_string, "clientstatus"}, - {ev_string, "crypto_encryptmethod"}, - {ev_string, "crypto_idfp"}, - {ev_string, "crypto_keyfp"}, - {ev_string, "crypto_mykeyfp"}, - {ev_string, "crypto_signmethod"}, - {ev_string, "message"}, - {ev_string, "netaddress"}, - {ev_string, "playermodel"}, - {ev_string, "playerskin"}, - {ev_vector, "angles"}, - {ev_vector, "angles"}, - {ev_vector, "avelocity"}, -// {ev_vector, "axis_forward"}, -// {ev_vector, "axis_left"}, -// {ev_vector, "axis_up"}, - {ev_vector, "glowmod"}, - {ev_vector, "maxs"}, - {ev_vector, "mins"}, - {ev_vector, "movedir"}, - {ev_vector, "movement"}, - {ev_vector, "origin"}, - {ev_vector, "punchvector"}, -// {ev_vector, "spinvelocity"}, - {ev_vector, "velocity"}, +#define PRVM_DECLARE_serverglobalfloat(x) +#define PRVM_DECLARE_serverglobalvector(x) +#define PRVM_DECLARE_serverglobalstring(x) +#define PRVM_DECLARE_serverglobaledict(x) +#define PRVM_DECLARE_serverglobalfunction(x) +#define PRVM_DECLARE_clientglobalfloat(x) +#define PRVM_DECLARE_clientglobalvector(x) +#define PRVM_DECLARE_clientglobalstring(x) +#define PRVM_DECLARE_clientglobaledict(x) +#define PRVM_DECLARE_clientglobalfunction(x) +#define PRVM_DECLARE_menuglobalfloat(x) +#define PRVM_DECLARE_menuglobalvector(x) +#define PRVM_DECLARE_menuglobalstring(x) +#define PRVM_DECLARE_menuglobaledict(x) +#define PRVM_DECLARE_menuglobalfunction(x) +#define PRVM_DECLARE_serverfieldfloat(x) {ev_float, #x}, +#define PRVM_DECLARE_serverfieldvector(x) {ev_vector, #x}, +#define PRVM_DECLARE_serverfieldstring(x) {ev_string, #x}, +#define PRVM_DECLARE_serverfieldedict(x) {ev_entity, #x}, +#define PRVM_DECLARE_serverfieldfunction(x) {ev_function, #x}, +#define PRVM_DECLARE_clientfieldfloat(x) +#define PRVM_DECLARE_clientfieldvector(x) +#define PRVM_DECLARE_clientfieldstring(x) +#define PRVM_DECLARE_clientfieldedict(x) +#define PRVM_DECLARE_clientfieldfunction(x) +#define PRVM_DECLARE_menufieldfloat(x) +#define PRVM_DECLARE_menufieldvector(x) +#define PRVM_DECLARE_menufieldstring(x) +#define PRVM_DECLARE_menufieldedict(x) +#define PRVM_DECLARE_menufieldfunction(x) +#define PRVM_DECLARE_serverfunction(x) +#define PRVM_DECLARE_clientfunction(x) +#define PRVM_DECLARE_menufunction(x) +#define PRVM_DECLARE_field(x) +#define PRVM_DECLARE_global(x) +#define PRVM_DECLARE_function(x) +#include "prvm_offsets.h" +#undef PRVM_DECLARE_serverglobalfloat +#undef PRVM_DECLARE_serverglobalvector +#undef PRVM_DECLARE_serverglobalstring +#undef PRVM_DECLARE_serverglobaledict +#undef PRVM_DECLARE_serverglobalfunction +#undef PRVM_DECLARE_clientglobalfloat +#undef PRVM_DECLARE_clientglobalvector +#undef PRVM_DECLARE_clientglobalstring +#undef PRVM_DECLARE_clientglobaledict +#undef PRVM_DECLARE_clientglobalfunction +#undef PRVM_DECLARE_menuglobalfloat +#undef PRVM_DECLARE_menuglobalvector +#undef PRVM_DECLARE_menuglobalstring +#undef PRVM_DECLARE_menuglobaledict +#undef PRVM_DECLARE_menuglobalfunction +#undef PRVM_DECLARE_serverfieldfloat +#undef PRVM_DECLARE_serverfieldvector +#undef PRVM_DECLARE_serverfieldstring +#undef PRVM_DECLARE_serverfieldedict +#undef PRVM_DECLARE_serverfieldfunction +#undef PRVM_DECLARE_clientfieldfloat +#undef PRVM_DECLARE_clientfieldvector +#undef PRVM_DECLARE_clientfieldstring +#undef PRVM_DECLARE_clientfieldedict +#undef PRVM_DECLARE_clientfieldfunction +#undef PRVM_DECLARE_menufieldfloat +#undef PRVM_DECLARE_menufieldvector +#undef PRVM_DECLARE_menufieldstring +#undef PRVM_DECLARE_menufieldedict +#undef PRVM_DECLARE_menufieldfunction +#undef PRVM_DECLARE_serverfunction +#undef PRVM_DECLARE_clientfunction +#undef PRVM_DECLARE_menufunction +#undef PRVM_DECLARE_field +#undef PRVM_DECLARE_global +#undef PRVM_DECLARE_function }; #define SV_REQGLOBALS (sizeof(sv_reqglobals) / sizeof(prvm_required_field_t)) prvm_required_field_t sv_reqglobals[] = { - {ev_entity, "self"}, - {ev_entity, "trace_ent"}, - {ev_entity, "trace_networkentity"}, - {ev_float, "coop"}, - {ev_float, "deathmatch"}, - {ev_float, "dmg_save"}, - {ev_float, "dmg_take"}, - {ev_float, "drawfont"}, - {ev_float, "drawfontscale"}, - {ev_float, "gettaginfo_parent"}, - {ev_float, "intermission"}, - {ev_float, "particles_alphamax"}, - {ev_float, "particles_alphamin"}, - {ev_float, "require_spawnfunc_prefix"}, - {ev_float, "sb_showscores"}, - {ev_float, "serverdeltatime"}, - {ev_float, "serverprevtime"}, - {ev_float, "servertime"}, - {ev_float, "time"}, - {ev_float, "trace_allsolid"}, - {ev_float, "trace_dphitcontents"}, - {ev_float, "trace_dphitq3surfaceflags"}, - {ev_float, "trace_dpstartcontents"}, - {ev_float, "trace_fraction"}, - {ev_float, "trace_inopen"}, - {ev_float, "trace_inwater"}, - {ev_float, "trace_plane_dist"}, - {ev_float, "trace_startsolid"}, - {ev_float, "transparent_offset"}, - {ev_string, "SV_InitCmd"}, - {ev_string, "gettaginfo_name"}, - {ev_string, "trace_dphittexturename"}, - {ev_string, "worldstatus"}, - {ev_vector, "dmg_origin"}, - {ev_vector, "gettaginfo_forward"}, - {ev_vector, "gettaginfo_offset"}, - {ev_vector, "gettaginfo_right"}, - {ev_vector, "gettaginfo_up"}, - {ev_vector, "particles_colormax"}, - {ev_vector, "particles_colormin"}, - {ev_vector, "trace_endpos"}, - {ev_vector, "trace_plane_normal"}, - {ev_vector, "v_forward"}, - {ev_vector, "v_right"}, - {ev_vector, "v_up"}, - {ev_vector, "view_angles"}, - {ev_vector, "view_punchangle"}, - {ev_vector, "view_punchvector"}, +#define PRVM_DECLARE_serverglobalfloat(x) {ev_float, #x}, +#define PRVM_DECLARE_serverglobalvector(x) {ev_vector, #x}, +#define PRVM_DECLARE_serverglobalstring(x) {ev_string, #x}, +#define PRVM_DECLARE_serverglobaledict(x) {ev_entity, #x}, +#define PRVM_DECLARE_serverglobalfunction(x) {ev_function, #x}, +#define PRVM_DECLARE_clientglobalfloat(x) +#define PRVM_DECLARE_clientglobalvector(x) +#define PRVM_DECLARE_clientglobalstring(x) +#define PRVM_DECLARE_clientglobaledict(x) +#define PRVM_DECLARE_clientglobalfunction(x) +#define PRVM_DECLARE_menuglobalfloat(x) +#define PRVM_DECLARE_menuglobalvector(x) +#define PRVM_DECLARE_menuglobalstring(x) +#define PRVM_DECLARE_menuglobaledict(x) +#define PRVM_DECLARE_menuglobalfunction(x) +#define PRVM_DECLARE_serverfieldfloat(x) +#define PRVM_DECLARE_serverfieldvector(x) +#define PRVM_DECLARE_serverfieldstring(x) +#define PRVM_DECLARE_serverfieldedict(x) +#define PRVM_DECLARE_serverfieldfunction(x) +#define PRVM_DECLARE_clientfieldfloat(x) +#define PRVM_DECLARE_clientfieldvector(x) +#define PRVM_DECLARE_clientfieldstring(x) +#define PRVM_DECLARE_clientfieldedict(x) +#define PRVM_DECLARE_clientfieldfunction(x) +#define PRVM_DECLARE_menufieldfloat(x) +#define PRVM_DECLARE_menufieldvector(x) +#define PRVM_DECLARE_menufieldstring(x) +#define PRVM_DECLARE_menufieldedict(x) +#define PRVM_DECLARE_menufieldfunction(x) +#define PRVM_DECLARE_serverfunction(x) +#define PRVM_DECLARE_clientfunction(x) +#define PRVM_DECLARE_menufunction(x) +#define PRVM_DECLARE_field(x) +#define PRVM_DECLARE_global(x) +#define PRVM_DECLARE_function(x) +#include "prvm_offsets.h" +#undef PRVM_DECLARE_serverglobalfloat +#undef PRVM_DECLARE_serverglobalvector +#undef PRVM_DECLARE_serverglobalstring +#undef PRVM_DECLARE_serverglobaledict +#undef PRVM_DECLARE_serverglobalfunction +#undef PRVM_DECLARE_clientglobalfloat +#undef PRVM_DECLARE_clientglobalvector +#undef PRVM_DECLARE_clientglobalstring +#undef PRVM_DECLARE_clientglobaledict +#undef PRVM_DECLARE_clientglobalfunction +#undef PRVM_DECLARE_menuglobalfloat +#undef PRVM_DECLARE_menuglobalvector +#undef PRVM_DECLARE_menuglobalstring +#undef PRVM_DECLARE_menuglobaledict +#undef PRVM_DECLARE_menuglobalfunction +#undef PRVM_DECLARE_serverfieldfloat +#undef PRVM_DECLARE_serverfieldvector +#undef PRVM_DECLARE_serverfieldstring +#undef PRVM_DECLARE_serverfieldedict +#undef PRVM_DECLARE_serverfieldfunction +#undef PRVM_DECLARE_clientfieldfloat +#undef PRVM_DECLARE_clientfieldvector +#undef PRVM_DECLARE_clientfieldstring +#undef PRVM_DECLARE_clientfieldedict +#undef PRVM_DECLARE_clientfieldfunction +#undef PRVM_DECLARE_menufieldfloat +#undef PRVM_DECLARE_menufieldvector +#undef PRVM_DECLARE_menufieldstring +#undef PRVM_DECLARE_menufieldedict +#undef PRVM_DECLARE_menufieldfunction +#undef PRVM_DECLARE_serverfunction +#undef PRVM_DECLARE_clientfunction +#undef PRVM_DECLARE_menufunction +#undef PRVM_DECLARE_field +#undef PRVM_DECLARE_global +#undef PRVM_DECLARE_function }; //============================================================================ -void SV_AreaStats_f(void) +static void SV_AreaStats_f(void) { World_PrintAreaStats(&sv.world, "server"); } @@ -460,6 +430,7 @@ void SV_Init (void) extern cvar_t csqc_progname; //[515]: csqc crc check and right csprogs name according to progs.dat extern cvar_t csqc_progcrc; extern cvar_t csqc_progsize; + extern cvar_t csqc_usedemoprogs; Cvar_RegisterVariable(&sv_worldmessage); Cvar_RegisterVariable(&sv_worldname); @@ -469,12 +440,14 @@ void SV_Init (void) Cvar_RegisterVariable (&csqc_progname); Cvar_RegisterVariable (&csqc_progcrc); Cvar_RegisterVariable (&csqc_progsize); + Cvar_RegisterVariable (&csqc_usedemoprogs); Cmd_AddCommand("sv_saveentfile", SV_SaveEntFile_f, "save map entities to .ent file (to allow external editing)"); Cmd_AddCommand("sv_areastats", SV_AreaStats_f, "prints statistics on entity culling during collision traces"); Cmd_AddCommand_WithClientCommand("sv_startdownload", NULL, SV_StartDownload_f, "begins sending a file to the client (network protocol use only)"); Cmd_AddCommand_WithClientCommand("download", NULL, SV_Download_f, "downloads a specified file from the server"); + Cvar_RegisterVariable (&sv_disablenotify); Cvar_RegisterVariable (&coop); Cvar_RegisterVariable (&deathmatch); Cvar_RegisterVariable (&fraglimit); @@ -489,6 +462,7 @@ void SV_Init (void) Cvar_RegisterVariable (&sv_accelerate); Cvar_RegisterVariable (&sv_aim); Cvar_RegisterVariable (&sv_airaccel_qw); + Cvar_RegisterVariable (&sv_airaccel_qw_stretchfactor); Cvar_RegisterVariable (&sv_airaccel_sideways_friction); Cvar_RegisterVariable (&sv_airaccelerate); Cvar_RegisterVariable (&sv_airstopaccelerate); @@ -543,7 +517,7 @@ void SV_Init (void) Cvar_RegisterVariable (&sv_gameplayfix_noairborncorpse); Cvar_RegisterVariable (&sv_gameplayfix_noairborncorpse_allowsuspendeditems); Cvar_RegisterVariable (&sv_gameplayfix_nudgeoutofsolid); - Cvar_RegisterVariable (&sv_gameplayfix_nudgeoutofsolid_bias); + Cvar_RegisterVariable (&sv_gameplayfix_nudgeoutofsolid_separation); Cvar_RegisterVariable (&sv_gameplayfix_q2airaccelerate); Cvar_RegisterVariable (&sv_gameplayfix_nogravityonground); Cvar_RegisterVariable (&sv_gameplayfix_setmodelrealbox); @@ -555,6 +529,10 @@ void SV_Init (void) Cvar_RegisterVariable (&sv_gameplayfix_swiminbmodels); Cvar_RegisterVariable (&sv_gameplayfix_upwardvelocityclearsongroundflag); Cvar_RegisterVariable (&sv_gameplayfix_downtracesupportsongroundflag); + Cvar_RegisterVariable (&sv_gameplayfix_q1bsptracelinereportstexture); + Cvar_RegisterVariable (&sv_gameplayfix_unstickplayers); + Cvar_RegisterVariable (&sv_gameplayfix_unstickentities); + Cvar_RegisterVariable (&sv_gameplayfix_fixedcheckwatertransition); Cvar_RegisterVariable (&sv_gravity); Cvar_RegisterVariable (&sv_idealpitchscale); Cvar_RegisterVariable (&sv_jumpstep); @@ -582,9 +560,11 @@ void SV_Init (void) Cvar_RegisterVariable (&sv_warsowbunny_turnaccel); Cvar_RegisterVariable (&sv_warsowbunny_backtosideratio); Cvar_RegisterVariable (&sv_onlycsqcnetworking); + Cvar_RegisterVariable (&sv_areadebug); Cvar_RegisterVariable (&sys_ticrate); Cvar_RegisterVariable (&teamplay); Cvar_RegisterVariable (&timelimit); + Cvar_RegisterVariable (&sv_threaded); Cvar_RegisterVariable (&saved1); Cvar_RegisterVariable (&saved2); @@ -631,12 +611,13 @@ void SV_Init (void) static void SV_SaveEntFile_f(void) { + char vabuf[1024]; if (!sv.active || !sv.worldmodel) { Con_Print("Not running a server\n"); return; } - FS_WriteFile(va("%s.ent", sv.worldnamenoextension), sv.worldmodel->brush.entities, (fs_offset_t)strlen(sv.worldmodel->brush.entities)); + FS_WriteFile(va(vabuf, sizeof(vabuf), "%s.ent", sv.worldnamenoextension), sv.worldmodel->brush.entities, (fs_offset_t)strlen(sv.worldmodel->brush.entities)); } @@ -725,9 +706,13 @@ Larger attenuations will drop off. (max 4 attenuation) ================== */ -void SV_StartSound (prvm_edict_t *entity, int channel, const char *sample, int volume, float attenuation) +void SV_StartSound (prvm_edict_t *entity, int channel, const char *sample, int volume, float attenuation, qboolean reliable, float speed) { - int sound_num, field_mask, i, ent; + prvm_prog_t *prog = SVVM_prog; + sizebuf_t *dest; + int sound_num, field_mask, i, ent, speed4000; + + dest = (reliable ? &sv.reliable_datagram : &sv.datagram); if (volume < 0 || volume > 255) { @@ -741,12 +726,14 @@ void SV_StartSound (prvm_edict_t *entity, int channel, const char *sample, int v return; } - if (channel < 0 || channel > 7) + if (!IS_CHAN(channel)) { Con_Printf ("SV_StartSound: channel = %i\n", channel); return; } + channel = CHAN_ENGINE2NET(channel); + if (sv.datagram.cursize > MAX_PACKETFRAGMENT-21) return; @@ -757,37 +744,45 @@ void SV_StartSound (prvm_edict_t *entity, int channel, const char *sample, int v ent = PRVM_NUM_FOR_EDICT(entity); + speed4000 = (int)floor(speed * 4000.0f + 0.5f); field_mask = 0; if (volume != DEFAULT_SOUND_PACKET_VOLUME) field_mask |= SND_VOLUME; if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION) field_mask |= SND_ATTENUATION; - if (ent >= 8192) + if (speed4000 && speed4000 != 4000) + field_mask |= SND_SPEEDUSHORT4000; + if (ent >= 8192 || channel < 0 || channel > 7) field_mask |= SND_LARGEENTITY; - if (sound_num >= 256 || channel >= 8) + if (sound_num >= 256) field_mask |= SND_LARGESOUND; // directed messages go only to the entity they are targeted on - MSG_WriteByte (&sv.datagram, svc_sound); - MSG_WriteByte (&sv.datagram, field_mask); + MSG_WriteByte (dest, svc_sound); + MSG_WriteByte (dest, field_mask); if (field_mask & SND_VOLUME) - MSG_WriteByte (&sv.datagram, volume); + MSG_WriteByte (dest, volume); if (field_mask & SND_ATTENUATION) - MSG_WriteByte (&sv.datagram, (int)(attenuation*64)); + MSG_WriteByte (dest, (int)(attenuation*64)); + if (field_mask & SND_SPEEDUSHORT4000) + MSG_WriteShort (dest, speed4000); if (field_mask & SND_LARGEENTITY) { - MSG_WriteShort (&sv.datagram, ent); - MSG_WriteByte (&sv.datagram, channel); + MSG_WriteShort (dest, ent); + MSG_WriteChar (dest, channel); } else - MSG_WriteShort (&sv.datagram, (ent<<3) | channel); + MSG_WriteShort (dest, (ent<<3) | channel); if ((field_mask & SND_LARGESOUND) || sv.protocol == PROTOCOL_NEHAHRABJP2) - MSG_WriteShort (&sv.datagram, sound_num); + MSG_WriteShort (dest, sound_num); else - MSG_WriteByte (&sv.datagram, sound_num); + MSG_WriteByte (dest, sound_num); for (i = 0;i < 3;i++) - MSG_WriteCoord (&sv.datagram, entity->fields.server->origin[i]+0.5*(entity->fields.server->mins[i]+entity->fields.server->maxs[i]), sv.protocol); - SV_FlushBroadcastMessages(); + MSG_WriteCoord (dest, PRVM_serveredictvector(entity, origin)[i]+0.5*(PRVM_serveredictvector(entity, mins)[i]+PRVM_serveredictvector(entity, maxs)[i]), sv.protocol); + + // TODO do we have to do anything here when dest is &sv.reliable_datagram? + if(!reliable) + SV_FlushBroadcastMessages(); } /* @@ -803,9 +798,9 @@ function, therefore the check for it is omitted. ================== */ -void SV_StartPointSound (vec3_t origin, const char *sample, int volume, float attenuation) +void SV_StartPointSound (vec3_t origin, const char *sample, int volume, float attenuation, float speed) { - int sound_num, field_mask, i; + int sound_num, field_mask, i, speed4000; if (volume < 0 || volume > 255) { @@ -827,6 +822,7 @@ void SV_StartPointSound (vec3_t origin, const char *sample, int volume, float at if (!sound_num) return; + speed4000 = (int)(speed * 40.0f); field_mask = 0; if (volume != DEFAULT_SOUND_PACKET_VOLUME) field_mask |= SND_VOLUME; @@ -834,6 +830,8 @@ void SV_StartPointSound (vec3_t origin, const char *sample, int volume, float at field_mask |= SND_ATTENUATION; if (sound_num >= 256) field_mask |= SND_LARGESOUND; + if (speed4000 && speed4000 != 4000) + field_mask |= SND_SPEEDUSHORT4000; // directed messages go only to the entity they are targeted on MSG_WriteByte (&sv.datagram, svc_sound); @@ -842,6 +840,8 @@ void SV_StartPointSound (vec3_t origin, const char *sample, int volume, float at MSG_WriteByte (&sv.datagram, volume); if (field_mask & SND_ATTENUATION) MSG_WriteByte (&sv.datagram, (int)(attenuation*64)); + if (field_mask & SND_SPEEDUSHORT4000) + MSG_WriteShort (&sv.datagram, speed4000); // Always write entnum 0 for the world entity MSG_WriteShort (&sv.datagram, (0<<3) | 0); if (field_mask & SND_LARGESOUND) @@ -871,8 +871,10 @@ This will be sent on the initial connection and upon each server load. */ void SV_SendServerinfo (client_t *client) { + prvm_prog_t *prog = SVVM_prog; int i; char message[128]; + char vabuf[1024]; // we know that this client has a netconnection and thus is not a bot @@ -951,11 +953,11 @@ void SV_SendServerinfo (client_t *client) { Con_DPrintf("sending csqc info to client (\"%s\" with size %i and crc %i)\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc); MSG_WriteByte (&client->netconnection->message, svc_stufftext); - MSG_WriteString (&client->netconnection->message, va("csqc_progname %s\n", sv.csqc_progname)); + MSG_WriteString (&client->netconnection->message, va(vabuf, sizeof(vabuf), "csqc_progname %s\n", sv.csqc_progname)); MSG_WriteByte (&client->netconnection->message, svc_stufftext); - MSG_WriteString (&client->netconnection->message, va("csqc_progsize %i\n", sv.csqc_progsize)); + MSG_WriteString (&client->netconnection->message, va(vabuf, sizeof(vabuf), "csqc_progsize %i\n", sv.csqc_progsize)); MSG_WriteByte (&client->netconnection->message, svc_stufftext); - MSG_WriteString (&client->netconnection->message, va("csqc_progcrc %i\n", sv.csqc_progcrc)); + MSG_WriteString (&client->netconnection->message, va(vabuf, sizeof(vabuf), "csqc_progcrc %i\n", sv.csqc_progcrc)); if(client->sv_demo_file != NULL) { @@ -971,10 +973,10 @@ void SV_SendServerinfo (client_t *client) } //[515]: init stufftext string (it is sent before svc_serverinfo) - if (PRVM_GetString(PRVM_GLOBALFIELDSTRING(prog->globaloffsets.SV_InitCmd))) + if (PRVM_GetString(prog, PRVM_serverglobalstring(SV_InitCmd))) { MSG_WriteByte (&client->netconnection->message, svc_stufftext); - MSG_WriteString (&client->netconnection->message, va("%s\n", PRVM_GetString(PRVM_GLOBALFIELDSTRING(prog->globaloffsets.SV_InitCmd)))); + MSG_WriteString (&client->netconnection->message, va(vabuf, sizeof(vabuf), "%s\n", PRVM_GetString(prog, PRVM_serverglobalstring(SV_InitCmd)))); } } @@ -1004,7 +1006,7 @@ void SV_SendServerinfo (client_t *client) else MSG_WriteByte (&client->netconnection->message, GAME_COOP); - MSG_WriteString (&client->netconnection->message,PRVM_GetString(prog->edicts->fields.server->message)); + MSG_WriteString (&client->netconnection->message,PRVM_GetString(prog, PRVM_serveredictstring(prog->edicts, message))); for (i = 1;i < MAX_MODELS && sv.model_precache[i][0];i++) MSG_WriteString (&client->netconnection->message, sv.model_precache[i]); @@ -1016,8 +1018,8 @@ void SV_SendServerinfo (client_t *client) // send music MSG_WriteByte (&client->netconnection->message, svc_cdtrack); - MSG_WriteByte (&client->netconnection->message, (int)prog->edicts->fields.server->sounds); - MSG_WriteByte (&client->netconnection->message, (int)prog->edicts->fields.server->sounds); + MSG_WriteByte (&client->netconnection->message, (int)PRVM_serveredictfloat(prog->edicts, sounds)); + MSG_WriteByte (&client->netconnection->message, (int)PRVM_serveredictfloat(prog->edicts, sounds)); // set view // store this in clientcamera, too @@ -1057,6 +1059,7 @@ once for a player each game, not once for each level change. */ void SV_ConnectClient (int clientnum, netconn_t *netconnection) { + prvm_prog_t *prog = SVVM_prog; client_t *client; int i; @@ -1109,13 +1112,14 @@ void SV_ConnectClient (int clientnum, netconn_t *netconnection) { // call the progs to get default spawn parms for the new client // set self to world to intentionally cause errors with broken SetNewParms code in some mods - prog->globals.server->self = 0; - PRVM_ExecuteProgram (prog->globals.server->SetNewParms, "QC function SetNewParms is missing"); + PRVM_serverglobalfloat(time) = sv.time; + PRVM_serverglobaledict(self) = 0; + prog->ExecuteProgram(prog, PRVM_serverfunction(SetNewParms), "QC function SetNewParms is missing"); for (i=0 ; ispawn_parms[i] = (&prog->globals.server->parm1)[i]; + client->spawn_parms[i] = (&PRVM_serverglobalfloat(parm1))[i]; // set up the entity for this client (including .colormap, .team, etc) - PRVM_ED_ClearEdict(client->edict); + PRVM_ED_ClearEdict(prog, client->edict); } // don't call SendServerinfo for a fresh botclient because its fields have @@ -1148,6 +1152,7 @@ crosses a waterline. static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int enumber) { + prvm_prog_t *prog = SVVM_prog; int i; unsigned int sendflags; unsigned int version; @@ -1161,44 +1166,44 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c // fast path for games that do not use legacy entity networking // note: still networks clients even if they are legacy - sendentity = PRVM_EDICTFIELDFUNCTION(ent, prog->fieldoffsets.SendEntity); + sendentity = PRVM_serveredictfunction(ent, SendEntity); if (sv_onlycsqcnetworking.integer && !sendentity && enumber > svs.maxclients) return false; // this 2 billion unit check is actually to detect NAN origins // (we really don't want to send those) - if (!(VectorLength2(ent->fields.server->origin) < 2000000000.0*2000000000.0)) + if (!(VectorLength2(PRVM_serveredictvector(ent, origin)) < 2000000000.0*2000000000.0)) return false; // EF_NODRAW prevents sending for any reason except for your own // client, so we must keep all clients in this superset - effects = (unsigned)ent->fields.server->effects; + effects = (unsigned)PRVM_serveredictfloat(ent, effects); // we can omit invisible entities with no effects that are not clients // LordHavoc: this could kill tags attached to an invisible entity, I // just hope we never have to support that case - i = (int)ent->fields.server->modelindex; - modelindex = (i >= 1 && i < MAX_MODELS && ent->fields.server->model && *PRVM_GetString(ent->fields.server->model) && sv.models[i]) ? i : 0; + i = (int)PRVM_serveredictfloat(ent, modelindex); + modelindex = (i >= 1 && i < MAX_MODELS && PRVM_serveredictstring(ent, model) && *PRVM_GetString(prog, PRVM_serveredictstring(ent, model)) && sv.models[i]) ? i : 0; flags = 0; - i = (int)(PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.glow_size) * 0.25f); + i = (int)(PRVM_serveredictfloat(ent, glow_size) * 0.25f); glowsize = (unsigned char)bound(0, i, 255); - if (PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.glow_trail)) + if (PRVM_serveredictfloat(ent, glow_trail)) flags |= RENDER_GLOWTRAIL; - if (PRVM_EDICTFIELDEDICT(ent, prog->fieldoffsets.viewmodelforclient)) + if (PRVM_serveredictedict(ent, viewmodelforclient)) flags |= RENDER_VIEWMODEL; - v = PRVM_EDICTFIELDVECTOR(ent, prog->fieldoffsets.color); + v = PRVM_serveredictvector(ent, color); f = v[0]*256; light[0] = (unsigned short)bound(0, f, 65535); f = v[1]*256; light[1] = (unsigned short)bound(0, f, 65535); f = v[2]*256; light[2] = (unsigned short)bound(0, f, 65535); - f = PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.light_lev); + f = PRVM_serveredictfloat(ent, light_lev); light[3] = (unsigned short)bound(0, f, 65535); - lightstyle = (unsigned char)PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.style); - lightpflags = (unsigned char)PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.pflags); + lightstyle = (unsigned char)PRVM_serveredictfloat(ent, style); + lightpflags = (unsigned char)PRVM_serveredictfloat(ent, pflags); if (gamemode == GAME_TENEBRAE) { @@ -1249,33 +1254,34 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c // early culling checks // (final culling is done by SV_MarkWriteEntityStateToClient) - customizeentityforclient = PRVM_EDICTFIELDFUNCTION(ent, prog->fieldoffsets.customizeentityforclient); + customizeentityforclient = PRVM_serveredictfunction(ent, customizeentityforclient); if (!customizeentityforclient && enumber > svs.maxclients && (!modelindex && !specialvisibilityradius)) return false; *cs = defaultstate; cs->active = ACTIVE_NETWORK; cs->number = enumber; - VectorCopy(ent->fields.server->origin, cs->origin); - VectorCopy(ent->fields.server->angles, cs->angles); + VectorCopy(PRVM_serveredictvector(ent, origin), cs->origin); + VectorCopy(PRVM_serveredictvector(ent, angles), cs->angles); cs->flags = flags; cs->effects = effects; - cs->colormap = (unsigned)ent->fields.server->colormap; + cs->colormap = (unsigned)PRVM_serveredictfloat(ent, colormap); cs->modelindex = modelindex; - cs->skin = (unsigned)ent->fields.server->skin; - cs->frame = (unsigned)ent->fields.server->frame; - cs->viewmodelforclient = PRVM_EDICTFIELDEDICT(ent, prog->fieldoffsets.viewmodelforclient); - cs->exteriormodelforclient = PRVM_EDICTFIELDEDICT(ent, prog->fieldoffsets.exteriormodeltoclient); - cs->nodrawtoclient = PRVM_EDICTFIELDEDICT(ent, prog->fieldoffsets.nodrawtoclient); - cs->drawonlytoclient = PRVM_EDICTFIELDEDICT(ent, prog->fieldoffsets.drawonlytoclient); + cs->skin = (unsigned)PRVM_serveredictfloat(ent, skin); + cs->frame = (unsigned)PRVM_serveredictfloat(ent, frame); + cs->viewmodelforclient = PRVM_serveredictedict(ent, viewmodelforclient); + cs->exteriormodelforclient = PRVM_serveredictedict(ent, exteriormodeltoclient); + cs->nodrawtoclient = PRVM_serveredictedict(ent, nodrawtoclient); + cs->drawonlytoclient = PRVM_serveredictedict(ent, drawonlytoclient); cs->customizeentityforclient = customizeentityforclient; - cs->tagentity = PRVM_EDICTFIELDEDICT(ent, prog->fieldoffsets.tag_entity); - cs->tagindex = (unsigned char)PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.tag_index); + cs->tagentity = PRVM_serveredictedict(ent, tag_entity); + cs->tagindex = (unsigned char)PRVM_serveredictfloat(ent, tag_index); cs->glowsize = glowsize; + cs->traileffectnum = PRVM_serveredictfloat(ent, traileffectnum); // don't need to init cs->colormod because the defaultstate did that for us //cs->colormod[0] = cs->colormod[1] = cs->colormod[2] = 32; - v = PRVM_EDICTFIELDVECTOR(ent, prog->fieldoffsets.colormod); + v = PRVM_serveredictvector(ent, colormod); if (VectorLength2(v)) { i = (int)(v[0] * 32.0f);cs->colormod[0] = bound(0, i, 255); @@ -1285,7 +1291,7 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c // don't need to init cs->glowmod because the defaultstate did that for us //cs->glowmod[0] = cs->glowmod[1] = cs->glowmod[2] = 32; - v = PRVM_EDICTFIELDVECTOR(ent, prog->fieldoffsets.glowmod); + v = PRVM_serveredictvector(ent, glowmod); if (VectorLength2(v)) { i = (int)(v[0] * 32.0f);cs->glowmod[0] = bound(0, i, 255); @@ -1296,14 +1302,14 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c cs->modelindex = modelindex; cs->alpha = 255; - f = (PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.alpha) * 255.0f); + f = (PRVM_serveredictfloat(ent, alpha) * 255.0f); if (f) { i = (int)f; cs->alpha = (unsigned char)bound(0, i, 255); } // halflife - f = (PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.renderamt)); + f = (PRVM_serveredictfloat(ent, renderamt)); if (f) { i = (int)f; @@ -1311,7 +1317,7 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c } cs->scale = 16; - f = (PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.scale) * 16.0f); + f = (PRVM_serveredictfloat(ent, scale) * 16.0f); if (f) { i = (int)f; @@ -1319,43 +1325,44 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c } cs->glowcolor = 254; - f = PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.glow_color); + f = PRVM_serveredictfloat(ent, glow_color); if (f) cs->glowcolor = (int)f; - if (PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.fullbright)) + if (PRVM_serveredictfloat(ent, fullbright)) cs->effects |= EF_FULLBRIGHT; - f = PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.modelflags); + f = PRVM_serveredictfloat(ent, modelflags); if (f) cs->effects |= ((unsigned int)f & 0xff) << 24; - if (ent->fields.server->movetype == MOVETYPE_STEP) + if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_STEP) cs->flags |= RENDER_STEP; if (cs->number != sv.writeentitiestoclient_cliententitynumber && (cs->effects & EF_LOWPRECISION) && cs->origin[0] >= -32768 && cs->origin[1] >= -32768 && cs->origin[2] >= -32768 && cs->origin[0] <= 32767 && cs->origin[1] <= 32767 && cs->origin[2] <= 32767) cs->flags |= RENDER_LOWPRECISION; - if (ent->fields.server->colormap >= 1024) + if (PRVM_serveredictfloat(ent, colormap) >= 1024) cs->flags |= RENDER_COLORMAPPED; if (cs->viewmodelforclient) cs->flags |= RENDER_VIEWMODEL; // show relative to the view - if (PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.sendcomplexanimation)) + if (PRVM_serveredictfloat(ent, sendcomplexanimation)) { cs->flags |= RENDER_COMPLEXANIMATION; - if (PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.skeletonindex) >= 1) + if (PRVM_serveredictfloat(ent, skeletonindex) >= 1) cs->skeletonobject = ent->priv.server->skeleton; - cs->framegroupblend[0].frame = PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.frame); - cs->framegroupblend[1].frame = PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.frame2); - cs->framegroupblend[2].frame = PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.frame3); - cs->framegroupblend[3].frame = PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.frame4); - cs->framegroupblend[0].start = PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.frame1time); - cs->framegroupblend[1].start = PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.frame2time); - cs->framegroupblend[2].start = PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.frame3time); - cs->framegroupblend[3].start = PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.frame4time); - cs->framegroupblend[1].lerp = PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.lerpfrac); - cs->framegroupblend[2].lerp = PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.lerpfrac3); - cs->framegroupblend[3].lerp = PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.lerpfrac4); + cs->framegroupblend[0].frame = PRVM_serveredictfloat(ent, frame); + cs->framegroupblend[1].frame = PRVM_serveredictfloat(ent, frame2); + cs->framegroupblend[2].frame = PRVM_serveredictfloat(ent, frame3); + cs->framegroupblend[3].frame = PRVM_serveredictfloat(ent, frame4); + cs->framegroupblend[0].start = PRVM_serveredictfloat(ent, frame1time); + cs->framegroupblend[1].start = PRVM_serveredictfloat(ent, frame2time); + cs->framegroupblend[2].start = PRVM_serveredictfloat(ent, frame3time); + cs->framegroupblend[3].start = PRVM_serveredictfloat(ent, frame4time); + cs->framegroupblend[1].lerp = PRVM_serveredictfloat(ent, lerpfrac); + cs->framegroupblend[2].lerp = PRVM_serveredictfloat(ent, lerpfrac3); + cs->framegroupblend[3].lerp = PRVM_serveredictfloat(ent, lerpfrac4); cs->framegroupblend[0].lerp = 1.0f - cs->framegroupblend[1].lerp - cs->framegroupblend[2].lerp - cs->framegroupblend[3].lerp; + cs->frame = 0; // don't need the legacy frame } cs->light[0] = light[0]; @@ -1392,8 +1399,8 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c else { // if there is no model (or it could not be loaded), use the physics box - VectorAdd(cs->origin, ent->fields.server->mins, cullmins); - VectorAdd(cs->origin, ent->fields.server->maxs, cullmaxs); + VectorAdd(cs->origin, PRVM_serveredictvector(ent, mins), cullmins); + VectorAdd(cs->origin, PRVM_serveredictvector(ent, maxs), cullmaxs); } if (specialvisibilityradius) { @@ -1431,10 +1438,10 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c // (to let the QC know that they've been read) if (sendentity) { - sendflags = (unsigned int)PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.SendFlags); - PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.SendFlags) = 0; + sendflags = (unsigned int)PRVM_serveredictfloat(ent, SendFlags); + PRVM_serveredictfloat(ent, SendFlags) = 0; // legacy self.Version system - if ((version = (unsigned int)PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.Version))) + if ((version = (unsigned int)PRVM_serveredictfloat(ent, Version))) { if (sv.csqcentityversion[enumber] != version) sendflags = 0xFFFFFF; @@ -1451,8 +1458,9 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c return true; } -void SV_PrepareEntitiesForSending(void) +static void SV_PrepareEntitiesForSending(void) { + prvm_prog_t *prog = SVVM_prog; int e; prvm_edict_t *ent; // send all entities that touch the pvs @@ -1473,6 +1481,7 @@ void SV_PrepareEntitiesForSending(void) qboolean SV_CanSeeBox(int numtraces, vec_t enlarge, vec3_t eye, vec3_t entboxmins, vec3_t entboxmaxs) { + prvm_prog_t *prog = SVVM_prog; float pitchsign; float alpha; float starttransformed[3], endtransformed[3]; @@ -1518,7 +1527,7 @@ qboolean SV_CanSeeBox(int numtraces, vec_t enlarge, vec3_t eye, vec3_t entboxmin // get the list of entities in the sweep box if (sv_cullentities_trace_entityocclusion.integer) - numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts); + numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts); if (numtouchedicts > MAX_EDICTS) { // this never happens @@ -1531,16 +1540,16 @@ qboolean SV_CanSeeBox(int numtraces, vec_t enlarge, vec3_t eye, vec3_t entboxmin for (touchindex = 0;touchindex < originalnumtouchedicts;touchindex++) { touch = touchedicts[touchindex]; - if (touch->fields.server->solid != SOLID_BSP) + if (PRVM_serveredictfloat(touch, solid) != SOLID_BSP) continue; model = SV_GetModelFromEdict(touch); if (!model || !model->brush.TraceLineOfSight) continue; // skip obviously transparent entities - alpha = PRVM_EDICTFIELDFLOAT(touch, prog->fieldoffsets.alpha); + alpha = PRVM_serveredictfloat(touch, alpha); if (alpha && alpha < 1) continue; - if ((int)touch->fields.server->effects & EF_ADDITIVE) + if ((int)PRVM_serveredictfloat(touch, effects) & EF_ADDITIVE) continue; touchedicts[numtouchedicts++] = touch; } @@ -1562,8 +1571,8 @@ qboolean SV_CanSeeBox(int numtraces, vec_t enlarge, vec3_t eye, vec3_t entboxmin if(model && model->brush.TraceLineOfSight) { // get the entity matrix - pitchsign = SV_GetPitchSign(touch); - Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1); + pitchsign = SV_GetPitchSign(prog, touch); + Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], pitchsign * PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1); Matrix4x4_Invert_Simple(&imatrix, &matrix); // see if the ray hits this entity Matrix4x4_Transform(&imatrix, eye, starttransformed); @@ -1586,8 +1595,9 @@ qboolean SV_CanSeeBox(int numtraces, vec_t enlarge, vec3_t eye, vec3_t entboxmin return false; } -void SV_MarkWriteEntityStateToClient(entity_state_t *s) +static void SV_MarkWriteEntityStateToClient(entity_state_t *s) { + prvm_prog_t *prog = SVVM_prog; int isbmodel; dp_model_t *model; prvm_edict_t *ed; @@ -1598,9 +1608,10 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s) if (s->customizeentityforclient) { - prog->globals.server->self = s->number; - prog->globals.server->other = sv.writeentitiestoclient_cliententitynumber; - PRVM_ExecuteProgram(s->customizeentityforclient, "customizeentityforclient: NULL function"); + PRVM_serverglobalfloat(time) = sv.time; + PRVM_serverglobaledict(self) = s->number; + PRVM_serverglobaledict(other) = sv.writeentitiestoclient_cliententitynumber; + prog->ExecuteProgram(prog, s->customizeentityforclient, "customizeentityforclient: NULL function"); if(!PRVM_G_FLOAT(OFS_RETURN) || !SV_PrepareEntityForSending(PRVM_EDICT_NUM(s->number), s, s->number)) return; } @@ -1644,7 +1655,7 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s) ed = PRVM_EDICT_NUM(s->number); // if not touching a visible leaf - if (sv_cullentities_pvs.integer && !r_novis.integer && sv.writeentitiestoclient_pvsbytes) + if (sv_cullentities_pvs.integer && !r_novis.integer && !r_trippy.integer && sv.writeentitiestoclient_pvsbytes) { if (ed->priv.server->pvs_numclusters < 0) { @@ -1671,7 +1682,7 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s) } // or not seen by random tracelines - if (sv_cullentities_trace.integer && !isbmodel && sv.worldmodel->brush.TraceLineOfSight) + if (sv_cullentities_trace.integer && !isbmodel && sv.worldmodel->brush.TraceLineOfSight && !r_trippy.integer) { int samples = s->number <= svs.maxclients @@ -1714,8 +1725,9 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s) #if MAX_LEVELNETWORKEYES > 0 #define MAX_EYE_RECURSION 1 // increase if recursion gets supported by portals -void SV_AddCameraEyes(void) +static void SV_AddCameraEyes(void) { + prvm_prog_t *prog = SVVM_prog; int e, i, j, k; prvm_edict_t *ed; static int cameras[MAX_LEVELNETWORKEYES]; @@ -1724,9 +1736,6 @@ void SV_AddCameraEyes(void) int n_cameras = 0; vec3_t mi, ma; - if(!prog->fieldoffsets.camera_transform) - return; - for(i = 0; i < sv.writeentitiestoclient_numeyes; ++i) eye_levels[i] = 0; @@ -1735,17 +1744,18 @@ void SV_AddCameraEyes(void) { if (!ed->priv.server->free) { - if(PRVM_EDICTFIELDFUNCTION(ed, prog->fieldoffsets.camera_transform)) + if(PRVM_serveredictfunction(ed, camera_transform)) { - prog->globals.server->self = e; - prog->globals.server->other = sv.writeentitiestoclient_cliententitynumber; - VectorCopy(sv.writeentitiestoclient_eyes[0], PRVM_GLOBALFIELDVECTOR(prog->globaloffsets.trace_endpos)); + PRVM_serverglobalfloat(time) = sv.time; + PRVM_serverglobaledict(self) = e; + PRVM_serverglobaledict(other) = sv.writeentitiestoclient_cliententitynumber; + VectorCopy(sv.writeentitiestoclient_eyes[0], PRVM_serverglobalvector(trace_endpos)); VectorCopy(sv.writeentitiestoclient_eyes[0], PRVM_G_VECTOR(OFS_PARM0)); VectorClear(PRVM_G_VECTOR(OFS_PARM1)); - PRVM_ExecuteProgram(PRVM_EDICTFIELDFUNCTION(ed, prog->fieldoffsets.camera_transform), "QC function e.camera_transform is missing"); - if(!VectorCompare(PRVM_GLOBALFIELDVECTOR(prog->globaloffsets.trace_endpos), sv.writeentitiestoclient_eyes[0])) + prog->ExecuteProgram(prog, PRVM_serveredictfunction(ed, camera_transform), "QC function e.camera_transform is missing"); + if(!VectorCompare(PRVM_serverglobalvector(trace_endpos), sv.writeentitiestoclient_eyes[0])) { - VectorCopy(PRVM_GLOBALFIELDVECTOR(prog->globaloffsets.trace_endpos), camera_origins[n_cameras]); + VectorCopy(PRVM_serverglobalvector(trace_endpos), camera_origins[n_cameras]); cameras[n_cameras] = e; ++n_cameras; if(n_cameras >= MAX_LEVELNETWORKEYES) @@ -1765,8 +1775,8 @@ void SV_AddCameraEyes(void) if(!cameras[j]) continue; ed = PRVM_EDICT_NUM(cameras[j]); - VectorAdd(ed->fields.server->origin, ed->fields.server->mins, mi); - VectorAdd(ed->fields.server->origin, ed->fields.server->maxs, ma); + VectorAdd(PRVM_serveredictvector(ed, origin), PRVM_serveredictvector(ed, mins), mi); + VectorAdd(PRVM_serveredictvector(ed, origin), PRVM_serveredictvector(ed, maxs), ma); for(k = 0; k < sv.writeentitiestoclient_numeyes; ++k) if(eye_levels[k] <= MAX_EYE_RECURSION) { @@ -1789,8 +1799,9 @@ void SV_AddCameraEyes(void) } #endif -void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg, int maxsize) +static void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg, int maxsize) { + prvm_prog_t *prog = SVVM_prog; qboolean need_empty = false; int i, numsendstates, numcsqcsendstates; entity_state_t *s; @@ -1814,7 +1825,7 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t * // get eye location sv.writeentitiestoclient_cliententitynumber = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes camera = PRVM_EDICT_NUM( client->clientcamera ); - VectorAdd(camera->fields.server->origin, clent->fields.server->view_ofs, eye); + VectorAdd(PRVM_serveredictvector(camera, origin), PRVM_serveredictvector(clent, view_ofs), eye); sv.writeentitiestoclient_pvsbytes = 0; // get the PVS values for the eye location, later FatPVS calls will merge if (sv.worldmodel && sv.worldmodel->brush.FatPVS) @@ -1829,7 +1840,7 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t * { vec_t predtime = bound(0, host_client->ping, sv_cullentities_trace_prediction_time.value); vec3_t predeye; - VectorMA(eye, predtime, camera->fields.server->velocity, predeye); + VectorMA(eye, predtime, PRVM_serveredictvector(camera, velocity), predeye); if (SV_CanSeeBox(1, 0, eye, predeye, predeye)) { VectorCopy(predeye, sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes]); @@ -1884,8 +1895,17 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t * else EntityFrameCSQC_WriteFrame(msg, maxsize, numcsqcsendstates, sv.writeentitiestoclient_csqcsendstates, 0); - if(client->num_skippedentityframes >= 10) - need_empty = true; // force every 10th frame to be not empty (or cl_movement replay takes too long) + // force every 16th frame to be not empty (or cl_movement replay takes + // too long) + // BTW, this should normally not kick in any more due to the check + // below, except if the client stopped sending movement frames + if(client->num_skippedentityframes >= 16) + need_empty = true; + + // help cl_movement a bit more + if(client->movesequence != client->lastmovesequence) + need_empty = true; + client->lastmovesequence = client->movesequence; if (client->entitydatabase5) success = EntityFrame5_WriteFrame(msg, maxsize, client->entitydatabase5, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1, client->movesequence, need_empty); @@ -1919,12 +1939,13 @@ SV_CleanupEnts */ static void SV_CleanupEnts (void) { + prvm_prog_t *prog = SVVM_prog; int e; prvm_edict_t *ent; ent = PRVM_NEXT_EDICT(prog->edicts); for (e=1 ; enum_edicts ; e++, ent = PRVM_NEXT_EDICT(ent)) - ent->fields.server->effects = (int)ent->fields.server->effects & ~EF_MUZZLEFLASH; + PRVM_serveredictfloat(ent, effects) = (int)PRVM_serveredictfloat(ent, effects) & ~EF_MUZZLEFLASH; } /* @@ -1935,6 +1956,7 @@ SV_WriteClientdataToMessage */ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats) { + prvm_prog_t *prog = SVVM_prog; int bits; int i; prvm_edict_t *other; @@ -1948,17 +1970,17 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t // // send a damage message // - if (ent->fields.server->dmg_take || ent->fields.server->dmg_save) + if (PRVM_serveredictfloat(ent, dmg_take) || PRVM_serveredictfloat(ent, dmg_save)) { - other = PRVM_PROG_TO_EDICT(ent->fields.server->dmg_inflictor); + other = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, dmg_inflictor)); MSG_WriteByte (msg, svc_damage); - MSG_WriteByte (msg, (int)ent->fields.server->dmg_save); - MSG_WriteByte (msg, (int)ent->fields.server->dmg_take); + MSG_WriteByte (msg, (int)PRVM_serveredictfloat(ent, dmg_save)); + MSG_WriteByte (msg, (int)PRVM_serveredictfloat(ent, dmg_take)); for (i=0 ; i<3 ; i++) - MSG_WriteCoord (msg, other->fields.server->origin[i] + 0.5*(other->fields.server->mins[i] + other->fields.server->maxs[i]), sv.protocol); + MSG_WriteCoord (msg, PRVM_serveredictvector(other, origin)[i] + 0.5*(PRVM_serveredictvector(other, mins)[i] + PRVM_serveredictvector(other, maxs)[i]), sv.protocol); - ent->fields.server->dmg_take = 0; - ent->fields.server->dmg_save = 0; + PRVM_serveredictfloat(ent, dmg_take) = 0; + PRVM_serveredictfloat(ent, dmg_save) = 0; } // @@ -1967,15 +1989,15 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t SV_SetIdealPitch (); // how much to look up / down ideally // a fixangle might get lost in a dropped packet. Oh well. - if(ent->fields.server->fixangle) + if(PRVM_serveredictfloat(ent, fixangle)) { // angle fixing was requested by global thinking code... // so store the current angles for later use - memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles)); + memcpy(host_client->fixangle_angles, PRVM_serveredictvector(ent, angles), sizeof(host_client->fixangle_angles)); host_client->fixangle_angles_set = TRUE; // and clear fixangle for the next frame - ent->fields.server->fixangle = 0; + PRVM_serveredictfloat(ent, fixangle) = 0; } if (host_client->fixangle_angles_set) @@ -1988,68 +2010,71 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t // stuff the sigil bits into the high bits of items for sbar, or else // mix in items2 - if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) - items = (int)ent->fields.server->items | ((int)PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.items2) << 23); + // LordHavoc: detecting items2 turned out to be tricky, check if the field + // was forcefully declared, we want to override serverflags if it was + // declared by the qc intentionally, but not if we added it in the engine. + if (prog->fieldoffsets.items2 < (int)(prog->numfielddefs - SV_REQGLOBALS)) + items = (int)PRVM_serveredictfloat(ent, items) | ((int)PRVM_serveredictfloat(ent, items2) << 23); else - items = (int)ent->fields.server->items | ((int)prog->globals.server->serverflags << 28); + items = (int)PRVM_serveredictfloat(ent, items) | ((int)PRVM_serverglobalfloat(serverflags) << 28); - VectorCopy(PRVM_EDICTFIELDVECTOR(ent, prog->fieldoffsets.punchvector), punchvector); + VectorCopy(PRVM_serveredictvector(ent, punchvector), punchvector); // cache weapon model name and index in client struct to save time // (this search can be almost 1% of cpu time!) - s = PRVM_GetString(ent->fields.server->weaponmodel); + s = PRVM_GetString(prog, PRVM_serveredictstring(ent, weaponmodel)); if (strcmp(s, client->weaponmodel)) { strlcpy(client->weaponmodel, s, sizeof(client->weaponmodel)); client->weaponmodelindex = SV_ModelIndex(s, 1); } - viewzoom = (int)(PRVM_EDICTFIELDFLOAT(ent, prog->fieldoffsets.viewzoom) * 255.0f); + viewzoom = (int)(PRVM_serveredictfloat(ent, viewzoom) * 255.0f); if (viewzoom == 0) viewzoom = 255; bits = 0; - if ((int)ent->fields.server->flags & FL_ONGROUND) + if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND) bits |= SU_ONGROUND; - if (ent->fields.server->waterlevel >= 2) + if (PRVM_serveredictfloat(ent, waterlevel) >= 2) bits |= SU_INWATER; - if (ent->fields.server->idealpitch) + if (PRVM_serveredictfloat(ent, idealpitch)) bits |= SU_IDEALPITCH; for (i=0 ; i<3 ; i++) { - if (ent->fields.server->punchangle[i]) + if (PRVM_serveredictvector(ent, punchangle)[i]) bits |= (SU_PUNCH1<fields.server->velocity[i]) + if (PRVM_serveredictvector(ent, velocity)[i]) bits |= (SU_VELOCITY1<fieldoffsets.gravity);if (!gravity) gravity = 1.0f; + gravity = PRVM_serveredictfloat(ent, gravity);if (!gravity) gravity = 1.0f; memset(stats, 0, sizeof(int[MAX_CL_STATS])); - stats[STAT_VIEWHEIGHT] = (int)ent->fields.server->view_ofs[2]; + stats[STAT_VIEWHEIGHT] = (int)PRVM_serveredictvector(ent, view_ofs)[2]; stats[STAT_ITEMS] = items; - stats[STAT_WEAPONFRAME] = (int)ent->fields.server->weaponframe; - stats[STAT_ARMOR] = (int)ent->fields.server->armorvalue; + stats[STAT_WEAPONFRAME] = (int)PRVM_serveredictfloat(ent, weaponframe); + stats[STAT_ARMOR] = (int)PRVM_serveredictfloat(ent, armorvalue); stats[STAT_WEAPON] = client->weaponmodelindex; - stats[STAT_HEALTH] = (int)ent->fields.server->health; - stats[STAT_AMMO] = (int)ent->fields.server->currentammo; - stats[STAT_SHELLS] = (int)ent->fields.server->ammo_shells; - stats[STAT_NAILS] = (int)ent->fields.server->ammo_nails; - stats[STAT_ROCKETS] = (int)ent->fields.server->ammo_rockets; - stats[STAT_CELLS] = (int)ent->fields.server->ammo_cells; - stats[STAT_ACTIVEWEAPON] = (int)ent->fields.server->weapon; + stats[STAT_HEALTH] = (int)PRVM_serveredictfloat(ent, health); + stats[STAT_AMMO] = (int)PRVM_serveredictfloat(ent, currentammo); + stats[STAT_SHELLS] = (int)PRVM_serveredictfloat(ent, ammo_shells); + stats[STAT_NAILS] = (int)PRVM_serveredictfloat(ent, ammo_nails); + stats[STAT_ROCKETS] = (int)PRVM_serveredictfloat(ent, ammo_rockets); + stats[STAT_CELLS] = (int)PRVM_serveredictfloat(ent, ammo_cells); + stats[STAT_ACTIVEWEAPON] = (int)PRVM_serveredictfloat(ent, weapon); stats[STAT_VIEWZOOM] = viewzoom; - stats[STAT_TOTALSECRETS] = (int)prog->globals.server->total_secrets; - stats[STAT_TOTALMONSTERS] = (int)prog->globals.server->total_monsters; + stats[STAT_TOTALSECRETS] = (int)PRVM_serverglobalfloat(total_secrets); + stats[STAT_TOTALMONSTERS] = (int)PRVM_serverglobalfloat(total_monsters); // the QC bumps these itself by sending svc_'s, so we have to keep them // zero or they'll be corrected by the engine - //stats[STAT_SECRETS] = prog->globals.server->found_secrets; - //stats[STAT_MONSTERS] = prog->globals.server->killed_monsters; + //stats[STAT_SECRETS] = PRVM_serverglobalfloat(found_secrets); + //stats[STAT_MONSTERS] = PRVM_serverglobalfloat(killed_monsters); // movement settings for prediction // note: these are not sent in protocols with lower MAX_CL_STATS limits @@ -2073,6 +2098,7 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t statsf[STAT_MOVEVARS_MAXAIRSPEED] = sv_maxairspeed.value; statsf[STAT_MOVEVARS_STEPHEIGHT] = sv_stepheight.value; statsf[STAT_MOVEVARS_AIRACCEL_QW] = sv_airaccel_qw.value; + statsf[STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR] = sv_airaccel_qw_stretchfactor.value; statsf[STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION] = sv_airaccel_sideways_friction.value; statsf[STAT_MOVEVARS_FRICTION] = sv_friction.value; statsf[STAT_MOVEVARS_WATERFRICTION] = sv_waterfriction.value >= 0 ? sv_waterfriction.value : sv_friction.value; @@ -2122,16 +2148,16 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t MSG_WriteChar (msg, stats[STAT_VIEWHEIGHT]); if (bits & SU_IDEALPITCH) - MSG_WriteChar (msg, (int)ent->fields.server->idealpitch); + MSG_WriteChar (msg, (int)PRVM_serveredictfloat(ent, idealpitch)); for (i=0 ; i<3 ; i++) { if (bits & (SU_PUNCH1<fields.server->punchangle[i]); + MSG_WriteChar(msg, (int)PRVM_serveredictvector(ent, punchangle)[i]); else - MSG_WriteAngle16i(msg, ent->fields.server->punchangle[i]); + MSG_WriteAngle16i(msg, PRVM_serveredictvector(ent, punchangle)[i]); } if (bits & (SU_PUNCHVEC1<fields.server->velocity[i] * (1.0f / 16.0f))); + MSG_WriteChar(msg, (int)(PRVM_serveredictvector(ent, velocity)[i] * (1.0f / 16.0f))); else - MSG_WriteCoord32f(msg, ent->fields.server->velocity[i]); + MSG_WriteCoord32f(msg, PRVM_serveredictvector(ent, velocity)[i]); } } @@ -2353,7 +2379,7 @@ static void SV_SendClientDatagram (client_t *client) // add the client specific data to the datagram SV_WriteClientdataToMessage (client, client->edict, &msg, stats); // now update the stats[] array using any registered custom fields - VM_SV_UpdateCustomStats (client, client->edict, &msg, stats); + VM_SV_UpdateCustomStats(client, client->edict, &msg, stats); // set host_client->statsdeltabits Protocol_UpdateClientStats (stats); @@ -2416,6 +2442,7 @@ SV_UpdateToReliableMessages */ static void SV_UpdateToReliableMessages (void) { + prvm_prog_t *prog = SVVM_prog; int i, j; client_t *client; const char *name; @@ -2430,12 +2457,12 @@ static void SV_UpdateToReliableMessages (void) host_client->edict = PRVM_EDICT_NUM(i+1); // DP_SV_CLIENTNAME - name = PRVM_GetString(host_client->edict->fields.server->netname); + name = PRVM_GetString(prog, PRVM_serveredictstring(host_client->edict, netname)); if (name == NULL) name = ""; // always point the string back at host_client->name to keep it safe strlcpy (host_client->name, name, sizeof (host_client->name)); - host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name); + PRVM_serveredictstring(host_client->edict, netname) = PRVM_SetEngineString(prog, host_client->name); if (strcmp(host_client->old_name, host_client->name)) { if (host_client->spawned) @@ -2449,7 +2476,7 @@ static void SV_UpdateToReliableMessages (void) } // DP_SV_CLIENTCOLORS - host_client->colors = (int)PRVM_EDICTFIELDFLOAT(host_client->edict, prog->fieldoffsets.clientcolors); + host_client->colors = (int)PRVM_serveredictfloat(host_client->edict, clientcolors); if (host_client->old_colors != host_client->colors) { host_client->old_colors = host_client->colors; @@ -2460,27 +2487,23 @@ static void SV_UpdateToReliableMessages (void) } // NEXUIZ_PLAYERMODEL - if( prog->fieldoffsets.playermodel >= 0 ) { - model = PRVM_GetString(PRVM_EDICTFIELDSTRING(host_client->edict, prog->fieldoffsets.playermodel)); - if (model == NULL) - model = ""; - // always point the string back at host_client->name to keep it safe - strlcpy (host_client->playermodel, model, sizeof (host_client->playermodel)); - PRVM_EDICTFIELDSTRING(host_client->edict, prog->fieldoffsets.playermodel) = PRVM_SetEngineString(host_client->playermodel); - } + model = PRVM_GetString(prog, PRVM_serveredictstring(host_client->edict, playermodel)); + if (model == NULL) + model = ""; + // always point the string back at host_client->name to keep it safe + strlcpy (host_client->playermodel, model, sizeof (host_client->playermodel)); + PRVM_serveredictstring(host_client->edict, playermodel) = PRVM_SetEngineString(prog, host_client->playermodel); // NEXUIZ_PLAYERSKIN - if( prog->fieldoffsets.playerskin >= 0 ) { - skin = PRVM_GetString(PRVM_EDICTFIELDSTRING(host_client->edict, prog->fieldoffsets.playerskin)); - if (skin == NULL) - skin = ""; - // always point the string back at host_client->name to keep it safe - strlcpy (host_client->playerskin, skin, sizeof (host_client->playerskin)); - PRVM_EDICTFIELDSTRING(host_client->edict, prog->fieldoffsets.playerskin) = PRVM_SetEngineString(host_client->playerskin); - } + skin = PRVM_GetString(prog, PRVM_serveredictstring(host_client->edict, playerskin)); + if (skin == NULL) + skin = ""; + // always point the string back at host_client->name to keep it safe + strlcpy (host_client->playerskin, skin, sizeof (host_client->playerskin)); + PRVM_serveredictstring(host_client->edict, playerskin) = PRVM_SetEngineString(prog, host_client->playerskin); // TODO: add an extension name for this [1/17/2008 Black] - clientcamera = PRVM_EDICTFIELDEDICT(host_client->edict, prog->fieldoffsets.clientcamera); + clientcamera = PRVM_serveredictedict(host_client->edict, clientcamera); if (clientcamera > 0) { int oldclientcamera = host_client->clientcamera; @@ -2488,7 +2511,7 @@ static void SV_UpdateToReliableMessages (void) clientcamera = PRVM_NUM_FOR_EDICT(host_client->edict); host_client->clientcamera = clientcamera; - if (oldclientcamera != host_client->clientcamera) + if (oldclientcamera != host_client->clientcamera && host_client->netconnection) { MSG_WriteByte(&host_client->netconnection->message, svc_setview); MSG_WriteShort(&host_client->netconnection->message, host_client->clientcamera); @@ -2496,7 +2519,7 @@ static void SV_UpdateToReliableMessages (void) } // frags - host_client->frags = (int)host_client->edict->fields.server->frags; + host_client->frags = (int)PRVM_serveredictfloat(host_client->edict, frags); if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC) if(!host_client->spawned && host_client->netconnection) host_client->frags = -666; @@ -2523,7 +2546,7 @@ static void SV_UpdateToReliableMessages (void) SV_SendClientMessages ======================= */ -void SV_SendClientMessages (void) +void SV_SendClientMessages(void) { int i, prepared = false; @@ -2555,7 +2578,7 @@ void SV_SendClientMessages (void) // only prepare entities once per frame SV_PrepareEntitiesForSending(); } - SV_SendClientDatagram (host_client); + SV_SendClientDatagram(host_client); } // clear muzzle flashes @@ -2837,9 +2860,23 @@ int SV_ModelIndex(const char *s, int precachemode) if (precachemode == 1) Con_Printf("SV_ModelIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename); strlcpy(sv.model_precache[i], filename, sizeof(sv.model_precache[i])); - sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, s[0] == '*' ? sv.worldname : NULL); - if (sv.state != ss_loading) + if (sv.state == ss_loading) + { + // running from SV_SpawnServer which is launched from the client console command interpreter + sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, s[0] == '*' ? sv.worldname : NULL); + } + else { + if (svs.threaded) + { + // 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 + sv.models[i] = Mod_FindName (sv.model_precache[i], s[0] == '*' ? sv.worldname : NULL); + } + else + { + // running single threaded, so we can load the model here + sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, s[0] == '*' ? sv.worldname : NULL); + } MSG_WriteByte(&sv.reliable_datagram, svc_precache); MSG_WriteShort(&sv.reliable_datagram, i); MSG_WriteString(&sv.reliable_datagram, filename); @@ -2946,7 +2983,7 @@ int SV_ParticleEffectIndex(const char *name) argc = 0; for (;;) { - if (!COM_ParseToken_Simple(&text, true, false) || !strcmp(com_token, "\n")) + if (!COM_ParseToken_Simple(&text, true, false, true) || !strcmp(com_token, "\n")) break; if (argc < 16) { @@ -3002,10 +3039,11 @@ dp_model_t *SV_GetModelByIndex(int modelindex) dp_model_t *SV_GetModelFromEdict(prvm_edict_t *ed) { + prvm_prog_t *prog = SVVM_prog; int modelindex; if (!ed || ed->priv.server->free) return NULL; - modelindex = (int)ed->fields.server->modelindex; + modelindex = (int)PRVM_serveredictfloat(ed, modelindex); return (modelindex > 0 && modelindex < MAX_MODELS) ? sv.models[modelindex] : NULL; } @@ -3017,6 +3055,7 @@ SV_CreateBaseline */ static void SV_CreateBaseline (void) { + prvm_prog_t *prog = SVVM_prog; int i, entnum, large; prvm_edict_t *svent; @@ -3031,14 +3070,14 @@ static void SV_CreateBaseline (void) if (svent->priv.server->free) continue; - if (entnum > svs.maxclients && !svent->fields.server->modelindex) + if (entnum > svs.maxclients && !PRVM_serveredictfloat(svent, modelindex)) continue; // create entity baseline - VectorCopy (svent->fields.server->origin, svent->priv.server->baseline.origin); - VectorCopy (svent->fields.server->angles, svent->priv.server->baseline.angles); - svent->priv.server->baseline.frame = (int)svent->fields.server->frame; - svent->priv.server->baseline.skin = (int)svent->fields.server->skin; + VectorCopy (PRVM_serveredictvector(svent, origin), svent->priv.server->baseline.origin); + VectorCopy (PRVM_serveredictvector(svent, angles), svent->priv.server->baseline.angles); + svent->priv.server->baseline.frame = (int)PRVM_serveredictfloat(svent, frame); + svent->priv.server->baseline.skin = (int)PRVM_serveredictfloat(svent, skin); if (entnum > 0 && entnum <= svs.maxclients) { svent->priv.server->baseline.colormap = entnum; @@ -3047,7 +3086,7 @@ static void SV_CreateBaseline (void) else { svent->priv.server->baseline.colormap = 0; - svent->priv.server->baseline.modelindex = (int)svent->fields.server->modelindex; + svent->priv.server->baseline.modelindex = (int)PRVM_serveredictfloat(svent, modelindex); } large = false; @@ -3098,7 +3137,7 @@ Load csprogs.dat and comperss it so it doesn't need to be reloaded on request. ================ */ -void SV_Prepare_CSQC(void) +static void SV_Prepare_CSQC(void) { fs_offset_t progsize; @@ -3149,9 +3188,10 @@ transition to another level */ void SV_SaveSpawnparms (void) { + prvm_prog_t *prog = SVVM_prog; int i, j; - svs.serverflags = (int)prog->globals.server->serverflags; + svs.serverflags = (int)PRVM_serverglobalfloat(serverflags); for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++) { @@ -3159,10 +3199,11 @@ void SV_SaveSpawnparms (void) continue; // call the progs to get default spawn parms for the new client - prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict); - PRVM_ExecuteProgram (prog->globals.server->SetChangeParms, "QC function SetChangeParms is missing"); + PRVM_serverglobalfloat(time) = sv.time; + PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict); + prog->ExecuteProgram(prog, PRVM_serverfunction(SetChangeParms), "QC function SetChangeParms is missing"); for (j=0 ; jspawn_parms[j] = (&prog->globals.server->parm1)[j]; + host_client->spawn_parms[j] = (&PRVM_serverglobalfloat(parm1))[j]; } } @@ -3176,11 +3217,13 @@ This is called at the start of each level void SV_SpawnServer (const char *server) { + prvm_prog_t *prog = SVVM_prog; prvm_edict_t *ent; int i; char *entities; dp_model_t *worldmodel; char modelname[sizeof(sv.worldname)]; + char vabuf[1024]; Con_DPrintf("SpawnServer: %s\n", server); @@ -3196,6 +3239,11 @@ void SV_SpawnServer (const char *server) } } +// SV_LockThreadMutex(); + + if(cls.state == ca_dedicated) + Sys_MakeProcessNice(); + if (cls.state != ca_dedicated) { SCR_BeginLoadingPlaque(); @@ -3204,15 +3252,14 @@ void SV_SpawnServer (const char *server) if(sv.active) { - SV_VM_Begin(); World_End(&sv.world); - if(prog->funcoffsets.SV_Shutdown) + if(PRVM_serverfunction(SV_Shutdown)) { - func_t s = prog->funcoffsets.SV_Shutdown; - prog->funcoffsets.SV_Shutdown = 0; // prevent it from getting called again - PRVM_ExecuteProgram(s,"SV_Shutdown() required"); + func_t s = PRVM_serverfunction(SV_Shutdown); + PRVM_serverglobalfloat(time) = sv.time; + PRVM_serverfunction(SV_Shutdown) = 0; // prevent it from getting called again + prog->ExecuteProgram(prog, s,"SV_Shutdown() required"); } - SV_VM_End(); } // free q3 shaders so that any newly downloaded shaders will be active @@ -3222,6 +3269,12 @@ void SV_SpawnServer (const char *server) if (!worldmodel || !worldmodel->TraceBox) { Con_Printf("Couldn't load map %s\n", modelname); + + if(cls.state == ca_dedicated) + Sys_MakeProcessMean(); + +// SV_UnlockThreadMutex(); + return; } @@ -3308,8 +3361,6 @@ void SV_SpawnServer (const char *server) sv.protocol = PROTOCOL_QUAKE; } - SV_VM_Begin(); - // load progs to get entity field count //PR_LoadProgs ( sv_progs.string ); @@ -3332,7 +3383,7 @@ void SV_SpawnServer (const char *server) prog->allowworldwrites = true; sv.paused = false; - prog->globals.server->time = sv.time = 1.0; + sv.time = 1.0; Mod_ClearUsed(); worldmodel->used = true; @@ -3343,7 +3394,7 @@ void SV_SpawnServer (const char *server) // // clear world interaction links // - World_SetSize(&sv.world, sv.worldname, sv.worldmodel->normalmins, sv.worldmodel->normalmaxs); + World_SetSize(&sv.world, sv.worldname, sv.worldmodel->normalmins, sv.worldmodel->normalmaxs, prog); World_Start(&sv.world); strlcpy(sv.sound_precache[0], "", sizeof(sv.sound_precache[0])); @@ -3363,26 +3414,26 @@ void SV_SpawnServer (const char *server) // // AK possible hack since num_edicts is still 0 ent = PRVM_EDICT_NUM(0); - memset (ent->fields.server, 0, prog->entityfields * 4); + memset (ent->fields.vp, 0, prog->entityfields * 4); ent->priv.server->free = false; - ent->fields.server->model = PRVM_SetEngineString(sv.worldname); - ent->fields.server->modelindex = 1; // world model - ent->fields.server->solid = SOLID_BSP; - ent->fields.server->movetype = MOVETYPE_PUSH; - VectorCopy(sv.world.mins, ent->fields.server->mins); - VectorCopy(sv.world.maxs, ent->fields.server->maxs); - VectorCopy(sv.world.mins, ent->fields.server->absmin); - VectorCopy(sv.world.maxs, ent->fields.server->absmax); + PRVM_serveredictstring(ent, model) = PRVM_SetEngineString(prog, sv.worldname); + PRVM_serveredictfloat(ent, modelindex) = 1; // world model + PRVM_serveredictfloat(ent, solid) = SOLID_BSP; + PRVM_serveredictfloat(ent, movetype) = MOVETYPE_PUSH; + VectorCopy(sv.world.mins, PRVM_serveredictvector(ent, mins)); + VectorCopy(sv.world.maxs, PRVM_serveredictvector(ent, maxs)); + VectorCopy(sv.world.mins, PRVM_serveredictvector(ent, absmin)); + VectorCopy(sv.world.maxs, PRVM_serveredictvector(ent, absmax)); if (coop.value) - prog->globals.server->coop = coop.integer; + PRVM_serverglobalfloat(coop) = coop.integer; else - prog->globals.server->deathmatch = deathmatch.integer; + PRVM_serverglobalfloat(deathmatch) = deathmatch.integer; - prog->globals.server->mapname = PRVM_SetEngineString(sv.name); + PRVM_serverglobalstring(mapname) = PRVM_SetEngineString(prog, sv.name); // serverflags are for cross level information (sigils) - prog->globals.server->serverflags = svs.serverflags; + PRVM_serverglobalfloat(serverflags) = svs.serverflags; // we need to reset the spawned flag on all connected clients here so that // their thinks don't run during startup (before PutClientInServer) @@ -3392,29 +3443,29 @@ void SV_SpawnServer (const char *server) { host_client->spawned = false; host_client->edict = PRVM_EDICT_NUM(i + 1); - PRVM_ED_ClearEdict(host_client->edict); + PRVM_ED_ClearEdict(prog, host_client->edict); } // load replacement entity file if found - if (sv_entpatch.integer && (entities = (char *)FS_LoadFile(va("%s.ent", sv.worldnamenoextension), tempmempool, true, NULL))) + if (sv_entpatch.integer && (entities = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s.ent", sv.worldnamenoextension), tempmempool, true, NULL))) { Con_Printf("Loaded %s.ent\n", sv.worldnamenoextension); - PRVM_ED_LoadFromFile (entities); + PRVM_ED_LoadFromFile(prog, entities); Mem_Free(entities); } else - PRVM_ED_LoadFromFile (sv.worldmodel->brush.entities); + PRVM_ED_LoadFromFile(prog, sv.worldmodel->brush.entities); // LordHavoc: clear world angles (to fix e3m3.bsp) - VectorClear(prog->edicts->fields.server->angles); + VectorClear(PRVM_serveredictvector(prog->edicts, angles)); // all setup is completed, any further precache statements are errors // sv.state = ss_active; // LordHavoc: workaround for svc_precache bug prog->allowworldwrites = false; // run two frames to allow everything to settle - prog->globals.server->time = sv.time = 1.0001; + sv.time = 1.0001; for (i = 0;i < 2;i++) { sv.frametime = 0.1; @@ -3430,9 +3481,6 @@ void SV_SpawnServer (const char *server) sv.state = ss_active; // LordHavoc: workaround for svc_precache bug - // to prevent network timeouts - realtime = Sys_DoubleTime(); - // send serverinfo to all connected clients, and set up botclients coming back from a level change for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++) { @@ -3449,38 +3497,41 @@ void SV_SpawnServer (const char *server) // copy spawn parms out of the client_t for (j=0 ; j< NUM_SPAWN_PARMS ; j++) - (&prog->globals.server->parm1)[j] = host_client->spawn_parms[j]; + (&PRVM_serverglobalfloat(parm1))[j] = host_client->spawn_parms[j]; // call the spawn function host_client->clientconnectcalled = true; - prog->globals.server->time = sv.time; - prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict); - PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing"); - PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing"); + PRVM_serverglobalfloat(time) = sv.time; + PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict); + prog->ExecuteProgram(prog, PRVM_serverfunction(ClientConnect), "QC function ClientConnect is missing"); + prog->ExecuteProgram(prog, PRVM_serverfunction(PutClientInServer), "QC function PutClientInServer is missing"); host_client->spawned = true; } } // update the map title cvar - strlcpy(sv.worldmessage, PRVM_GetString(prog->edicts->fields.server->message), sizeof(sv.worldmessage)); // map title (not related to filename) + strlcpy(sv.worldmessage, PRVM_GetString(prog, PRVM_serveredictstring(prog->edicts, message)), sizeof(sv.worldmessage)); // map title (not related to filename) Cvar_SetQuick(&sv_worldmessage, sv.worldmessage); Con_DPrint("Server spawned.\n"); NetConn_Heartbeat (2); - SV_VM_End(); + if(cls.state == ca_dedicated) + Sys_MakeProcessMean(); + +// SV_UnlockThreadMutex(); } ///////////////////////////////////////////////////// // SV VM stuff -static void SV_VM_CB_BeginIncreaseEdicts(void) +static void SVVM_begin_increase_edicts(prvm_prog_t *prog) { // links don't survive the transition, so unlink everything World_UnlinkAll(&sv.world); } -static void SV_VM_CB_EndIncreaseEdicts(void) +static void SVVM_end_increase_edicts(prvm_prog_t *prog) { int i; prvm_edict_t *ent; @@ -3491,7 +3542,7 @@ static void SV_VM_CB_EndIncreaseEdicts(void) SV_LinkEdict(ent); } -static void SV_VM_CB_InitEdict(prvm_edict_t *e) +static void SVVM_init_edict(prvm_prog_t *prog, prvm_edict_t *e) { // LordHavoc: for consistency set these here int num = PRVM_NUM_FOR_EDICT(e) - 1; @@ -3501,88 +3552,67 @@ static void SV_VM_CB_InitEdict(prvm_edict_t *e) if (num >= 0 && num < svs.maxclients) { // set colormap and team on newly created player entity - e->fields.server->colormap = num + 1; - e->fields.server->team = (svs.clients[num].colors & 15) + 1; + PRVM_serveredictfloat(e, colormap) = num + 1; + PRVM_serveredictfloat(e, team) = (svs.clients[num].colors & 15) + 1; // set netname/clientcolors back to client values so that // DP_SV_CLIENTNAME and DP_SV_CLIENTCOLORS will not immediately // reset them - e->fields.server->netname = PRVM_SetEngineString(svs.clients[num].name); - PRVM_EDICTFIELDFLOAT(e, prog->fieldoffsets.clientcolors) = svs.clients[num].colors; + PRVM_serveredictstring(e, netname) = PRVM_SetEngineString(prog, svs.clients[num].name); + PRVM_serveredictfloat(e, clientcolors) = svs.clients[num].colors; // NEXUIZ_PLAYERMODEL and NEXUIZ_PLAYERSKIN - if( prog->fieldoffsets.playermodel >= 0 ) - PRVM_EDICTFIELDSTRING(e, prog->fieldoffsets.playermodel) = PRVM_SetEngineString(svs.clients[num].playermodel); - if( prog->fieldoffsets.playerskin >= 0 ) - PRVM_EDICTFIELDSTRING(e, prog->fieldoffsets.playerskin) = PRVM_SetEngineString(svs.clients[num].playerskin); + PRVM_serveredictstring(e, playermodel) = PRVM_SetEngineString(prog, svs.clients[num].playermodel); + PRVM_serveredictstring(e, playerskin) = PRVM_SetEngineString(prog, svs.clients[num].playerskin); // Assign netaddress (IP Address, etc) - if(prog->fieldoffsets.netaddress >= 0) - { // Valid Field; Process - if(svs.clients[num].netconnection != NULL) - {// Valid Address; Assign - // Acquire Readable Address - LHNETADDRESS_ToString(&svs.clients[num].netconnection->peeraddress, svs.clients[num].netaddress, sizeof(svs.clients[num].netaddress), false); - PRVM_EDICTFIELDSTRING(e, prog->fieldoffsets.netaddress) = PRVM_SetEngineString(svs.clients[num].netaddress); - } - else - // Invalid / Bot - PRVM_EDICTFIELDSTRING(e, prog->fieldoffsets.netaddress) = PRVM_SetEngineString("null/botclient"); - } - if(prog->fieldoffsets.crypto_idfp >= 0) - { // Valid Field; Process - if(svs.clients[num].netconnection != NULL && svs.clients[num].netconnection->crypto.authenticated && svs.clients[num].netconnection->crypto.client_idfp[0]) - PRVM_EDICTFIELDSTRING(e, prog->fieldoffsets.crypto_idfp) = PRVM_SetEngineString(svs.clients[num].netconnection->crypto.client_idfp); - else - PRVM_EDICTFIELDSTRING(e, prog->fieldoffsets.crypto_idfp) = 0; - } - if(prog->fieldoffsets.crypto_keyfp >= 0) - { // Valid Field; Process - if(svs.clients[num].netconnection != NULL && svs.clients[num].netconnection->crypto.authenticated && svs.clients[num].netconnection->crypto.client_keyfp[0]) - PRVM_EDICTFIELDSTRING(e, prog->fieldoffsets.crypto_keyfp) = PRVM_SetEngineString(svs.clients[num].netconnection->crypto.client_keyfp); - else - PRVM_EDICTFIELDSTRING(e, prog->fieldoffsets.crypto_keyfp) = 0; - } - if(prog->fieldoffsets.crypto_mykeyfp >= 0) - { // Valid Field; Process - if(svs.clients[num].netconnection != NULL && svs.clients[num].netconnection->crypto.authenticated && svs.clients[num].netconnection->crypto.server_keyfp[0]) - PRVM_EDICTFIELDSTRING(e, prog->fieldoffsets.crypto_mykeyfp) = PRVM_SetEngineString(svs.clients[num].netconnection->crypto.server_keyfp); - else - PRVM_EDICTFIELDSTRING(e, prog->fieldoffsets.crypto_mykeyfp) = 0; - } - if(prog->fieldoffsets.crypto_encryptmethod >= 0) - { // Valid Field; Process - if(svs.clients[num].netconnection != NULL && svs.clients[num].netconnection->crypto.authenticated && svs.clients[num].netconnection->crypto.use_aes) - PRVM_EDICTFIELDSTRING(e, prog->fieldoffsets.crypto_encryptmethod) = PRVM_SetEngineString("AES128"); - else - PRVM_EDICTFIELDSTRING(e, prog->fieldoffsets.crypto_encryptmethod) = 0; - } - if(prog->fieldoffsets.crypto_signmethod >= 0) - { // Valid Field; Process - if(svs.clients[num].netconnection != NULL && svs.clients[num].netconnection->crypto.authenticated) - PRVM_EDICTFIELDSTRING(e, prog->fieldoffsets.crypto_signmethod) = PRVM_SetEngineString("HMAC-SHA256"); - else - PRVM_EDICTFIELDSTRING(e, prog->fieldoffsets.crypto_signmethod) = 0; + if(svs.clients[num].netconnection != NULL) + { + // Acquire Readable Address + LHNETADDRESS_ToString(&svs.clients[num].netconnection->peeraddress, svs.clients[num].netaddress, sizeof(svs.clients[num].netaddress), false); + PRVM_serveredictstring(e, netaddress) = PRVM_SetEngineString(prog, svs.clients[num].netaddress); } + else + PRVM_serveredictstring(e, netaddress) = PRVM_SetEngineString(prog, "null/botclient"); + if(svs.clients[num].netconnection != NULL && svs.clients[num].netconnection->crypto.authenticated && svs.clients[num].netconnection->crypto.client_idfp[0]) + PRVM_serveredictstring(e, crypto_idfp) = PRVM_SetEngineString(prog, svs.clients[num].netconnection->crypto.client_idfp); + else + PRVM_serveredictstring(e, crypto_idfp) = 0; + if(svs.clients[num].netconnection != NULL && svs.clients[num].netconnection->crypto.authenticated && svs.clients[num].netconnection->crypto.client_keyfp[0]) + PRVM_serveredictstring(e, crypto_keyfp) = PRVM_SetEngineString(prog, svs.clients[num].netconnection->crypto.client_keyfp); + else + PRVM_serveredictstring(e, crypto_keyfp) = 0; + if(svs.clients[num].netconnection != NULL && svs.clients[num].netconnection->crypto.authenticated && svs.clients[num].netconnection->crypto.server_keyfp[0]) + PRVM_serveredictstring(e, crypto_mykeyfp) = PRVM_SetEngineString(prog, svs.clients[num].netconnection->crypto.server_keyfp); + else + PRVM_serveredictstring(e, crypto_mykeyfp) = 0; + if(svs.clients[num].netconnection != NULL && svs.clients[num].netconnection->crypto.authenticated && svs.clients[num].netconnection->crypto.use_aes) + PRVM_serveredictstring(e, crypto_encryptmethod) = PRVM_SetEngineString(prog, "AES128"); + else + PRVM_serveredictstring(e, crypto_encryptmethod) = 0; + if(svs.clients[num].netconnection != NULL && svs.clients[num].netconnection->crypto.authenticated) + PRVM_serveredictstring(e, crypto_signmethod) = PRVM_SetEngineString(prog, "HMAC-SHA256"); + else + PRVM_serveredictstring(e, crypto_signmethod) = 0; } } -static void SV_VM_CB_FreeEdict(prvm_edict_t *ed) +static void SVVM_free_edict(prvm_prog_t *prog, prvm_edict_t *ed) { int i; int e; World_UnlinkEdict(ed); // unlink from world bsp - ed->fields.server->model = 0; - ed->fields.server->takedamage = 0; - ed->fields.server->modelindex = 0; - ed->fields.server->colormap = 0; - ed->fields.server->skin = 0; - ed->fields.server->frame = 0; - VectorClear(ed->fields.server->origin); - VectorClear(ed->fields.server->angles); - ed->fields.server->nextthink = -1; - ed->fields.server->solid = 0; - - VM_RemoveEdictSkeleton(ed); + PRVM_serveredictstring(ed, model) = 0; + PRVM_serveredictfloat(ed, takedamage) = 0; + PRVM_serveredictfloat(ed, modelindex) = 0; + PRVM_serveredictfloat(ed, colormap) = 0; + PRVM_serveredictfloat(ed, skin) = 0; + PRVM_serveredictfloat(ed, frame) = 0; + VectorClear(PRVM_serveredictvector(ed, origin)); + VectorClear(PRVM_serveredictvector(ed, angles)); + PRVM_serveredictfloat(ed, nextthink) = -1; + PRVM_serveredictfloat(ed, solid) = 0; + + VM_RemoveEdictSkeleton(prog, ed); World_Physics_RemoveFromEntity(&sv.world, ed); World_Physics_RemoveJointFromEntity(&sv.world, ed); @@ -3597,7 +3627,7 @@ static void SV_VM_CB_FreeEdict(prvm_edict_t *ed) } } -static void SV_VM_CB_CountEdicts(void) +static void SVVM_count_edicts(prvm_prog_t *prog) { int i; prvm_edict_t *ent; @@ -3610,11 +3640,11 @@ static void SV_VM_CB_CountEdicts(void) if (ent->priv.server->free) continue; active++; - if (ent->fields.server->solid) + if (PRVM_serveredictfloat(ent, solid)) solid++; - if (ent->fields.server->model) + if (PRVM_serveredictstring(ent, model)) models++; - if (ent->fields.server->movetype == MOVETYPE_STEP) + if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_STEP) step++; } @@ -3625,21 +3655,21 @@ static void SV_VM_CB_CountEdicts(void) Con_Printf("step :%3i\n", step); } -static qboolean SV_VM_CB_LoadEdict(prvm_edict_t *ent) +static qboolean SVVM_load_edict(prvm_prog_t *prog, prvm_edict_t *ent) { // remove things from different skill levels or deathmatch if (gamemode != GAME_TRANSFUSION) //Transfusion does this in QC { if (deathmatch.integer) { - if (((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_DEATHMATCH)) + if (((int)PRVM_serveredictfloat(ent, spawnflags) & SPAWNFLAG_NOT_DEATHMATCH)) { return false; } } - else if ((current_skill <= 0 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_EASY )) - || (current_skill == 1 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_MEDIUM)) - || (current_skill >= 2 && ((int)ent->fields.server->spawnflags & SPAWNFLAG_NOT_HARD ))) + else if ((current_skill <= 0 && ((int)PRVM_serveredictfloat(ent, spawnflags) & SPAWNFLAG_NOT_EASY )) + || (current_skill == 1 && ((int)PRVM_serveredictfloat(ent, spawnflags) & SPAWNFLAG_NOT_MEDIUM)) + || (current_skill >= 2 && ((int)PRVM_serveredictfloat(ent, spawnflags) & SPAWNFLAG_NOT_HARD ))) { return false; } @@ -3649,16 +3679,14 @@ static qboolean SV_VM_CB_LoadEdict(prvm_edict_t *ent) static void SV_VM_Setup(void) { - PRVM_Begin; - PRVM_InitProg( PRVM_SERVERPROG ); + prvm_prog_t *prog = SVVM_prog; + PRVM_Prog_Init(prog); // allocate the mempools // TODO: move the magic numbers/constants into #defines [9/13/2006 Black] prog->progs_mempool = Mem_AllocPool("Server Progs", 0, NULL); prog->builtins = vm_sv_builtins; prog->numbuiltins = vm_sv_numbuiltins; - prog->headercrc = PROGHEADER_CRC; - prog->headercrc2 = PROGHEADER_CRC_TENEBRAE; prog->max_edicts = 512; if (sv.protocol == PROTOCOL_QUAKE) prog->limit_edicts = 640; // before quake mission pack 1 this was 512 @@ -3676,64 +3704,329 @@ static void SV_VM_Setup(void) prog->extensionstring = vm_sv_extensions; prog->loadintoworld = true; - prog->begin_increase_edicts = SV_VM_CB_BeginIncreaseEdicts; - prog->end_increase_edicts = SV_VM_CB_EndIncreaseEdicts; - prog->init_edict = SV_VM_CB_InitEdict; - prog->free_edict = SV_VM_CB_FreeEdict; - prog->count_edicts = SV_VM_CB_CountEdicts; - prog->load_edict = SV_VM_CB_LoadEdict; - prog->init_cmd = VM_SV_Cmd_Init; - prog->reset_cmd = VM_SV_Cmd_Reset; - prog->error_cmd = Host_Error; - prog->ExecuteProgram = SVVM_ExecuteProgram; + // all callbacks must be defined (pointers are not checked before calling) + prog->begin_increase_edicts = SVVM_begin_increase_edicts; + prog->end_increase_edicts = SVVM_end_increase_edicts; + prog->init_edict = SVVM_init_edict; + prog->free_edict = SVVM_free_edict; + prog->count_edicts = SVVM_count_edicts; + prog->load_edict = SVVM_load_edict; + prog->init_cmd = SVVM_init_cmd; + prog->reset_cmd = SVVM_reset_cmd; + prog->error_cmd = Host_Error; + prog->ExecuteProgram = SVVM_ExecuteProgram; - PRVM_LoadProgs( sv_progs.string, SV_REQFUNCS, sv_reqfuncs, SV_REQFIELDS, sv_reqfields, SV_REQGLOBALS, sv_reqglobals); + PRVM_Prog_Load(prog, sv_progs.string, SV_REQFUNCS, sv_reqfuncs, SV_REQFIELDS, sv_reqfields, SV_REQGLOBALS, sv_reqglobals); // some mods compiled with scrambling compilers lack certain critical // global names and field names such as "self" and "time" and "nextthink" // so we have to set these offsets manually, matching the entvars_t - PRVM_ED_FindFieldOffset_FromStruct(entvars_t, angles); - PRVM_ED_FindFieldOffset_FromStruct(entvars_t, chain); - PRVM_ED_FindFieldOffset_FromStruct(entvars_t, classname); - PRVM_ED_FindFieldOffset_FromStruct(entvars_t, frame); - PRVM_ED_FindFieldOffset_FromStruct(entvars_t, groundentity); - PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ideal_yaw); - PRVM_ED_FindFieldOffset_FromStruct(entvars_t, nextthink); - PRVM_ED_FindFieldOffset_FromStruct(entvars_t, think); - PRVM_ED_FindFieldOffset_FromStruct(entvars_t, yaw_speed); - PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, self); - PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, time); - PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_forward); - PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_right); - PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_up); - PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_allsolid); - PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_startsolid); - PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_fraction); - PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inwater); - PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inopen); - PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_endpos); - PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_normal); - PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_dist); - PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_ent); - // OP_STATE is always supported on server (due to entvars_t) + // but we only do this if the prog header crc matches, otherwise it's totally freeform + if (prog->progs_crc == PROGHEADER_CRC || prog->progs_crc == PROGHEADER_CRC_TENEBRAE) + { + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, modelindex); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, absmin); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, absmax); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ltime); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, movetype); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, solid); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, origin); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, oldorigin); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, velocity); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, angles); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, avelocity); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, punchangle); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, classname); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, model); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, frame); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, skin); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, effects); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, mins); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, maxs); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, size); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, touch); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, use); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, think); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, blocked); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, nextthink); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, groundentity); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, health); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, frags); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, weapon); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, weaponmodel); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, weaponframe); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, currentammo); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ammo_shells); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ammo_nails); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ammo_rockets); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ammo_cells); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, items); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, takedamage); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, chain); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, deadflag); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, view_ofs); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, button0); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, button1); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, button2); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, impulse); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, fixangle); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, v_angle); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, idealpitch); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, netname); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, enemy); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, flags); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, colormap); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, team); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, max_health); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, teleport_time); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, armortype); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, armorvalue); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, waterlevel); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, watertype); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, ideal_yaw); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, yaw_speed); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, aiment); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, goalentity); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, spawnflags); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, target); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, targetname); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, dmg_take); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, dmg_save); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, dmg_inflictor); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, owner); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, movedir); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, message); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, sounds); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, noise); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, noise1); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, noise2); + PRVM_ED_FindFieldOffset_FromStruct(entvars_t, noise3); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, self); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, other); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, world); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, time); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, frametime); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, force_retouch); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, mapname); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, deathmatch); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, coop); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, teamplay); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, serverflags); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, total_secrets); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, total_monsters); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, found_secrets); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, killed_monsters); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm1); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm2); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm3); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm4); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm5); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm6); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm7); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm8); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm9); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm10); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm11); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm12); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm13); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm14); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm15); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, parm16); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_forward); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_up); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, v_right); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_allsolid); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_startsolid); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_fraction); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_endpos); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_normal); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_plane_dist); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_ent); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inopen); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, trace_inwater); + PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, msg_entity); +// PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, main); +// PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, StartFrame); +// PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, PlayerPreThink); +// PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, PlayerPostThink); +// PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, ClientKill); +// PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, ClientConnect); +// PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, PutClientInServer); +// PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, ClientDisconnect); +// PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, SetNewParms); +// PRVM_ED_FindGlobalOffset_FromStruct(globalvars_t, SetChangeParms); + } + else + Con_DPrintf("%s: %s system vars have been modified (CRC %i != engine %i), will not load in other engines", prog->name, sv_progs.string, prog->progs_crc, PROGHEADER_CRC); + + // OP_STATE is always supported on server because we add fields/globals for it prog->flag |= PRVM_OP_STATE; VM_CustomStats_Clear();//[515]: csqc - PRVM_End; - SV_Prepare_CSQC(); } -void SV_VM_Begin(void) +extern cvar_t host_maxwait; +extern cvar_t host_framerate; +static int SV_ThreadFunc(void *voiddata) { - PRVM_Begin; - PRVM_SetProg( PRVM_SERVERPROG ); + prvm_prog_t *prog = SVVM_prog; + qboolean playing = false; + double sv_timer = 0; + double sv_deltarealtime, sv_oldrealtime, sv_realtime; + double wait; + int i; + char vabuf[1024]; + sv_realtime = Sys_DirtyTime(); + while (!svs.threadstop) + { + // FIXME: we need to handle Host_Error in the server thread somehow +// if (setjmp(sv_abortframe)) +// continue; // something bad happened in the server game + + sv_oldrealtime = sv_realtime; + sv_realtime = Sys_DirtyTime(); + sv_deltarealtime = sv_realtime - sv_oldrealtime; + if (sv_deltarealtime < 0 || sv_deltarealtime >= 1800) sv_deltarealtime = 0; + + sv_timer += sv_deltarealtime; + + svs.perf_acc_realtime += sv_deltarealtime; + + // 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) + SV_LockThreadMutex(); + + // Look for clients who have spawned + playing = false; + if (sv.active) + for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++) + if(host_client->spawned) + if(host_client->netconnection) + playing = true; + if(!playing) + { + // Nobody is looking? Then we won't do timing... + // Instead, reset it to zero + 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; + } + else if(svs.perf_acc_realtime > 5) + { + svs.perf_cpuload = 1 - svs.perf_acc_sleeptime / svs.perf_acc_realtime; + svs.perf_lost = svs.perf_acc_lost / svs.perf_acc_realtime; + if(svs.perf_acc_offset_samples > 0) + { + svs.perf_offset_max = svs.perf_acc_offset_max; + svs.perf_offset_avg = svs.perf_acc_offset / svs.perf_acc_offset_samples; + svs.perf_offset_sdev = sqrt(svs.perf_acc_offset_squared / svs.perf_acc_offset_samples - svs.perf_offset_avg * svs.perf_offset_avg); + } + if(svs.perf_lost > 0 && developer_extra.integer) + Con_DPrintf("Server can't keep up: %s\n", Host_TimingReport(vabuf, sizeof(vabuf))); + 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; + } + + // get new packets + if (sv.active) + NetConn_ServerFrame(); + + // if the accumulators haven't become positive yet, wait a while + wait = sv_timer * -1000000.0; + if (wait >= 1) + { + double time0, delta; + SV_UnlockThreadMutex(); // don't keep mutex locked while sleeping + if (host_maxwait.value <= 0) + wait = min(wait, 1000000.0); + else + wait = min(wait, host_maxwait.value * 1000.0); + if(wait < 1) + wait = 1; // because we cast to int + time0 = Sys_DirtyTime(); + Sys_Sleep((int)wait); + delta = Sys_DirtyTime() - time0;if (delta < 0 || delta >= 1800) delta = 0; + svs.perf_acc_sleeptime += delta; + continue; + } + + if (sv.active && sv_timer > 0) + { + // execute one server frame + double advancetime; + float offset; - prog->globals.server->time = (float) sv.time; + if (sys_ticrate.value <= 0) + advancetime = min(sv_timer, 0.1); // don't step more than 100ms + else + advancetime = sys_ticrate.value; + + if(advancetime > 0) + { + offset = sv_timer + (Sys_DirtyTime() - sv_realtime); // LordHavoc: FIXME: I don't understand this line + ++svs.perf_acc_offset_samples; + svs.perf_acc_offset += offset; + svs.perf_acc_offset_squared += offset * offset; + if(svs.perf_acc_offset_max < offset) + svs.perf_acc_offset_max = offset; + } + + // only advance time if not paused + // the game also pauses in singleplayer when menu or console is used + sv.frametime = advancetime * slowmo.value; + if (host_framerate.value) + sv.frametime = host_framerate.value; + if (sv.paused || (cl.islocalgame && (key_dest != key_game || key_consoleactive || cl.csqc_paused))) + sv.frametime = 0; + + sv_timer -= advancetime; + + // move things around and think unless paused + if (sv.frametime) + SV_Physics(); + + // send all messages to the clients + SV_SendClientMessages(); + + if (sv.paused == 1 && sv_realtime > sv.pausedstart && sv.pausedstart > 0) + { + PRVM_serverglobalfloat(time) = sv.time; + prog->globals.generic[OFS_PARM0] = sv_realtime - sv.pausedstart; + prog->ExecuteProgram(prog, PRVM_serverfunction(SV_PausedTic), "QC function SV_PausedTic is missing"); + } + + // send an heartbeat if enough time has passed since the last one + NetConn_Heartbeat(0); + + } + + // we're back to safe code now + SV_UnlockThreadMutex(); + + // if there is some time remaining from this frame, reset the timers + if (sv_timer >= 0) + { + svs.perf_acc_lost += sv_timer; + sv_timer = 0; + } + } + return 0; +} + +void SV_StartThread(void) +{ + if (!sv_threaded.integer || !Thread_HasThreads()) + return; + svs.threaded = true; + svs.threadstop = false; + svs.threadmutex = Thread_CreateMutex(); + svs.thread = Thread_CreateThread(SV_ThreadFunc, NULL); } -void SV_VM_End(void) +void SV_StopThread(void) { - PRVM_End; + if (!svs.threaded) + return; + svs.threadstop = true; + Thread_WaitThread(svs.thread, 0); + Thread_DestroyMutex(svs.threadmutex); + svs.threaded = false; }