This commit is contained in:
BIGZi0348
2025-01-10 19:42:25 +03:00
parent e1624039f7
commit 91d34b4135
9 changed files with 0 additions and 886 deletions

View File

@@ -1,256 +0,0 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Shared._Miracle.Nya;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Shared.Configuration;
using Robust.Shared.Reflection;
namespace Content.Client._Miracle.Nya;
public sealed class NyaCheckClientSystem : EntitySystem
{
[Dependency] private readonly IReflectionManager _reflection = default!;
[Dependency] private readonly IConfigurationManager _configuration = default!;
[Dependency] private readonly IEntitySystemManager _esm = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IUserInterfaceManager _ui = default!;
[ViewVariables(VVAccess.ReadOnly)]
private readonly string[] _allowed =
[
"Content.Client",
"Content.Shared",
"Content.Server",
"Content.Shared.Database",
"Robust.Client",
"Robust.Shared",
"Robust.Server"
];
public override void Initialize()
{
base.Initialize();
SubscribeNetworkEvent<CheatCheckRequestEvent>(OnCheckRequest);
}
private void OnCheckRequest(CheatCheckRequestEvent ev)
{
var response = RunChecks();
RaiseNetworkEvent(response);
}
private CheatCheckResponseEvent RunChecks()
{
return new CheatCheckResponseEvent
{
HasPatchMetadata = FoundPatchMetadataTypes(),
ReflectionOffender = FoundExtraTypesIReflection(out var reflectionOffender) ? reflectionOffender : null,
HasMoonyware = FoundMoonywareModuleReflection(),
HasHarmony = CheckForHarmony(),
IoCOffender = TypesNotFromContentIoC(out var iocOffender) ? iocOffender : null,
ExtraModuleOffender = CheckExtraModule(out var moduleOffender) ? moduleOffender : null,
CvarOffender = CheckCommonCheatCvars(out var cvarOffender) ? cvarOffender : null,
SystemOffender = FoundTypesEntitySystemManager(out var systemOffender) ? systemOffender : null,
ComponentOffender = CheckComponents(out var componentOffender) ? componentOffender : null,
WindowOffender = CheckExtraWindows(out var windowOffender) ? windowOffender : null
};
}
private bool FoundPatchMetadataTypes()
{
var found = Type.GetType("MarseyPatch") ?? Type.GetType("SubverterPatch");
return found is not null;
}
private bool CheckForHarmony()
{
var harmonyType = Type.GetType("HarmonyLib.Harmony, 0Harmony");
return harmonyType != null;
}
private bool FoundExtraTypesIReflection([NotNullWhen(true)] out string? offender)
{
offender = null;
string[] typenames = ["SubverterPatch", "MarseyPatch", "MarseyEntry", "Sedition", "Ware"];
var types = _reflection.FindAllTypes();
foreach (var type in types)
{
foreach (var name in typenames)
{
if (!type.Name.Contains(name))
continue;
offender = type.Name;
return true;
}
}
return false;
}
private bool FoundMoonywareModuleReflection()
{
var modules = _reflection.Assemblies;
foreach (var asm in modules)
{
if (asm.FullName!.Contains("Moonyware"))
return true;
}
return false;
}
private bool FoundTypesEntitySystemManager([NotNullWhen(true)] out string? offend)
{
offend = null;
var types = _esm.GetEntitySystemTypes();
foreach (var type in types)
{
if (!NotFromGameModule(type))
continue;
offend = type.FullName!;
return true;
}
return false;
}
private bool TypesNotFromContentIoC([NotNullWhen(true)] out string? offend)
{
offend = null;
var types = IoCManager.Instance!.GetRegisteredTypes();
foreach (var type in types)
{
if (!NotFromGameModule(type))
continue;
offend = type.FullName!;
return true;
}
return false;
}
private bool CheckExtraModule([NotNullWhen(true)] out string? offend)
{
offend = null;
var modules = _reflection.Assemblies;
foreach (var module in modules)
{
var allowed = false;
foreach (var allow in _allowed)
{
if (module.FullName!.Contains(allow))
{
allowed = true;
break;
}
}
if (allowed)
continue;
offend = module.FullName!;
return true;
}
return false;
}
private bool CheckCommonCheatCvars([NotNullWhen(true)] out string? offend)
{
string[] keywords =
[
"aimbot",
"visuals",
"esp",
"noslip",
"exploit",
"fun",
"scan",
];
offend = null;
var cvars = _configuration.GetRegisteredCVars();
foreach (var cvar in cvars)
{
if (!keywords.Any(kw => cvar.Contains(kw, StringComparison.CurrentCultureIgnoreCase)))
continue;
offend = cvar;
return true;
}
return false;
}
private bool CheckComponents([NotNullWhen(true)] out string? offend)
{
offend = null;
if (_player.LocalEntity is null)
return false;
var comps = AllComps(_player.LocalEntity.Value);
foreach (var comp in comps)
{
var type = comp.GetType();
if (!NotFromGameModule(type))
continue;
offend = type.FullName!;
return true;
}
return false;
}
private bool CheckExtraWindows([NotNullWhen(true)] out string? offend)
{
offend = null;
var children = _ui.WindowRoot.Children;
foreach (var child in children)
{
var type = child.GetType();
if (!NotFromGameModule(type))
continue;
offend = type.FullName!;
return true;
}
return false;
}
private bool NotFromGameModule(Type type)
{
var name = type.FullName;
foreach (var allow in _allowed)
{
if (name!.Contains(allow))
return false;
}
return true;
}
}

View File

@@ -1,38 +0,0 @@
using System.IO;
using Content.Shared._Miracle.Nya;
using Robust.Client.Graphics;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
namespace Content.Client._Miracle.Nya;
public sealed class NyaGrabSystem : EntitySystem
{
[Dependency] private readonly IClyde _clyde = default!;
public override void Initialize()
{
base.Initialize();
SubscribeNetworkEvent<ScreengrabRequestEvent>(OnScreengrabRequest);
}
private async void OnScreengrabRequest(ScreengrabRequestEvent ev)
{
var image = await _clyde.ScreenshotAsync(ScreenshotType.Final);
var array = ImageToByteArray(image);
if (array.Length > 1_500_000)
return;
var msg = new ScreengrabResponseEvent { Screengrab = array };
RaiseNetworkEvent(msg);
}
private byte[] ImageToByteArray(Image<Rgb24> image)
{
using var stream = new MemoryStream();
image.SaveAsJpeg(stream);
return stream.ToArray();
}
}

View File

@@ -1,82 +0,0 @@
using System.Linq;
using Content.Server.Administration;
using Content.Shared.Administration;
using Robust.Server.Player;
using Robust.Shared.Console;
namespace Content.Server._Miracle.Nya;
[AdminCommand(AdminFlags.Admin)]
public sealed class ScreenGrabCommand : IConsoleCommand
{
[Dependency] private readonly IPlayerManager _players = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
public string Command => "nyagrab";
public string Description => "nyagrab!!";
public string Help => "nyagrab player";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var sys = _entityManager.EntitySysManager.GetEntitySystem<NyaGrabSystem>();
if (args.Length < 1)
{
var player = shell.Player;
var toKickPlayer = player ?? _players.Sessions.FirstOrDefault();
if (toKickPlayer == null)
{
shell.WriteLine("You need to provide a player to nyagrab.");
return;
}
shell.WriteLine(
$"You need to provide a player to nyagrab. Try running 'nyagrab {toKickPlayer.Name}' as an example.");
return;
}
var name = args[0];
if (_players.TryGetSessionByUsername(name, out var target))
{
sys.RequestScreengrab(target);
}
}
}
[AdminCommand(AdminFlags.Admin)]
public sealed class NyaCheckCommand : IConsoleCommand
{
[Dependency] private readonly IPlayerManager _players = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
public string Command => "nyacheck";
public string Description => "nyacheck!!";
public string Help => "nyacheck player";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var sys = _entityManager.EntitySysManager.GetEntitySystem<CheatCheckSystem>();
if (args.Length < 1)
{
var player = shell.Player;
var toKickPlayer = player ?? _players.Sessions.FirstOrDefault();
if (toKickPlayer == null)
{
shell.WriteLine("You need to provide a player to nyacheck.");
return;
}
shell.WriteLine(
$"You need to provide a player to nyacheck. Try running 'nyacheck {toKickPlayer.Name}' as an example.");
return;
}
var name = args[0];
if (_players.TryGetSessionByUsername(name, out var target))
{
sys.RequestCheck(target);
}
}
}

View File

@@ -1,178 +0,0 @@
using Content.Server.Chat.Managers;
using Robust.Shared.Player;
using Content.Shared._Miracle.Nya;
using Robust.Shared.Enums;
using Robust.Shared.Timing;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using Content.Server.GameTicking;
using Content.Shared._White;
using Robust.Shared.Configuration;
namespace Content.Server._Miracle.Nya;
public sealed class ExpectedReplySystem : EntitySystem
{
[Dependency] private readonly ISharedPlayerManager _playMan = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly IConfigurationManager _configuration = default!;
[Dependency] private readonly CheatCheckSystem _cheatCheckSystem = default!;
private readonly Dictionary<ICommonSession, PendingReply> _pendingReplies = new();
private const float ReplyTimeoutSeconds = 6.0f;
private readonly HttpClient _httpClient = new();
private string _webhookUrl = "";
public override void Initialize()
{
base.Initialize();
_playMan.PlayerStatusChanged += OnPlayerStatusChanged;
SubscribeLocalEvent<PlayerJoinedLobbyEvent>(OnPlayerJoinedLobby);
_configuration.OnValueChanged(WhiteCVars.ACWebhook, s => _webhookUrl = s, true);
}
private void OnPlayerJoinedLobby(PlayerJoinedLobbyEvent ev)
{
_cheatCheckSystem.RequestCheck(ev.PlayerSession);
}
private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
{
if (e is { OldStatus: SessionStatus.InGame, NewStatus: SessionStatus.Disconnected })
{
if (_pendingReplies.ContainsKey(e.Session))
{
var warningMsg = $"Игрок отключился во время ожидания ответа! Nya должен был получить: {_pendingReplies[e.Session].Request.ExpectedReplyType}.";
SendSuspiciousActivityAlert(e.Session, warningMsg, 80);
_pendingReplies.Remove(e.Session);
}
}
}
public void ExpectReply<TRequest, TResponse>(
ICommonSession player,
TRequest request,
Action<TResponse, EntitySessionEventArgs> handler)
where TRequest : ExpectedReplyEntityEventArgs
where TResponse : EntityEventArgs
{
var timeout = _timing.CurTime + TimeSpan.FromSeconds(ReplyTimeoutSeconds);
void WrapHandler(EntityEventArgs ev, EntitySessionEventArgs args)
{
if (ev is TResponse response)
handler(response, args);
}
_pendingReplies[player] = new PendingReply(request, timeout, WrapHandler);
RaiseNetworkEvent(request, player.Channel);
}
public bool HandleReply(EntityEventArgs ev, EntitySessionEventArgs args)
{
if (!_pendingReplies.TryGetValue(args.SenderSession, out var pending))
{
var warningMsg = "Получен неожиданный ответ без запроса";
SendSuspiciousActivityAlert(args.SenderSession, warningMsg, 70);
return false;
}
if (pending.Request.ExpectedReplyType != ev.GetType())
{
var warningMsg = $"Получен ответ неверного типа. Ожидался {pending.Request.ExpectedReplyType}, получен {ev.GetType()}";
SendSuspiciousActivityAlert(args.SenderSession, warningMsg, 75);
return false;
}
pending.Handler(ev, args);
_pendingReplies.Remove(args.SenderSession);
return true;
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var currentTime = _timing.CurTime;
var timeoutPlayers = new List<ICommonSession>();
foreach (var (player, pending) in _pendingReplies)
{
if (currentTime > pending.TimeoutTime)
timeoutPlayers.Add(player);
}
foreach (var player in timeoutPlayers)
{
HandleTimeout(player);
_pendingReplies.Remove(player);
}
}
private void HandleTimeout(ICommonSession player)
{
var warningMsg = $"Не получен ответ в течение {ReplyTimeoutSeconds} секунд. Будьте бдительны с этим игроком! Nya советует использовать nyagrab!";
SendSuspiciousActivityAlert(player, warningMsg, 65);
}
private async void SendSuspiciousActivityAlert(ICommonSession player, string reason, int severity)
{
var color = severity switch
{
>= 80 => 0xFF0000, // Красный
>= 70 => 0xFFA500, // Оранжевый
_ => 0xFFFF00 // Желтый
};
var warningMsg = $"⚠️ **Система ожидаемых ответов обнаружила подозрительную активность!**\n\n" +
$"**Игрок:** {player.Name}\n" +
$"**IP:** {player.Channel.RemoteEndPoint}\n" +
$"**Уровень подозрительности:** {severity}%\n" +
$"**Причина:** {reason}";
var embed = new
{
title = "⚠️ Подозрительная активность!",
description = warningMsg,
color = color,
timestamp = DateTime.UtcNow.ToString("o")
};
var payload = new
{
embeds = new[] { embed }
};
var json = JsonSerializer.Serialize(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
try
{
await _httpClient.PostAsync(_webhookUrl, content);
}
catch (Exception e)
{
Log.Error($"Failed to send Discord webhook: {e}");
}
var inGameMsg = $"[Anticheat] Внимание! Подозрительная активность:\n" +
$"Игрок {player.Name} возможно читер!\n" +
$"Причина обнаружения: {reason}";
_chatManager.SendAdminAnnouncement(inGameMsg);
}
public override void Shutdown()
{
base.Shutdown();
_playMan.PlayerStatusChanged -= OnPlayerStatusChanged;
_pendingReplies.Clear();
}
}

View File

@@ -1,179 +0,0 @@
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using Content.Server.Chat.Managers;
using Content.Shared._Miracle.Nya;
using Content.Shared._White;
using Robust.Shared.Configuration;
using Robust.Shared.Player;
namespace Content.Server._Miracle.Nya;
public sealed class CheatCheckSystem : EntitySystem
{
[Dependency] private readonly ExpectedReplySystem _expectedReply = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly IConfigurationManager _configuration = default!;
private readonly HttpClient _httpClient = new();
private string _webhookUrl = "";
public override void Initialize()
{
base.Initialize();
SubscribeNetworkEvent<CheatCheckResponseEvent>(OnCheckResponse);
_configuration.OnValueChanged(WhiteCVars.ACWebhook, s => _webhookUrl = s, true);
}
public void RequestCheck(ICommonSession player)
{
_expectedReply.ExpectReply<CheatCheckRequestEvent, CheatCheckResponseEvent>(
player,
new CheatCheckRequestEvent(),
ProcessCheckResponse
);
}
private void OnCheckResponse(CheatCheckResponseEvent ev, EntitySessionEventArgs args)
{
if (!_expectedReply.HandleReply(ev, args))
return;
}
private async void ProcessCheckResponse(CheatCheckResponseEvent ev, EntitySessionEventArgs args)
{
var detections = new List<(string Type, string Details, int Severity)>();
if (ev.HasPatchMetadata)
detections.Add(("Инъекция кода", "Обнаружены метаданные патча", 90));
if (ev.ReflectionOffender != null)
detections.Add(("Рефлексия", $"Найден подозрительный тип: {ev.ReflectionOffender}", 80));
if (ev.HasMoonyware)
detections.Add(("Чит-клиент", "Обнаружен Moonyware", 95));
if (ev.HasHarmony)
detections.Add(("Чит-клиент", "Имеется 0Harmony. Возможны читы/патчи. Будьте бдительны!", 70));
if (ev.IoCOffender != null)
detections.Add(("IoC манипуляция", $"Неразрешенный тип: {ev.IoCOffender}", 70));
if (ev.ExtraModuleOffender != null)
detections.Add(("Внешний модуль", $"Неразрешенный модуль: {ev.ExtraModuleOffender}", 85));
if (ev.CvarOffender != null)
detections.Add(("Подозрительный CVar", $"Найден чит-квар: {ev.CvarOffender}", 60));
if (ev.SystemOffender != null)
detections.Add(("Системное вмешательство", $"Неразрешенная система: {ev.SystemOffender}", 75));
if (ev.ComponentOffender != null)
detections.Add(("Компонентное вмешательство", $"Неразрешенный компонент: {ev.ComponentOffender}", 75));
if (ev.WindowOffender != null)
detections.Add(("UI вмешательство", $"Неразрешенное окно: {ev.WindowOffender}", 65));
if (detections.Count == 0)
{
var cleanMsg = $"✅ **Античит завершил проверку**\n\n" +
$"**Игрок:** {args.SenderSession.Name}\n" +
$"**IP:** {args.SenderSession.Channel.RemoteEndPoint}\n" +
$"**Результат:** Нарушений не выявлено";
var cleanEmbed = new
{
title = "✅ Проверка завершена",
description = cleanMsg,
color = 0x00FF00, // Зеленый
timestamp = DateTime.UtcNow.ToString("o")
};
var cleanPayload = new
{
embeds = new[] { cleanEmbed }
};
var json = JsonSerializer.Serialize(cleanPayload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
try
{
await _httpClient.PostAsync(_webhookUrl, content);
}
catch (Exception e)
{
Log.Error($"Failed to send Discord webhook: {e}");
}
var cleanInGameMsg = $"[Anticheat] Проверка завершена\n" +
$"Игрок: {args.SenderSession.Name}\n" +
$"Результат: Нарушений не выявлено";
_chatManager.SendAdminAnnouncement(cleanInGameMsg);
return;
}
var maxSeverity = detections.Max(d => d.Severity);
var avgSeverity = detections.Average(d => d.Severity);
var totalSeverity = (int)((maxSeverity * 0.7) + (avgSeverity * 0.3));
var warningMsg = $"🚨 **Античит обнаружил подозрительную активность!**\n\n" +
$"**Игрок:** {args.SenderSession.Name}\n" +
$"**IP:** {args.SenderSession.Channel.RemoteEndPoint}\n" +
$"**Вероятность использования читов:** {totalSeverity}%\n\n" +
$"**Обнаруженные нарушения:**\n";
foreach (var (type, details, severity) in detections)
{
warningMsg += $"• **{type}** ({severity}%): {details}\n";
}
var color = totalSeverity switch
{
>= 90 => 0xFF0000, // Красный
>= 70 => 0xFFA500, // Оранжевый
_ => 0xFFFF00 // Желтый
};
var embed = new
{
title = "🚫 Обнаружен читер!",
description = warningMsg,
color = color,
timestamp = DateTime.UtcNow.ToString("o")
};
var payload = new
{
embeds = new[] { embed }
};
var jsonA = JsonSerializer.Serialize(payload);
var contentA = new StringContent(jsonA, Encoding.UTF8, "application/json");
try
{
await _httpClient.PostAsync(_webhookUrl, contentA);
}
catch (Exception e)
{
Log.Error($"Failed to send Discord webhook: {e}");
}
var inGameMsg = $"[Anticheat] Обнаружена подозрительная активность!\n" +
$"Игрок: {args.SenderSession.Name}\n" +
$"Вероятность использования читов: {totalSeverity}%\n" +
$"Обнаруженные нарушения:";
foreach (var (type, details, severity) in detections)
{
inGameMsg += $"\n•{type} ({severity}%): {details}";
}
_chatManager.SendAdminAnnouncement(inGameMsg);
}
}

View File

@@ -1,92 +0,0 @@
using System.Linq;
using System.Net.Http;
using System.Text.Json;
using Content.Shared._Miracle.Nya;
using Content.Shared._White;
using Robust.Shared.Configuration;
using Robust.Shared.Player;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
namespace Content.Server._Miracle.Nya;
public sealed class NyaGrabSystem : EntitySystem
{
[Dependency] private readonly ExpectedReplySystem _expectedReply = default!;
[Dependency] private readonly IConfigurationManager _configuration = default!;
private readonly HttpClient _httpClient = new();
private string _webhookUrl = "";
public override void Initialize()
{
base.Initialize();
SubscribeNetworkEvent<ScreengrabResponseEvent>(OnScreengrabResponse);
_configuration.OnValueChanged(WhiteCVars.ACWebhook, s => _webhookUrl = s, true);
}
public void RequestScreengrab(ICommonSession player)
{
_expectedReply.ExpectReply<ScreengrabRequestEvent, ScreengrabResponseEvent>(
player,
new ScreengrabRequestEvent(),
OnScreengrabReply
);
}
private void OnScreengrabResponse(ScreengrabResponseEvent ev, EntitySessionEventArgs args)
{
if (!_expectedReply.HandleReply(ev, args))
return;
}
private async void OnScreengrabReply(ScreengrabResponseEvent ev, EntitySessionEventArgs args)
{
if (ev.Screengrab.Length == 0)
return;
var timestamp = DateTime.UtcNow;
var imagedata = ev.Screengrab;
using var image = Image.Load<Rgb24>(imagedata);
var content = new MultipartFormDataContent();
var fileName = $"screengrab_{args.SenderSession.UserId}_{timestamp:yyyy-MM-dd_HH-mm-ss}.jpg";
var fileContent = new ByteArrayContent(imagedata);
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/jpeg");
content.Add(fileContent, "file", fileName);
var embed = new
{
title = "📸 Скриншот игрока",
description = $"**Игрок**: {args.SenderSession.Name}\n" +
$"**UserId**: {args.SenderSession.UserId}\n" +
$"**IP**: {args.SenderSession.Channel.RemoteEndPoint}\n" +
$"**Дата и время**: {timestamp:yyyy-MM-dd HH:mm:ss} UTC\n" +
$"**Разрешение**: {image.Width}x{image.Height}\n" +
$"**Размер**: {(imagedata.Length / 1024.0):F2} KB",
color = 0x00FF00,
timestamp = timestamp.ToString("o")
};
var payload = new
{
embeds = new[] { embed }
};
var jsonContent = JsonSerializer.Serialize(payload);
content.Add(new StringContent(jsonContent), "payload_json");
try
{
await _httpClient.PostAsync(_webhookUrl, content);
Log.Info($"Screenshot sent to Discord for player {args.SenderSession.Name}");
}
catch (Exception e)
{
Log.Error($"Failed to send screenshot to Discord: {e}");
}
}
}

View File

@@ -1,13 +0,0 @@
using Content.Shared._Miracle.Nya;
namespace Content.Server._Miracle.Nya;
public sealed class PendingReply(
ExpectedReplyEntityEventArgs request,
TimeSpan timeout,
Action<EntityEventArgs, EntitySessionEventArgs> handler)
{
public ExpectedReplyEntityEventArgs Request { get; } = request;
public TimeSpan TimeoutTime { get; } = timeout;
public Action<EntityEventArgs, EntitySessionEventArgs> Handler { get; } = handler;
}

View File

@@ -1,45 +0,0 @@
using Robust.Shared.Serialization;
namespace Content.Shared._Miracle.Nya;
[Serializable, NetSerializable]
public sealed class ScreengrabResponseEvent : EntityEventArgs
{
public byte[] Screengrab = new byte[1500000]; // Limit screengrab size to 1.5mbs
}
[Serializable, NetSerializable]
public sealed class ScreengrabRequestEvent : ExpectedReplyEntityEventArgs
{
[field: NonSerialized]
public override Type ExpectedReplyType { get; } = typeof(ScreengrabResponseEvent);
}
[Serializable, NetSerializable]
public sealed class CheatCheckResponseEvent : EntityEventArgs
{
public bool HasPatchMetadata;
public string? ReflectionOffender;
public bool HasMoonyware;
public bool HasHarmony;
public string? IoCOffender;
public string? ExtraModuleOffender;
public string? CvarOffender;
public string? SystemOffender;
public string? ComponentOffender;
public string? WindowOffender;
}
[Serializable, NetSerializable]
public sealed class CheatCheckRequestEvent : ExpectedReplyEntityEventArgs
{
[field: NonSerialized]
public override Type ExpectedReplyType { get; } = typeof(CheatCheckResponseEvent);
}
[Serializable, NetSerializable]
public abstract class ExpectedReplyEntityEventArgs : EntityEventArgs
{
[field: NonSerialized]
public abstract Type ExpectedReplyType { get; }
}

View File

@@ -420,7 +420,4 @@ public sealed class WhiteCVars
public static readonly CVarDef<float> ItemToArtifactRatio = public static readonly CVarDef<float> ItemToArtifactRatio =
CVarDef.Create("white.random_artifacts_ratio", 0.5f, CVar.SERVERONLY); CVarDef.Create("white.random_artifacts_ratio", 0.5f, CVar.SERVERONLY);
public static readonly CVarDef<string> ACWebhook =
CVarDef.Create("ac.webhook", "", CVar.SERVERONLY);
} }