X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=sv_main.c;h=d94bb0321ec868dcd887d062bb05f54571071a7b;hb=add1a1b0abc10b8e720d74aeac52ad8276ee9fe0;hp=87187b51508c8a1d6eee3b8e9582441fed63f4e6;hpb=8ab7e82de6aed81dec7da6debee9d4e37cea9c2b;p=xonotic%2Fdarkplaces.git diff --git a/sv_main.c b/sv_main.c index 87187b51..d94bb032 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)"}; @@ -121,6 +120,9 @@ cvar_t sv_gameplayfix_swiminbmodels = {0, "sv_gameplayfix_swiminbmodels", "1", " 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)"}; @@ -148,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"}; @@ -409,7 +413,7 @@ prvm_required_field_t sv_reqglobals[] = //============================================================================ -void SV_AreaStats_f(void) +static void SV_AreaStats_f(void) { World_PrintAreaStats(&sv.world, "server"); } @@ -443,6 +447,7 @@ void SV_Init (void) 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); @@ -525,6 +530,9 @@ void SV_Init (void) 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); @@ -552,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); @@ -601,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)); } @@ -695,10 +706,11 @@ Larger attenuations will drop off. (max 4 attenuation) ================== */ -void SV_StartSound (prvm_edict_t *entity, int channel, const char *sample, int volume, float attenuation, qboolean reliable) +void SV_StartSound (prvm_edict_t *entity, int channel, const char *sample, int volume, float attenuation, qboolean reliable, float speed) { + prvm_prog_t *prog = SVVM_prog; sizebuf_t *dest; - int sound_num, field_mask, i, ent; + int sound_num, field_mask, i, ent, speed4000; dest = (reliable ? &sv.reliable_datagram : &sv.datagram); @@ -732,11 +744,14 @@ 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 (speed4000 && speed4000 != 4000) + field_mask |= SND_SPEEDUSHORT4000; if (ent >= 8192 || channel < 0 || channel > 7) field_mask |= SND_LARGEENTITY; if (sound_num >= 256) @@ -749,6 +764,8 @@ void SV_StartSound (prvm_edict_t *entity, int channel, const char *sample, int v MSG_WriteByte (dest, volume); if (field_mask & SND_ATTENUATION) MSG_WriteByte (dest, (int)(attenuation*64)); + if (field_mask & SND_SPEEDUSHORT4000) + MSG_WriteShort (dest, speed4000); if (field_mask & SND_LARGEENTITY) { MSG_WriteShort (dest, ent); @@ -781,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) { @@ -805,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; @@ -812,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); @@ -820,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) @@ -849,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 @@ -929,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) { @@ -949,10 +973,10 @@ void SV_SendServerinfo (client_t *client) } //[515]: init stufftext string (it is sent before svc_serverinfo) - if (PRVM_GetString(PRVM_serverglobalstring(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_serverglobalstring(SV_InitCmd)))); + MSG_WriteString (&client->netconnection->message, va(vabuf, sizeof(vabuf), "%s\n", PRVM_GetString(prog, PRVM_serverglobalstring(SV_InitCmd)))); } } @@ -982,7 +1006,7 @@ void SV_SendServerinfo (client_t *client) else MSG_WriteByte (&client->netconnection->message, GAME_COOP); - MSG_WriteString (&client->netconnection->message,PRVM_GetString(PRVM_serveredictstring(prog->edicts, 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]); @@ -1035,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; @@ -1087,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 + PRVM_serverglobalfloat(time) = sv.time; PRVM_serverglobaledict(self) = 0; - PRVM_ExecuteProgram (PRVM_serverfunction(SetNewParms), "QC function SetNewParms is missing"); + prog->ExecuteProgram(prog, PRVM_serverfunction(SetNewParms), "QC function SetNewParms is missing"); for (i=0 ; ispawn_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 @@ -1126,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; @@ -1133,7 +1160,7 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c unsigned int customizeentityforclient; unsigned int sendentity; float f; - float *v; + prvm_vec_t *v; vec3_t cullmins, cullmaxs; dp_model_t *model; @@ -1156,7 +1183,7 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c // LordHavoc: this could kill tags attached to an invisible entity, I // just hope we never have to support that case i = (int)PRVM_serveredictfloat(ent, modelindex); - modelindex = (i >= 1 && i < MAX_MODELS && PRVM_serveredictstring(ent, model) && *PRVM_GetString(PRVM_serveredictstring(ent, model)) && sv.models[i]) ? i : 0; + 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_serveredictfloat(ent, glow_size) * 0.25f); @@ -1335,6 +1362,7 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c 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]; @@ -1430,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 @@ -1452,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]; @@ -1497,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 @@ -1541,7 +1571,7 @@ 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); + 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 @@ -1565,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; @@ -1577,9 +1608,10 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s) if (s->customizeentityforclient) { + PRVM_serverglobalfloat(time) = sv.time; PRVM_serverglobaledict(self) = s->number; PRVM_serverglobaledict(other) = sv.writeentitiestoclient_cliententitynumber; - PRVM_ExecuteProgram(s->customizeentityforclient, "customizeentityforclient: NULL function"); + 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; } @@ -1693,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]; @@ -1713,12 +1746,13 @@ void SV_AddCameraEyes(void) { if(PRVM_serveredictfunction(ed, camera_transform)) { + 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_serveredictfunction(ed, camera_transform), "QC function e.camera_transform is missing"); + 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_serverglobalvector(trace_endpos), camera_origins[n_cameras]); @@ -1765,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; @@ -1860,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); @@ -1895,6 +1939,7 @@ SV_CleanupEnts */ static void SV_CleanupEnts (void) { + prvm_prog_t *prog = SVVM_prog; int e; prvm_edict_t *ent; @@ -1911,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; @@ -1947,7 +1993,7 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t { // angle fixing was requested by global thinking code... // so store the current angles for later use - memcpy(host_client->fixangle_angles, PRVM_serveredictvector(ent, angles), sizeof(host_client->fixangle_angles)); + VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles); host_client->fixangle_angles_set = TRUE; // and clear fixangle for the next frame @@ -1962,18 +2008,16 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t host_client->fixangle_angles_set = FALSE; } - // 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)PRVM_serveredictfloat(ent, items) | ((int)PRVM_serveredictfloat(ent, items2) << 23); - else - items = (int)PRVM_serveredictfloat(ent, items) | ((int)PRVM_serverglobalfloat(serverflags) << 28); + // the runes are in serverflags, pack them into the items value, also pack + // in the items2 value for mission pack huds + // (used only in the mission packs, which do not use serverflags) + items = (int)PRVM_serveredictfloat(ent, items) | ((int)PRVM_serveredictfloat(ent, items2) << 23) | ((int)PRVM_serverglobalfloat(serverflags) << 28); 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(PRVM_serveredictstring(ent, weaponmodel)); + s = PRVM_GetString(prog, PRVM_serveredictstring(ent, weaponmodel)); if (strcmp(s, client->weaponmodel)) { strlcpy(client->weaponmodel, s, sizeof(client->weaponmodel)); @@ -2330,7 +2374,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); @@ -2393,6 +2437,7 @@ SV_UpdateToReliableMessages */ static void SV_UpdateToReliableMessages (void) { + prvm_prog_t *prog = SVVM_prog; int i, j; client_t *client; const char *name; @@ -2407,12 +2452,12 @@ static void SV_UpdateToReliableMessages (void) host_client->edict = PRVM_EDICT_NUM(i+1); // DP_SV_CLIENTNAME - name = PRVM_GetString(PRVM_serveredictstring(host_client->edict, 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)); - PRVM_serveredictstring(host_client->edict, 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) @@ -2437,20 +2482,20 @@ static void SV_UpdateToReliableMessages (void) } // NEXUIZ_PLAYERMODEL - model = PRVM_GetString(PRVM_serveredictstring(host_client->edict, 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(host_client->playermodel); + PRVM_serveredictstring(host_client->edict, playermodel) = PRVM_SetEngineString(prog, host_client->playermodel); // NEXUIZ_PLAYERSKIN - skin = PRVM_GetString(PRVM_serveredictstring(host_client->edict, 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(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_serveredictedict(host_client->edict, clientcamera); @@ -2461,7 +2506,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 +2541,7 @@ static void SV_UpdateToReliableMessages (void) SV_SendClientMessages ======================= */ -void SV_SendClientMessages (void) +void SV_SendClientMessages(void) { int i, prepared = false; @@ -2528,7 +2573,7 @@ void SV_SendClientMessages (void) // only prepare entities once per frame SV_PrepareEntitiesForSending(); } - SV_SendClientDatagram (host_client); + SV_SendClientDatagram(host_client); } // clear muzzle flashes @@ -2810,9 +2855,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); @@ -2919,7 +2978,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) { @@ -2975,6 +3034,7 @@ 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; @@ -2990,6 +3050,7 @@ SV_CreateBaseline */ static void SV_CreateBaseline (void) { + prvm_prog_t *prog = SVVM_prog; int i, entnum, large; prvm_edict_t *svent; @@ -3071,7 +3132,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; @@ -3122,6 +3183,7 @@ transition to another level */ void SV_SaveSpawnparms (void) { + prvm_prog_t *prog = SVVM_prog; int i, j; svs.serverflags = (int)PRVM_serverglobalfloat(serverflags); @@ -3132,8 +3194,9 @@ void SV_SaveSpawnparms (void) continue; // call the progs to get default spawn parms for the new client + PRVM_serverglobalfloat(time) = sv.time; PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict); - PRVM_ExecuteProgram (PRVM_serverfunction(SetChangeParms), "QC function SetChangeParms is missing"); + prog->ExecuteProgram(prog, PRVM_serverfunction(SetChangeParms), "QC function SetChangeParms is missing"); for (j=0 ; jspawn_parms[j] = (&PRVM_serverglobalfloat(parm1))[j]; } @@ -3149,11 +3212,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); @@ -3169,23 +3234,27 @@ void SV_SpawnServer (const char *server) } } +// SV_LockThreadMutex(); + + if(cls.state == ca_dedicated) + Sys_MakeProcessNice(); + if (cls.state != ca_dedicated) { - SCR_BeginLoadingPlaque(); + SCR_BeginLoadingPlaque(false); S_StopAllSounds(); } if(sv.active) { - SV_VM_Begin(); World_End(&sv.world); if(PRVM_serverfunction(SV_Shutdown)) { func_t s = PRVM_serverfunction(SV_Shutdown); + PRVM_serverglobalfloat(time) = sv.time; PRVM_serverfunction(SV_Shutdown) = 0; // prevent it from getting called again - PRVM_ExecuteProgram(s,"SV_Shutdown() required"); + prog->ExecuteProgram(prog, s,"SV_Shutdown() required"); } - SV_VM_End(); } // free q3 shaders so that any newly downloaded shaders will be active @@ -3195,6 +3264,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; } @@ -3281,8 +3356,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 ); @@ -3305,7 +3378,7 @@ void SV_SpawnServer (const char *server) prog->allowworldwrites = true; sv.paused = false; - PRVM_serverglobalfloat(time) = sv.time = 1.0; + sv.time = 1.0; Mod_ClearUsed(); worldmodel->used = true; @@ -3316,7 +3389,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])); @@ -3336,9 +3409,9 @@ void SV_SpawnServer (const char *server) // // AK possible hack since num_edicts is still 0 ent = PRVM_EDICT_NUM(0); - memset (ent->fields.vp, 0, prog->entityfields * 4); + memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t)); ent->priv.server->free = false; - PRVM_serveredictstring(ent, model) = PRVM_SetEngineString(sv.worldname); + 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; @@ -3352,7 +3425,7 @@ void SV_SpawnServer (const char *server) else PRVM_serverglobalfloat(deathmatch) = deathmatch.integer; - PRVM_serverglobalstring(mapname) = PRVM_SetEngineString(sv.name); + PRVM_serverglobalstring(mapname) = PRVM_SetEngineString(prog, sv.name); // serverflags are for cross level information (sigils) PRVM_serverglobalfloat(serverflags) = svs.serverflags; @@ -3365,18 +3438,18 @@ 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) @@ -3387,7 +3460,7 @@ void SV_SpawnServer (const char *server) prog->allowworldwrites = false; // run two frames to allow everything to settle - PRVM_serverglobalfloat(time) = sv.time = 1.0001; + sv.time = 1.0001; for (i = 0;i < 2;i++) { sv.frametime = 0.1; @@ -3403,9 +3476,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++) { @@ -3428,32 +3498,35 @@ void SV_SpawnServer (const char *server) host_client->clientconnectcalled = true; PRVM_serverglobalfloat(time) = sv.time; PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict); - PRVM_ExecuteProgram (PRVM_serverfunction(ClientConnect), "QC function ClientConnect is missing"); - PRVM_ExecuteProgram (PRVM_serverfunction(PutClientInServer), "QC function PutClientInServer is missing"); + 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(PRVM_serveredictstring(prog->edicts, 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; @@ -3464,7 +3537,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; @@ -3479,44 +3552,44 @@ static void SV_VM_CB_InitEdict(prvm_edict_t *e) // set netname/clientcolors back to client values so that // DP_SV_CLIENTNAME and DP_SV_CLIENTCOLORS will not immediately // reset them - PRVM_serveredictstring(e, netname) = PRVM_SetEngineString(svs.clients[num].name); + PRVM_serveredictstring(e, netname) = PRVM_SetEngineString(prog, svs.clients[num].name); PRVM_serveredictfloat(e, clientcolors) = svs.clients[num].colors; // NEXUIZ_PLAYERMODEL and NEXUIZ_PLAYERSKIN - PRVM_serveredictstring(e, playermodel) = PRVM_SetEngineString(svs.clients[num].playermodel); - PRVM_serveredictstring(e, 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(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(svs.clients[num].netaddress); + PRVM_serveredictstring(e, netaddress) = PRVM_SetEngineString(prog, svs.clients[num].netaddress); } else - PRVM_serveredictstring(e, netaddress) = PRVM_SetEngineString("null/botclient"); + 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(svs.clients[num].netconnection->crypto.client_idfp); + 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(svs.clients[num].netconnection->crypto.client_keyfp); + 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(svs.clients[num].netconnection->crypto.server_keyfp); + 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("AES128"); + 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("HMAC-SHA256"); + 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; @@ -3534,7 +3607,7 @@ static void SV_VM_CB_FreeEdict(prvm_edict_t *ed) PRVM_serveredictfloat(ed, nextthink) = -1; PRVM_serveredictfloat(ed, solid) = 0; - VM_RemoveEdictSkeleton(ed); + VM_RemoveEdictSkeleton(prog, ed); World_Physics_RemoveFromEntity(&sv.world, ed); World_Physics_RemoveJointFromEntity(&sv.world, ed); @@ -3549,7 +3622,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; @@ -3577,7 +3650,7 @@ 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 @@ -3601,8 +3674,8 @@ 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] @@ -3626,18 +3699,19 @@ 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" @@ -3778,27 +3852,177 @@ static void SV_VM_Setup(void) // 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", PRVM_NAME, sv_progs.string, prog->progs_crc, PROGHEADER_CRC); + 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(sv.time < 10) + { + // don't accumulate time for the first 10 seconds of a match + // so things can settle + 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) + if(playing) + 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; + } - PRVM_serverglobalfloat(time) = (float) sv.time; + // 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; + + 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.fp[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_VM_End(void) +void SV_StartThread(void) { - PRVM_End; + 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_StopThread(void) +{ + if (!svs.threaded) + return; + svs.threadstop = true; + Thread_WaitThread(svs.thread, 0); + Thread_DestroyMutex(svs.threadmutex); + svs.threaded = false; }