Files
OldThink/Content.Server/_Miracle/Nya/ExpectedReplySystem.cs
2024-10-29 19:34:16 +05:00

179 lines
6.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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();
}
}