lots of changes

This commit is contained in:
2026-10-22 16:17:56 +01:00
parent e4a75367aa
commit ac3ffd5be2
53 changed files with 7716 additions and 193 deletions
+158 -5
View File
@@ -2,6 +2,7 @@
#include <sdktools>
#include <cstrike>
#include <zombiereloaded>
#include <ripext>
#pragma semicolon 1
#pragma newdecls required
@@ -14,7 +15,19 @@ bool g_bWarmup = false;
ConVar g_CVar_sm_warmuptime;
ConVar g_CVar_sm_warmupratio;
ConVar g_CVar_sm_warmupmaxtime;
ConVar g_CVar_sm_warmupmintime;
ConVar g_CVar_sm_warmupbase;
ConVar g_CVar_sm_warmupperplayer;
ConVar g_CVar_sm_warmuppermib;
ConVar g_CVar_sm_warmupdefaultmib;
ConVar g_CVar_sm_warmupfastdl;
bool g_bLateLoad = false;
// Max warmup inputs: compressed map size on fastdl and the most players seen still connecting
float g_fMapMiB = 0.0;
int g_iPeakConnecting = 0;
int g_iMapSerial = 0;
bool g_bTriedLowercase = false;
Handle g_hWarmupEndFwd;
bool g_bRoundEnded = false;
@@ -40,8 +53,14 @@ public void OnPluginStart()
HookEvent("round_end", OnRoundEnd, EventHookMode_PostNoCopy);
g_CVar_sm_warmuptime = CreateConVar("sm_warmuptime", "10", "Warmup timer.", 0, true, 0.0, true, 60.0);
g_CVar_sm_warmupratio = CreateConVar("sm_warmupratio", "0.60", "Ratio of connected players that need to be in game to start warmup timer.", 0, true, 0.0, true, 1.0);
g_CVar_sm_warmupmaxtime = CreateConVar("sm_warmupmaxtime", "45", "Max Warmup timer.", 0, true, 0.0, true, 120.0);
g_CVar_sm_warmupratio = CreateConVar("sm_warmupratio", "0.90", "Ratio of connected players that need to be in game to start warmup timer.", 0, true, 0.0, true, 1.0);
g_CVar_sm_warmupmaxtime = CreateConVar("sm_warmupmaxtime", "150", "Upper limit for the computed max warmup time.", 0, true, 0.0, true, 600.0);
g_CVar_sm_warmupmintime = CreateConVar("sm_warmupmintime", "30", "Lower limit for the computed max warmup time.", 0, true, 0.0, true, 600.0);
g_CVar_sm_warmupbase = CreateConVar("sm_warmupbase", "20", "Base seconds of the computed max warmup time.", 0, true, 0.0);
g_CVar_sm_warmupperplayer = CreateConVar("sm_warmupperplayer", "0.0", "Max warmup seconds added per player still connecting.", 0, true, 0.0);
g_CVar_sm_warmuppermib = CreateConVar("sm_warmuppermib", "0.5", "Max warmup seconds added per MiB of the compressed map on fastdl.", 0, true, 0.0);
g_CVar_sm_warmupdefaultmib = CreateConVar("sm_warmupdefaultmib", "60", "Map size in MiB to assume until, or if, the fastdl lookup fails.", 0, true, 0.0);
g_CVar_sm_warmupfastdl = CreateConVar("sm_warmupfastdl", "https://fastdl.unloze.com/css_ze/maps/", "FastDL maps URL, <map>.bsp.bz2 is appended.");
g_hWarmupEndFwd = CreateGlobalForward("TeamManager_WarmupEnd", ET_Ignore);
@@ -81,17 +100,151 @@ public void OnConfigsExecuted()
g_bRoundEnded = false;
g_bZombieSpawned = false;
if(g_CVar_sm_warmuptime.IntValue > 0 || g_CVar_sm_warmupratio.FloatValue > 0.0)
g_iMapSerial++;
g_iPeakConnecting = 0;
g_fMapMiB = g_CVar_sm_warmupdefaultmib.FloatValue;
g_bTriedLowercase = false;
// sm_warmuptime 0 disables warmup entirely, some maps break if the round is restarted
if(g_CVar_sm_warmuptime.IntValue > 0)
{
char sMap[PLATFORM_MAX_PATH];
GetCurrentMap(sMap, sizeof(sMap));
RequestMapSize(sMap);
g_bWarmup = true;
CreateTimer(1.0, OnWarmupTimer, 0, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE);
}
}
void RequestMapSize(const char[] sMap)
{
char sBaseURL[256];
g_CVar_sm_warmupfastdl.GetString(sBaseURL, sizeof(sBaseURL));
char sURL[512];
Format(sURL, sizeof(sURL), "%s%s.bsp.bz2", sBaseURL, sMap);
// rip.ext has no HEAD, a one byte range request returns the full size in Content-Range instead
HTTPRequest request = new HTTPRequest(sURL);
request.SetHeader("Range", "bytes=0-0");
request.Timeout = 10;
request.Get(OnMapSizeResponse, g_iMapSerial);
}
public void OnMapSizeResponse(HTTPResponse response, any serial, const char[] error)
{
if(serial != g_iMapSerial)
return;
char sMap[PLATFORM_MAX_PATH];
GetCurrentMap(sMap, sizeof(sMap));
if(error[0])
{
LogMessage("Map size lookup for %s failed (%s), assuming %.1f MiB.", sMap, error, g_fMapMiB);
return;
}
char sSize[64];
bool bFound = false;
if(response.Status == HTTPStatus_PartialContent && response.GetHeader("Content-Range", sSize, sizeof(sSize)))
{
int iSlash = FindCharInString(sSize, '/', true);
if(iSlash != -1)
{
strcopy(sSize, sizeof(sSize), sSize[iSlash + 1]);
bFound = true;
}
}
else if(response.Status == HTTPStatus_OK && response.GetHeader("Content-Length", sSize, sizeof(sSize)))
bFound = true;
else if(response.Status == HTTPStatus_NotFound && !g_bTriedLowercase)
{
// fastdl is case sensitive and mostly stores lowercase names
g_bTriedLowercase = true;
char sLower[PLATFORM_MAX_PATH];
strcopy(sLower, sizeof(sLower), sMap);
for(int i = 0; sLower[i]; i++)
sLower[i] = CharToLower(sLower[i]);
if(!StrEqual(sLower, sMap))
{
RequestMapSize(sLower);
return;
}
}
// float, a large file would overflow an int byte count
float fBytes = bFound ? StringToFloat(sSize) : 0.0;
if(fBytes <= 0.0)
{
LogMessage("Map size lookup for %s got no size (HTTP %d), assuming %.1f MiB.", sMap, response.Status, g_fMapMiB);
return;
}
g_fMapMiB = fBytes / 1048576.0;
LogMessage("Map %s is %.1f MiB on fastdl.", sMap, g_fMapMiB);
}
int GetMaxWarmupTime()
{
float fTime = g_CVar_sm_warmupbase.FloatValue
+ g_CVar_sm_warmupperplayer.FloatValue * g_iPeakConnecting
+ g_CVar_sm_warmuppermib.FloatValue * g_fMapMiB;
int iTime = RoundToCeil(fTime);
int iMin = g_CVar_sm_warmupmintime.IntValue;
int iMax = g_CVar_sm_warmupmaxtime.IntValue;
if(iTime < iMin)
iTime = iMin;
if(iTime > iMax)
iTime = iMax;
return iTime;
}
public Action OnWarmupTimer(Handle timer)
{
if(g_iMaxWarmup >= g_CVar_sm_warmupmaxtime.IntValue || g_iWarmup >= g_CVar_sm_warmuptime.IntValue)
// Disabled after warmup started (e.g. a map config ran late): stop without restarting the round
if(g_CVar_sm_warmuptime.IntValue <= 0)
{
g_iMaxWarmup = 0;
g_bWarmup = false;
Call_StartForward(g_hWarmupEndFwd);
Call_Finish();
return Plugin_Stop;
}
// Only players still loading in count, anyone who left during the map change is already gone
int iConnecting = 0;
int iReady = 0;
for(int client = 1; client <= MaxClients; client++)
{
if(!IsClientConnected(client) || IsFakeClient(client))
continue;
if(IsClientInGame(client))
iReady++;
else
iConnecting++;
}
if(iConnecting > g_iPeakConnecting)
g_iPeakConnecting = iConnecting;
int iMaxWarmupTime = GetMaxWarmupTime();
if(g_iMaxWarmup >= iMaxWarmupTime || g_iWarmup >= g_CVar_sm_warmuptime.IntValue)
{
char sMap[PLATFORM_MAX_PATH];
GetCurrentMap(sMap, sizeof(sMap));
LogMessage("Warmup on %s ended after %ds with %d ready players: %.1f MiB, %d peak connecting, max warmup %ds.",
sMap, g_iMaxWarmup + g_iWarmup, iReady, g_fMapMiB, g_iPeakConnecting, iMaxWarmupTime);
g_iMaxWarmup = 0;
g_bWarmup = false;
float fDelay = 3.0;
@@ -110,7 +263,7 @@ public Action OnWarmupTimer(Handle timer)
if(ClientsInGame < ClientsNeeded)
{
g_iMaxWarmup++;
PrintCenterTextAll("Warmup: Waiting for %d more players to join or %d seconds.", ClientsNeeded - ClientsInGame, g_CVar_sm_warmupmaxtime.IntValue - g_iMaxWarmup);
PrintCenterTextAll("Warmup: Waiting for %d more players to join or %d seconds.", ClientsNeeded - ClientsInGame, iMaxWarmupTime - g_iMaxWarmup);
return Plugin_Continue;
}
else