From fbc8a56fe151995480da92d4247c917e1682341b Mon Sep 17 00:00:00 2001 From: Marisa Kirisame Date: Fri, 11 Sep 2026 09:40:21 +0100 Subject: [PATCH 1/2] Update lag compensation --- .../scripting/LagCompensation_nide_edit.sp | 213 ++++++++++++++---- 1 file changed, 163 insertions(+), 50 deletions(-) diff --git a/LagCompensation/scripting/LagCompensation_nide_edit.sp b/LagCompensation/scripting/LagCompensation_nide_edit.sp index 8a928a6..1dc1b3d 100644 --- a/LagCompensation/scripting/LagCompensation_nide_edit.sp +++ b/LagCompensation/scripting/LagCompensation_nide_edit.sp @@ -18,7 +18,7 @@ public Plugin myinfo = name = "LagCompensation", author = "BotoX, Vauff, .Rushaway, maxime1907", description = "", - version = "1.0.6", + version = "1.1.0", url = "" }; @@ -28,7 +28,7 @@ bool g_bHasOnEntitySpawned = false; bool g_bCheckPing = false; int g_iCompenseEntityClass; -// Dont change this. +// Don't change this. #define MAX_EDICTS 2048 #define FSOLID_FORCE_WORLD_ALIGNED 0x0040 #define FSOLID_ROOT_PARENT_ALIGNED 0x0100 @@ -104,6 +104,7 @@ enum struct EntityLagData int iNotMoving; bool bRestore; bool bLateKill; + char sClassname[64]; } LagRecord g_aaLagRecords[MAX_ENTITIES][MAX_RECORDS]; @@ -159,10 +160,11 @@ int g_aaFilterClientSolidTouch[((MAXPLAYERS + 1) * MAX_EDICTS) / 32]; int g_aBlockTriggerMoved[MAX_EDICTS / 32]; int g_aBlacklisted[MAX_EDICTS / 32]; -Handle g_hCookie_EnableLagComp; -Handle g_hCookie_LagCompMessages; -bool g_bEnableLagComp[MAXPLAYERS + 1]; +Handle g_hCookie_LagCompSettings; +bool g_bDisableLagComp[MAXPLAYERS + 1]; +bool g_bPlayerLaserCompensated[MAXPLAYERS + 1]; bool g_bLagCompMessages[MAXPLAYERS + 1]; +bool g_bSettingsTouched[MAXPLAYERS + 1]; int g_iDisableLagComp[MAXPLAYERS + 1]; ConVar g_cvCompenseEntityClass; ConVar g_cvCheckUserPing; @@ -173,8 +175,8 @@ public void OnPluginStart() LoadTranslations("common.phrases"); g_cvCompenseEntityClass = CreateConVar("sm_lagcomp_entityclass", "0", "Only lagcompensate: [0 = All | 1 = func_physbox only]", _, true, 0.0, true, 1.0); - g_cvCheckUserPing = CreateConVar("sm_lagcomp_checkuserping", "0", "Check users ping before lagcompensating them [0 = Disabled | 1 = Enabled]", _, true, 0.0, true, 1.0); - g_cvMinimumPing = CreateConVar("sm_lagcomp_ping", "150", "Minimum ping threshold to apply lagcomp on a client", _, true, 0.0); + g_cvCheckUserPing = CreateConVar("sm_lagcomp_checkuserping", "0", "Check user's ping before lagcompensating them [0 = Disabled | 1 = Enabled]", _, true, 0.0, true, 1.0); + g_cvMinimumPing = CreateConVar("sm_lagcomp_ping", "180", "Minimum ping threshold to apply lagcomp on a client", _, true, 0.0); AutoExecConfig(true); @@ -347,16 +349,16 @@ public void OnPluginStart() HookConVarChange(g_cvMinimumPing, OnConVarChanged); - g_hCookie_EnableLagComp = RegClientCookie("enable_lagcomp", "Client LagComp Status", CookieAccess_Private); - g_hCookie_LagCompMessages = RegClientCookie("lagcomp_messages", "Print LagComp messages in chat", CookieAccess_Private); - RegConsoleCmd("sm_lagcomp", OnToggleLagCompSettings); + g_hCookie_LagCompSettings = RegClientCookie("lagcomp_settings", "LagCompensation Settings", CookieAccess_Private); + RegConsoleCmd("sm_lagcomp", OnLagCompSettings); RegConsoleCmd("sm_0ping", OnToggleLagCompSettings); + RegConsoleCmd("sm_0pinglasers", OnToggleLagCompLasersSettings); RegConsoleCmd("sm_checklag", CheckLagComp); SetCookieMenuItem(MenuHandler_CookieMenu, 0, "LagCompensation"); CreateTimer(0.1, DisableLagCompTimer, _, TIMER_REPEAT); - RegAdminCmd("sm_unlag", Command_AddLagCompensation, ADMFLAG_ROOT, "sm_unlag "); + RegAdminCmd("sm_unlag", Command_AddLagCompensation, ADMFLAG_ROOT, "sm_unlag [late]"); RegAdminCmd("sm_lagged", Command_CheckLagCompensated, ADMFLAG_ROOT, "sm_lagged"); FilterClientSolidTouch(g_aaFilterClientSolidTouch, true); @@ -486,18 +488,27 @@ public void OnMapEnd() public void OnClientConnected(int client) { - g_bEnableLagComp[client] = false; + // Must match the defaults in ParseClientCookie() for clients whose cookies never cache. + g_bDisableLagComp[client] = false; + g_bPlayerLaserCompensated[client] = true; + g_bLagCompMessages[client] = false; + g_bSettingsTouched[client] = false; g_iDisableLagComp[client] = 0; g_aLerpTicks[client] = 0; } public void OnClientCookiesCached(int client) { - char sBuffer[16]; - GetClientCookie(client, g_hCookie_EnableLagComp, sBuffer, sizeof(sBuffer)); - g_bEnableLagComp[client] = StringToInt(sBuffer) != 0; //this wording should make more sense, if the cookie exists its true to enable it. - GetClientCookie(client, g_hCookie_LagCompMessages, sBuffer, sizeof(sBuffer)); - g_bLagCompMessages[client] = StringToInt(sBuffer) != 0; + // A toggle made before the cookies arrived wins, so flush it instead of + // overwriting the client's choice with the stored value. + if(g_bSettingsTouched[client]) + { + SaveLagCompSettings(client); + return; + } + + // Load all settings from single cookie + ParseClientCookie(client, g_bDisableLagComp[client], g_bPlayerLaserCompensated[client], g_bLagCompMessages[client]); } public void OnClientSettingsChanged(int client) @@ -511,7 +522,10 @@ public void OnClientSettingsChanged(int client) public void OnClientDisconnect(int client) { - g_bEnableLagComp[client] = false; + g_bDisableLagComp[client] = false; + g_bPlayerLaserCompensated[client] = true; + g_bLagCompMessages[client] = false; + g_bSettingsTouched[client] = false; g_iDisableLagComp[client] = 0; } @@ -529,8 +543,9 @@ public void OnEntityCreated(int entity, const char[] classname) DHookEntity(g_hAcceptInput, true, entity); } - if (!g_bHasOnEntitySpawned && (!strncmp(classname, "func_physbox", 12, false) || (g_iCompenseEntityClass == 0 && strcmp(classname, "trigger_hurt", false) == 0 || - strcmp(classname, "trigger_push", false) == 0 || strcmp(classname, "trigger_teleport", false) == 0))) + if (!g_bHasOnEntitySpawned && (!strncmp(classname, "func_physbox", 12, false) || + (g_iCompenseEntityClass == 0 && (strcmp(classname, "trigger_hurt", false) == 0 || + strcmp(classname, "trigger_push", false) == 0 || strcmp(classname, "trigger_teleport", false) == 0)))) { SDKHook(entity, SDKHook_SpawnPost, OnSDKHookEntitySpawnPost); } @@ -570,7 +585,7 @@ public MRESReturn Hook_AcceptInput(int entity, Handle hReturn, Handle hParams) if(iCaller <= 0 || iCaller >= MAX_EDICTS) return MRES_Ignored; - // Dont lagcompensate anything that has a game_ui button in their hierarchy. + // Don't lagcompensate anything that has a game_ui button in their hierarchy. BlacklistFamily(iCaller); return MRES_Ignored; @@ -623,7 +638,7 @@ void BlacklistChildren(int parent) bool CheckEntityForLagComp(int entity, const char[] classname, bool bRecursive=false, bool bGoodParents=false) { - if(entity < 0 || entity > MAX_EDICTS) + if(entity < 0 || entity >= MAX_EDICTS) return false; if(!IsValidEntity(entity)) @@ -668,7 +683,7 @@ bool CheckEntityForLagComp(int entity, const char[] classname, bool bRecursive=f return false; } - if(CheckBit(g_aBlacklisted, entity)) + if(CheckBit(g_aBlacklisted, iParent)) { return false; } @@ -736,7 +751,7 @@ public void OnEntityDestroyed(int entity) if(g_bCleaningUp) return; - if(entity < 0 || entity > MAX_EDICTS) + if(entity < 0 || entity >= MAX_EDICTS) return; ClearBit(g_aBlacklisted, entity); @@ -758,7 +773,7 @@ public MRESReturn Detour_OnUTIL_Remove(Handle hParams) return MRES_Ignored; int entity = DHookGetParamObjectPtrVar(hParams, 1, g_iNetworkableOuter, ObjectValueType_CBaseEntityPtr); - if(entity < 0 || entity > MAX_EDICTS) + if(entity < 0 || entity >= MAX_EDICTS) return MRES_Ignored; int iIndex = g_aLagCompensated[entity]; @@ -858,8 +873,8 @@ public MRESReturn Detour_OnFrameUpdatePostEntityThink() for(int i = 0; i < g_iNumEntities; i++) { // Dont make the entity check untouch in FrameUpdatePostEntityThink. - // If the player didnt get simulated in the current frame then - // they didnt have a chance to touch this entity. + // If the player didn't get simulated in the current frame then + // they didn't have a chance to touch this entity. // Hence the touchlink could be broken and we only let the player check untouch. int EFlags = GetEntData(g_aEntityLagData[i].iEntity, g_iEFlags); EFlags &= ~EFL_CHECK_UNTOUCH; @@ -877,7 +892,7 @@ public void OnRunThinkFunctions(bool simulating) 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(!IsPlayerAlive(client) || IsFakeClient(client)) + if(!IsClientInGame(client) || !IsPlayerAlive(client) || IsFakeClient(client)) return Plugin_Continue; int iTargetTick = tickcount - g_aLerpTicks[client]; @@ -928,6 +943,13 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3 continue; int iRecord = iDelta; + + if(!g_bPlayerLaserCompensated[client] && + (strcmp(g_aEntityLagData[i].sClassname, "trigger_hurt", false) == 0 || + strcmp(g_aEntityLagData[i].sClassname, "trigger_push", false) == 0 || + strcmp(g_aEntityLagData[i].sClassname, "trigger_teleport", false) == 0)) + iRecord = 0; + if(iRecord >= g_aEntityLagData[i].iNumRecords) iRecord = g_aEntityLagData[i].iNumRecords - 1; @@ -1112,6 +1134,10 @@ bool AddEntityForLagCompensation(int iEntity, bool bLateKill) if(g_bCleaningUp) return false; + // sm_unlag takes an arbitrary index from the console, so validate before indexing anything. + if(iEntity < 0 || iEntity >= MAX_EDICTS || !IsValidEntity(iEntity)) + return false; + if(g_aLagCompensated[iEntity] != -1) return false; @@ -1144,6 +1170,7 @@ bool AddEntityForLagCompensation(int iEntity, bool bLateKill) g_aEntityLagData[i].iNotMoving = MAX_RECORDS; g_aEntityLagData[i].bRestore = false; g_aEntityLagData[i].bLateKill = bLateKill; + g_aEntityLagData[i].sClassname = sClassname; if(bLateKill) { @@ -1226,6 +1253,7 @@ void EntityLagData_Copy(EntityLagData obj, const EntityLagData other) obj.iNotMoving = other.iNotMoving; obj.bRestore = other.bRestore; obj.bLateKill = other.bLateKill; + obj.sClassname = other.sClassname; } void LagRecord_Copy(LagRecord obj, const LagRecord other) @@ -1253,6 +1281,59 @@ bool CompareVectors(const float vec1[3], const float vec2[3]) return vec1[0] == vec2[0] && vec1[1] == vec2[1] && vec1[2] == vec2[2]; } +void SaveLagCompSettings(int client) +{ + // Remember that this client chose something, so OnClientCookiesCached + // flushes it rather than loading over the top of it. + g_bSettingsTouched[client] = true; + + // Cookie reads/writes are meaningless until the cookies arrive; we get called back then. + if(!AreClientCookiesCached(client)) + return; + + // Read current cookie values + bool currentDisableLagComp, currentPlayerLaserCompensated, currentLagCompMessages; + ParseClientCookie(client, currentDisableLagComp, currentPlayerLaserCompensated, currentLagCompMessages); + + // Check if values have changed + bool disableChanged = g_bDisableLagComp[client] != currentDisableLagComp; + bool laserChanged = g_bPlayerLaserCompensated[client] != currentPlayerLaserCompensated; + bool messagesChanged = g_bLagCompMessages[client] != currentLagCompMessages; + + // If no values have changed, no need to save + if (!disableChanged && !laserChanged && !messagesChanged) { + return; + } + + // Otherwise, save the chain format + char sBuffer[8]; + Format(sBuffer, sizeof(sBuffer), "%d%d%d", + g_bDisableLagComp[client] ? 1 : 0, + g_bPlayerLaserCompensated[client] ? 1 : 0, + g_bLagCompMessages[client] ? 1 : 0); + SetClientCookie(client, g_hCookie_LagCompSettings, sBuffer); +} + +void ParseClientCookie(int client, bool &disableLagComp, bool &playerLaserCompensated, bool &lagCompMessages) +{ + char buffer[16]; + GetClientCookie(client, g_hCookie_LagCompSettings, buffer, sizeof(buffer)); + + // Parse chain format: disable|laser|messages + if (strlen(buffer) >= 3) + { + disableLagComp = (buffer[0] == '1'); + playerLaserCompensated = (buffer[1] == '1'); + lagCompMessages = (buffer[2] == '1'); + } + else + { + // Default values + disableLagComp = false; + playerLaserCompensated = true; + lagCompMessages = false; + } +} public Action Command_AddLagCompensation(int client, int argc) { @@ -1355,9 +1436,9 @@ stock void PrintToBoth(const char[] format, any ...) for(int client = 1; client <= MaxClients; client++) { - if (IsClientInGame(client) && g_bLagCompMessages[client]) + if (IsClientInGame(client) && g_bLagCompMessages[client] && CheckCommandAccess(client, "sm_lagged", ADMFLAG_ROOT)) { - PrintToChat(client, "%s", buffer); + PrintToConsole(client, "%s", buffer); } } } @@ -1370,22 +1451,25 @@ public Action DisableLagCompTimer(Handle timer) continue; // https://github.com/ValveSoftware/source-sdk-2013/blob/master/sp/src/game/server/util.cpp#L747 + // Lag compensation only kicks in above the ping threshold, so ramp it off below it. if (g_bCheckPing) { int iAvgPing = RoundFloat(GetClientAvgLatency(client, NetFlow_Outgoing) * 1000); - if (iAvgPing > g_iMinPing) + if (iAvgPing <= g_iMinPing) { - g_iDisableLagComp[client]++; - return Plugin_Continue; + if(g_iDisableLagComp[client] < MAX_RECORDS) + g_iDisableLagComp[client]++; + + continue; } } - if(!g_bEnableLagComp[client] && g_iDisableLagComp[client] < MAX_RECORDS) + if(g_bDisableLagComp[client] && g_iDisableLagComp[client] < MAX_RECORDS) { g_iDisableLagComp[client]++; } - else if(g_bEnableLagComp[client] && g_iDisableLagComp[client] > 0) + else if(!g_bDisableLagComp[client] && g_iDisableLagComp[client] > 0) { g_iDisableLagComp[client]--; } @@ -1406,33 +1490,52 @@ public Action OnToggleLagCompSettings(int client, int args) return Plugin_Handled; } +public Action OnToggleLagCompLasersSettings(int client, int args) +{ + ToggleLagCompLasers(client); + return Plugin_Handled; +} + public void ToggleLagCompSettings(int client) { - if(!client) + if(!client || !IsClientInGame(client) || IsFakeClient(client)) return; - g_bEnableLagComp[client] = !g_bEnableLagComp[client]; - SetClientCookie(client, g_hCookie_EnableLagComp, g_bEnableLagComp[client] ? "1" : ""); + g_bDisableLagComp[client] = !g_bDisableLagComp[client]; + SaveLagCompSettings(client); - CPrintToChat(client, "%s LagCompensation has been %s.", PREFIX, g_bEnableLagComp[client] ? "enabled" : "disabled"); + CPrintToChat(client, "%s LagCompensation has been %s.", PREFIX, g_bDisableLagComp[client] ? "disabled" : "enabled"); +} + +public void ToggleLagCompLasers(int client) +{ + if(!client || !IsClientInGame(client) || IsFakeClient(client)) + return; + + g_bPlayerLaserCompensated[client] = !g_bPlayerLaserCompensated[client]; + SaveLagCompSettings(client); + + CPrintToChat(client, "%s LagCompensation for Lasers has been %s.", PREFIX, g_bPlayerLaserCompensated[client] ? "enabled" : "disabled"); + if (g_bCheckPing && g_bPlayerLaserCompensated[client]) + CPrintToChat(client, "%s It will take effect if your real ping is higher than %dms.", PREFIX, g_iMinPing); } public void ToggleLagCompMessages(int client) { - if(!client) + if(!client || !IsClientInGame(client) || IsFakeClient(client)) return; g_bLagCompMessages[client] = !g_bLagCompMessages[client]; - SetClientCookie(client, g_hCookie_LagCompMessages, g_bLagCompMessages[client] ? "1" : ""); + SaveLagCompSettings(client); CPrintToChat(client, "%s LagCompensation messages have been %s.", PREFIX, g_bLagCompMessages[client] ? "enabled" : "disabled"); } public Action CheckLagComp(int client, int args) { - if (args > 1) + if (args > 1 || (args == 0 && !client)) { - CPrintToChat(client, "%s Usage sm_checklag ", PREFIX); + CReplyToCommand(client, "%s Usage: sm_checklag ", PREFIX); return Plugin_Handled; } @@ -1455,7 +1558,14 @@ public Action CheckLagComp(int client, int args) return Plugin_Handled; } - CPrintToChat(client, "%s LagCompensation is %s for {olive}%N", PREFIX, g_bEnableLagComp[target] ? sEnable : "disabled", target); + CReplyToCommand(client, "%s LagCompensation is %s for {olive}%N", PREFIX, g_bDisableLagComp[target] ? "disabled" : sEnable, target); + CReplyToCommand(client, "%s Laser compensation: %s", PREFIX, g_bPlayerLaserCompensated[target] ? "enabled" : "disabled"); + + // g_iDisableLagComp also ramps from the ping threshold, so report it when they haven't opted out. + if(!g_bDisableLagComp[target] && g_iDisableLagComp[target] > 0) + CReplyToCommand(client, "%s Currently reduced: %d of %d ticks skipped%s.", PREFIX, + g_iDisableLagComp[target], MAX_RECORDS, + g_bCheckPing ? " (ping at or below the threshold ramps this up)" : ""); return Plugin_Handled; } @@ -1463,15 +1573,17 @@ public Action CheckLagComp(int client, int args) public void ShowSettingsMenu(int client) { Menu menu = new Menu(MenuHandler_MainMenu); - menu.SetTitle("LagCompensation Settings", client); + menu.SetTitle("LagCompensation Settings"); menu.ExitBackButton = true; - char buffer[128], msg_buffer[128]; - Format(buffer, sizeof(buffer), "LagCompensation: %s", g_bEnableLagComp[client] ? "Enabled" : "Disabled"); + char buffer[128], buffer_lasers[128], msg_buffer[128]; + Format(buffer, sizeof(buffer), "LagCompensation: %s", g_bDisableLagComp[client] ? "Disabled" : "Enabled"); + Format(buffer_lasers, sizeof(buffer_lasers), "LagCompensation for Lasers: %s", g_bPlayerLaserCompensated[client] ? "Enabled" : "Disabled"); Format(msg_buffer, sizeof(msg_buffer), "LagCompensation Messages: %s", g_bLagCompMessages[client] ? "Enabled" : "Disabled"); menu.AddItem("0", buffer); - menu.AddItem("1", msg_buffer); + menu.AddItem("1", buffer_lasers); + menu.AddItem("2", msg_buffer, CheckCommandAccess(client, "sm_lagged", ADMFLAG_ROOT) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED); menu.Display(client, MENU_TIME_FOREVER); } @@ -1482,7 +1594,7 @@ public void MenuHandler_CookieMenu(int client, CookieMenuAction action, any info { case(CookieMenuAction_DisplayOption): { - Format(buffer, maxlen, "LagCompensation", client); + strcopy(buffer, maxlen, "LagCompensation"); } case(CookieMenuAction_SelectOption): { @@ -1500,7 +1612,8 @@ public int MenuHandler_MainMenu(Menu menu, MenuAction action, int client, int se switch(selection) { case(0): ToggleLagCompSettings(client); - case(1): ToggleLagCompMessages(client); + case(1): ToggleLagCompLasers(client); + case(2): ToggleLagCompMessages(client); } ShowSettingsMenu(client); From 789b8ebef9aebe8d860bde8152a485203424998b Mon Sep 17 00:00:00 2001 From: jenz Date: Fri, 11 Sep 2026 16:05:36 +0200 Subject: [PATCH 2/2] now the menu displays the category in parantheses for each map --- .../scripting/mapchooser_extended_unloze.sp | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/mapchooser_extended/scripting/mapchooser_extended_unloze.sp b/mapchooser_extended/scripting/mapchooser_extended_unloze.sp index e677814..88057db 100755 --- a/mapchooser_extended/scripting/mapchooser_extended_unloze.sp +++ b/mapchooser_extended/scripting/mapchooser_extended_unloze.sp @@ -2310,6 +2310,16 @@ NominateResult InternalNominateMap(char[] map, int owner) return Nominate_Added; } +public void GetMapCategory(const char[] mapname, char[] output_category, int maxlen) +{ + strcopy(output_category, maxlen, "Uncategorized"); + if (g_Config && g_Config.JumpToKey(mapname)) + { + g_Config.GetString("MapCategory", output_category, maxlen, "Uncategorized"); + g_Config.Rewind(); + } +} + /* Add natives to allow nominate and initiate vote to be call */ /* native bool NominateMap(const char[] map, bool force, &NominateError:error); */ @@ -3054,7 +3064,17 @@ public int Native_SimulateMapEnd(Handle plugin, int numParams) stock void AddMapItem(const char[] map) { - AddMenuItem(g_VoteMenu, map, map); + char map_with_category[1024]; + if (StrContains(map, "ze_") == 0) + { + GetMapCategory(map, map_with_category, sizeof(map_with_category)); + Format(map_with_category, sizeof(map_with_category), "%s (%s)", map, map_with_category); + AddMenuItem(g_VoteMenu, map, map_with_category); + } + else + { + AddMenuItem(g_VoteMenu, map, map); + } } stock void GetMapItem(Handle menu, int position, char[] map, int mapLen)