sm-plugins/fog_controller/scripting/fog_controller_client.sp

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);
}