zio fork 1.8.2b-zio15: in-game gags/mutes/silences expire with TimeMode 0
Some checks are pending
Build and Publish / build (push) Waiting to run
Build and Publish / publish (push) Blocked by required conditions

The in-memory penalty was only cleared when its end time exactly matched the
database row's, which never happens for one given in game (sub-second vs
to-the-second), so it lasted until reconnect. Match by type within 5 seconds,
and stop applying a penalty as soon as it's marked passed.
This commit is contained in:
Astra 2026-10-03 00:15:03 +01:00
parent 7b3d0ed452
commit dc5ad30952
4 changed files with 39 additions and 5 deletions

View file

@ -212,7 +212,15 @@ internal class MuteManager(IDatabaseProvider? databaseProvider)
foreach (var muteRecord in muteRecords)
{
DateTime endDateTime = muteRecord.ends;
PlayerPenaltyManager.RemovePenaltiesByDateTime(slot, endDateTime);
// zio: match by type and approximate end time; see RemovePenaltiesByDateTime.
string muteType = muteRecord.type;
var penaltyType = muteType switch
{
"MUTE" => PenaltyType.Mute,
"SILENCE" => PenaltyType.Silence,
_ => PenaltyType.Gag
};
PlayerPenaltyManager.RemovePenaltiesByDateTime(slot, penaltyType, endDateTime);
}
}

View file

@ -95,9 +95,11 @@ public static class PlayerPenaltyManager
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.TimeMode == 0)
{
if (penaltiesList.Count == 0) return false;
endDateTime = penaltiesList.First().EndDateTime;
// zio: a penalty marked passed is over, even before RemoveExpiredPenalties drops it.
var active = penaltiesList.FirstOrDefault(p => !p.Passed || p.Duration == 0);
if (active == default) return false;
endDateTime = active.EndDateTime;
return true;
}
@ -255,6 +257,26 @@ public static class PlayerPenaltyManager
}
}
/// <summary>
/// zio: marks a penalty of one type as passed when its end is within a few seconds of the
/// database's. A penalty added in game keeps the end time from the command, which has
/// sub-second precision and is taken a moment before the one the database stores (to the
/// second), so an exact match never finds it and the penalty outlives its database row.
/// </summary>
public static void RemovePenaltiesByDateTime(int slot, PenaltyType penaltyType, DateTime dateTime)
{
if (!Penalties.TryGetValue(slot, out var penaltyDict) ||
!penaltyDict.TryGetValue(penaltyType, out var penaltiesList)) return;
for (var i = 0; i < penaltiesList.Count; i++)
{
if ((penaltiesList[i].EndDateTime - dateTime).Duration() > TimeSpan.FromSeconds(5)) continue;
var penalty = penaltiesList[i];
penalty.Passed = true;
penaltiesList[i] = penalty;
}
}
/// <summary>
/// Removes or expires penalties automatically across all players based on their duration or "passed" flag.
/// </summary>