[Feat] Panda socket (#616)
* base server side * token check & base command response * panda base command response addition & some commands * web event, rest of commands & events * fix empty api * [DRAFT] Panda HTTP server (#570) * [Sponsor] Nairsark ghost (#492) * [Sponsor] Trora ghost (#493) * [Sponsor] Trora ghost * ckey * Vtergot fluff (#494) * Твики (#491) * Economy additions * Tweak implant cooldowns * Cult stuff * Random appearance aspect nuke ops fix * Auto shuttle enable on round end * Holy water threshold * Automatic changelog update * [Sponsor] Geraldiy fluff (#496) * [Fix] Reputation respawn hotfix (#497) * Automatic changelog update * Новые аспекты (#495) * Add ReflectAspect * Add SlipperyAspect * Add TraitorRichAspect * Add WhisperAspect * Add DarknessAspect & StolenFloorAspect * Add WindowLeakAspect * Add CatEarsAspect * Add NothingAspect * Fix fast and furious clone * Add SkeletonAspect * Add cvar ceanup * Automatic changelog update * [Sponsor] Fluff knife cappy (#498) * [Sponsor] Fluff KnifeCappy * loadout * Всякое (#501) * eftpos form jurist * ebal parameda * Automatic changelog update * [Sponsor] Zilendorie ghost upgrade (#502) * Апдейт карт, станция прибытия (#503) * Automatic changelog update * Fix aspects (#500) * Automatic changelog update * Rules popup fix (#504) * Defib fix (#499) * Automatic changelog update * Фикс флаффа (#507) * Фикс прибытия (#509) * Automatic changelog update * Фиксы (#508) * Fix cult blindfold * Add stamina resistances * Energy bolt is energy * Laser shield is anti-laser * Cult blindfold welding protection * Eject id cards on deconstruct * Wires panel power fix * Add markings for species * Ebow gaming * feat: настенные консольки (#505) * Automatic changelog update * [Sponsor] Fluff Medicgaming (#510) * Привязка банковского аккаунта (#506) * Sustenance vend price fix * Account link * Automatic changelog update * antag ban fix (#511) * antag ban fix * rename some shit * Bugfixes (#512) * Bullets go through open crates * Bullets don't hit pulled dead bodies * No glued cuffs * Missed reflect aspect mark * vehicles cannot be shot (#18910) Co-authored-by: deltanedas <@deltanedas:kde.org> * Cleanup --------- Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com> * Automatic changelog update * атмос гейминг indeed (#513) * fix hypernob + plasma and trit fire * add 11 new gas types * actually fix hypernob --------- Co-authored-by: halicopter <kirillhalic@gmail.com> * Automatic changelog update * [Sponsor] thechaotic fluff * Revert "[Sponsor] thechaotic fluff" This reverts commit d19c807751258f9cfb205db01b0e4617b638fe98. * [Sponsor] Fluff updates (#518) * warete update * renosan & warete size * [Sponsor] thechaotic fluff (#519) (cherry picked from commit d19c807751258f9cfb205db01b0e4617b638fe98) * fix tts sanitize (#517) * Automatic changelog update * [Fluff] ghost regari * Buffs (#515) * FIRE axe * Bow pro * Bang * Meleeeee * Disarm two-handed hard * Automatic changelog update * Игнор вайтлист требования ролей если стоит флаг TOOLS (#514) * forAmins be like * >вайтлист в девелопменте * а описание кто напишет а * Размеры попапов в чате (#525) * попытка деконить магнит пока он активен гибает интернет приколистов (#524) * Интернет прикол * попап * Prevent using cultist items & halberd (#523) * Fix skeleton aspect (#522) * Fix cult door (#521) * Fix cult door * Prevent emagging * Walls and griders * Fix * Drone fix (#520) * Drone fix * RCD * Automatic changelog update * Смешное в конце раунда с аспектом (#516) * Cats * Fix * Automatic changelog update * Fix cult win conditions (#527) * Runes stuff (#526) * Blood boil 2.0 * Attach to grid * No * Automatic changelog update * [Sponsor] thefrendlypsychopath ghost (#529) * knifeCappy (#528) Co-authored-by: Mona Hmiza <you@example.com> * [Feat] TTS 8 new voices * borg fix * Automatic changelog update * мапперы тестили * fix void prototype * [Fix] Cult chat fix (#536) * ERT tweak (#530) * tweak values * tweak spawn logic * Invisible rune (#531) * Automatic changelog update * [Sponsor] Oleg_Tinkoff fluff (#534) * [Sponsor] Oleg_Tinkoff fluff * name fix * Small fixes (#537) * No sleeping emotes * Beakers & jugs in fridge * Drone fix * Fix teleport pulling * Fix loc * More fixes * Fix ghost role * Stuff * Automatic changelog update * Nerf grilles (#541) * Nerf grilles * tweak * Bola fix (#540) * Automatic changelog update * Куча говна (#539) * No Emp resistance (#538) * No EMP resistance * Cleanup * Automatic changelog update * mappews mapped some mowe~ * anothew update UwU * [Sponsor] Geraldiy fluff 2 (#542) * fix drydock * Objective changes (#543) * Automatic changelog update * UwU spawnews are now fixed, hopefully~ * UwU tired of it alweady * Automatic changelog update * Респрайт капитанского лазера (#546) * antique lasergun resprite lol * лицуха * Automatic changelog update * Апдейт арахнидов (#544) * Web underwear * Fix layers * Change melee sound * Arachnid 2: Episode 2 (#19984) * Shield * minor sprite changes and buffs * structure buff * Crafting stuff * tweaks * 88-88 * Better web pocket sprites. * yeah it's fine now. * Fix * Sprite tweaks * This I guess * Eye sprite --------- Co-authored-by: PixelTK <85175107+PixelTheKermit@users.noreply.github.com> * [Sponsor] Zilendorie fluff (#545) * Automatic changelog update * Fire buff (#549) * EntityStorage deletion fix? (#548) * strip fix (#21552) (#547) Co-authored-by: PixelTK <85175107+PixelTheKermit@users.noreply.github.com> * Automatic changelog update * [Fluff] SSAO Ghost * [Fluff] svinka ghost tweak * [Fluff] Svinka Coat * Meaty-Ore Idea (#550) * qwe * yeah * Vtergot fluff fix (#552) * наяриваем на лишние пиксели * Чтобы мапперы не втыкали * fix atmos (#551) * fix atmos * fixie * Automatic changelog update * Meow hotfix (#553) * Many stuff (#555) * Drone bucket * Weird insuls * Not too strong * No cult door bolts * Emergency shuttle after round end fix * Fix spiders * Automatic changelog update * [FEAT] Всякие прикольные разности и вкусности (#554) * feat: трикодер * feat: принтер документов * fix: текст фелинидов * feat: возможность менять голос эмоутов * feat: мяукаем при аспекте мяуканья * feat: ПНВ * fix: забирай свои метадаты * fix: oopsies * fix: линтер снова * fix: пожалуйста линтер отстань * Automatic changelog update * Гарпии (#533) * harpy initial * fix and some locale * ru locale * actions refactor shit * пофиксив гавпий~~~ пойду тестить~~ * hawpies are ready UwU * cweanup OwO * hawpies fixed a bit, still cant seawch them nya~ * hawpies can be stwipped now, fixie-dixie awwived~ * emotes fixie-dixied nya~ * говно * говно говна линтер соси * Automatic changelog update * Revert "Гарпии (#533)" (#557) This reverts commit e3f2166bf7cbd775278a396a3f8d8215a2d5c506. * Doom fluff (#556) * DOOMMAX fluff * Detective meow * Сеньёр помидор офицер * RSI validator su4ka * [Fluff] MR_Regari ghost tweak * [Fluff] svinka ghost tweak * [Fluff] Antohag gasmask fluff * ГАРПИИ (#559) * harpy initial * fix and some locale * ru locale * actions refactor shit * пофиксив гавпий~~~ пойду тестить~~ * hawpies are ready UwU * cweanup OwO * hawpies fixed a bit, still cant seawch them nya~ * hawpies can be stwipped now, fixie-dixie awwived~ * emotes fixie-dixied nya~ * говно * говно говна линтер соси * Automatic changelog update * [Tweak] Бумажная работа и фикс крафта пнв. (#560) * fix: персонал станции вспомнил как делать пнв * tweak: блюспейс технологии убраны у принтера документов * feat: заказ бумаги в карго * feat: бумажная дверь * Automatic changelog update * doommaxx-fluff nothing interesting * fluff skufa (#562) * [Sponsor] Fluff Forg (#567) * [Sponsor] Fluff Forg * fix size * sound * [Feat] TTS 15 new voices (#568) * Automatic changelog update * base server side * token check & base command response * panda base command response addition & some commands * web event, rest of commands & events * fix empty api --------- Co-authored-by: Cinkafox <70429757+Cinkafox@users.noreply.github.com> Co-authored-by: Aviu00 <93730715+Aviu00@users.noreply.github.com> Co-authored-by: RavmorganButOnCocaine <valtos@nextmail.ru> Co-authored-by: Kotovskiy <77529717+wCATw@users.noreply.github.com> Co-authored-by: ThereDrD0 <88589686+ThereDrD0@users.noreply.github.com> Co-authored-by: Remuchi <72476615+Remuchi@users.noreply.github.com> Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com> Co-authored-by: KurokoTurbo <92106367+melanoTurbo@users.noreply.github.com> Co-authored-by: halicopter <kirillhalic@gmail.com> Co-authored-by: rhailrake <49613070+rhailrake@users.noreply.github.com> Co-authored-by: Subversionary <109166122+Subversionary@users.noreply.github.com> Co-authored-by: RavMorgan <48182970+RavMorgan@users.noreply.github.com> Co-authored-by: Mona Hmiza <you@example.com> Co-authored-by: Valtos <valtos@spaces.ru> Co-authored-by: REBOLUTION228-a11 <128076300+REBOLUTION228-a11@users.noreply.github.com> Co-authored-by: PixelTK <85175107+PixelTheKermit@users.noreply.github.com> * newtonsoft version change * remove semaphore * remove double dooc * fix admin stealth * cleanup * remove utka sockets --------- Co-authored-by: Cinkafox <70429757+Cinkafox@users.noreply.github.com> Co-authored-by: Aviu00 <93730715+Aviu00@users.noreply.github.com> Co-authored-by: RavmorganButOnCocaine <valtos@nextmail.ru> Co-authored-by: Kotovskiy <77529717+wCATw@users.noreply.github.com> Co-authored-by: ThereDrD0 <88589686+ThereDrD0@users.noreply.github.com> Co-authored-by: Remuchi <72476615+Remuchi@users.noreply.github.com> Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com> Co-authored-by: KurokoTurbo <92106367+melanoTurbo@users.noreply.github.com> Co-authored-by: halicopter <kirillhalic@gmail.com> Co-authored-by: rhailrake <49613070+rhailrake@users.noreply.github.com> Co-authored-by: Subversionary <109166122+Subversionary@users.noreply.github.com> Co-authored-by: RavMorgan <48182970+RavMorgan@users.noreply.github.com> Co-authored-by: Mona Hmiza <you@example.com> Co-authored-by: Valtos <valtos@spaces.ru> Co-authored-by: REBOLUTION228-a11 <128076300+REBOLUTION228-a11@users.noreply.github.com> Co-authored-by: PixelTK <85175107+PixelTheKermit@users.noreply.github.com>
This commit is contained in:
@@ -1,10 +1,7 @@
|
||||
using Content.Server.Administration.Managers;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using Content.Server.Database;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.UtkaIntegration;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
@@ -19,7 +16,7 @@ public sealed class BanCommand : LocalizedCommands
|
||||
[Dependency] private readonly IPlayerLocator _locator = default!;
|
||||
[Dependency] private readonly IBanManager _bans = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly UtkaTCPWrapper _utkaSockets = default!; // WD
|
||||
[Dependency] private readonly PandaWebManager _pandaWeb = default!; // WD
|
||||
[Dependency] private readonly IEntityManager _entMan = default!; // WD
|
||||
|
||||
public override string Command => "ban";
|
||||
@@ -128,7 +125,7 @@ public sealed class BanCommand : LocalizedCommands
|
||||
Rid = EntitySystem.Get<GameTicker>().RoundId,
|
||||
BanId = banId
|
||||
};
|
||||
_utkaSockets.SendMessageToAll(utkaBanned);
|
||||
_pandaWeb.SendBotMessage(utkaBanned);
|
||||
_entMan.EventBus.RaiseEvent(EventSource.Local, utkaBanned);
|
||||
//WD end
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.Database;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.White;
|
||||
using Content.Server.White.PandaSocket.Interfaces;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Players;
|
||||
@@ -32,6 +36,9 @@ public sealed class BanManager : IBanManager, IPostInjectInit
|
||||
[Dependency] private readonly IChatManager _chat = default!;
|
||||
[Dependency] private readonly INetManager _netManager = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
[Dependency] private readonly IPlayerLocator _playerLocator = default!; // WD
|
||||
[Dependency] private readonly PandaWebManager _pandaWeb = default!; // WD
|
||||
[Dependency] private readonly IEntityManager _entMan = default!; // WD
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
@@ -317,4 +324,242 @@ public sealed class BanManager : IBanManager, IPostInjectInit
|
||||
{
|
||||
_sawmill = _logManager.GetSawmill(SawmillId);
|
||||
}
|
||||
|
||||
//WD start
|
||||
public async void UtkaCreateDepartmentBan(string admin, string target, DepartmentPrototype department, string reason, uint minutes, bool isGlobalBan,
|
||||
IPandaStatusHandlerContext context)
|
||||
{
|
||||
var located = await _playerLocator.LookupIdByNameOrIdAsync(target);
|
||||
if (located == null)
|
||||
{
|
||||
UtkaSendResponse(false, context);
|
||||
return;
|
||||
}
|
||||
|
||||
var targetUid = located.UserId;
|
||||
var targetHWid = located.LastHWId;
|
||||
var targetAddress = located.LastAddress;
|
||||
|
||||
DateTimeOffset? expires = null;
|
||||
if (minutes > 0)
|
||||
{
|
||||
expires = DateTimeOffset.Now + TimeSpan.FromMinutes(minutes);
|
||||
}
|
||||
|
||||
(IPAddress, int)? addressRange = null;
|
||||
if (targetAddress != null)
|
||||
{
|
||||
if (targetAddress.IsIPv4MappedToIPv6)
|
||||
targetAddress = targetAddress.MapToIPv4();
|
||||
|
||||
// Ban /64 for IPv4, /32 for IPv4.
|
||||
var cidr = targetAddress.AddressFamily == AddressFamily.InterNetworkV6 ? 64 : 32;
|
||||
addressRange = (targetAddress, cidr);
|
||||
}
|
||||
|
||||
var cfg = UnsafePseudoIoC.ConfigurationManager;
|
||||
var serverName = cfg.GetCVar(CCVars.AdminLogsServerName);
|
||||
|
||||
if (isGlobalBan)
|
||||
{
|
||||
serverName = "unknown";
|
||||
}
|
||||
|
||||
var locatedPlayer = await _playerLocator.LookupIdByNameOrIdAsync(admin);
|
||||
if (locatedPlayer == null)
|
||||
{
|
||||
UtkaSendResponse(false, context);
|
||||
return;
|
||||
}
|
||||
var player = locatedPlayer.UserId;
|
||||
|
||||
UtkaSendResponse(true, context);
|
||||
|
||||
_systems.TryGetEntitySystem<GameTicker>(out var ticker);
|
||||
int? roundId = ticker == null || ticker.RoundId == 0 ? null : ticker.RoundId;
|
||||
var playtime = (await _db.GetPlayTimes(targetUid)).Find(p => p.Tracker == PlayTimeTrackingShared.TrackerOverall)?.TimeSpent ?? TimeSpan.Zero;
|
||||
|
||||
foreach (var job in department.Roles)
|
||||
{
|
||||
var role = string.Concat(JobPrefix, job);
|
||||
|
||||
var banDef = new ServerRoleBanDef(
|
||||
null,
|
||||
targetUid,
|
||||
addressRange,
|
||||
targetHWid,
|
||||
DateTimeOffset.Now,
|
||||
expires,
|
||||
roundId,
|
||||
playtime,
|
||||
reason,
|
||||
NoteSeverity.High,
|
||||
player,
|
||||
null,
|
||||
role,
|
||||
serverName);
|
||||
|
||||
if (!await AddRoleBan(banDef))
|
||||
continue;
|
||||
|
||||
var banId = await UtkaGetBanId(reason, role, targetUid);
|
||||
|
||||
UtkaSendJobBanEvent(admin, target, minutes, job, isGlobalBan, reason, banId);
|
||||
}
|
||||
|
||||
SendRoleBans(located);
|
||||
}
|
||||
|
||||
public async void UtkaCreateJobBan(string admin, string target, string job, string reason, uint minutes, bool isGlobalBan,
|
||||
IPandaStatusHandlerContext context)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<JobPrototype>(job, out _))
|
||||
{
|
||||
UtkaSendResponse(false, context);
|
||||
return;
|
||||
}
|
||||
|
||||
var role = string.Concat(JobPrefix, job);
|
||||
|
||||
var located = await _playerLocator.LookupIdByNameOrIdAsync(target);
|
||||
if (located == null)
|
||||
{
|
||||
UtkaSendResponse(false, context);
|
||||
return;
|
||||
}
|
||||
|
||||
var targetUid = located.UserId;
|
||||
var targetHWid = located.LastHWId;
|
||||
var targetAddress = located.LastAddress;
|
||||
|
||||
DateTimeOffset? expires = null;
|
||||
if (minutes > 0)
|
||||
{
|
||||
expires = DateTimeOffset.Now + TimeSpan.FromMinutes(minutes);
|
||||
}
|
||||
|
||||
(IPAddress, int)? addressRange = null;
|
||||
if (targetAddress != null)
|
||||
{
|
||||
if (targetAddress.IsIPv4MappedToIPv6)
|
||||
targetAddress = targetAddress.MapToIPv4();
|
||||
|
||||
// Ban /64 for IPv4, /32 for IPv4.
|
||||
var cidr = targetAddress.AddressFamily == AddressFamily.InterNetworkV6 ? 64 : 32;
|
||||
addressRange = (targetAddress, cidr);
|
||||
}
|
||||
|
||||
var cfg = UnsafePseudoIoC.ConfigurationManager;
|
||||
var serverName = cfg.GetCVar(CCVars.AdminLogsServerName);
|
||||
|
||||
if (isGlobalBan)
|
||||
{
|
||||
serverName = "unknown";
|
||||
}
|
||||
|
||||
var locatedPlayer = await _playerLocator.LookupIdByNameOrIdAsync(admin);
|
||||
if (locatedPlayer == null)
|
||||
{
|
||||
UtkaSendResponse(false, context);
|
||||
return;
|
||||
}
|
||||
|
||||
_systems.TryGetEntitySystem<GameTicker>(out var ticker);
|
||||
int? roundId = ticker == null || ticker.RoundId == 0 ? null : ticker.RoundId;
|
||||
var playtime = (await _db.GetPlayTimes(targetUid)).Find(p => p.Tracker == PlayTimeTrackingShared.TrackerOverall)?.TimeSpent ?? TimeSpan.Zero;
|
||||
|
||||
var player = locatedPlayer.UserId;
|
||||
var banDef = new ServerRoleBanDef(
|
||||
null,
|
||||
targetUid,
|
||||
addressRange,
|
||||
targetHWid,
|
||||
DateTimeOffset.Now,
|
||||
expires,
|
||||
roundId,
|
||||
playtime,
|
||||
reason,
|
||||
NoteSeverity.High,
|
||||
player,
|
||||
null,
|
||||
role,
|
||||
serverName);
|
||||
|
||||
if (!await AddRoleBan(banDef))
|
||||
{
|
||||
UtkaSendResponse(false, context);
|
||||
return;
|
||||
}
|
||||
|
||||
var banId = await UtkaGetBanId(reason, role, targetUid);
|
||||
|
||||
UtkaSendJobBanEvent(admin, target, minutes, job, isGlobalBan, reason, banId);
|
||||
UtkaSendResponse(true, context);
|
||||
|
||||
SendRoleBans(located);
|
||||
}
|
||||
|
||||
private void UtkaSendResponse(bool banned, IPandaStatusHandlerContext context)
|
||||
{
|
||||
var utkaBanned = new UtkaJobBanResponse()
|
||||
{
|
||||
Banned = banned
|
||||
};
|
||||
|
||||
context.RespondJsonAsync(utkaBanned);
|
||||
}
|
||||
|
||||
private async void UtkaSendJobBanEvent(string ackey, string ckey, uint duration, string job, bool global,
|
||||
string reason, int banId)
|
||||
{
|
||||
if (job.Contains("Job:"))
|
||||
{
|
||||
job = job.Replace("Job:", "");
|
||||
}
|
||||
|
||||
var utkaBanned = new UtkaBannedEvent()
|
||||
{
|
||||
ACkey = ackey,
|
||||
Ckey = ckey,
|
||||
Duration = duration,
|
||||
Bantype = job,
|
||||
Global = global,
|
||||
Reason = reason,
|
||||
Rid = EntitySystem.Get<GameTicker>().RoundId,
|
||||
BanId = banId
|
||||
};
|
||||
|
||||
_pandaWeb.SendBotMessage(utkaBanned);
|
||||
_entMan.EventBus.RaiseEvent(EventSource.Local, utkaBanned);
|
||||
}
|
||||
|
||||
private async Task<int> UtkaGetBanId(string reason, string role, NetUserId targetUid)
|
||||
{
|
||||
var banId = 0;
|
||||
var banList = await _db.GetServerRoleBansAsync(null, targetUid, null);
|
||||
|
||||
foreach (var ban in banList)
|
||||
{
|
||||
if (ban.Reason == reason)
|
||||
{
|
||||
if (ban.Role == role && ban.Id != null)
|
||||
{
|
||||
banId = ban.Id.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return banId;
|
||||
}
|
||||
|
||||
public void SendRoleBans(LocatedPlayerData located)
|
||||
{
|
||||
if (!_playerManager.TryGetSessionById(located.UserId, out var player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SendRoleBans(player);
|
||||
}
|
||||
//WD end
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.White.PandaSocket.Interfaces;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
@@ -56,4 +58,13 @@ public interface IBanManager
|
||||
/// </summary>
|
||||
/// <param name="pSession">Player's session</param>
|
||||
public void SendRoleBans(ICommonSession pSession);
|
||||
|
||||
// WD START
|
||||
public void UtkaCreateDepartmentBan(string admin, string target, DepartmentPrototype department,
|
||||
string reason, uint minutes, bool isGlobalBan,
|
||||
IPandaStatusHandlerContext context);
|
||||
|
||||
public void UtkaCreateJobBan(string admin, string target, string job, string reason, uint minutes, bool isGlobalBan,
|
||||
IPandaStatusHandlerContext context);
|
||||
// WD END
|
||||
}
|
||||
|
||||
@@ -1,451 +0,0 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Database;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.UtkaIntegration;
|
||||
using Content.Server.White;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Players.PlayTimeTracking;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Administration.Managers;
|
||||
|
||||
public sealed class RoleBanManager
|
||||
{
|
||||
[Dependency] private readonly IServerDbManager _db = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IPlayerLocator _playerLocator = default!;
|
||||
[Dependency] private readonly UtkaTCPWrapper _utkaSockets = default!; // WD
|
||||
[Dependency] private readonly IEntitySystemManager _systems = default!; // WD
|
||||
[Dependency] private readonly IBanManager _banManager = default!; // WD
|
||||
[Dependency] private readonly IEntityManager _entMan = default!; // WD
|
||||
|
||||
private const string JobPrefix = "Job:";
|
||||
|
||||
private readonly Dictionary<NetUserId, HashSet<ServerRoleBanDef>> _cachedRoleBans = new();
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||
}
|
||||
|
||||
private async void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
|
||||
{
|
||||
if (e.NewStatus != SessionStatus.Connected
|
||||
|| _cachedRoleBans.ContainsKey(e.Session.UserId))
|
||||
return;
|
||||
|
||||
var netChannel = e.Session.ConnectedClient;
|
||||
await CacheDbRoleBans(e.Session.UserId, netChannel.RemoteEndPoint.Address, netChannel.UserData.HWId.Length == 0 ? null : netChannel.UserData.HWId);
|
||||
}
|
||||
|
||||
private async Task<bool> AddRoleBan(ServerRoleBanDef banDef)
|
||||
{
|
||||
if (banDef.UserId != null)
|
||||
{
|
||||
if (!_cachedRoleBans.TryGetValue(banDef.UserId.Value, out var roleBans))
|
||||
{
|
||||
roleBans = new HashSet<ServerRoleBanDef>();
|
||||
_cachedRoleBans.Add(banDef.UserId.Value, roleBans);
|
||||
}
|
||||
if (!roleBans.Contains(banDef))
|
||||
roleBans.Add(banDef);
|
||||
}
|
||||
|
||||
await _db.AddServerRoleBanAsync(banDef);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SendRoleBans(LocatedPlayerData located)
|
||||
{
|
||||
if (!_playerManager.TryGetSessionById(located.UserId, out var player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_banManager.SendRoleBans(player);
|
||||
}
|
||||
|
||||
public HashSet<string>? GetRoleBans(NetUserId playerUserId)
|
||||
{
|
||||
return _cachedRoleBans.TryGetValue(playerUserId, out var roleBans) ? roleBans.Select(banDef => banDef.Role).ToHashSet() : null;
|
||||
}
|
||||
|
||||
private async Task CacheDbRoleBans(NetUserId userId, IPAddress? address = null, ImmutableArray<byte>? hwId = null)
|
||||
{
|
||||
var roleBans = await _db.GetServerRoleBansAsync(address, userId, hwId, false);
|
||||
|
||||
var userRoleBans = new HashSet<ServerRoleBanDef>();
|
||||
foreach (var ban in roleBans)
|
||||
{
|
||||
userRoleBans.Add(ban);
|
||||
}
|
||||
|
||||
_cachedRoleBans[userId] = userRoleBans;
|
||||
}
|
||||
|
||||
public void Restart()
|
||||
{
|
||||
// Clear out players that have disconnected.
|
||||
var toRemove = new List<NetUserId>();
|
||||
foreach (var player in _cachedRoleBans.Keys)
|
||||
{
|
||||
if (!_playerManager.TryGetSessionById(player, out _))
|
||||
toRemove.Add(player);
|
||||
}
|
||||
|
||||
foreach (var player in toRemove)
|
||||
{
|
||||
_cachedRoleBans.Remove(player);
|
||||
}
|
||||
|
||||
// Check for expired bans
|
||||
foreach (var (_, roleBans) in _cachedRoleBans)
|
||||
{
|
||||
roleBans.RemoveWhere(ban => DateTimeOffset.Now > ban.ExpirationTime);
|
||||
}
|
||||
}
|
||||
|
||||
#region Job Bans
|
||||
public async void CreateJobBan(IConsoleShell shell, string target, string job, string reason, uint minutes, bool isGlobalBan)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(job, out JobPrototype? _))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-roleban-job-parse", ("job", job)));
|
||||
return;
|
||||
}
|
||||
|
||||
var role = string.Concat(JobPrefix, job); // WD edit
|
||||
CreateRoleBan(shell, target, role, reason, minutes, isGlobalBan, job); // WD edit
|
||||
}
|
||||
|
||||
//WD start
|
||||
public async void UtkaCreateDepartmentBan(string admin, string target, DepartmentPrototype department, string reason, uint minutes, bool isGlobalBan)
|
||||
{
|
||||
var located = await _playerLocator.LookupIdByNameOrIdAsync(target);
|
||||
if (located == null)
|
||||
{
|
||||
UtkaSendResponse(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var targetUid = located.UserId;
|
||||
var targetHWid = located.LastHWId;
|
||||
var targetAddress = located.LastAddress;
|
||||
|
||||
DateTimeOffset? expires = null;
|
||||
if (minutes > 0)
|
||||
{
|
||||
expires = DateTimeOffset.Now + TimeSpan.FromMinutes(minutes);
|
||||
}
|
||||
|
||||
(IPAddress, int)? addressRange = null;
|
||||
if (targetAddress != null)
|
||||
{
|
||||
if (targetAddress.IsIPv4MappedToIPv6)
|
||||
targetAddress = targetAddress.MapToIPv4();
|
||||
|
||||
// Ban /64 for IPv4, /32 for IPv4.
|
||||
var cidr = targetAddress.AddressFamily == AddressFamily.InterNetworkV6 ? 64 : 32;
|
||||
addressRange = (targetAddress, cidr);
|
||||
}
|
||||
|
||||
var cfg = UnsafePseudoIoC.ConfigurationManager;
|
||||
var serverName = cfg.GetCVar(CCVars.AdminLogsServerName);
|
||||
|
||||
if (isGlobalBan)
|
||||
{
|
||||
serverName = "unknown";
|
||||
}
|
||||
|
||||
var locatedPlayer = await _playerLocator.LookupIdByNameOrIdAsync(admin);
|
||||
if (locatedPlayer == null)
|
||||
{
|
||||
UtkaSendResponse(false);
|
||||
return;
|
||||
}
|
||||
var player = locatedPlayer.UserId;
|
||||
|
||||
UtkaSendResponse(true);
|
||||
|
||||
_systems.TryGetEntitySystem<GameTicker>(out var ticker);
|
||||
int? roundId = ticker == null || ticker.RoundId == 0 ? null : ticker.RoundId;
|
||||
var playtime = (await _db.GetPlayTimes(targetUid)).Find(p => p.Tracker == PlayTimeTrackingShared.TrackerOverall)?.TimeSpent ?? TimeSpan.Zero;
|
||||
|
||||
foreach (var job in department.Roles)
|
||||
{
|
||||
var role = string.Concat(JobPrefix, job);
|
||||
|
||||
var banDef = new ServerRoleBanDef(
|
||||
null,
|
||||
targetUid,
|
||||
addressRange,
|
||||
targetHWid,
|
||||
DateTimeOffset.Now,
|
||||
expires,
|
||||
roundId,
|
||||
playtime,
|
||||
reason,
|
||||
NoteSeverity.High,
|
||||
player,
|
||||
null,
|
||||
role,
|
||||
serverName);
|
||||
|
||||
if (!await AddRoleBan(banDef))
|
||||
continue;
|
||||
|
||||
var banId = await UtkaGetBanId(reason, role, targetUid);
|
||||
|
||||
UtkaSendJobBanEvent(admin, target, minutes, job, isGlobalBan, reason, banId);
|
||||
}
|
||||
|
||||
SendRoleBans(located);
|
||||
}
|
||||
|
||||
public async void UtkaCreateJobBan(string admin, string target, string job, string reason, uint minutes, bool isGlobalBan)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<JobPrototype>(job, out _))
|
||||
{
|
||||
UtkaSendResponse(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var role = string.Concat(JobPrefix, job);
|
||||
|
||||
var located = await _playerLocator.LookupIdByNameOrIdAsync(target);
|
||||
if (located == null)
|
||||
{
|
||||
UtkaSendResponse(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var targetUid = located.UserId;
|
||||
var targetHWid = located.LastHWId;
|
||||
var targetAddress = located.LastAddress;
|
||||
|
||||
DateTimeOffset? expires = null;
|
||||
if (minutes > 0)
|
||||
{
|
||||
expires = DateTimeOffset.Now + TimeSpan.FromMinutes(minutes);
|
||||
}
|
||||
|
||||
(IPAddress, int)? addressRange = null;
|
||||
if (targetAddress != null)
|
||||
{
|
||||
if (targetAddress.IsIPv4MappedToIPv6)
|
||||
targetAddress = targetAddress.MapToIPv4();
|
||||
|
||||
// Ban /64 for IPv4, /32 for IPv4.
|
||||
var cidr = targetAddress.AddressFamily == AddressFamily.InterNetworkV6 ? 64 : 32;
|
||||
addressRange = (targetAddress, cidr);
|
||||
}
|
||||
|
||||
var cfg = UnsafePseudoIoC.ConfigurationManager;
|
||||
var serverName = cfg.GetCVar(CCVars.AdminLogsServerName);
|
||||
|
||||
if (isGlobalBan)
|
||||
{
|
||||
serverName = "unknown";
|
||||
}
|
||||
|
||||
var locatedPlayer = await _playerLocator.LookupIdByNameOrIdAsync(admin);
|
||||
if (locatedPlayer == null)
|
||||
{
|
||||
UtkaSendResponse(false);
|
||||
return;
|
||||
}
|
||||
|
||||
_systems.TryGetEntitySystem<GameTicker>(out var ticker);
|
||||
int? roundId = ticker == null || ticker.RoundId == 0 ? null : ticker.RoundId;
|
||||
var playtime = (await _db.GetPlayTimes(targetUid)).Find(p => p.Tracker == PlayTimeTrackingShared.TrackerOverall)?.TimeSpent ?? TimeSpan.Zero;
|
||||
|
||||
var player = locatedPlayer.UserId;
|
||||
var banDef = new ServerRoleBanDef(
|
||||
null,
|
||||
targetUid,
|
||||
addressRange,
|
||||
targetHWid,
|
||||
DateTimeOffset.Now,
|
||||
expires,
|
||||
roundId,
|
||||
playtime,
|
||||
reason,
|
||||
NoteSeverity.High,
|
||||
player,
|
||||
null,
|
||||
role,
|
||||
serverName);
|
||||
|
||||
if (!await AddRoleBan(banDef))
|
||||
{
|
||||
UtkaSendResponse(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var banId = await UtkaGetBanId(reason, role, targetUid);
|
||||
|
||||
UtkaSendJobBanEvent(admin, target, minutes, job, isGlobalBan, reason, banId);
|
||||
UtkaSendResponse(true);
|
||||
|
||||
SendRoleBans(located);
|
||||
}
|
||||
//WD end
|
||||
|
||||
public HashSet<string>? GetJobBans(NetUserId playerUserId)
|
||||
{
|
||||
if (!_cachedRoleBans.TryGetValue(playerUserId, out var roleBans))
|
||||
return null;
|
||||
return roleBans
|
||||
.Where(ban => ban.Role.StartsWith(JobPrefix, StringComparison.Ordinal))
|
||||
.Select(ban => ban.Role[JobPrefix.Length..])
|
||||
.ToHashSet();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
private async void CreateRoleBan(IConsoleShell shell, string target, string role, string reason, uint minutes, bool isGlobalBan, string? job = null) // WD edit
|
||||
{
|
||||
var located = await _playerLocator.LookupIdByNameOrIdAsync(target);
|
||||
if (located == null)
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-roleban-name-parse"));
|
||||
return;
|
||||
}
|
||||
|
||||
var targetUid = located.UserId;
|
||||
var targetHWid = located.LastHWId;
|
||||
var targetAddress = located.LastAddress;
|
||||
|
||||
DateTimeOffset? expires = null;
|
||||
if (minutes > 0)
|
||||
{
|
||||
expires = DateTimeOffset.Now + TimeSpan.FromMinutes(minutes);
|
||||
}
|
||||
|
||||
(IPAddress, int)? addressRange = null;
|
||||
if (targetAddress != null)
|
||||
{
|
||||
if (targetAddress.IsIPv4MappedToIPv6)
|
||||
targetAddress = targetAddress.MapToIPv4();
|
||||
|
||||
// Ban /64 for IPv4, /32 for IPv4.
|
||||
var cidr = targetAddress.AddressFamily == AddressFamily.InterNetworkV6 ? 64 : 32;
|
||||
addressRange = (targetAddress, cidr);
|
||||
}
|
||||
|
||||
var cfg = UnsafePseudoIoC.ConfigurationManager;
|
||||
var serverName = cfg.GetCVar(CCVars.AdminLogsServerName);
|
||||
|
||||
if (isGlobalBan)
|
||||
{
|
||||
serverName = "unknown";
|
||||
}
|
||||
|
||||
_systems.TryGetEntitySystem<GameTicker>(out var ticker);
|
||||
int? roundId = ticker == null || ticker.RoundId == 0 ? null : ticker.RoundId;
|
||||
var playtime = (await _db.GetPlayTimes(targetUid)).Find(p => p.Tracker == PlayTimeTrackingShared.TrackerOverall)?.TimeSpent ?? TimeSpan.Zero;
|
||||
|
||||
var player = shell.Player;
|
||||
var banDef = new ServerRoleBanDef(
|
||||
null,
|
||||
targetUid,
|
||||
addressRange,
|
||||
targetHWid,
|
||||
DateTimeOffset.Now,
|
||||
expires,
|
||||
roundId,
|
||||
playtime,
|
||||
reason,
|
||||
NoteSeverity.High,
|
||||
player?.UserId,
|
||||
null,
|
||||
role,
|
||||
serverName);
|
||||
|
||||
if (!await AddRoleBan(banDef))
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("cmd-roleban-existing", ("target", target), ("role", role)));
|
||||
return;
|
||||
}
|
||||
|
||||
var length = expires == null ? Loc.GetString("cmd-roleban-inf") : Loc.GetString("cmd-roleban-until", ("expires", expires));
|
||||
shell.WriteLine(Loc.GetString("cmd-roleban-success", ("target", target), ("role", role), ("reason", reason), ("length", length), ("server", serverName)));
|
||||
|
||||
// WD start
|
||||
var banId = await UtkaGetBanId(reason, role, targetUid);
|
||||
|
||||
if (job != null)
|
||||
UtkaSendJobBanEvent(shell.Player!.Name, target, minutes, job, isGlobalBan, reason, banId);
|
||||
//WD end
|
||||
}
|
||||
#endregion
|
||||
|
||||
//WD start
|
||||
private void UtkaSendResponse(bool banned)
|
||||
{
|
||||
var utkaBanned = new UtkaJobBanResponse()
|
||||
{
|
||||
Banned = banned
|
||||
};
|
||||
|
||||
_utkaSockets.SendMessageToAll(utkaBanned);
|
||||
}
|
||||
|
||||
private async void UtkaSendJobBanEvent(string ackey, string ckey, uint duration, string job, bool global,
|
||||
string reason, int banId)
|
||||
{
|
||||
if (job.Contains("Job:"))
|
||||
{
|
||||
job = job.Replace("Job:", "");
|
||||
}
|
||||
|
||||
var utkaBanned = new UtkaBannedEvent()
|
||||
{
|
||||
ACkey = ackey,
|
||||
Ckey = ckey,
|
||||
Duration = duration,
|
||||
Bantype = job,
|
||||
Global = global,
|
||||
Reason = reason,
|
||||
Rid = EntitySystem.Get<GameTicker>().RoundId,
|
||||
BanId = banId
|
||||
};
|
||||
|
||||
_utkaSockets.SendMessageToAll(utkaBanned);
|
||||
_entMan.EventBus.RaiseEvent(EventSource.Local, utkaBanned);
|
||||
}
|
||||
|
||||
private async Task<int> UtkaGetBanId(string reason, string role, NetUserId targetUid)
|
||||
{
|
||||
var banId = 0;
|
||||
var banList = await _db.GetServerRoleBansAsync(null, targetUid, null);
|
||||
|
||||
foreach (var ban in banList)
|
||||
{
|
||||
if (ban.Reason == reason)
|
||||
{
|
||||
if (ban.Role == role && ban.Id != null)
|
||||
{
|
||||
banId = ban.Id.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return banId;
|
||||
}
|
||||
//WD end
|
||||
}
|
||||
@@ -8,7 +8,7 @@ using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.UtkaIntegration;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Mind;
|
||||
@@ -34,7 +34,7 @@ namespace Content.Server.Administration.Systems
|
||||
[Dependency] private readonly IPlayerLocator _playerLocator = default!;
|
||||
[Dependency] private readonly GameTicker _gameTicker = default!;
|
||||
[Dependency] private readonly SharedMindSystem _minds = default!;
|
||||
[Dependency] private readonly UtkaTCPWrapper _utkaSockets = default!; // WD
|
||||
[Dependency] private readonly PandaWebManager _pandaWeb = default!; // WD
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
private readonly HttpClient _httpClient = new();
|
||||
@@ -658,7 +658,7 @@ namespace Content.Server.Administration.Systems
|
||||
Entity = entity
|
||||
};
|
||||
|
||||
_utkaSockets.SendMessageToAll(utkaAhelpEvent);
|
||||
_pandaWeb.SendBotMessage(utkaAhelpEvent);
|
||||
}
|
||||
//WD-EDIT
|
||||
}
|
||||
|
||||
@@ -6,9 +6,8 @@ using Content.Server.Administration.Logs;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.Administration.Systems;
|
||||
using Content.Server.MoMMI;
|
||||
using Content.Server.Players;
|
||||
using Content.Server.Preferences.Managers;
|
||||
using Content.Server.UtkaIntegration;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
using Content.Server.White.Reputation;
|
||||
using Content.Server.White.Sponsors;
|
||||
using Content.Shared.Administration;
|
||||
@@ -55,7 +54,7 @@ namespace Content.Server.Chat.Managers
|
||||
|
||||
/// WD-EDIT
|
||||
[Dependency] private readonly SponsorsManager _sponsorsManager = default!;
|
||||
[Dependency] private readonly UtkaTCPWrapper _utkaSocketWrapper = default!;
|
||||
[Dependency] private readonly PandaWebManager _pandaWeb = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly ReputationManager _repManager = default!;
|
||||
[Dependency] private readonly ITaskManager _taskManager = default!;
|
||||
@@ -210,15 +209,6 @@ namespace Content.Server.Chat.Managers
|
||||
("playerName", sender), ("message", FormattedMessage.EscapeText(message)));
|
||||
|
||||
ChatMessageToMany(ChatChannel.Admin, message, wrappedMessage, EntityUid.Invalid, false, false, admins.Select(p => p.ConnectedClient));
|
||||
|
||||
var asayEventMessage = new UtkaChatEventMessage()
|
||||
{
|
||||
Command = "asay",
|
||||
Ckey = sender,
|
||||
Message = message
|
||||
};
|
||||
|
||||
_utkaSocketWrapper.SendMessageToAll(asayEventMessage);
|
||||
}
|
||||
|
||||
public bool TrySendNewMessage(ICommonSession session, string newMessage, bool checkLength = false)
|
||||
@@ -348,7 +338,7 @@ namespace Content.Server.Chat.Managers
|
||||
Message = message,
|
||||
};
|
||||
|
||||
_utkaSocketWrapper.SendMessageToAll(toUtkaMessage);
|
||||
_pandaWeb.SendBotMessage(toUtkaMessage);
|
||||
//WD-EDIT
|
||||
}
|
||||
|
||||
@@ -391,7 +381,7 @@ namespace Content.Server.Chat.Managers
|
||||
Message = message
|
||||
};
|
||||
|
||||
_utkaSocketWrapper.SendMessageToAll(asayEventMessage);
|
||||
_pandaWeb.SendBotMessage(asayEventMessage);
|
||||
//WD-EDIT
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ using Content.Server.Popups;
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Server.Station.Components;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Server.UtkaIntegration;
|
||||
using Content.Server.White.AspectsSystem.Aspects.Components;
|
||||
using Content.Server.White.Other.Speech;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
@@ -67,7 +67,7 @@ public sealed partial class ChatSystem : SharedChatSystem
|
||||
[Dependency] private readonly GameTicker _gameTicker = default!; // WD
|
||||
|
||||
//WD-EDIT
|
||||
[Dependency] private readonly UtkaTCPWrapper _utkaSockets = default!;
|
||||
[Dependency] private readonly PandaWebManager _pandaWeb = default!;
|
||||
//WD-EDIT
|
||||
|
||||
public const int VoiceRange = 10; // how far voice goes in world units
|
||||
@@ -648,7 +648,7 @@ public sealed partial class ChatSystem : SharedChatSystem
|
||||
CharacterName = MetaData(source).EntityName
|
||||
};
|
||||
|
||||
_utkaSockets.SendMessageToAll(utkaEmoteEvent);
|
||||
_pandaWeb.SendBotMessage(utkaEmoteEvent);
|
||||
|
||||
//WD-EDIT
|
||||
}
|
||||
|
||||
@@ -29,11 +29,10 @@ using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Content.Server.UtkaIntegration;
|
||||
using Content.Server.White;
|
||||
using Content.Server.White.JoinQueue;
|
||||
using Content.Server.White.Jukebox;
|
||||
using Content.Server.White.Reputation;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
using Content.Server.White.Sponsors;
|
||||
using Content.Server.White.Stalin;
|
||||
using Content.Server.White.TTS;
|
||||
@@ -119,6 +118,8 @@ namespace Content.Server.Entry
|
||||
IoCManager.Resolve<StalinManager>().Initialize();
|
||||
IoCManager.Resolve<ServerJukeboxSongsSyncManager>().Initialize();
|
||||
IoCManager.Resolve<SalusManager>().Initialize();
|
||||
IoCManager.Resolve<PandaStatusHost>().Start();
|
||||
IoCManager.Resolve<PandaWebManager>().Initialize();
|
||||
//WD-EDIT
|
||||
|
||||
_voteManager.Initialize();
|
||||
@@ -157,12 +158,6 @@ namespace Content.Server.Entry
|
||||
IoCManager.Resolve<IGameMapManager>().Initialize();
|
||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<GameTicker>().PostInitialize();
|
||||
IoCManager.Resolve<IBanManager>().Initialize();
|
||||
|
||||
//WD-EDIT
|
||||
IoCManager.Resolve<UtkaTCPWrapper>().Initialize();
|
||||
UtkaTCPServer.RegisterCommands();
|
||||
//WD-EDIT
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,13 +20,10 @@ using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
using Content.Server.UtkaIntegration;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
using Content.Server.White.Reputation;
|
||||
using Content.Server.White.Stalin;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.White;
|
||||
using Robust.Shared.Asynchronous;
|
||||
|
||||
namespace Content.Server.GameTicking
|
||||
{
|
||||
@@ -36,7 +33,7 @@ namespace Content.Server.GameTicking
|
||||
[Dependency] private readonly ITaskManager _taskManager = default!;
|
||||
|
||||
//WD-EDIT
|
||||
[Dependency] private readonly UtkaTCPWrapper _utkaSocketWrapper = default!;
|
||||
[Dependency] private readonly PandaWebManager _pandaWeb = default!;
|
||||
[Dependency] private readonly StalinManager _stalinManager = default!;
|
||||
[Dependency] private readonly ReputationSystem _repSys = default!;
|
||||
//WD-EDIT
|
||||
@@ -570,7 +567,7 @@ namespace Content.Server.GameTicking
|
||||
Message = status
|
||||
};
|
||||
|
||||
_utkaSocketWrapper.SendMessageToAll(utkaRoundStatusEvent);
|
||||
_pandaWeb.SendBotMessage(utkaRoundStatusEvent);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,10 @@ using Content.Server.ServerInfo;
|
||||
using Content.Server.ServerUpdates;
|
||||
using Content.Server.Voting.Managers;
|
||||
using Content.Server.Worldgen.Tools;
|
||||
using Content.Server.UtkaIntegration;
|
||||
using Content.Server.White;
|
||||
using Content.Server.White.JoinQueue;
|
||||
using Content.Server.White.Jukebox;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
using Content.Server.White.Reputation;
|
||||
using Content.Server.White.Sponsors;
|
||||
using Content.Server.White.Stalin;
|
||||
@@ -71,12 +71,13 @@ namespace Content.Server.IoC
|
||||
// WD-EDIT
|
||||
IoCManager.Register<SponsorsManager>();
|
||||
IoCManager.Register<JoinQueueManager>();
|
||||
IoCManager.Register<UtkaTCPWrapper>();
|
||||
IoCManager.Register<TTSManager>();
|
||||
IoCManager.Register<StalinManager>();
|
||||
IoCManager.Register<ServerJukeboxSongsSyncManager>();
|
||||
IoCManager.Register<SalusManager>();
|
||||
IoCManager.Register<ReputationManager>();
|
||||
IoCManager.Register<PandaStatusHost>();
|
||||
IoCManager.Register<PandaWebManager>();
|
||||
// WD-EDIT
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ using Content.Server.Shuttles.Components;
|
||||
using Content.Server.Shuttles.Systems;
|
||||
using Content.Server.Station.Components;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Server.UtkaIntegration;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.GameTicking;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
@@ -47,7 +47,7 @@ namespace Content.Server.RoundEnd
|
||||
[Dependency] private readonly StationSystem _stationSystem = default!;
|
||||
|
||||
//WD-EDIT
|
||||
[Dependency] private readonly UtkaTCPWrapper _utkaSocketWrapper = default!;
|
||||
[Dependency] private readonly PandaWebManager _pandaWeb = default!;
|
||||
//WD-EDIT
|
||||
|
||||
public TimeSpan DefaultCooldownDuration { get; set; } = TimeSpan.FromSeconds(30);
|
||||
@@ -307,7 +307,7 @@ namespace Content.Server.RoundEnd
|
||||
Message = status
|
||||
};
|
||||
|
||||
_utkaSocketWrapper.SendMessageToAll(utkaRoundStatusEvent);
|
||||
_pandaWeb.SendBotMessage(utkaRoundStatusEvent);
|
||||
}
|
||||
//WD-EDIT
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ using Content.Server.Shuttles.Components;
|
||||
using Content.Server.Shuttles.Events;
|
||||
using Content.Server.Station.Components;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Server.UtkaIntegration;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
@@ -64,7 +64,7 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
|
||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||
|
||||
//WD-EDIT
|
||||
[Dependency] private readonly UtkaTCPWrapper _utkaSocketWrapper = default!;
|
||||
[Dependency] private readonly PandaWebManager _pandaWeb = default!;
|
||||
//WD-EDIT
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
@@ -584,6 +584,6 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
|
||||
Message = status
|
||||
};
|
||||
|
||||
_utkaSocketWrapper.SendMessageToAll(utkaRoundStatusEvent);
|
||||
_pandaWeb.SendBotMessage(utkaRoundStatusEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
using System.Net;
|
||||
using Content.Server.UtkaIntegration.TCP;
|
||||
|
||||
namespace Content.Server.UtkaIntegration;
|
||||
|
||||
public interface IUtkaCommand
|
||||
{
|
||||
string Name { get; }
|
||||
Type RequestMessageType { get; }
|
||||
public void Execute(UtkaTCPSession session, UtkaBaseMessage baseMessage);
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.UtkaIntegration.TCP;
|
||||
using Content.Shared.Chat;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.UtkaIntegration;
|
||||
|
||||
public sealed class UtkaAssayCommand : IUtkaCommand
|
||||
{
|
||||
public string Name => "asay";
|
||||
public Type RequestMessageType => typeof(UtkaAsayRequest);
|
||||
|
||||
public void Execute(UtkaTCPSession session, UtkaBaseMessage baseMessage)
|
||||
{
|
||||
if(baseMessage is not UtkaAsayRequest message) return;
|
||||
|
||||
var ckey = message.ACkey;
|
||||
|
||||
if(string.IsNullOrWhiteSpace(message.Message) || string.IsNullOrWhiteSpace(ckey)) return;
|
||||
|
||||
var chatManager = IoCManager.Resolve<IChatManager>();
|
||||
|
||||
chatManager.SendHookAdminChat(ckey, message.Message);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
using Content.Server.UtkaIntegration.TCP;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.White;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Server.UtkaIntegration;
|
||||
|
||||
public sealed class UtkaAuthenticationCommand : IUtkaCommand
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
[Dependency] private readonly UtkaTCPWrapper _utkaTcpWrapper = default!;
|
||||
|
||||
public string Name => "handshake";
|
||||
public Type RequestMessageType => typeof(UtkaHandshakeMessage);
|
||||
|
||||
public void Execute(UtkaTCPSession session, UtkaBaseMessage baseMessage)
|
||||
{
|
||||
if (baseMessage is not UtkaHandshakeMessage message)
|
||||
return;
|
||||
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(message.Key))
|
||||
{
|
||||
SendMessage(session, "key_missmatch");
|
||||
return;
|
||||
}
|
||||
|
||||
var key = _configurationManager.GetCVar(WhiteCVars.UtkaSocketKey);
|
||||
|
||||
if (key != message.Key)
|
||||
{
|
||||
SendMessage(session, "key_missmatch");
|
||||
return;
|
||||
}
|
||||
|
||||
if (session.Authenticated)
|
||||
{
|
||||
SendMessage(session, "already_authentificated");
|
||||
return;
|
||||
}
|
||||
|
||||
session.Authenticated = true;
|
||||
SendMessage(session, "handshake_accepted");
|
||||
}
|
||||
|
||||
private void SendMessage(UtkaTCPSession session, string message)
|
||||
{
|
||||
var response = new UtkaHandshakeMessage()
|
||||
{
|
||||
Key = _configurationManager.GetCVar(WhiteCVars.UtkaSocketKey),
|
||||
Message = message
|
||||
};
|
||||
|
||||
_utkaTcpWrapper.SendMessageToClient(session, response);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using Content.Server.Administration.Systems;
|
||||
using Content.Server.UtkaIntegration.TCP;
|
||||
using Robust.Server.Player;
|
||||
|
||||
namespace Content.Server.UtkaIntegration;
|
||||
|
||||
public sealed class UtkaPmCommand : IUtkaCommand
|
||||
{
|
||||
public string Name => "discord_pm";
|
||||
public Type RequestMessageType => typeof(UtkaPmRequest);
|
||||
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private UtkaTCPWrapper _utkaSocketWrapper = default!;
|
||||
|
||||
public void Execute(UtkaTCPSession session, UtkaBaseMessage baseMessage)
|
||||
{
|
||||
if(baseMessage is not UtkaPmRequest message) return;
|
||||
var _bwoink = EntitySystem.Get<BwoinkSystem>();
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
if(string.IsNullOrWhiteSpace(message.Message) || string.IsNullOrWhiteSpace(message.Sender) || string.IsNullOrWhiteSpace(message.Reciever)) return;
|
||||
|
||||
var toUtkaMessage = new UtkaPmResponse();
|
||||
if (!_playerManager.TryGetUserId(message.Reciever, out var reciever))
|
||||
{
|
||||
toUtkaMessage.Message = false;
|
||||
_utkaSocketWrapper.SendMessageToAll(toUtkaMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
_bwoink.SendUtkaBwoinkMessage(reciever, message.Sender, message.Message);
|
||||
|
||||
toUtkaMessage.Message = true;
|
||||
_utkaSocketWrapper.SendMessageToAll(toUtkaMessage);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.UtkaIntegration.TCP;
|
||||
|
||||
namespace Content.Server.UtkaIntegration;
|
||||
|
||||
public sealed class UtkaRestartRoundCommand : IUtkaCommand
|
||||
{
|
||||
[Dependency] private UtkaTCPWrapper _utkaSocket = default!;
|
||||
|
||||
public string Name => "restart_round";
|
||||
public Type RequestMessageType => typeof(UtkaRestartRoundRequest);
|
||||
public void Execute(UtkaTCPSession session, UtkaBaseMessage baseMessage)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
EntitySystem.Get<GameTicker>().RestartRound();
|
||||
|
||||
var response = new UtkaRestartRoundResponse()
|
||||
{
|
||||
Restarted = true
|
||||
};
|
||||
|
||||
_utkaSocket.SendMessageToAll(response);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using System.Net;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.UtkaIntegration.TCP;
|
||||
|
||||
namespace Content.Server.UtkaIntegration;
|
||||
|
||||
public sealed class UtkaSendOOCMessage : IUtkaCommand
|
||||
{
|
||||
public string Name => "ooc";
|
||||
public Type RequestMessageType => typeof(UtkaOOCRequest);
|
||||
|
||||
public void Execute(UtkaTCPSession session, UtkaBaseMessage baseMessage)
|
||||
{
|
||||
if (baseMessage is not UtkaOOCRequest message) return;
|
||||
if(string.IsNullOrWhiteSpace(message.Message) || string.IsNullOrWhiteSpace(message.CKey)) return;
|
||||
|
||||
|
||||
var chatSystem = IoCManager.Resolve<IChatManager>();
|
||||
chatSystem.SendHookOOC($"{message.CKey}", $"{message.Message}");
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.UtkaIntegration.TCP;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.White;
|
||||
using NetCoreServer;
|
||||
using Robust.Shared.Asynchronous;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Timing;
|
||||
using YamlDotNet.Core.Tokens;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
|
||||
namespace Content.Server.UtkaIntegration;
|
||||
|
||||
public sealed class UtkaTCPServer : TcpServer
|
||||
{
|
||||
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly ITaskManager _taskManager = default!;
|
||||
[Dependency] private readonly ITimerManager _timerManager = default!;
|
||||
|
||||
public static readonly ConcurrentDictionary<string, IUtkaCommand> Commands = new();
|
||||
private List<UtkaTCPSession> _authenticatedSessions = new();
|
||||
|
||||
private string? _key;
|
||||
|
||||
protected override TcpSession CreateSession() { return new UtkaTCPSession(this); }
|
||||
public UtkaTCPServer(IPAddress address, int port) : base(address, port)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_cfg.OnValueChanged(WhiteCVars.UtkaSocketKey, key => _key = key, true);
|
||||
OptionKeepAlive = true;
|
||||
}
|
||||
|
||||
public void SendMessageToAll(UtkaBaseMessage message)
|
||||
{
|
||||
foreach (var session in Sessions.Values.Cast<UtkaTCPSession>())
|
||||
{
|
||||
if(!session.Authenticated) continue;
|
||||
|
||||
var json = JsonSerializer.Serialize(message, message.GetType());
|
||||
|
||||
session.SendAsync(json + "&%^sep^%&");
|
||||
}
|
||||
}
|
||||
|
||||
public void SendMessageToClient(UtkaTCPSession session, UtkaBaseMessage message)
|
||||
{
|
||||
session.SendAsync(JsonSerializer.Serialize(message, message.GetType()) + "&%^sep^%&");
|
||||
}
|
||||
|
||||
protected override void OnConnected(TcpSession session)
|
||||
{
|
||||
var utkaSession = (UtkaTCPSession) session;
|
||||
var cancellationToken = new CancellationTokenSource();
|
||||
|
||||
utkaSession.OnMessageReceived += (sender, message) =>
|
||||
{
|
||||
ExecuteCommand(utkaSession, message);
|
||||
};
|
||||
|
||||
var autoDisconnectionTimer = new Timer(25000, false, () =>
|
||||
{
|
||||
if (!utkaSession.Authenticated)
|
||||
{
|
||||
utkaSession.Disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
_timerManager.AddTimer(autoDisconnectionTimer, cancellationToken.Token);
|
||||
}
|
||||
|
||||
protected override void OnDisconnecting(TcpSession session)
|
||||
{
|
||||
_authenticatedSessions.Remove((session as UtkaTCPSession)!);
|
||||
base.OnDisconnecting(session);
|
||||
}
|
||||
|
||||
protected override void OnError(SocketError error)
|
||||
{
|
||||
}
|
||||
private void ExecuteCommand(UtkaTCPSession session, UtkaBaseMessage fromUtkaMessage)
|
||||
{
|
||||
var command = fromUtkaMessage.Command!;
|
||||
|
||||
if (!Commands.ContainsKey(command))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_taskManager.RunOnMainThread(() => Commands[command].Execute(session, fromUtkaMessage));
|
||||
}
|
||||
|
||||
public static void RegisterCommands()
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var types = assembly.GetTypes();
|
||||
|
||||
var commands = types.Where(type => typeof(IUtkaCommand).IsAssignableFrom(type) && type.GetInterfaces().Contains(typeof(IUtkaCommand))).ToList();
|
||||
|
||||
foreach (var command in commands)
|
||||
{
|
||||
if (Activator.CreateInstance(command) is IUtkaCommand utkaCommand)
|
||||
{
|
||||
Commands[utkaCommand.Name] = utkaCommand;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using NetCoreServer;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Content.Server.UtkaIntegration.TCP;
|
||||
|
||||
public sealed class UtkaTCPSession : TcpSession
|
||||
{
|
||||
public event EventHandler<UtkaBaseMessage>? OnMessageReceived;
|
||||
private string BufferCahce = string.Empty;
|
||||
|
||||
public bool Authenticated { get; set; }
|
||||
|
||||
public UtkaTCPSession(TcpServer server) : base(server)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnReceived(byte[] buffer, long offset, long size)
|
||||
{
|
||||
BufferCahce += Encoding.UTF8.GetString(buffer, (int) offset, (int) size);
|
||||
|
||||
HandleCache();
|
||||
}
|
||||
|
||||
protected override void OnError(SocketError error)
|
||||
{
|
||||
SendAsync($"{error.ToString()}&%^sep^%&");
|
||||
base.OnError(error);
|
||||
}
|
||||
|
||||
protected override void OnConnected()
|
||||
{
|
||||
SendAsync("Utka sosal handshake&%^sep^%&");
|
||||
base.OnConnected();
|
||||
}
|
||||
|
||||
private bool ValidateMessage(string message, out UtkaBaseMessage? fromDiscordMessage)
|
||||
{
|
||||
fromDiscordMessage = null;
|
||||
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var commandName = JObject.Parse(message)["command"];
|
||||
if (commandName == null)
|
||||
return false;
|
||||
|
||||
var utkaCommand = UtkaTCPServer.Commands.Values.FirstOrDefault(x => x.Name == commandName.ToString());
|
||||
|
||||
if (utkaCommand == null)
|
||||
return false;
|
||||
|
||||
var messageType = utkaCommand.RequestMessageType;
|
||||
|
||||
try
|
||||
{
|
||||
fromDiscordMessage = JsonSerializer.Deserialize(message, messageType) as UtkaBaseMessage;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnDisconnected()
|
||||
{
|
||||
OnDisconnecting();
|
||||
Dispose();
|
||||
BufferCahce = string.Empty;
|
||||
}
|
||||
|
||||
private void HandleCache()
|
||||
{
|
||||
var handles = BufferCahce.Split("&%^sep^%&");
|
||||
|
||||
for (var i = 0; i < handles.Length; i++)
|
||||
{
|
||||
var handle = handles[i];
|
||||
|
||||
if (i + 1 == handles.Length && !BufferCahce.EndsWith("&%^sep^%&"))
|
||||
continue;
|
||||
|
||||
if (handle.Length == 0 || !handle.StartsWith("{") || !handle.EndsWith("}"))
|
||||
continue;
|
||||
|
||||
var pos = BufferCahce.IndexOf(handle);
|
||||
|
||||
BufferCahce = BufferCahce.Substring(0, pos) + BufferCahce.Substring(pos + handle.Length + "&%^sep^%&".Length);
|
||||
|
||||
if (!ValidateMessage(handle, out var message))
|
||||
{
|
||||
SendAsync("Validation fail&%^sep^%&");
|
||||
return;
|
||||
}
|
||||
|
||||
OnMessageReceived?.Invoke(this, message!);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
using System.Net;
|
||||
using Content.Server.UtkaIntegration.TCP;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.White;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Server.UtkaIntegration;
|
||||
|
||||
public sealed class UtkaTCPWrapper
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
private UtkaTCPServer _server = default!;
|
||||
private string _key = string.Empty;
|
||||
|
||||
private bool _initialized;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
if(_initialized) return;
|
||||
|
||||
_key = _cfg.GetCVar(WhiteCVars.UtkaSocketKey);
|
||||
|
||||
if (string.IsNullOrEmpty(_key))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var port = _cfg.GetCVar(CVars.NetPort) + 100;
|
||||
|
||||
try
|
||||
{
|
||||
_server = new UtkaTCPServer(IPAddress.Any, port);
|
||||
_server.Start();
|
||||
_initialized = true;
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_initialized = false;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void SendMessageToAll(UtkaBaseMessage message)
|
||||
{
|
||||
_server.SendMessageToAll(message);
|
||||
}
|
||||
|
||||
public void SendMessageToClient(UtkaTCPSession session, UtkaBaseMessage message)
|
||||
{
|
||||
_server.SendMessageToClient(session, message);
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
_server.Stop();
|
||||
_server.Multicast("Server shutting down.");
|
||||
_server.DisconnectAll();
|
||||
_server.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,15 @@
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.UtkaIntegration.TCP;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Content.Server.White.PandaSocket.Interfaces;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
|
||||
namespace Content.Server.UtkaIntegration;
|
||||
namespace Content.Server.White.PandaSocket.Commands;
|
||||
|
||||
public sealed class UtkaAdminWhoCommand : IUtkaCommand
|
||||
public sealed class PandaAdminWhoCommand : IPandaCommand
|
||||
{
|
||||
public string Name => "adminwho";
|
||||
public Type RequestMessageType => typeof(UtkaAdminWhoRequest);
|
||||
|
||||
[Dependency] private readonly UtkaTCPWrapper _utkaSocketWrapper = default!;
|
||||
|
||||
public void Execute(UtkaTCPSession session, UtkaBaseMessage baseMessage)
|
||||
public void Execute(IPandaStatusHandlerContext context, PandaBaseMessage baseMessage)
|
||||
{
|
||||
if(baseMessage is not UtkaAdminWhoRequest message) return;
|
||||
IoCManager.InjectDependencies(this);
|
||||
@@ -30,6 +22,11 @@ public sealed class UtkaAdminWhoCommand : IUtkaCommand
|
||||
|
||||
foreach (var admin in admins)
|
||||
{
|
||||
var adminData = adminManager.GetAdminData(admin)!;
|
||||
|
||||
if (adminData.Stealth)
|
||||
continue;
|
||||
|
||||
adminsList.Add(admin.Name);
|
||||
}
|
||||
|
||||
@@ -38,6 +35,11 @@ public sealed class UtkaAdminWhoCommand : IUtkaCommand
|
||||
Admins = adminsList
|
||||
};
|
||||
|
||||
_utkaSocketWrapper.SendMessageToAll(toUtkaMessage);
|
||||
Response(context, toUtkaMessage);
|
||||
}
|
||||
|
||||
public void Response(IPandaStatusHandlerContext context, PandaBaseMessage? message = null)
|
||||
{
|
||||
context.RespondJsonAsync(message!);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.Net;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.White.PandaSocket.Interfaces;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
|
||||
namespace Content.Server.White.PandaSocket.Commands;
|
||||
|
||||
public sealed class PandaAsayCommand : IPandaCommand
|
||||
{
|
||||
public string Name => "asay";
|
||||
public Type RequestMessageType => typeof(UtkaAsayRequest);
|
||||
public void Execute(IPandaStatusHandlerContext context, PandaBaseMessage baseMessage)
|
||||
{
|
||||
if (baseMessage is not UtkaAsayRequest message) return;
|
||||
if(string.IsNullOrWhiteSpace(message.Message) || string.IsNullOrWhiteSpace(message.ACkey)) return;
|
||||
|
||||
var ckey = message.ACkey;
|
||||
var chatManager = IoCManager.Resolve<IChatManager>();
|
||||
|
||||
chatManager.SendHookAdminChat(ckey, message.Message);
|
||||
|
||||
Response(context);
|
||||
}
|
||||
|
||||
public void Response(IPandaStatusHandlerContext context, PandaBaseMessage? message = null)
|
||||
{
|
||||
context.RespondAsync("Success", HttpStatusCode.OK);
|
||||
}
|
||||
}
|
||||
@@ -3,27 +3,28 @@ using System.Net.Sockets;
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.Database;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.UtkaIntegration.TCP;
|
||||
using Content.Server.White.PandaSocket.Interfaces;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Players.PlayTimeTracking;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Server.UtkaIntegration;
|
||||
namespace Content.Server.White.PandaSocket.Commands;
|
||||
|
||||
public sealed class UtkaBanCommand : IUtkaCommand
|
||||
public sealed class PandaBanCommand : IPandaCommand
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private UtkaTCPWrapper _utkaSocketWrapper = default!;
|
||||
[Dependency] private readonly IServerDbManager _db = default!;
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
[Dependency] private readonly PandaWebManager _pandaWeb = default!;
|
||||
|
||||
private const ILocalizationManager LocalizationManager = default!;
|
||||
|
||||
public string Name => "ban";
|
||||
public Type RequestMessageType => typeof(UtkaBanRequest);
|
||||
public async void Execute(UtkaTCPSession session, UtkaBaseMessage baseMessage)
|
||||
public async void Execute(IPandaStatusHandlerContext context, PandaBaseMessage baseMessage)
|
||||
{
|
||||
if (baseMessage is not UtkaBanRequest message) return;
|
||||
|
||||
@@ -34,7 +35,7 @@ public sealed class UtkaBanCommand : IUtkaCommand
|
||||
var locatedPlayer = await locator.LookupIdByNameOrIdAsync(message.ACkey!);
|
||||
if (locatedPlayer == null)
|
||||
{
|
||||
UtkaSendResponse(false);
|
||||
UtkaSendResponse(context, false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -48,7 +49,7 @@ public sealed class UtkaBanCommand : IUtkaCommand
|
||||
var located = await locator.LookupIdByNameOrIdAsync(target);
|
||||
if (located == null)
|
||||
{
|
||||
UtkaSendResponse(false);
|
||||
UtkaSendResponse(context, false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -58,7 +59,7 @@ public sealed class UtkaBanCommand : IUtkaCommand
|
||||
|
||||
if (player == targetUid)
|
||||
{
|
||||
UtkaSendResponse(false);
|
||||
UtkaSendResponse(context, false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -105,7 +106,7 @@ public sealed class UtkaBanCommand : IUtkaCommand
|
||||
null,
|
||||
serverName);
|
||||
|
||||
UtkaSendResponse(true);
|
||||
UtkaSendResponse(context, true);
|
||||
|
||||
await _db.AddServerBanAsync(banDef);
|
||||
|
||||
@@ -129,17 +130,23 @@ public sealed class UtkaBanCommand : IUtkaCommand
|
||||
Rid = EntitySystem.Get<GameTicker>().RoundId,
|
||||
BanId = banId
|
||||
};
|
||||
_utkaSocketWrapper.SendMessageToAll(utkaBanned);
|
||||
|
||||
_pandaWeb.SendBotMessage(utkaBanned);
|
||||
_entMan.EventBus.RaiseEvent(EventSource.Local, utkaBanned);
|
||||
}
|
||||
|
||||
private void UtkaSendResponse(bool banned)
|
||||
public void Response(IPandaStatusHandlerContext context, PandaBaseMessage? message = null)
|
||||
{
|
||||
context.RespondJsonAsync(message!);
|
||||
}
|
||||
|
||||
private void UtkaSendResponse(IPandaStatusHandlerContext context, bool banned)
|
||||
{
|
||||
var utkaResponse = new UtkaBanResponse()
|
||||
{
|
||||
Banned = banned
|
||||
};
|
||||
|
||||
_utkaSocketWrapper.SendMessageToAll(utkaResponse);
|
||||
Response(context, utkaResponse);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,22 @@
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.UtkaIntegration.TCP;
|
||||
using Content.Server.White.PandaSocket.Interfaces;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.UtkaIntegration;
|
||||
namespace Content.Server.White.PandaSocket.Commands;
|
||||
|
||||
public sealed class UtkaJobBanCommand : IUtkaCommand
|
||||
public sealed class PandaJobBanCommand : IPandaCommand
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public string Name => "jobban";
|
||||
public Type RequestMessageType => typeof(UtkaJobBanRequest);
|
||||
public void Execute(UtkaTCPSession session, UtkaBaseMessage baseMessage)
|
||||
public void Execute(IPandaStatusHandlerContext context, PandaBaseMessage baseMessage)
|
||||
{
|
||||
if (baseMessage is not UtkaJobBanRequest message) return;
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
if (baseMessage is not UtkaJobBanRequest message) return;
|
||||
var target = message.Ckey!;
|
||||
var job = message.Type!;
|
||||
var reason = message.Reason!;
|
||||
@@ -23,14 +24,16 @@ public sealed class UtkaJobBanCommand : IUtkaCommand
|
||||
var isGlobalBan = (bool) message.Global!;
|
||||
var admin = message.ACkey!;
|
||||
|
||||
var roleBanManager = IoCManager.Resolve<RoleBanManager>();
|
||||
var banManager = IoCManager.Resolve<IBanManager>();
|
||||
|
||||
if (_prototypeManager.TryIndex<DepartmentPrototype>(job, out var departmentProto))
|
||||
roleBanManager.UtkaCreateDepartmentBan(admin, target, departmentProto, reason, minutes, isGlobalBan);
|
||||
banManager.UtkaCreateDepartmentBan(admin, target, departmentProto, reason, minutes, isGlobalBan, context);
|
||||
|
||||
else
|
||||
roleBanManager.UtkaCreateJobBan(admin, target, job, reason, minutes, isGlobalBan);
|
||||
banManager.UtkaCreateJobBan(admin, target, job, reason, minutes, isGlobalBan, context);
|
||||
}
|
||||
|
||||
public void Response(IPandaStatusHandlerContext context, PandaBaseMessage? message = null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
48
Content.Server/White/PandaSocket/Commands/PandaPmCommand.cs
Normal file
48
Content.Server/White/PandaSocket/Commands/PandaPmCommand.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System.Net;
|
||||
using Content.Server.Administration.Systems;
|
||||
using Content.Server.White.PandaSocket.Interfaces;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
using Robust.Server.Player;
|
||||
|
||||
namespace Content.Server.White.PandaSocket.Commands;
|
||||
|
||||
public sealed class PandaPmCommand : IPandaCommand
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
public string Name => "discordpm";
|
||||
public Type RequestMessageType => typeof(UtkaPmRequest);
|
||||
public void Execute(IPandaStatusHandlerContext context, PandaBaseMessage baseMessage)
|
||||
{
|
||||
if (baseMessage is not UtkaPmRequest message) return;
|
||||
var _bwoink = EntitySystem.Get<BwoinkSystem>();
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
var toUtkaMessage = new UtkaPmResponse();
|
||||
|
||||
if(string.IsNullOrWhiteSpace(message.Message) || string.IsNullOrWhiteSpace(message.Sender) || string.IsNullOrWhiteSpace(message.Receiver))
|
||||
{
|
||||
toUtkaMessage.Message = false;
|
||||
Response(context, toUtkaMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_playerManager.TryGetUserId(message.Receiver, out var reciever))
|
||||
{
|
||||
toUtkaMessage.Message = false;
|
||||
Response(context, toUtkaMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
_bwoink.SendUtkaBwoinkMessage(reciever, message.Sender, message.Message);
|
||||
|
||||
toUtkaMessage.Message = true;
|
||||
|
||||
Response(context, toUtkaMessage);
|
||||
}
|
||||
|
||||
public void Response(IPandaStatusHandlerContext context, PandaBaseMessage? message = null)
|
||||
{
|
||||
context.RespondJsonAsync(message!);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.White.PandaSocket.Interfaces;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
|
||||
namespace Content.Server.White.PandaSocket.Commands;
|
||||
|
||||
public sealed class PandaRestartRoundCommand : IPandaCommand
|
||||
{
|
||||
public string Name => "restartround";
|
||||
public Type RequestMessageType => typeof(UtkaRestartRoundRequest);
|
||||
public void Execute(IPandaStatusHandlerContext context, PandaBaseMessage baseMessage)
|
||||
{
|
||||
if (baseMessage is not UtkaRestartRoundRequest message) return;
|
||||
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
EntitySystem.Get<GameTicker>().RestartRound();
|
||||
|
||||
var response = new UtkaRestartRoundResponse()
|
||||
{
|
||||
Restarted = true
|
||||
};
|
||||
|
||||
Response(context, response);
|
||||
}
|
||||
|
||||
public void Response(IPandaStatusHandlerContext context, PandaBaseMessage? message = null)
|
||||
{
|
||||
context.RespondJsonAsync(message!);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.Net;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.White.PandaSocket.Interfaces;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
|
||||
namespace Content.Server.White.PandaSocket.Commands;
|
||||
|
||||
public sealed class PandaSendOOCCommand : IPandaCommand
|
||||
{
|
||||
public string Name => "ooc";
|
||||
public Type RequestMessageType => typeof(UtkaOOCRequest);
|
||||
public async void Execute(IPandaStatusHandlerContext context, PandaBaseMessage baseMessage)
|
||||
{
|
||||
if (baseMessage is not UtkaOOCRequest message) return;
|
||||
if(string.IsNullOrWhiteSpace(message.Message) || string.IsNullOrWhiteSpace(message.CKey)) return;
|
||||
|
||||
var chatSystem = IoCManager.Resolve<IChatManager>();
|
||||
chatSystem.SendHookOOC($"{message.CKey}", $"{message.Message}");
|
||||
|
||||
Response(context);
|
||||
}
|
||||
|
||||
public async void Response(IPandaStatusHandlerContext context, PandaBaseMessage? message = null)
|
||||
{
|
||||
await context.RespondAsync("Success", HttpStatusCode.OK);
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,36 @@
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.AlertLevel;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Maps;
|
||||
using Content.Server.RoundEnd;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Server.UtkaIntegration.TCP;
|
||||
using Content.Server.White.PandaSocket.Interfaces;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.UtkaIntegration;
|
||||
namespace Content.Server.White.PandaSocket.Commands;
|
||||
|
||||
public sealed class UtkaStatusCommand : IUtkaCommand
|
||||
public sealed class PandaStatusCommand : IPandaCommand
|
||||
{
|
||||
public string Name => "status";
|
||||
public Type RequestMessageType => typeof(UtkaStatusRequsets);
|
||||
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IAdminManager _adminManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly UtkaTCPWrapper _utkaSocketWrapper = default!;
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
[Dependency] private readonly IGameMapManager _gameMapManager = default!;
|
||||
|
||||
public void Execute(UtkaTCPSession session, UtkaBaseMessage baseMessage)
|
||||
public string Name => "status";
|
||||
public Type RequestMessageType => typeof(UtkaStatusRequest);
|
||||
public void Execute(IPandaStatusHandlerContext context, PandaBaseMessage baseMessage)
|
||||
{
|
||||
if(baseMessage is not UtkaStatusRequsets message) return;
|
||||
if (baseMessage is not UtkaStatusRequest message) return;
|
||||
|
||||
var _gameTicker = EntitySystem.Get<GameTicker>();
|
||||
var _roundEndSystem = EntitySystem.Get<RoundEndSystem>();
|
||||
var _station = EntitySystem.Get<StationSystem>();
|
||||
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
|
||||
@@ -39,16 +38,9 @@ public sealed class UtkaStatusCommand : IUtkaCommand
|
||||
|
||||
var admins = _adminManager.ActiveAdmins.Select(x => x.Name).ToList().Count;
|
||||
|
||||
string shuttleData = string.Empty;
|
||||
var shuttleData = string.Empty;
|
||||
|
||||
if (_roundEndSystem.ExpectedCountdownEnd == null)
|
||||
{
|
||||
shuttleData = "idle";
|
||||
}
|
||||
else
|
||||
{
|
||||
shuttleData = "called";
|
||||
}
|
||||
shuttleData = _roundEndSystem.ExpectedCountdownEnd == null ? "idle" : "called";
|
||||
|
||||
var roundDuration = _gameTicker.RoundDuration().TotalSeconds;
|
||||
|
||||
@@ -61,7 +53,7 @@ public sealed class UtkaStatusCommand : IUtkaCommand
|
||||
continue;
|
||||
}
|
||||
|
||||
if (alert is { CurrentLevel: { } })
|
||||
if (alert is { CurrentLevel: not null })
|
||||
{
|
||||
stationCode = alert.CurrentLevel;
|
||||
|
||||
@@ -80,6 +72,11 @@ public sealed class UtkaStatusCommand : IUtkaCommand
|
||||
StationCode = stationCode
|
||||
};
|
||||
|
||||
_utkaSocketWrapper.SendMessageToAll(toUtkaMessage);
|
||||
Response(context, toUtkaMessage);
|
||||
}
|
||||
|
||||
public void Response(IPandaStatusHandlerContext context, PandaBaseMessage? message = null)
|
||||
{
|
||||
context.RespondJsonAsync(message!);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.Database;
|
||||
using Content.Server.UtkaIntegration.TCP;
|
||||
using Content.Server.White.PandaSocket.Interfaces;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
|
||||
namespace Content.Server.UtkaIntegration;
|
||||
namespace Content.Server.White.PandaSocket.Commands;
|
||||
|
||||
public sealed class UtkaUnJobBanCommand : IUtkaCommand
|
||||
public sealed class PandaUnJobBanCommand : IPandaCommand
|
||||
{
|
||||
[Dependency] private UtkaTCPWrapper _utkaSocketWrapper = default!;
|
||||
|
||||
public string Name => "unjobban";
|
||||
public Type RequestMessageType => typeof(UtkaUnJobBanRequest);
|
||||
public async void Execute(UtkaTCPSession session, UtkaBaseMessage baseMessage)
|
||||
public async void Execute(IPandaStatusHandlerContext context, PandaBaseMessage baseMessage)
|
||||
{
|
||||
if (baseMessage is not UtkaUnJobBanRequest message) return;
|
||||
|
||||
@@ -21,7 +20,7 @@ public sealed class UtkaUnJobBanCommand : IUtkaCommand
|
||||
var located = await locator.LookupIdByNameOrIdAsync(message.ACkey!);
|
||||
if (located == null)
|
||||
{
|
||||
UtkaSendResponse(false);
|
||||
UtkaSendResponse(false, context);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -30,29 +29,34 @@ public sealed class UtkaUnJobBanCommand : IUtkaCommand
|
||||
var ban = await dbMan.GetServerRoleBanAsync(message.Bid!.Value);
|
||||
if (ban == null || ban.Unban != null)
|
||||
{
|
||||
UtkaSendResponse(false);
|
||||
UtkaSendResponse(false, context);
|
||||
return;
|
||||
}
|
||||
|
||||
var adminData = await dbMan.GetAdminDataForAsync(player);
|
||||
if (adminData?.AdminRank == null || ban.ServerName != "unknown" && adminData.AdminServer is not (null or "unknown") && adminData.AdminServer != ban.ServerName)
|
||||
{
|
||||
UtkaSendResponse(false);
|
||||
UtkaSendResponse(false, context);
|
||||
return;
|
||||
}
|
||||
|
||||
await dbMan.AddServerRoleUnbanAsync(new ServerRoleUnbanDef(message.Bid!.Value, player, DateTimeOffset.Now));
|
||||
|
||||
UtkaSendResponse(true);
|
||||
UtkaSendResponse(true, context);
|
||||
}
|
||||
|
||||
private void UtkaSendResponse(bool unbanned)
|
||||
public void Response(IPandaStatusHandlerContext context, PandaBaseMessage? message = null)
|
||||
{
|
||||
context.RespondJsonAsync(message!);
|
||||
}
|
||||
|
||||
private void UtkaSendResponse(bool unbanned, IPandaStatusHandlerContext context)
|
||||
{
|
||||
var utkaResponse = new UtkaUnJobBanResponse()
|
||||
{
|
||||
Unbanned = unbanned
|
||||
};
|
||||
|
||||
_utkaSocketWrapper.SendMessageToAll(utkaResponse);
|
||||
Response(context, utkaResponse);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.Database;
|
||||
using Content.Server.UtkaIntegration.TCP;
|
||||
using Content.Server.White.PandaSocket.Interfaces;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
|
||||
namespace Content.Server.UtkaIntegration;
|
||||
namespace Content.Server.White.PandaSocket.Commands;
|
||||
|
||||
public sealed class UtkaUnbanCommand : IUtkaCommand
|
||||
public sealed class PandaUnbanCommand : IPandaCommand
|
||||
{
|
||||
[Dependency] private UtkaTCPWrapper _utkaSocketWrapper = default!;
|
||||
|
||||
public string Name => "unban";
|
||||
public Type RequestMessageType => typeof(UtkaUnbanRequest);
|
||||
public async void Execute(UtkaTCPSession session, UtkaBaseMessage baseMessage)
|
||||
public async void Execute(IPandaStatusHandlerContext context, PandaBaseMessage baseMessage)
|
||||
{
|
||||
if (baseMessage is not UtkaUnbanRequest message) return;
|
||||
|
||||
@@ -21,7 +20,7 @@ public sealed class UtkaUnbanCommand : IUtkaCommand
|
||||
var located = await locator.LookupIdByNameOrIdAsync(message.ACkey!);
|
||||
if (located == null)
|
||||
{
|
||||
UtkaSendResponse(false);
|
||||
UtkaSendResponse(false, context);
|
||||
return;
|
||||
}
|
||||
var player = located.UserId;
|
||||
@@ -31,29 +30,34 @@ public sealed class UtkaUnbanCommand : IUtkaCommand
|
||||
|
||||
if (ban == null || ban.Unban != null)
|
||||
{
|
||||
UtkaSendResponse(false);
|
||||
UtkaSendResponse(false, context);
|
||||
return;
|
||||
}
|
||||
|
||||
var adminData = await dbMan.GetAdminDataForAsync(player);
|
||||
if (adminData?.AdminRank == null || ban.ServerName != "unknown" && adminData.AdminServer is not (null or "unknown") && adminData.AdminServer != ban.ServerName)
|
||||
{
|
||||
UtkaSendResponse(false);
|
||||
UtkaSendResponse(false, context);
|
||||
return;
|
||||
}
|
||||
|
||||
await dbMan.AddServerUnbanAsync(new ServerUnbanDef(banId, player, DateTimeOffset.Now));
|
||||
|
||||
UtkaSendResponse(true);
|
||||
UtkaSendResponse(true, context);
|
||||
}
|
||||
|
||||
private void UtkaSendResponse(bool unbanned)
|
||||
public void Response(IPandaStatusHandlerContext context, PandaBaseMessage? message = null)
|
||||
{
|
||||
context.RespondJsonAsync(message!);
|
||||
}
|
||||
|
||||
private void UtkaSendResponse(bool unbanned, IPandaStatusHandlerContext context)
|
||||
{
|
||||
var utkaResponse = new UtkaUnbanResponse()
|
||||
{
|
||||
Unbanned = unbanned
|
||||
};
|
||||
|
||||
_utkaSocketWrapper.SendMessageToAll(utkaResponse);
|
||||
Response(context, utkaResponse);
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,21 @@
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Content.Server.UtkaIntegration.TCP;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Server.Player;
|
||||
using Content.Server.White.PandaSocket.Interfaces;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.UtkaIntegration;
|
||||
namespace Content.Server.White.PandaSocket.Commands;
|
||||
|
||||
public sealed class UtkaWhoCommand : IUtkaCommand
|
||||
public sealed class PandaWhoCommand : IPandaCommand
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
public string Name => "who";
|
||||
public Type RequestMessageType => typeof(UtkaWhoRequest);
|
||||
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private UtkaTCPWrapper _utkaSocketWrapper = default!;
|
||||
|
||||
public void Execute(UtkaTCPSession session, UtkaBaseMessage baseMessage)
|
||||
public void Execute(IPandaStatusHandlerContext context, PandaBaseMessage baseMessage)
|
||||
{
|
||||
if(baseMessage is not UtkaWhoRequest _) return;
|
||||
if (baseMessage is not UtkaWhoRequest) return;
|
||||
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
@@ -35,6 +29,11 @@ public sealed class UtkaWhoCommand : IUtkaCommand
|
||||
Players = playerNames.ToList()
|
||||
};
|
||||
|
||||
_utkaSocketWrapper.SendMessageToAll(toUtkaMessage);
|
||||
Response(context, toUtkaMessage);
|
||||
}
|
||||
|
||||
public void Response(IPandaStatusHandlerContext context, PandaBaseMessage? message = null)
|
||||
{
|
||||
context.RespondJsonAsync(message!);
|
||||
}
|
||||
}
|
||||
11
Content.Server/White/PandaSocket/Interfaces/IPandaCommand.cs
Normal file
11
Content.Server/White/PandaSocket/Interfaces/IPandaCommand.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
|
||||
namespace Content.Server.White.PandaSocket.Interfaces;
|
||||
|
||||
public interface IPandaCommand
|
||||
{
|
||||
string Name { get; }
|
||||
Type RequestMessageType { get; }
|
||||
public void Execute(IPandaStatusHandlerContext context, PandaBaseMessage baseMessage);
|
||||
public void Response(IPandaStatusHandlerContext context, PandaBaseMessage? message = null);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Content.Server.White.PandaSocket.Interfaces;
|
||||
|
||||
public interface IPandaStatusHandlerContext
|
||||
{
|
||||
HttpMethod RequestMethod { get; }
|
||||
IPEndPoint RemoteEndPoint { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Stream that reads the request body data,
|
||||
/// </summary>
|
||||
Stream RequestBody { get; }
|
||||
Uri Url { get; }
|
||||
bool IsGetLike { get; }
|
||||
IReadOnlyDictionary<string, StringValues> RequestHeaders { get; }
|
||||
|
||||
IDictionary<string, string> ResponseHeaders { get; }
|
||||
bool KeepAlive { get; set; }
|
||||
|
||||
Task RespondNoContentAsync();
|
||||
|
||||
Task RespondAsync(
|
||||
string text,
|
||||
HttpStatusCode code = HttpStatusCode.OK,
|
||||
string contentType = "text/plain; charset=utf-8");
|
||||
|
||||
Task RespondAsync(
|
||||
string text,
|
||||
int code = 200,
|
||||
string contentType = "text/plain; charset=utf-8");
|
||||
|
||||
Task RespondAsync(
|
||||
byte[] data,
|
||||
HttpStatusCode code = HttpStatusCode.OK,
|
||||
string contentType = "text/plain; charset=utf-8");
|
||||
|
||||
Task RespondAsync(
|
||||
byte[] data,
|
||||
int code = 200,
|
||||
string contentType = "text/plain; charset=utf-8");
|
||||
|
||||
Task RespondErrorAsync(HttpStatusCode code);
|
||||
|
||||
Task RespondJsonAsync(object jsonData, HttpStatusCode code = HttpStatusCode.OK);
|
||||
|
||||
Task<Stream> RespondStreamAsync(HttpStatusCode code = HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
public delegate Task<bool> PandaStatusHostHandlerAsync(
|
||||
IPandaStatusHandlerContext context);
|
||||
@@ -1,26 +1,14 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Content.Server.UtkaIntegration;
|
||||
namespace Content.Server.White.PandaSocket.Main;
|
||||
|
||||
public class UtkaBaseMessage
|
||||
public class PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public virtual string? Command { get; set; }
|
||||
}
|
||||
|
||||
public class UtkaHandshakeMessage : UtkaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string Command => "handshake";
|
||||
|
||||
[JsonPropertyName("key")]
|
||||
public string? Key { get; set; }
|
||||
|
||||
[JsonPropertyName("message")]
|
||||
public string? Message { get; set; }
|
||||
}
|
||||
|
||||
public class UtkaOOCRequest : UtkaBaseMessage
|
||||
public class UtkaOOCRequest : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "ooc";
|
||||
@@ -32,49 +20,49 @@ public class UtkaOOCRequest : UtkaBaseMessage
|
||||
public string? Message { get; set; }
|
||||
}
|
||||
|
||||
public class UtkaAsayRequest : UtkaBaseMessage
|
||||
public class UtkaAsayRequest : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "asay";
|
||||
|
||||
[JsonPropertyName("a_ckey")]
|
||||
[JsonPropertyName("ackey")]
|
||||
public string? ACkey { get; set; }
|
||||
|
||||
[JsonPropertyName("message")]
|
||||
public string? Message { get; set; }
|
||||
}
|
||||
|
||||
public class UtkaPmRequest : UtkaBaseMessage
|
||||
public class UtkaPmRequest : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "discord_pm";
|
||||
public override string? Command => "discordpm";
|
||||
|
||||
[JsonPropertyName("sender")]
|
||||
public string? Sender { get; set; }
|
||||
|
||||
[JsonPropertyName("receiver")]
|
||||
public string? Reciever { get; set; }
|
||||
public string? Receiver { get; set; }
|
||||
|
||||
[JsonPropertyName("message")]
|
||||
public string? Message { get; set; }
|
||||
}
|
||||
|
||||
public class UtkaPmResponse : UtkaBaseMessage
|
||||
public class UtkaPmResponse : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "discord_pm";
|
||||
public override string? Command => "discordpm";
|
||||
|
||||
[JsonPropertyName("message")]
|
||||
public bool? Message { get; set; }
|
||||
}
|
||||
|
||||
public class UtkaWhoRequest : UtkaBaseMessage
|
||||
public class UtkaWhoRequest : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "who";
|
||||
}
|
||||
|
||||
public class UtkaWhoResponse : UtkaBaseMessage
|
||||
public class UtkaWhoResponse : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "who";
|
||||
@@ -83,13 +71,13 @@ public class UtkaWhoResponse : UtkaBaseMessage
|
||||
public List<string>? Players { get; set; }
|
||||
}
|
||||
|
||||
public class UtkaAdminWhoRequest : UtkaBaseMessage
|
||||
public class UtkaAdminWhoRequest : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "adminwho";
|
||||
}
|
||||
|
||||
public class UtkaAdminWhoResponse : UtkaBaseMessage
|
||||
public class UtkaAdminWhoResponse : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "adminwho";
|
||||
@@ -98,13 +86,13 @@ public class UtkaAdminWhoResponse : UtkaBaseMessage
|
||||
public List<string>? Admins { get; set; }
|
||||
}
|
||||
|
||||
public class UtkaStatusRequsets : UtkaBaseMessage
|
||||
public class UtkaStatusRequest : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "status";
|
||||
}
|
||||
|
||||
public class UtkaStatusResponse : UtkaBaseMessage
|
||||
public class UtkaStatusResponse : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "status";
|
||||
@@ -118,27 +106,166 @@ public class UtkaStatusResponse : UtkaBaseMessage
|
||||
[JsonPropertyName("map")]
|
||||
public string? Map { get; set; }
|
||||
|
||||
[JsonPropertyName("round_duration")]
|
||||
[JsonPropertyName("roundduration")]
|
||||
public double RoundDuration { get; set; }
|
||||
|
||||
[JsonPropertyName("shuttle_status")]
|
||||
[JsonPropertyName("shuttlestatus")]
|
||||
public string? ShuttleStatus { get; set; }
|
||||
|
||||
[JsonPropertyName("station_code")]
|
||||
[JsonPropertyName("stationcode")]
|
||||
public string? StationCode { get; set; }
|
||||
}
|
||||
|
||||
public class UtkaRoundstatusUpdate : UtkaBaseMessage
|
||||
public sealed class UtkaBanRequest : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "roundstatus";
|
||||
public override string? Command => "ban";
|
||||
|
||||
[JsonPropertyName("message")]
|
||||
public string? Message { get; set; }
|
||||
[JsonPropertyName("ckey")]
|
||||
public string? Ckey { get; set; }
|
||||
|
||||
[JsonPropertyName("ackey")]
|
||||
public string? ACkey { get; set; }
|
||||
|
||||
[JsonPropertyName("reason")]
|
||||
public string? Reason { get; set; }
|
||||
|
||||
[JsonPropertyName("duration")]
|
||||
public uint? Duration { get; set; }
|
||||
|
||||
[JsonPropertyName("global")]
|
||||
public bool? Global { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaBanResponse : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "ban";
|
||||
|
||||
public sealed class UtkaChatEventMessage : UtkaBaseMessage
|
||||
[JsonPropertyName("banned")]
|
||||
public bool? Banned { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaJobBanRequest : PandaBaseMessage
|
||||
{
|
||||
public override string? Command => "jobban";
|
||||
|
||||
[JsonPropertyName("ckey")]
|
||||
public string? Ckey { get; set; }
|
||||
|
||||
[JsonPropertyName("ackey")]
|
||||
public string? ACkey { get; set; }
|
||||
|
||||
[JsonPropertyName("reason")]
|
||||
public string? Reason { get; set; }
|
||||
|
||||
[JsonPropertyName("duration")]
|
||||
public uint? Duration { get; set; }
|
||||
|
||||
[JsonPropertyName("global")]
|
||||
public bool? Global { get; set; }
|
||||
|
||||
[JsonPropertyName("type")]
|
||||
public string? Type { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaJobBanResponse : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "jobban";
|
||||
|
||||
[JsonPropertyName("banned")]
|
||||
public bool? Banned { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaRestartRoundRequest : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "restartround";
|
||||
}
|
||||
|
||||
public sealed class UtkaRestartRoundResponse : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "restartround";
|
||||
|
||||
[JsonPropertyName("restarted")]
|
||||
public bool? Restarted { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaUnbanRequest : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "unban";
|
||||
|
||||
[JsonPropertyName("ackey")]
|
||||
public string? ACkey { get; set; }
|
||||
|
||||
[JsonPropertyName("bid")]
|
||||
public int? Bid { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaUnbanResponse : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "unban";
|
||||
|
||||
[JsonPropertyName("unbanned")]
|
||||
public bool? Unbanned { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaUnJobBanRequest : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "unjobban";
|
||||
|
||||
[JsonPropertyName("ackey")]
|
||||
public string? ACkey { get; set; }
|
||||
|
||||
[JsonPropertyName("bid")]
|
||||
public int? Bid { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaUnJobBanResponse : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "unjobban";
|
||||
|
||||
[JsonPropertyName("unbanned")]
|
||||
public bool? Unbanned { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaBannedEvent : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "banned";
|
||||
|
||||
[JsonPropertyName("ckey")]
|
||||
public string? Ckey { get; set; }
|
||||
|
||||
[JsonPropertyName("ackey")]
|
||||
public string? ACkey { get; set; }
|
||||
|
||||
[JsonPropertyName("bantype")]
|
||||
public string? Bantype { get; set; }
|
||||
|
||||
[JsonPropertyName("duration")]
|
||||
public uint? Duration { get; set; }
|
||||
|
||||
[JsonPropertyName("global")]
|
||||
public bool? Global { get; set; }
|
||||
|
||||
[JsonPropertyName("reason")]
|
||||
public string? Reason { get; set; }
|
||||
|
||||
[JsonPropertyName("rid")]
|
||||
public int? Rid { get; set; }
|
||||
|
||||
[JsonPropertyName("banid")]
|
||||
public int? BanId { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaChatEventMessage : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("ckey")]
|
||||
public string? Ckey { get; set; }
|
||||
@@ -147,7 +274,7 @@ public sealed class UtkaChatEventMessage : UtkaBaseMessage
|
||||
public string? Message { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaRoundStatusEvent : UtkaBaseMessage
|
||||
public sealed class UtkaRoundStatusEvent : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "roundstatus";
|
||||
@@ -156,7 +283,7 @@ public sealed class UtkaRoundStatusEvent : UtkaBaseMessage
|
||||
public string? Message { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaChatMeEvent : UtkaBaseMessage
|
||||
public sealed class UtkaChatMeEvent : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "me";
|
||||
@@ -171,7 +298,7 @@ public sealed class UtkaChatMeEvent : UtkaBaseMessage
|
||||
public string? CharacterName { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaAhelpPmEvent : UtkaBaseMessage
|
||||
public sealed class UtkaAhelpPmEvent : PandaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "pm";
|
||||
@@ -194,152 +321,3 @@ public sealed class UtkaAhelpPmEvent : UtkaBaseMessage
|
||||
[JsonPropertyName("entity")]
|
||||
public string? Entity { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaBannedEvent : UtkaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "banned";
|
||||
|
||||
[JsonPropertyName("ckey")]
|
||||
public string? Ckey { get; set; }
|
||||
|
||||
[JsonPropertyName("a_ckey")]
|
||||
public string? ACkey { get; set; }
|
||||
|
||||
[JsonPropertyName("bantype")]
|
||||
public string? Bantype { get; set; }
|
||||
|
||||
[JsonPropertyName("duration")]
|
||||
public uint? Duration { get; set; }
|
||||
|
||||
[JsonPropertyName("global")]
|
||||
public bool? Global { get; set; }
|
||||
|
||||
[JsonPropertyName("reason")]
|
||||
public string? Reason { get; set; }
|
||||
|
||||
[JsonPropertyName("rid")]
|
||||
public int? Rid { get; set; }
|
||||
|
||||
[JsonPropertyName("ban_id")]
|
||||
public int? BanId { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaBanRequest : UtkaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "ban";
|
||||
|
||||
[JsonPropertyName("ckey")]
|
||||
public string? Ckey { get; set; }
|
||||
|
||||
[JsonPropertyName("a_ckey")]
|
||||
public string? ACkey { get; set; }
|
||||
|
||||
[JsonPropertyName("reason")]
|
||||
public string? Reason { get; set; }
|
||||
|
||||
[JsonPropertyName("duration")]
|
||||
public uint? Duration { get; set; }
|
||||
|
||||
[JsonPropertyName("global")]
|
||||
public bool? Global { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaBanResponse : UtkaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "ban";
|
||||
|
||||
[JsonPropertyName("banned")]
|
||||
public bool? Banned { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaJobBanRequest : UtkaBaseMessage
|
||||
{
|
||||
public override string? Command => "jobban";
|
||||
|
||||
[JsonPropertyName("ckey")]
|
||||
public string? Ckey { get; set; }
|
||||
|
||||
[JsonPropertyName("a_ckey")]
|
||||
public string? ACkey { get; set; }
|
||||
|
||||
[JsonPropertyName("reason")]
|
||||
public string? Reason { get; set; }
|
||||
|
||||
[JsonPropertyName("duration")]
|
||||
public uint? Duration { get; set; }
|
||||
|
||||
[JsonPropertyName("global")]
|
||||
public bool? Global { get; set; }
|
||||
|
||||
[JsonPropertyName("type")]
|
||||
public string? Type { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaJobBanResponse : UtkaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "jobban";
|
||||
|
||||
[JsonPropertyName("banned")]
|
||||
public bool? Banned { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaRestartRoundRequest : UtkaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "restart_round";
|
||||
}
|
||||
|
||||
public sealed class UtkaRestartRoundResponse : UtkaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "restart_round";
|
||||
|
||||
[JsonPropertyName("restarted")]
|
||||
public bool? Restarted { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaUnbanRequest : UtkaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "unban";
|
||||
|
||||
[JsonPropertyName("a_ckey")]
|
||||
public string? ACkey { get; set; }
|
||||
|
||||
[JsonPropertyName("bid")]
|
||||
public int? Bid { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaUnbanResponse : UtkaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "unban";
|
||||
|
||||
[JsonPropertyName("unbanned")]
|
||||
public bool? Unbanned { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaUnJobBanRequest : UtkaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "unjobban";
|
||||
|
||||
[JsonPropertyName("a_ckey")]
|
||||
public string? ACkey { get; set; }
|
||||
|
||||
[JsonPropertyName("bid")]
|
||||
public int? Bid { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UtkaUnJobBanResponse : UtkaBaseMessage
|
||||
{
|
||||
[JsonPropertyName("command")]
|
||||
public override string? Command => "unjobban";
|
||||
|
||||
[JsonPropertyName("unbanned")]
|
||||
public bool? Unbanned { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Content.Server.White.PandaSocket.Interfaces;
|
||||
|
||||
namespace Content.Server.White.PandaSocket.Main;
|
||||
|
||||
public sealed partial class PandaStatusHost
|
||||
{
|
||||
|
||||
private void RegisterHandlers()
|
||||
{
|
||||
AddHandler(HandleChecker);
|
||||
AddHandler(HandleQueryCheck);
|
||||
AddHandler(HandleRequest);
|
||||
}
|
||||
|
||||
private async Task<bool> HandleChecker(IPandaStatusHandlerContext context)
|
||||
{
|
||||
if (!context.IsGetLike || context.Url!.AbsolutePath != "/checker")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
await context.RespondAsync("Checking panda socket, ukta loh.", (HttpStatusCode) 418);
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<bool> HandleQueryCheck(IPandaStatusHandlerContext context)
|
||||
{
|
||||
if (!context.IsGetLike || context.Url!.AbsolutePath != "/querycheck")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var query = HttpUtility.ParseQueryString(context.Url.Query);
|
||||
var text = query["text"] ?? "None";
|
||||
|
||||
await context.RespondAsync(text, (HttpStatusCode) 418);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<bool> HandleRequest(IPandaStatusHandlerContext context)
|
||||
{
|
||||
if (!context.IsGetLike || context.Url!.AbsolutePath != "/request")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ValidateMessage(context.Url.Query, out var message) || message == null)
|
||||
return false;
|
||||
|
||||
ExecuteCommand(context, message);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
403
Content.Server/White/PandaSocket/Main/PandaStatusHost.cs
Normal file
403
Content.Server/White/PandaSocket/Main/PandaStatusHost.cs
Normal file
@@ -0,0 +1,403 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Mime;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Content.Server.White.PandaSocket.Interfaces;
|
||||
using Content.Shared.White;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Newtonsoft.Json;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Asynchronous;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Network;
|
||||
using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||
|
||||
namespace Content.Server.White.PandaSocket.Main;
|
||||
|
||||
public sealed partial class PandaStatusHost : IDisposable
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IServerNetManager _netManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IDependencyCollection _deps = default!;
|
||||
[Dependency] private readonly ILogManager _logMan = default!;
|
||||
[Dependency] private readonly ITaskManager _taskManager = default!;
|
||||
|
||||
private const string Sawmill = "panda.socket";
|
||||
|
||||
private static readonly ConcurrentDictionary<string, IPandaCommand> Commands = new();
|
||||
private readonly List<PandaStatusHostHandlerAsync> _handlers = new();
|
||||
private HttpListener? _listener;
|
||||
private TaskCompletionSource? _stopSource;
|
||||
private ISawmill _httpSawmill = default!;
|
||||
private string? _token;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
var statusBind = _cfg.GetCVar(WhiteCVars.PandaStatusBind);
|
||||
if (statusBind == "")
|
||||
return;
|
||||
|
||||
_httpSawmill = _logMan.GetSawmill($"{Sawmill}.http");
|
||||
|
||||
RegisterHandlers();
|
||||
RegisterCommands();
|
||||
|
||||
_token = _cfg.GetCVar(WhiteCVars.PandaToken);
|
||||
_cfg.OnValueChanged(WhiteCVars.PandaToken, token => _token = token, true);
|
||||
|
||||
_stopSource = new TaskCompletionSource();
|
||||
_listener = new HttpListener();
|
||||
_listener.Prefixes.Add($"http://{statusBind}/");
|
||||
_listener.Start();
|
||||
|
||||
Task.Run(ListenerThread);
|
||||
}
|
||||
|
||||
private void RegisterCommands()
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var types = assembly.GetTypes();
|
||||
|
||||
var commands = types.Where(type =>
|
||||
typeof(IPandaCommand).IsAssignableFrom(type) && type.GetInterfaces().Contains(typeof(IPandaCommand))).ToList();
|
||||
|
||||
foreach (var command in commands)
|
||||
{
|
||||
if (Activator.CreateInstance(command) is IPandaCommand pandaCommand)
|
||||
{
|
||||
Commands[pandaCommand.Name] = pandaCommand;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddHandler(PandaStatusHostHandlerAsync handler)
|
||||
{
|
||||
_handlers.Add(handler);
|
||||
}
|
||||
|
||||
private void ExecuteCommand(IPandaStatusHandlerContext context, PandaBaseMessage baseMessage)
|
||||
{
|
||||
var command = baseMessage.Command!;
|
||||
|
||||
if (!Commands.ContainsKey(command))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_taskManager.RunOnMainThread(() => Commands[command].Execute(context, baseMessage));
|
||||
}
|
||||
|
||||
private async Task ListenerThread()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var getContextTask = _listener!.GetContextAsync();
|
||||
var task = await Task.WhenAny(getContextTask, _stopSource!.Task);
|
||||
|
||||
if (task == _stopSource.Task)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Task.Run this so it gets run on another thread pool thread.
|
||||
#pragma warning disable CS4014
|
||||
Task.Run(async () =>
|
||||
#pragma warning restore CS4014
|
||||
{
|
||||
try
|
||||
{
|
||||
var ctx = await getContextTask;
|
||||
await ProcessRequestAsync(ctx);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_httpSawmill.Error($"Error inside ProcessRequestAsync:\n{e}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessRequestAsync(HttpListenerContext context)
|
||||
{
|
||||
var apiContext = (IPandaStatusHandlerContext) new ContextImpl(context);
|
||||
|
||||
_httpSawmill.Info(
|
||||
$"{apiContext.RequestMethod} {apiContext.Url.PathAndQuery} from {apiContext.RemoteEndPoint}");
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var handler in _handlers)
|
||||
{
|
||||
if (await handler(apiContext))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No handler returned true, assume no handlers care about this.
|
||||
// 404.
|
||||
await apiContext.RespondAsync("Not Found", HttpStatusCode.NotFound);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_httpSawmill.Error($"Exception in StatusHost: {e}");
|
||||
await apiContext.RespondErrorAsync(HttpStatusCode.InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ValidateMessage(string message, out PandaBaseMessage? baseMessage)
|
||||
{
|
||||
baseMessage = null;
|
||||
|
||||
if (string.IsNullOrEmpty(message))
|
||||
return false;
|
||||
|
||||
var collection = HttpUtility.ParseQueryString(message);
|
||||
#pragma warning disable CS8714
|
||||
var json = JsonSerializer.Serialize(collection.AllKeys.ToDictionary(y => y, y => collection[y]));
|
||||
#pragma warning restore CS8714
|
||||
var jsonDocument = JsonDocument.Parse(json);
|
||||
var root = jsonDocument.RootElement;
|
||||
|
||||
if (!root.TryGetProperty("token", out var token))
|
||||
return false;
|
||||
|
||||
if (token.GetString() != _token)
|
||||
return false;
|
||||
|
||||
if (!root.TryGetProperty("command", out var commandNameElement))
|
||||
return false;
|
||||
|
||||
var commandName = commandNameElement.GetString();
|
||||
if (commandName == null)
|
||||
return false;
|
||||
|
||||
var pandaCommand = Commands.Values.FirstOrDefault(x => x.Name == commandName);
|
||||
if (pandaCommand == null)
|
||||
return false;
|
||||
|
||||
var messageType = pandaCommand.RequestMessageType;
|
||||
|
||||
try
|
||||
{
|
||||
baseMessage = JsonConvert.DeserializeObject(json, messageType) as PandaBaseMessage;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_stopSource == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_stopSource.SetResult();
|
||||
_listener!.Stop();
|
||||
}
|
||||
|
||||
private sealed class ContextImpl : IPandaStatusHandlerContext
|
||||
{
|
||||
private readonly HttpListenerContext _context;
|
||||
private readonly Dictionary<string, string> _responseHeaders;
|
||||
public HttpMethod RequestMethod { get; }
|
||||
public IPEndPoint RemoteEndPoint => _context.Request.RemoteEndPoint!;
|
||||
public Stream RequestBody => _context.Request.InputStream;
|
||||
public Uri Url => _context.Request.Url!;
|
||||
public bool IsGetLike => RequestMethod == HttpMethod.Head || RequestMethod == HttpMethod.Get;
|
||||
public IReadOnlyDictionary<string, StringValues> RequestHeaders { get; }
|
||||
|
||||
public bool KeepAlive
|
||||
{
|
||||
get => _context.Response.KeepAlive;
|
||||
set => _context.Response.KeepAlive = value;
|
||||
}
|
||||
|
||||
public IDictionary<string, string> ResponseHeaders => _responseHeaders;
|
||||
|
||||
public ContextImpl(HttpListenerContext context)
|
||||
{
|
||||
_context = context;
|
||||
RequestMethod = new HttpMethod(context.Request.HttpMethod!);
|
||||
|
||||
var headers = new Dictionary<string, StringValues>();
|
||||
foreach (string? key in context.Request.Headers.Keys)
|
||||
{
|
||||
if (key == null)
|
||||
continue;
|
||||
|
||||
headers.Add(key, context.Request.Headers.GetValues(key));
|
||||
}
|
||||
|
||||
RequestHeaders = headers;
|
||||
_responseHeaders = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
public T? RequestBodyJson<T>()
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(RequestBody);
|
||||
}
|
||||
|
||||
public async Task<T?> RequestBodyJsonAsync<T>()
|
||||
{
|
||||
return await JsonSerializer.DeserializeAsync<T>(RequestBody);
|
||||
}
|
||||
|
||||
public void Respond(string text, HttpStatusCode code = HttpStatusCode.OK, string contentType = MediaTypeNames.Text.Plain)
|
||||
{
|
||||
Respond(text, (int) code, contentType);
|
||||
}
|
||||
|
||||
public void Respond(string text, int code = 200, string contentType = MediaTypeNames.Text.Plain)
|
||||
{
|
||||
_context.Response.StatusCode = code;
|
||||
_context.Response.ContentType = contentType;
|
||||
|
||||
if (RequestMethod == HttpMethod.Head)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var writer = new StreamWriter(_context.Response.OutputStream, new UTF8Encoding());
|
||||
|
||||
writer.Write(text);
|
||||
}
|
||||
|
||||
public void Respond(byte[] data, HttpStatusCode code = HttpStatusCode.OK, string contentType = MediaTypeNames.Text.Plain)
|
||||
{
|
||||
Respond(data, (int) code, contentType);
|
||||
}
|
||||
|
||||
public void Respond(byte[] data, int code = 200, string contentType = MediaTypeNames.Text.Plain)
|
||||
{
|
||||
_context.Response.StatusCode = code;
|
||||
_context.Response.ContentType = contentType;
|
||||
_context.Response.ContentLength64 = data.Length;
|
||||
|
||||
if (RequestMethod == HttpMethod.Head)
|
||||
{
|
||||
_context.Response.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
_context.Response.OutputStream.Write(data);
|
||||
_context.Response.Close();
|
||||
}
|
||||
|
||||
public Task RespondNoContentAsync()
|
||||
{
|
||||
RespondShared();
|
||||
|
||||
_context.Response.StatusCode = (int) HttpStatusCode.NoContent;
|
||||
_context.Response.Close();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task RespondAsync(string text, HttpStatusCode code = HttpStatusCode.OK, string contentType = "text/plain")
|
||||
{
|
||||
return RespondAsync(text, (int) code, contentType);
|
||||
}
|
||||
|
||||
public async Task RespondAsync(string text, int code = 200, string contentType = "text/plain")
|
||||
{
|
||||
RespondShared();
|
||||
|
||||
_context.Response.StatusCode = code;
|
||||
_context.Response.ContentType = contentType;
|
||||
|
||||
if (RequestMethod == HttpMethod.Head)
|
||||
return;
|
||||
|
||||
using var writer = new StreamWriter(_context.Response.OutputStream, new UTF8Encoding());
|
||||
|
||||
await writer.WriteAsync(text);
|
||||
}
|
||||
|
||||
public Task RespondAsync(byte[] data, HttpStatusCode code = HttpStatusCode.OK, string contentType = "text/plain")
|
||||
{
|
||||
return RespondAsync(data, (int) code, contentType);
|
||||
}
|
||||
|
||||
public async Task RespondAsync(byte[] data, int code = 200, string contentType = "text/plain")
|
||||
{
|
||||
RespondShared();
|
||||
|
||||
_context.Response.StatusCode = code;
|
||||
_context.Response.ContentType = contentType;
|
||||
_context.Response.ContentLength64 = data.Length;
|
||||
|
||||
if (RequestMethod == HttpMethod.Head)
|
||||
{
|
||||
_context.Response.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
await _context.Response.OutputStream.WriteAsync(data);
|
||||
_context.Response.Close();
|
||||
}
|
||||
|
||||
public void RespondError(HttpStatusCode code)
|
||||
{
|
||||
Respond(code.ToString(), code);
|
||||
}
|
||||
|
||||
public Task RespondErrorAsync(HttpStatusCode code)
|
||||
{
|
||||
return RespondAsync(code.ToString(), code);
|
||||
}
|
||||
|
||||
public void RespondJson(object jsonData, HttpStatusCode code = HttpStatusCode.OK)
|
||||
{
|
||||
RespondShared();
|
||||
|
||||
_context.Response.ContentType = "application/json";
|
||||
|
||||
JsonSerializer.Serialize(_context.Response.OutputStream, jsonData);
|
||||
|
||||
_context.Response.Close();
|
||||
}
|
||||
|
||||
public async Task RespondJsonAsync(object jsonData, HttpStatusCode code = HttpStatusCode.OK)
|
||||
{
|
||||
RespondShared();
|
||||
|
||||
_context.Response.ContentType = "application/json";
|
||||
|
||||
await JsonSerializer.SerializeAsync(_context.Response.OutputStream, jsonData);
|
||||
|
||||
_context.Response.Close();
|
||||
}
|
||||
|
||||
public Task<Stream> RespondStreamAsync(HttpStatusCode code = HttpStatusCode.OK)
|
||||
{
|
||||
RespondShared();
|
||||
|
||||
_context.Response.StatusCode = (int) code;
|
||||
|
||||
return Task.FromResult(_context.Response.OutputStream);
|
||||
}
|
||||
|
||||
private void RespondShared()
|
||||
{
|
||||
foreach (var (header, value) in _responseHeaders)
|
||||
{
|
||||
_context.Response.AddHeader(header, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Content.Server/White/PandaSocket/Main/PandaWebManager.cs
Normal file
53
Content.Server/White/PandaSocket/Main/PandaWebManager.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Web;
|
||||
using Content.Shared.White;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Robust.Shared.Configuration;
|
||||
using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||
|
||||
namespace Content.Server.White.PandaSocket.Main;
|
||||
|
||||
public sealed class PandaWebManager
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
private readonly HttpClient _httpClient = new();
|
||||
private string? _token;
|
||||
private string? _utkaUri;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_token = _cfg.GetCVar(WhiteCVars.PandaToken);
|
||||
_cfg.OnValueChanged(WhiteCVars.PandaToken, token => _token = token, true);
|
||||
|
||||
_utkaUri = _cfg.GetCVar(WhiteCVars.UtkaClientBind);
|
||||
_cfg.OnValueChanged(WhiteCVars.UtkaClientBind, uri => _utkaUri = uri, true);
|
||||
}
|
||||
|
||||
public async void SendBotMessage(PandaBaseMessage message)
|
||||
{
|
||||
if (_utkaUri is null || _token is null)
|
||||
return;
|
||||
|
||||
var json = JsonSerializer.Serialize(message, message.GetType());
|
||||
var jObj = (JObject) JsonConvert.DeserializeObject(json)!;
|
||||
var query = String.Join("&",
|
||||
jObj.Children().Cast<JProperty>()
|
||||
.Select(jp=>jp.Name + "=" + HttpUtility.UrlEncode(jp.Value.ToString())));
|
||||
|
||||
var request = $"http://{_utkaUri}?token={_token}&{query}";
|
||||
|
||||
try
|
||||
{
|
||||
await _httpClient.GetAsync(request);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,12 @@ using Content.Server.Administration;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Objectives;
|
||||
using Content.Server.Roles;
|
||||
using Content.Server.UtkaIntegration;
|
||||
using Content.Server.White.AspectsSystem.Base;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Server.White.PandaSocket.Main;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.White;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
@@ -341,4 +341,19 @@ public sealed class WhiteCVars
|
||||
*/
|
||||
public static readonly CVarDef<bool> EnableGrantAntag =
|
||||
CVarDef.Create("white.antag_grant_enabled", true, CVar.SERVERONLY);
|
||||
|
||||
/*
|
||||
* Panda Socket
|
||||
*/
|
||||
public static readonly CVarDef<string> PandaStatusBind =
|
||||
CVarDef.Create("white.panda_status_bind", "", CVar.SERVERONLY | CVar.ARCHIVE);
|
||||
|
||||
public static readonly CVarDef<int> PandaStatusMaxConnections =
|
||||
CVarDef.Create("white.panda_status_max_connections", 100, CVar.SERVERONLY);
|
||||
|
||||
public static readonly CVarDef<string> PandaToken =
|
||||
CVarDef.Create("white.panda_token", "ass", CVar.SERVERONLY | CVar.CONFIDENTIAL);
|
||||
|
||||
public static readonly CVarDef<string> UtkaClientBind =
|
||||
CVarDef.Create("white.utka_client_bind", "", CVar.SERVERONLY);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user