]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - sv_main.c
implemented .loc file support, including some editing (add command, and removenearest...
[xonotic/darkplaces.git] / sv_main.c
index 0f3eb5745d6827b488da481487f0d5a337c9f016..814e0e1e1cc8ae7f47d7336d2662517ba05c0799 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -46,6 +46,12 @@ extern cvar_t sv_random_seed;
 
 static cvar_t sv_cullentities_pvs = {0, "sv_cullentities_pvs", "1", "fast but loose culling of hidden entities"}; // fast but loose
 static cvar_t sv_cullentities_trace = {0, "sv_cullentities_trace", "0", "somewhat slow but very tight culling of hidden entities, minimizes network traffic and makes wallhack cheats useless"}; // tends to get false negatives, uses a timeout to keep entities visible a short time after becoming hidden
+static cvar_t sv_cullentities_trace_samples = {0, "sv_cullentities_trace_samples", "1", "number of samples to test for entity culling"};
+static cvar_t sv_cullentities_trace_samples_extra = {0, "sv_cullentities_trace_samples_extra", "2", "number of samples to test for entity culling when the entity affects its surroundings by e.g. dlight"};
+static cvar_t sv_cullentities_trace_enlarge = {0, "sv_cullentities_trace_enlarge", "0", "box enlargement for entity culling"};
+static cvar_t sv_cullentities_trace_delay = {0, "sv_cullentities_trace_delay", "1", "number of seconds until the entity gets actually culled"};
+static cvar_t sv_cullentities_trace_prediction = {0, "sv_cullentities_trace_prediction", "1", "also trace from the predicted player position"};
+static cvar_t sv_cullentities_nevercullbmodels = {0, "sv_cullentities_nevercullbmodels", "0", "if enabled the clients are always notified of moving doors and lifts and other submodels of world (warning: eats a lot of network bandwidth on some levels!)"};
 static cvar_t sv_cullentities_stats = {0, "sv_cullentities_stats", "0", "displays stats on network entities culled by various methods for each client"};
 static cvar_t sv_entpatch = {0, "sv_entpatch", "1", "enables loading of .ent files to override entities in the bsp (for example Threewave CTF server pack contains .ent patch files enabling play of CTF on id1 maps)"};
 
@@ -126,6 +132,12 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_nostep);
        Cvar_RegisterVariable (&sv_cullentities_pvs);
        Cvar_RegisterVariable (&sv_cullentities_trace);
+       Cvar_RegisterVariable (&sv_cullentities_trace_samples);
+       Cvar_RegisterVariable (&sv_cullentities_trace_samples_extra);
+       Cvar_RegisterVariable (&sv_cullentities_trace_enlarge);
+       Cvar_RegisterVariable (&sv_cullentities_trace_delay);
+       Cvar_RegisterVariable (&sv_cullentities_trace_prediction);
+       Cvar_RegisterVariable (&sv_cullentities_nevercullbmodels);
        Cvar_RegisterVariable (&sv_cullentities_stats);
        Cvar_RegisterVariable (&sv_entpatch);
        Cvar_RegisterVariable (&sv_gameplayfix_grenadebouncedownslopes);
@@ -396,6 +408,15 @@ void SV_SendServerinfo (client_t *client)
                MSG_WriteString (&client->netconnection->message, "cl_serverextension_download 1");
        }
 
+       // send at this time so it's guaranteed to get executed at the right time
+       {
+               client_t *save;
+               save = host_client;
+               host_client = client;
+               Curl_SendRequirements();
+               host_client = save;
+       }
+
        MSG_WriteByte (&client->netconnection->message, svc_serverinfo);
        MSG_WriteLong (&client->netconnection->message, Protocol_NumberForEnum(sv.protocol));
        MSG_WriteByte (&client->netconnection->message, svs.maxclients);
@@ -427,15 +448,17 @@ void SV_SendServerinfo (client_t *client)
        MSG_WriteByte (&client->netconnection->message, svc_signonnum);
        MSG_WriteByte (&client->netconnection->message, 1);
 
-       {
-               client_t *save;
-               save = host_client;
-               host_client = client;
-               Curl_SendRequirements();
-               host_client = save;
-       }
-
        client->spawned = false;                // need prespawn, spawn, etc
+
+       // clear movement info until client enters the new level properly
+       memset(&client->cmd, 0, sizeof(client->cmd));
+       client->movesequence = 0;
+#ifdef NUM_PING_TIMES
+       for (i = 0;i < NUM_PING_TIMES;i++)
+               client->ping_times[i] = 0;
+       client->num_pings = 0;
+#endif
+       client->ping = 0;
 }
 
 /*
@@ -756,10 +779,17 @@ qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int
                cullmaxs[1] = max(cullmaxs[1], cs->origin[1] + specialvisibilityradius);
                cullmaxs[2] = max(cullmaxs[2], cs->origin[2] + specialvisibilityradius);
        }
+       // calculate center of bbox for network prioritization purposes
+       VectorMAM(0.5f, cullmins, 0.5f, cullmaxs, cs->netcenter);
+       // if culling box has moved, update pvs cluster links
        if (!VectorCompare(cullmins, ent->priv.server->cullmins) || !VectorCompare(cullmaxs, ent->priv.server->cullmaxs))
        {
                VectorCopy(cullmins, ent->priv.server->cullmins);
                VectorCopy(cullmaxs, ent->priv.server->cullmaxs);
+               // a value of -1 for pvs_numclusters indicates that the links are not
+               // cached, and should be re-tested each time, this is the case if the
+               // culling box touches too many pvs clusters to store, or if the world
+               // model does not support FindBoxClusters
                ent->priv.server->pvs_numclusters = -1;
                if (sv.worldmodel && sv.worldmodel->brush.FindBoxClusters)
                {
@@ -805,10 +835,8 @@ static client_t *sv_writeentitiestoclient_client;
 void SV_MarkWriteEntityStateToClient(entity_state_t *s)
 {
        int isbmodel;
-       vec3_t testorigin;
        model_t *model;
        prvm_edict_t *ed;
-       trace_t trace;
        if (sententitiesconsideration[s->number] == sententitiesmark)
                return;
        sententitiesconsideration[s->number] = sententitiesmark;
@@ -837,6 +865,7 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
                if (!s->modelindex && s->specialvisibilityradius == 0)
                        return;
 
+               isbmodel = (model = sv.models[s->modelindex]) != NULL && model->name[0] == '*';
                // viewmodels don't have visibility checking
                if (s->viewmodelforclient)
                {
@@ -854,7 +883,8 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
                }
                // always send world submodels in newer protocols because they don't
                // generate much traffic (in old protocols they hog bandwidth)
-               else if (!(s->effects & EF_NODEPTHTEST) && !((isbmodel = (model = sv.models[s->modelindex]) != NULL && model->name[0] == '*') && (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE)))
+               // but only if sv_cullentities_alwayssendbmodels is on
+               else if (!(s->effects & EF_NODEPTHTEST) && (!isbmodel || !sv_cullentities_nevercullbmodels.integer || sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE))
                {
                        // entity has survived every check so far, check if visible
                        ed = PRVM_EDICT_NUM(s->number);
@@ -889,36 +919,45 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
                        // or not seen by random tracelines
                        if (sv_cullentities_trace.integer && !isbmodel)
                        {
-                               // LordHavoc: test center first
-                               testorigin[0] = (ed->priv.server->cullmins[0] + ed->priv.server->cullmaxs[0]) * 0.5f;
-                               testorigin[1] = (ed->priv.server->cullmins[1] + ed->priv.server->cullmaxs[1]) * 0.5f;
-                               testorigin[2] = (ed->priv.server->cullmins[2] + ed->priv.server->cullmaxs[2]) * 0.5f;
-                               sv.worldmodel->TraceBox(sv.worldmodel, 0, &trace, sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, SUPERCONTENTS_SOLID);
-                               if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
-                                       sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
-                               else
+                               int samples = s->specialvisibilityradius ? sv_cullentities_trace_samples_extra.integer : sv_cullentities_trace_samples.integer;
+                               float enlarge = sv_cullentities_trace_enlarge.value;
+
+                               qboolean visible = TRUE;
+
+                               do
                                {
-                                       // LordHavoc: test random offsets, to maximize chance of detection
-                                       testorigin[0] = lhrandom(ed->priv.server->cullmins[0], ed->priv.server->cullmaxs[0]);
-                                       testorigin[1] = lhrandom(ed->priv.server->cullmins[1], ed->priv.server->cullmaxs[1]);
-                                       testorigin[2] = lhrandom(ed->priv.server->cullmins[2], ed->priv.server->cullmaxs[2]);
-                                       sv.worldmodel->TraceBox(sv.worldmodel, 0, &trace, sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, SUPERCONTENTS_SOLID);
-                                       if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
-                                               sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
-                                       else
+                                       if(Mod_CanSeeBox_Trace(samples, enlarge, sv.worldmodel, sv_writeentitiestoclient_testeye, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
+                                               break; // directly visible from the server's view
+
+                                       if(sv_cullentities_trace_prediction.integer)
                                        {
-                                               if (s->specialvisibilityradius)
+                                               vec3_t predeye;
+
+                                               // get player velocity
+                                               float predtime = bound(0, host_client->ping, 0.2); // / 2
+                                                       // sorry, no wallhacking by high ping please, and at 200ms
+                                                       // ping a FPS is annoying to play anyway and a player is
+                                                       // likely to have changed his direction
+                                               VectorMA(sv_writeentitiestoclient_testeye, predtime, host_client->edict->fields.server->velocity, predeye);
+                                               if(sv.worldmodel->brush.TraceLineOfSight(sv.worldmodel, sv_writeentitiestoclient_testeye, predeye)) // must be able to go there...
                                                {
-                                                       // LordHavoc: test random offsets, to maximize chance of detection
-                                                       testorigin[0] = lhrandom(ed->priv.server->cullmins[0], ed->priv.server->cullmaxs[0]);
-                                                       testorigin[1] = lhrandom(ed->priv.server->cullmins[1], ed->priv.server->cullmaxs[1]);
-                                                       testorigin[2] = lhrandom(ed->priv.server->cullmins[2], ed->priv.server->cullmaxs[2]);
-                                                       sv.worldmodel->TraceBox(sv.worldmodel, 0, &trace, sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, SUPERCONTENTS_SOLID);
-                                                       if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
-                                                               sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
+                                                       if(Mod_CanSeeBox_Trace(samples, enlarge, sv.worldmodel, predeye, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
+                                                               break; // directly visible from the predicted view
+                                               }
+                                               else
+                                               {
+                                                       //Con_DPrintf("Trying to walk into solid in a pingtime... not predicting for culling\n");
                                                }
                                        }
+
+                                       // when we get here, we can't see the entity
+                                       visible = FALSE;
                                }
+                               while(0);
+
+                               if(visible)
+                                       sv_writeentitiestoclient_client->visibletime[s->number] = realtime + sv_cullentities_trace_delay.value;
+
                                if (realtime > sv_writeentitiestoclient_client->visibletime[s->number])
                                {
                                        sv_writeentitiestoclient_culled_trace++;
@@ -936,7 +975,7 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
 }
 
 entity_state_t sendstates[MAX_EDICTS];
-extern int csqc_clent;
+extern int csqc_clientnum;
 
 void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg, int *stats)
 {
@@ -961,7 +1000,8 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *
        if (sv.worldmodel && sv.worldmodel->brush.FatPVS)
                sv_writeentitiestoclient_pvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, sv_writeentitiestoclient_testeye, 8, sv_writeentitiestoclient_pvs, sizeof(sv_writeentitiestoclient_pvs));
 
-       csqc_clent = sv_writeentitiestoclient_clentnum = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
+       sv_writeentitiestoclient_clentnum = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
+       csqc_clientnum = sv_writeentitiestoclient_clentnum - 1;
 
        sententitiesmark++;
 
@@ -1722,6 +1762,126 @@ int SV_SoundIndex(const char *s, int precachemode)
        return 0;
 }
 
+// MUST match effectnameindex_t in client.h
+static const char *standardeffectnames[EFFECT_TOTAL] =
+{
+       "",
+       "TE_GUNSHOT",
+       "TE_GUNSHOTQUAD",
+       "TE_SPIKE",
+       "TE_SPIKEQUAD",
+       "TE_SUPERSPIKE",
+       "TE_SUPERSPIKEQUAD",
+       "TE_WIZSPIKE",
+       "TE_KNIGHTSPIKE",
+       "TE_EXPLOSION",
+       "TE_EXPLOSIONQUAD",
+       "TE_TAREXPLOSION",
+       "TE_TELEPORT",
+       "TE_LAVASPLASH",
+       "TE_SMALLFLASH",
+       "TE_FLAMEJET",
+       "EF_FLAME",
+       "TE_BLOOD",
+       "TE_SPARK",
+       "TE_PLASMABURN",
+       "TE_TEI_G3",
+       "TE_TEI_SMOKE",
+       "TE_TEI_BIGEXPLOSION",
+       "TE_TEI_PLASMAHIT",
+       "EF_STARDUST",
+       "TR_ROCKET",
+       "TR_GRENADE",
+       "TR_BLOOD",
+       "TR_WIZSPIKE",
+       "TR_SLIGHTBLOOD",
+       "TR_KNIGHTSPIKE",
+       "TR_VORESPIKE",
+       "TR_NEHAHRASMOKE",
+       "TR_NEXUIZPLASMA",
+       "TR_GLOWTRAIL",
+       "SVC_PARTICLE"
+};
+
+/*
+================
+SV_ParticleEffectIndex
+
+================
+*/
+int SV_ParticleEffectIndex(const char *name)
+{
+       int i, argc, linenumber, effectnameindex;
+       fs_offset_t filesize;
+       unsigned char *filedata;
+       const char *text, *textstart, *textend;
+       char argv[16][1024];
+       if (!sv.particleeffectnamesloaded)
+       {
+               sv.particleeffectnamesloaded = true;
+               memset(sv.particleeffectname, 0, sizeof(sv.particleeffectname));
+               for (i = 0;i < EFFECT_TOTAL;i++)
+                       strlcpy(sv.particleeffectname[i], standardeffectnames[i], sizeof(sv.particleeffectname[i]));
+               filedata = FS_LoadFile("effectinfo.txt", tempmempool, true, &filesize);
+               if (filedata)
+               {
+                       textstart = (const char *)filedata;
+                       textend = (const char *)filedata + filesize;
+                       text = textstart;
+                       for (linenumber = 1;;linenumber++)
+                       {
+                               argc = 0;
+                               for (;;)
+                               {
+                                       if (!COM_ParseToken(&text, true) || !strcmp(com_token, "\n"))
+                                               break;
+                                       if (argc < 16)
+                                       {
+                                               strlcpy(argv[argc], com_token, sizeof(argv[argc]));
+                                               argc++;
+                                       }
+                               }
+                               if (com_token[0] == 0)
+                                       break; // if the loop exited and it's not a \n, it's EOF
+                               if (argc < 1)
+                                       continue;
+                               if (!strcmp(argv[0], "effect"))
+                               {
+                                       if (argc == 2)
+                                       {
+                                               for (effectnameindex = 1;effectnameindex < SV_MAX_PARTICLEEFFECTNAME;effectnameindex++)
+                                               {
+                                                       if (sv.particleeffectname[effectnameindex][0])
+                                                       {
+                                                               if (!strcmp(sv.particleeffectname[effectnameindex], argv[1]))
+                                                                       break;
+                                                       }
+                                                       else
+                                                       {
+                                                               strlcpy(sv.particleeffectname[effectnameindex], argv[1], sizeof(sv.particleeffectname[effectnameindex]));
+                                                               break;
+                                                       }
+                                               }
+                                               // if we run out of names, abort
+                                               if (effectnameindex == SV_MAX_PARTICLEEFFECTNAME)
+                                               {
+                                                       Con_Printf("effectinfo.txt:%i: too many effects!\n", linenumber);
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+                       Mem_Free(filedata);
+               }
+       }
+       // search for the name
+       for (effectnameindex = 1;effectnameindex < SV_MAX_PARTICLEEFFECTNAME && sv.particleeffectname[effectnameindex][0];effectnameindex++)
+               if (!strcmp(sv.particleeffectname[effectnameindex], name))
+                       return effectnameindex;
+       // return 0 if we couldn't find it
+       return 0;
+}
+
 /*
 ================
 SV_CreateBaseline