X-Git-Url: https://de.git.xonotic.org/?a=blobdiff_plain;f=sv_main.c;h=8ebe87e0ca9e42afc0e257e98ecc2c79678f0fe0;hb=4da490fe21a712e43936dfb2e2d8657c0ee63136;hp=d330329f79dd270e41ef084ba854a9245c37da0d;hpb=fe0d5dc2d85167fb8042bcb5157e93728e74e53a;p=xonotic%2Fdarkplaces.git diff --git a/sv_main.c b/sv_main.c index d330329f..8ebe87e0 100644 --- 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... + { + 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 { - // 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; + //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++; @@ -1050,12 +1090,23 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t SV_SetIdealPitch (); // how much to look up / down ideally // a fixangle might get lost in a dropped packet. Oh well. - if ( ent->fields.server->fixangle ) + if(ent->fields.server->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)); + host_client->fixangle_angles_set = TRUE; + + // and clear fixangle for the next frame + ent->fields.server->fixangle = 0; + } + + if (host_client->fixangle_angles_set) { MSG_WriteByte (msg, svc_setangle); for (i=0 ; i < 3 ; i++) - MSG_WriteAngle (msg, ent->fields.server->angles[i], sv.protocol); - ent->fields.server->fixangle = 0; + MSG_WriteAngle (msg, host_client->fixangle_angles[i], sv.protocol); + host_client->fixangle_angles_set = FALSE; } // stuff the sigil bits into the high bits of items for sbar, or else @@ -1722,6 +1773,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 @@ -2378,6 +2549,7 @@ prvm_required_field_t reqfields[] = {ev_float, "buttonuse"}, {ev_float, "clientcolors"}, {ev_float, "cursor_active"}, + {ev_float, "disableclientprediction"}, {ev_float, "fullbright"}, {ev_float, "glow_color"}, {ev_float, "glow_size"}, @@ -2448,6 +2620,35 @@ void SV_VM_Setup(void) // TODO: add a requiredfuncs list (ask LH if this is necessary at all) PRVM_LoadProgs( sv_progs.string, 0, NULL, REQFIELDS, reqfields, 0, NULL ); + // 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) + prog->flag |= PRVM_OP_STATE; + VM_AutoSentStats_Clear();//[515]: csqc EntityFrameCSQC_ClearVersions();//[515]: csqc