279 lines
9.0 KiB
SourcePawn
279 lines
9.0 KiB
SourcePawn
#include <sourcemod>
|
|
#include <sdktools>
|
|
#include <dhooks>
|
|
|
|
public Plugin myinfo =
|
|
{
|
|
name = "Render Distance Limiter",
|
|
author = "Dysphie, repurposed for unloze webclient using some AI",
|
|
description = "",
|
|
version = "",
|
|
url = ""
|
|
};
|
|
|
|
int g_iClientFogEntity[MAXPLAYERS + 1] = { INVALID_ENT_REFERENCE, ... };
|
|
float g_OverrideDist[MAXPLAYERS + 1] = { -1.0, ... };
|
|
bool bEnabled[MAXPLAYERS + 1];
|
|
|
|
float g_fClientEstimatedFPS[MAXPLAYERS + 1];
|
|
|
|
// New tracking arrays for your global variables section
|
|
float g_fLastStableDist[MAXPLAYERS + 1] = { -1.0, ... };
|
|
float g_fDistCooldown[MAXPLAYERS + 1] = { 0.0, ... };
|
|
|
|
int g_iLastCmdNum[MAXPLAYERS + 1];
|
|
float g_fFPSIntervalStart[MAXPLAYERS + 1];
|
|
int g_iCmdsInInterval[MAXPLAYERS + 1];
|
|
|
|
public void OnPluginStart()
|
|
{
|
|
SetupDetours();
|
|
CreateTimer(0.5, Timer_printFPS, _, TIMER_REPEAT);
|
|
}
|
|
|
|
//ai generated
|
|
public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2])
|
|
{
|
|
if (IsFakeClient(client) || !bEnabled[client])
|
|
return Plugin_Continue;
|
|
|
|
// Check if this is a brand new user command execution frame
|
|
if (cmdnum != g_iLastCmdNum[client])
|
|
{
|
|
g_iLastCmdNum[client] = cmdnum;
|
|
g_iCmdsInInterval[client]++;
|
|
}
|
|
|
|
float fCurrentTime = GetEngineTime();
|
|
float fTimeElapsed = fCurrentTime - g_fFPSIntervalStart[client];
|
|
|
|
// Evaluate the true input frequency every 0.5 seconds
|
|
if (fTimeElapsed >= 0.5)
|
|
{
|
|
if (g_fFPSIntervalStart[client] > 0.0)
|
|
{
|
|
// Raw calculated input framerate based on sequential command progression
|
|
float fCalculatedFPS = float(g_iCmdsInInterval[client]) / fTimeElapsed;
|
|
|
|
// FAST-DROP BYPASS: If the client just lost more than 25% of their frame rate instantly
|
|
// skip the dampening math and force the estimation down immediately.
|
|
if (fCalculatedFPS < (g_fClientEstimatedFPS[client] * 0.75))
|
|
{
|
|
g_fClientEstimatedFPS[client] = fCalculatedFPS; // Hard-drop the value
|
|
}
|
|
else
|
|
{
|
|
// Smoothly recover/climb when frame rates are stable or increasing
|
|
g_fClientEstimatedFPS[client] = (g_fClientEstimatedFPS[client] * 0.8) + (fCalculatedFPS * 0.2);
|
|
}
|
|
}
|
|
|
|
// Reset interval buckets
|
|
g_fFPSIntervalStart[client] = fCurrentTime;
|
|
g_iCmdsInInterval[client] = 0;
|
|
}
|
|
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
//ai generated
|
|
public Action Timer_printFPS(Handle timer)
|
|
{
|
|
for (int i = 1; i <= MaxClients; i++)
|
|
{
|
|
if (IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i) && bEnabled[i])
|
|
{
|
|
float fCurrentFPS = g_fClientEstimatedFPS[i] - 4.0;
|
|
float currentDist = g_OverrideDist[i];
|
|
float targetDist = currentDist;
|
|
|
|
// 1. HARD CRASH: FPS has tanked down to unplayable levels.
|
|
if (fCurrentFPS <= 25.0)
|
|
{
|
|
// If we have a cached, proven stable distance, use it immediately!
|
|
if (g_fLastStableDist[i] != -1.0 && g_fLastStableDist[i] < currentDist)
|
|
{
|
|
targetDist = g_fLastStableDist[i];
|
|
}
|
|
else
|
|
{
|
|
// Emergency fallback steps if we dont have a cached history yet
|
|
if (currentDist == -1.0 || currentDist > 1200.0)
|
|
{
|
|
targetDist = 1200.0;
|
|
}
|
|
else
|
|
{
|
|
targetDist = 600.0;
|
|
}
|
|
}
|
|
|
|
// Penalize the cooldown heavily because we just choked on rendering
|
|
g_fDistCooldown[i] = GetEngineTime() + 15.0;
|
|
}
|
|
|
|
// 2. WARNING ZONE: Framerate is beginning to dip
|
|
else if (fCurrentFPS <= 38.0)
|
|
{
|
|
if (currentDist == -1.0 || currentDist > 2000.0)
|
|
{
|
|
targetDist = 2000.0;
|
|
g_fDistCooldown[i] = GetEngineTime() + 10.0;
|
|
}
|
|
}
|
|
|
|
// 3. RECOVERY ZONE: Performance is solid. Lets see if we can open up the view.
|
|
else if (fCurrentFPS >= 48.0)
|
|
{
|
|
if (currentDist != -1.0)
|
|
{
|
|
// Since the game is running flawlessly at this specific distance,
|
|
// we update our safety cache to remember this specific float.
|
|
g_fLastStableDist[i] = currentDist;
|
|
|
|
// Only venture out if our time barrier has expired
|
|
if (GetEngineTime() >= g_fDistCooldown[i])
|
|
{
|
|
if (currentDist == 600.0) targetDist = 1200.0;
|
|
else if (currentDist == 1200.0) targetDist = 2000.0;
|
|
else if (currentDist == 2000.0) targetDist = 4000.0;
|
|
else if (currentDist == 4000.0) targetDist = -1.0;
|
|
|
|
// Give the client a test window to sample the new distance
|
|
g_fDistCooldown[i] = GetEngineTime() + 8.0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If the client is running fine at infinite distance (-1.0),
|
|
// clear the history out so we can recalculate from a clean slate later
|
|
g_fLastStableDist[i] = -1.0;
|
|
}
|
|
}
|
|
|
|
if (targetDist != currentDist)
|
|
{
|
|
ApplyFogValues(i, targetDist);
|
|
}
|
|
}
|
|
}
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
void ApplyFogValues(int client, float dist)
|
|
{
|
|
g_OverrideDist[client] = dist;
|
|
int fog = EntRefToEntIndex(g_iClientFogEntity[client]);
|
|
|
|
// Create the persistent entity once if it vanished or hasnt been created yet
|
|
if (fog == -1 || !IsValidEntity(fog))
|
|
{
|
|
fog = CreateEntityByName("env_fog_controller");
|
|
if (fog != -1)
|
|
{
|
|
DispatchSpawn(fog);
|
|
g_iClientFogEntity[client] = EntIndexToEntRef(fog);
|
|
}
|
|
}
|
|
|
|
if (fog != -1 && IsValidEntity(fog))
|
|
{
|
|
if (dist == -1.0)
|
|
{
|
|
// Back to map defaults safely
|
|
SetEntProp(fog, Prop_Send, "m_fog.enable", 0);
|
|
SetEntPropFloat(fog, Prop_Send, "m_fog.farz", 0.0);
|
|
}
|
|
else
|
|
{
|
|
// Apply strict culling boundaries explicitly
|
|
SetEntProp(fog, Prop_Send, "m_fog.enable", 1);
|
|
SetEntPropFloat(fog, Prop_Send, "m_fog.farz", dist);
|
|
SetEntPropFloat(fog, Prop_Send, "m_fog.end", dist);
|
|
SetEntPropFloat(fog, Prop_Send, "m_fog.maxdensity", 1.0);
|
|
}
|
|
|
|
// Keep the entity assigned cleanly inside data tables
|
|
SetFogController(client, fog);
|
|
|
|
// Single fire state changes to replicate out smoothly
|
|
ChangeEdictState(client, FindSendPropInfo("CBasePlayer", "m_hCtrl"));
|
|
}
|
|
}
|
|
|
|
public void OnClientDisconnect(int client)
|
|
{
|
|
// Clean up entity allocation to prevent lingering map errors
|
|
int fog = EntRefToEntIndex(g_iClientFogEntity[client]);
|
|
if (fog != -1 && IsValidEntity(fog))
|
|
{
|
|
RemoveEntity(fog);
|
|
}
|
|
|
|
g_iClientFogEntity[client] = INVALID_ENT_REFERENCE;
|
|
g_OverrideDist[client] = -1.0;
|
|
bEnabled[client] = false;
|
|
}
|
|
|
|
void SetupDetours()
|
|
{
|
|
GameData gamedata = new GameData("player-fog.games");
|
|
if (!gamedata) {
|
|
SetFailState("Missing gamedata file");
|
|
}
|
|
|
|
DynamicDetour detour = DynamicDetour.FromConf(gamedata, "CBasePlayer::InputSetFogController");
|
|
if (!detour)
|
|
SetFailState("Failed to find signature CBasePlayer::InputSetFogController");
|
|
detour.Enable(Hook_Pre, Detour_InputSetFogController);
|
|
delete detour;
|
|
}
|
|
|
|
MRESReturn Detour_InputSetFogController(int client, DHookParam params)
|
|
{
|
|
RequestFrame(Frame_FogControllerChanged, GetClientSerial(client));
|
|
return MRES_Ignored;
|
|
}
|
|
|
|
void FogControllerChanged(int client)
|
|
{
|
|
if (g_OverrideDist[client] != -1.0) {
|
|
ApplyFogValues(client, g_OverrideDist[client]);
|
|
}
|
|
}
|
|
|
|
void Frame_FogControllerChanged(int clientSerial)
|
|
{
|
|
int client = GetClientFromSerial(clientSerial);
|
|
if (client && IsClientInGame(client))
|
|
{
|
|
FogControllerChanged(client);
|
|
}
|
|
}
|
|
|
|
public void OnClientAuthorized(int client, const char[] auth)
|
|
{
|
|
bEnabled[client] = false;
|
|
g_OverrideDist[client] = -1.0;
|
|
g_iClientFogEntity[client] = INVALID_ENT_REFERENCE;
|
|
|
|
char sIP[32];
|
|
GetClientIP(client, sIP, sizeof(sIP));
|
|
|
|
char allowed_ips[128];
|
|
ConVar sv_set_steam_id_ips = FindConVar("sv_set_steam_id_ips");
|
|
if (sv_set_steam_id_ips != null)
|
|
{
|
|
sv_set_steam_id_ips.GetString(allowed_ips, sizeof(allowed_ips));
|
|
if (StrEqual(sIP, allowed_ips))
|
|
{
|
|
bEnabled[client] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SetFogController(int client, int fog)
|
|
{
|
|
SetEntPropEnt(client, Prop_Data, "m_hCtrl", fog);
|
|
}
|