zio fork: @ staff chat while gagged, gag message with time left and reason
Gagged or silenced players can still send @ team chat to online staff. The chat-blocked message now says how long the gag or silence has left, or that it's permanent, and why. Reasons are kept in memory alongside each penalty. Adds release.sh, which ships CS2-SimpleAdmin.dll and lang/en.json.
This commit is contained in:
parent
cf284bd121
commit
6171162ae9
10 changed files with 230 additions and 11 deletions
|
|
@ -22,7 +22,7 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||
public override string ModuleName => "CS2-SimpleAdmin" + (Helper.IsDebugBuild ? " (DEBUG)" : " (RELEASE)");
|
||||
public override string ModuleDescription => "Simple admin plugin for Counter-Strike 2 :)";
|
||||
public override string ModuleAuthor => "daffyy";
|
||||
public override string ModuleVersion => "1.8.2b";
|
||||
public override string ModuleVersion => "1.8.2b-zio1";
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.369">
|
||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.376">
|
||||
<PrivateAssets>none</PrivateAssets>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
<IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ public partial class CS2_SimpleAdmin
|
|||
});
|
||||
|
||||
// Add penalty to the player's penalty manager
|
||||
PlayerPenaltyManager.AddPenalty(player.Slot, PenaltyType.Gag, Time.ActualDateTime().AddMinutes(time), time);
|
||||
PlayerPenaltyManager.AddPenalty(player.Slot, PenaltyType.Gag, Time.ActualDateTime().AddMinutes(time), time, reason);
|
||||
|
||||
// Determine message keys and arguments based on gag time (permanent or timed)
|
||||
var (messageKey, activityMessageKey, playerArgs, adminActivityArgs) = time == 0
|
||||
|
|
@ -398,7 +398,7 @@ public partial class CS2_SimpleAdmin
|
|||
});
|
||||
|
||||
// Add penalty to the player's penalty manager
|
||||
PlayerPenaltyManager.AddPenalty(player.Slot, PenaltyType.Mute, Time.ActualDateTime().AddMinutes(time), time);
|
||||
PlayerPenaltyManager.AddPenalty(player.Slot, PenaltyType.Mute, Time.ActualDateTime().AddMinutes(time), time, reason);
|
||||
|
||||
// Determine message keys and arguments based on mute time (permanent or timed)
|
||||
var (messageKey, activityMessageKey, playerArgs, adminActivityArgs) = time == 0
|
||||
|
|
@ -709,7 +709,7 @@ public partial class CS2_SimpleAdmin
|
|||
});
|
||||
|
||||
// Add penalty to the player's penalty manager
|
||||
PlayerPenaltyManager.AddPenalty(player.Slot, PenaltyType.Silence, Time.ActualDateTime().AddMinutes(time), time);
|
||||
PlayerPenaltyManager.AddPenalty(player.Slot, PenaltyType.Silence, Time.ActualDateTime().AddMinutes(time), time, reason);
|
||||
player.VoiceFlags = VoiceFlags.Muted;
|
||||
|
||||
// Determine message keys and arguments based on silence time (permanent or timed)
|
||||
|
|
|
|||
|
|
@ -347,13 +347,15 @@ public partial class CS2_SimpleAdmin
|
|||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
// zio: gagged players can still reach staff with @ in team chat, handled below.
|
||||
var staffChat = command == "say_team" && info.GetArg(1).StartsWith('@');
|
||||
// if (!Config.OtherSettings.UserMessageGagChatType)
|
||||
// {
|
||||
if (PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag, out DateTime? endDateTime) ||
|
||||
PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence, out endDateTime))
|
||||
if (!staffChat && (PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag, out DateTime? endDateTime) ||
|
||||
PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence, out endDateTime)))
|
||||
{
|
||||
if (_localizer != null && endDateTime is not null)
|
||||
player.SendLocalizedMessage(_localizer, "sa_player_penalty_chat_active", endDateTime.Value.ToString("g", player.GetLanguage()));
|
||||
SendChatPenaltyMessage(player, endDateTime.Value);
|
||||
return HookResult.Stop;
|
||||
}
|
||||
// }
|
||||
|
|
@ -388,6 +390,36 @@ public partial class CS2_SimpleAdmin
|
|||
return HookResult.Stop;
|
||||
}
|
||||
|
||||
// zio: says how long the gag (or silence) has left and why, in place of its end date.
|
||||
private void SendChatPenaltyMessage(CCSPlayerController player, DateTime endDateTime)
|
||||
{
|
||||
var type = PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag, out var gagEnd) && gagEnd == endDateTime
|
||||
? PenaltyType.Gag
|
||||
: PenaltyType.Silence;
|
||||
var (remaining, reason) = PlayerPenaltyManager.GetPenaltyDetails(player.Slot, type, endDateTime);
|
||||
var kind = type == PenaltyType.Gag ? "gag" : "silence";
|
||||
using (new WithTemporaryCulture(player.GetLanguage()))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(reason))
|
||||
reason = _localizer!["sa_player_penalty_no_reason"];
|
||||
if (remaining is null)
|
||||
player.SendLocalizedMessage(_localizer, $"sa_player_penalty_{kind}_active_perm", reason);
|
||||
else
|
||||
player.SendLocalizedMessage(_localizer, $"sa_player_penalty_{kind}_active", FormatRemaining(remaining.Value), reason);
|
||||
}
|
||||
}
|
||||
|
||||
private static string FormatRemaining(TimeSpan remaining)
|
||||
{
|
||||
if (remaining.TotalMinutes < 1)
|
||||
return "less than a minute";
|
||||
var parts = new List<string>();
|
||||
if (remaining.Days > 0) parts.Add($"{remaining.Days}d");
|
||||
if (remaining.Hours > 0) parts.Add($"{remaining.Hours}h");
|
||||
if (remaining.Minutes > 0 && remaining.Days == 0) parts.Add($"{remaining.Minutes}m");
|
||||
return string.Join(" ", parts);
|
||||
}
|
||||
|
||||
/*public HookResult OnCommandSay(CCSPlayerController? player, CommandInfo info)
|
||||
{
|
||||
if (player == null || !player.IsValid || player.IsBot)
|
||||
|
|
|
|||
|
|
@ -125,20 +125,22 @@ internal class PlayerManager
|
|||
string muteType = mute.type;
|
||||
DateTime ends = mute.ends;
|
||||
int duration = mute.duration;
|
||||
string? reason = mute.reason;
|
||||
int passed = mute.passed is null ? 0 : (int)mute.passed;
|
||||
switch (muteType)
|
||||
{
|
||||
// Apply mute penalty based on mute type
|
||||
case "GAG":
|
||||
PlayerPenaltyManager.AddPenalty(
|
||||
CS2_SimpleAdmin.PlayersInfo[steamId].Slot,
|
||||
PenaltyType.Gag, ends, duration);
|
||||
PenaltyType.Gag, ends, duration, reason, duration - passed);
|
||||
// if (CS2_SimpleAdmin._localizer != null)
|
||||
// mutesList[PenaltyType.Gag].Add(CS2_SimpleAdmin._localizer["sa_player_penalty_info_active_gag", ends.ToLocalTime().ToString(CultureInfo.CurrentCulture)]);
|
||||
break;
|
||||
case "MUTE":
|
||||
PlayerPenaltyManager.AddPenalty(
|
||||
CS2_SimpleAdmin.PlayersInfo[steamId].Slot,
|
||||
PenaltyType.Mute, ends, duration);
|
||||
PenaltyType.Mute, ends, duration, reason, duration - passed);
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
player.VoiceFlags = VoiceFlags.Muted;
|
||||
|
|
@ -149,7 +151,7 @@ internal class PlayerManager
|
|||
default:
|
||||
PlayerPenaltyManager.AddPenalty(
|
||||
CS2_SimpleAdmin.PlayersInfo[steamId].Slot,
|
||||
PenaltyType.Silence, ends, duration);
|
||||
PenaltyType.Silence, ends, duration, reason, duration - passed);
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
player.VoiceFlags = VoiceFlags.Muted;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,46 @@ public static class PlayerPenaltyManager
|
|||
private static readonly ConcurrentDictionary<int, Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>>> Penalties =
|
||||
new();
|
||||
|
||||
// zio: the reason and online-time bookkeeping for each penalty, kept beside Penalties so the
|
||||
// tuple type the API exposes doesn't change.
|
||||
private static readonly ConcurrentDictionary<(int Slot, PenaltyType Type, DateTime EndDateTime), (string? Reason, DateTime AddedAt, int MinutesLeft)> Details =
|
||||
new();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a penalty and remembers its reason, for telling the player why they're penalized.
|
||||
/// </summary>
|
||||
/// <param name="minutesLeft">Minutes left when added: the duration, less any already served
|
||||
/// online when TimeMode is 0.</param>
|
||||
public static void AddPenalty(int slot, PenaltyType penaltyType, DateTime endDateTime, int durationInMinutes, string? reason, int? minutesLeft = null)
|
||||
{
|
||||
AddPenalty(slot, penaltyType, endDateTime, durationInMinutes);
|
||||
Details[(slot, penaltyType, endDateTime)] = (reason, Time.ActualDateTime(), minutesLeft ?? durationInMinutes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns how long the penalty IsPenalized found has left (null if permanent) and its reason.
|
||||
/// </summary>
|
||||
public static (TimeSpan? Remaining, string? Reason) GetPenaltyDetails(int slot, PenaltyType penaltyType, DateTime endDateTime)
|
||||
{
|
||||
var penalty = GetPlayerPenalties(slot, penaltyType).FirstOrDefault(p => p.EndDateTime == endDateTime);
|
||||
var hasDetails = Details.TryGetValue((slot, penaltyType, endDateTime), out var details);
|
||||
if (penalty.Duration == 0)
|
||||
return (null, details.Reason);
|
||||
|
||||
var now = Time.ActualDateTime();
|
||||
// TimeMode 0 only counts time online, and the player has been online since it was added.
|
||||
var remaining = CS2_SimpleAdmin.Instance.Config.OtherSettings.TimeMode == 0 && hasDetails
|
||||
? TimeSpan.FromMinutes(details.MinutesLeft) - (now - details.AddedAt)
|
||||
: endDateTime - now;
|
||||
return (remaining < TimeSpan.Zero ? TimeSpan.Zero : remaining, details.Reason);
|
||||
}
|
||||
|
||||
private static void RemoveDetails(Func<(int Slot, PenaltyType Type, DateTime EndDateTime), bool> match)
|
||||
{
|
||||
foreach (var key in Details.Keys.Where(match).ToList())
|
||||
Details.TryRemove(key, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a penalty for a specific player slot and penalty type.
|
||||
/// </summary>
|
||||
|
|
@ -162,6 +202,7 @@ public static class PlayerPenaltyManager
|
|||
{
|
||||
Penalties.TryRemove(slot, out _);
|
||||
}
|
||||
RemoveDetails(k => k.Slot == slot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -170,6 +211,7 @@ public static class PlayerPenaltyManager
|
|||
public static void RemoveAllPenalties()
|
||||
{
|
||||
Penalties.Clear();
|
||||
Details.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -184,6 +226,7 @@ public static class PlayerPenaltyManager
|
|||
{
|
||||
penaltyDict.Remove(penaltyType);
|
||||
}
|
||||
RemoveDetails(k => k.Slot == slot && k.Type == penaltyType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -65,6 +65,11 @@
|
|||
"sa_discord_penalty_unknown": "Unknown registered",
|
||||
|
||||
"sa_player_penalty_chat_active": "{lightred}Your chat is blocked to: {grey}{0}",
|
||||
"sa_player_penalty_gag_active": "{lightred}You are gagged for {grey}{0}{lightred}. Reason: {grey}{1}\n{grey}Team chat starting with {lightred}@{grey} still reaches online staff.",
|
||||
"sa_player_penalty_gag_active_perm": "{lightred}You are gagged permanently. Reason: {grey}{0}\n{grey}Team chat starting with {lightred}@{grey} still reaches online staff.",
|
||||
"sa_player_penalty_silence_active": "{lightred}You are silenced for {grey}{0}{lightred}. Reason: {grey}{1}\n{grey}Team chat starting with {lightred}@{grey} still reaches online staff.",
|
||||
"sa_player_penalty_silence_active_perm": "{lightred}You are silenced permanently. Reason: {grey}{0}\n{grey}Team chat starting with {lightred}@{grey} still reaches online staff.",
|
||||
"sa_player_penalty_no_reason": "none given",
|
||||
|
||||
"sa_player_penalty_info_active_mute": "➔ Mute [{lightred}❌{default}] - Expire [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_gag": "➔ Gag [{lightred}❌{default}] - Expire [{lightred}{0}{default}]",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue