From: Rudolf Polzer Date: Tue, 25 Jun 2024 19:13:46 +0000 (-0400) Subject: sv_sendentities_csqc_randomize_order: send entities in somewhat randomized order. X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=cb7b1ca08865964d22d09522a99745014963fd39;p=xonotic%2Fdarkplaces.git sv_sendentities_csqc_randomize_order: send entities in somewhat randomized order. Should improve CSQC networking at low rates or high byte rate sending mods. No impact if sv_sendentities_csqc_randomize_order is 0. No impact to non-CSQC entities (these already have a superior priority system). Enabled by default; let's see how well this works. --- diff --git a/sv_ents_csqc.c b/sv_ents_csqc.c index 3e09b3ca..2be0df8b 100644 --- a/sv_ents_csqc.c +++ b/sv_ents_csqc.c @@ -1,6 +1,8 @@ #include "quakedef.h" #include "protocol.h" +extern cvar_t sv_sendentities_csqc_randomize_order; + // NOTE: this only works with DP5 protocol and upwards. For lower protocols // (including QUAKE), no packet loss handling for CSQC is done, which makes // CSQC basically useless. @@ -162,7 +164,7 @@ static void EntityFrameCSQC_DeallocFrame(client_t *client, int framenum) qbool EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numnumbers, const unsigned short *numbers, int framenum) { prvm_prog_t *prog = SVVM_prog; - int num, number, end, sendflags; + int num, number, end, sendflags, nonplayer_splitpoint, nonplayer_splitpoint_number, nonplayer_index; qbool sectionstarted = false; const unsigned short *n; prvm_edict_t *ed; @@ -216,14 +218,15 @@ qbool EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numnumbers, c } // now try to emit the entity updates - // (FIXME: prioritize by distance?) end = client->csqcnumedicts; + // First send all removals. + nonplayer_index = 0; for (number = 1;number < end;number++) { if (!(client->csqcentityscope[number] & SCOPE_WANTSEND)) continue; if(db->num >= NUM_CSQCENTITIES_PER_FRAME) - break; + goto outofspace; ed = prog->edicts + number; if (client->csqcentityscope[number] & SCOPE_WANTREMOVE) // Also implies ASSUMED_EXISTING. { @@ -247,9 +250,89 @@ qbool EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numnumbers, c ENTITYSIZEPROFILING_END(msg, number, 0); } if (msg->cursize + 17 >= maxsize) - break; + goto outofspace; } else + { + // An update. + sendflags = client->csqcentitysendflags[number]; + // Nothing to send? FINE. + if (!sendflags) + continue; + if (number > svs.maxclients) + ++nonplayer_index; + } + } + + // If sv_sendentities_csqc_randomize_order is false, this is always 0. + // As such, nonplayer_splitpoint_number will be exactly + // svs.maxclients + 1. Thus, the shifting below will be a NOP. + // + // Otherwise, a random subsection of the non-player entities will be + // sent in the first pass, and the rest in the second pass. + // + // This makes it random which entities will be sent or not in case of + // running out of space in the message, guaranteeing that every entity + // eventually gets a chance to be sent. + // + // Note that player entities are never included in this. This is to + // ensure they keep having priority over anything else. If even sending + // the player entities alone runs out of message space, the experience + // will be horrible anyway, not much we can do about it - except maybe + // better culling. + nonplayer_splitpoint_number = svs.maxclients + 1; + if (sv_sendentities_csqc_randomize_order.integer && nonplayer_index > 0) + { + nonplayer_splitpoint = rand() % nonplayer_index; + + // Convert the split point to an entity number. + // This must use the exact same conditions as the above + // incrementing of nonplayer_index. + nonplayer_index = 0; + for (number = 1;number < end;number++) + { + if (!(client->csqcentityscope[number] & SCOPE_WANTSEND)) + continue; + if(db->num >= NUM_CSQCENTITIES_PER_FRAME) + goto outofspace; + ed = prog->edicts + number; + if (!(client->csqcentityscope[number] & SCOPE_WANTREMOVE)) + { + // An update. + sendflags = client->csqcentitysendflags[number]; + // Nothing to send? FINE. + if (!sendflags) + continue; + if (number > svs.maxclients) + { + if (nonplayer_index == nonplayer_splitpoint) + { + nonplayer_splitpoint_number = number; + break; + } + ++nonplayer_index; + } + } + } + } + + for (num = 1;num < end;num++) + { + // Remap entity numbers as follows: + // - 1..maxclients stays as is + // - Otherwise, rotate so that maxclients+1 becomes nonplayer_splitpoint_number. + number = (num <= svs.maxclients) + ? num + : (num - (svs.maxclients + 1) + nonplayer_splitpoint_number); + if (number >= end) + number -= end - (svs.maxclients + 1); + + if (!(client->csqcentityscope[number] & SCOPE_WANTSEND)) + continue; + if(db->num >= NUM_CSQCENTITIES_PER_FRAME) + goto outofspace; + ed = prog->edicts + number; + if (!(client->csqcentityscope[number] & SCOPE_WANTREMOVE)) { // save the cursize value in case we overflow and have to rollback int oldcursize = msg->cursize; @@ -297,7 +380,7 @@ qbool EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numnumbers, c sectionstarted = 1; ENTITYSIZEPROFILING_END(msg, number, 0); if (msg->cursize + 17 >= maxsize) - break; + goto outofspace; } else { @@ -323,7 +406,7 @@ qbool EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numnumbers, c sectionstarted = 1; ENTITYSIZEPROFILING_END(msg, number, sendflags); if (msg->cursize + 17 >= maxsize) - break; + goto outofspace; continue; } } @@ -335,6 +418,8 @@ qbool EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numnumbers, c msg->overflowed = false; } } + +outofspace: if (sectionstarted) { // write index 0 to end the update (0 is never used by real entities) diff --git a/sv_main.c b/sv_main.c index 0743e0ed..1ef85e70 100644 --- a/sv_main.c +++ b/sv_main.c @@ -218,6 +218,8 @@ cvar_t sv_mapformat_is_quake3 = {CF_SERVER, "sv_mapformat_is_quake3", "0", "indi cvar_t sv_writepicture_quality = {CF_SERVER | CF_ARCHIVE, "sv_writepicture_quality", "10", "WritePicture quality offset (higher means better quality, but slower)"}; +cvar_t sv_sendentities_csqc_randomize_order = {CF_SERVER, "sv_sendentities_csqc_randomize_order", "1", "Randomize the order of sending CSQC entities (should behave better when packet size or bandwidth limits are exceeded)."}; + server_t sv; server_static_t svs; @@ -706,6 +708,8 @@ void SV_Init (void) Cvar_RegisterVariable (&sv_writepicture_quality); + Cvar_RegisterVariable (&sv_sendentities_csqc_randomize_order); + SV_InitOperatorCommands(); host.hook.SV_Shutdown = SV_Shutdown;