#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);
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)"};
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 = {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_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)"};
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"};
//============================================================================
-void SV_AreaStats_f(void)
+static void SV_AreaStats_f(void)
{
World_PrintAreaStats(&sv.world, "server");
}
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);
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);
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);
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);
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));
}
==================
*/
-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)
{
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;
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();
}
/*
==================
*/
-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)
{
if (!sound_num)
return;
+ speed4000 = (int)(speed * 40.0f);
field_mask = 0;
if (volume != DEFAULT_SOUND_PACKET_VOLUME)
field_mask |= SND_VOLUME;
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);
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)
*/
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
{
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)
{
}
//[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))));
}
}
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]);
// 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
*/
void SV_ConnectClient (int clientnum, netconn_t *netconnection)
{
+ prvm_prog_t *prog = SVVM_prog;
client_t *client;
int i;
{
// 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 ; i<NUM_SPAWN_PARMS ; i++)
- client->spawn_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
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;
unsigned int customizeentityforclient;
unsigned int sendentity;
float f;
- float *v;
+ prvm_vec_t *v;
vec3_t cullmins, cullmaxs;
dp_model_t *model;
// 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_serveredictfloat(ent, glow_size) * 0.25f);
*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->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->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;
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
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];
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)
{
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
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];
// 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
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)
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;
}
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);
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;
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;
}
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)
{
}
// 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
#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];
{
if(PRVM_serveredictfunction(ed, camera_transform))
{
- prog->globals.server->self = e;
- prog->globals.server->other = sv.writeentitiestoclient_cliententitynumber;
+ 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]);
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)
{
}
#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;
// 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)
{
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]);
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);
*/
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 ; e<prog->num_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;
}
/*
*/
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;
//
// 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;
}
//
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));
+ VectorCopy(PRVM_serveredictvector(ent, angles), 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)
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)ent->fields.server->items | ((int)PRVM_serveredictfloat(ent, items2) << 23);
- else
- items = (int)ent->fields.server->items | ((int)prog->globals.server->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(ent->fields.server->weaponmodel);
+ s = PRVM_GetString(prog, PRVM_serveredictstring(ent, weaponmodel));
if (strcmp(s, client->weaponmodel))
{
strlcpy(client->weaponmodel, s, sizeof(client->weaponmodel));
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<<i);
if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE && sv.protocol != PROTOCOL_NEHAHRABJP && sv.protocol != PROTOCOL_NEHAHRABJP2 && sv.protocol != PROTOCOL_NEHAHRABJP3)
if (punchvector[i])
bits |= (SU_PUNCHVEC1<<i);
- if (ent->fields.server->velocity[i])
+ if (PRVM_serveredictvector(ent, velocity)[i])
bits |= (SU_VELOCITY1<<i);
}
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
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<<i))
{
if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
- MSG_WriteChar(msg, (int)ent->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<<i))
{
if (bits & (SU_VELOCITY1<<i))
{
if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
- MSG_WriteChar(msg, (int)(ent->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]);
}
}
MSG_WriteByte (msg, stats[STAT_NAILS]);
MSG_WriteByte (msg, stats[STAT_ROCKETS]);
MSG_WriteByte (msg, stats[STAT_CELLS]);
- if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE || gamemode == GAME_NEXUIZ)
+ if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE || gamemode == GAME_QUOTH || gamemode == GAME_NEXUIZ)
{
for (i = 0;i < 32;i++)
if (stats[STAT_ACTIVEWEAPON] & (1<<i))
// 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);
*/
static void SV_UpdateToReliableMessages (void)
{
+ prvm_prog_t *prog = SVVM_prog;
int i, j;
client_t *client;
const char *name;
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)
}
// 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);
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);
}
// 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;
SV_SendClientMessages
=======================
*/
-void SV_SendClientMessages (void)
+void SV_SendClientMessages(void)
{
int i, prepared = false;
// only prepare entities once per frame
SV_PrepareEntitiesForSending();
}
- SV_SendClientDatagram (host_client);
+ SV_SendClientDatagram(host_client);
}
// clear muzzle flashes
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);
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)
{
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;
}
*/
static void SV_CreateBaseline (void)
{
+ prvm_prog_t *prog = SVVM_prog;
int i, entnum, large;
prvm_edict_t *svent;
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;
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;
reloaded on request.
================
*/
-void SV_Prepare_CSQC(void)
+static void SV_Prepare_CSQC(void)
{
fs_offset_t progsize;
*/
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++)
{
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 ; j<NUM_SPAWN_PARMS ; j++)
- host_client->spawn_parms[j] = (&prog->globals.server->parm1)[j];
+ host_client->spawn_parms[j] = (&PRVM_serverglobalfloat(parm1))[j];
}
}
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);
}
}
+// 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
if (!worldmodel || !worldmodel->TraceBox)
{
Con_Printf("Couldn't load map %s\n", modelname);
+
+ if(cls.state == ca_dedicated)
+ Sys_MakeProcessMean();
+
+// SV_UnlockThreadMutex();
+
return;
}
sv.protocol = PROTOCOL_QUAKE;
}
- SV_VM_Begin();
-
// load progs to get entity field count
//PR_LoadProgs ( sv_progs.string );
prog->allowworldwrites = true;
sv.paused = false;
- prog->globals.server->time = sv.time = 1.0;
+ sv.time = 1.0;
Mod_ClearUsed();
worldmodel->used = true;
//
// 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]));
//
// 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.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
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)
{
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;
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++)
{
// 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;
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;
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_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;
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);
}
}
-static void SV_VM_CB_CountEdicts(void)
+static void SVVM_count_edicts(prvm_prog_t *prog)
{
int i;
prvm_edict_t *ent;
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++;
}
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;
}
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
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, NULL, 0, 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(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;
+ }
+
+ // 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;
- prog->globals.server->time = (float) sv.time;
+ 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;
}