This commit is contained in:
BIGZi0348
2025-01-14 07:06:30 +03:00
committed by GitHub
9 changed files with 0 additions and 886 deletions

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