* Proton-1 finished!

Signed-off-by: pacable <igor.mamaev1@gmail.com>

* fix

Signed-off-by: pacable <igor.mamaev1@gmail.com>

---------

Signed-off-by: pacable <igor.mamaev1@gmail.com>
This commit is contained in:
pacable 2024-09-30 16:15:53 +03:00 committed by GitHub
parent ce81c1a7a2
commit 939249a38b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 473 additions and 2 deletions

View file

@ -16,6 +16,7 @@
<Button Visible="False" Name="Kick" Text="{Loc 'admin-player-actions-kick'}" StyleClasses="OpenBoth" /> <Button Visible="False" Name="Kick" Text="{Loc 'admin-player-actions-kick'}" StyleClasses="OpenBoth" />
<Button Visible="False" Name="Ban" Text="{Loc 'admin-player-actions-ban'}" StyleClasses="OpenBoth" /> <Button Visible="False" Name="Ban" Text="{Loc 'admin-player-actions-ban'}" StyleClasses="OpenBoth" />
<Button Visible="False" Name="Respawn" Text="{Loc 'admin-player-actions-respawn'}" StyleClasses="OpenBoth" /> <Button Visible="False" Name="Respawn" Text="{Loc 'admin-player-actions-respawn'}" StyleClasses="OpenBoth" />
<Button Visible="False" Name="Screenshot" Text="{Loc 'admin-player-actions-screenshot'}" StyleClasses="OpenBoth"/> <!-- Sunrise edit -->
<Button Visible="False" Name="Follow" Text="{Loc 'admin-player-actions-follow'}" StyleClasses="OpenLeft" /> <Button Visible="False" Name="Follow" Text="{Loc 'admin-player-actions-follow'}" StyleClasses="OpenLeft" />
</BoxContainer> </BoxContainer>
</BoxContainer> </BoxContainer>

View file

@ -167,6 +167,19 @@ namespace Content.Client.Administration.UI.Bwoink
_console.ExecuteCommand($"kick \"{_currentPlayer.Username}\""); _console.ExecuteCommand($"kick \"{_currentPlayer.Username}\"");
}; };
// Sunrise-start
Screenshot.OnPressed += _ =>
{
if (!AdminUIHelpers.TryConfirm(Screenshot, _confirmations))
{
return;
}
if (_currentPlayer is not null)
_console.ExecuteCommand($"screenshot \"{_currentPlayer.Username}\"");
};
// Sunrise-end
Follow.OnPressed += _ => Follow.OnPressed += _ =>
{ {
if (_currentPlayer is not null) if (_currentPlayer is not null)
@ -233,6 +246,11 @@ namespace Content.Client.Administration.UI.Bwoink
Respawn.Visible = _adminManager.CanCommand("respawn"); Respawn.Visible = _adminManager.CanCommand("respawn");
Respawn.Disabled = !Respawn.Visible || disabled; Respawn.Disabled = !Respawn.Visible || disabled;
// Sunrise-start
Screenshot.Visible = _adminManager.CanCommand("kick");
Screenshot.Disabled = !Screenshot.Visible || disabled;
// Sunrise-end
Follow.Visible = _adminManager.CanCommand("follow"); Follow.Visible = _adminManager.CanCommand("follow");
Follow.Disabled = !Follow.Visible || disabled; Follow.Disabled = !Follow.Visible || disabled;
} }

View file

@ -1,6 +1,6 @@
<DefaultWindow xmlns="https://spacestation14.io" <DefaultWindow xmlns="https://spacestation14.io"
xmlns:cc="clr-namespace:Content.Client.Administration.UI.Bwoink" xmlns:cc="clr-namespace:Content.Client.Administration.UI.Bwoink"
SetSize="900 500" SetSize="1000 600"
HeaderClass="windowHeaderAlert" HeaderClass="windowHeaderAlert"
TitleClass="windowTitleAlert" TitleClass="windowTitleAlert"
Title="{Loc 'bwoink-user-title'}" > Title="{Loc 'bwoink-user-title'}" >

View file

@ -13,6 +13,7 @@
<Button Name="SubmitKickButton" Text="{Loc admin-player-actions-kick}" Disabled="True"/> <Button Name="SubmitKickButton" Text="{Loc admin-player-actions-kick}" Disabled="True"/>
<Button Name="SubmitAHelpButton" Text="{Loc admin-player-actions-ahelp}" Disabled="True"/> <Button Name="SubmitAHelpButton" Text="{Loc admin-player-actions-ahelp}" Disabled="True"/>
<Button Name="SubmitRespawnButton" Text="{Loc admin-player-actions-respawn}" Disabled="True"/> <Button Name="SubmitRespawnButton" Text="{Loc admin-player-actions-respawn}" Disabled="True"/>
<Button Name="ScreenshotButton" Text="{Loc admin-player-actions-get-screenshot}" Disabled="True"/> <!-- Sunrise edit -->
</BoxContainer> </BoxContainer>
</BoxContainer> </BoxContainer>
</DefaultWindow> </DefaultWindow>

View file

@ -23,6 +23,7 @@ namespace Content.Client.Administration.UI.Tabs.AdminTab
SubmitKickButton.OnPressed += SubmitKickButtonOnPressed; SubmitKickButton.OnPressed += SubmitKickButtonOnPressed;
SubmitAHelpButton.OnPressed += SubmitAhelpButtonOnPressed; SubmitAHelpButton.OnPressed += SubmitAhelpButtonOnPressed;
SubmitRespawnButton.OnPressed += SubmitRespawnButtonOnPressed; SubmitRespawnButton.OnPressed += SubmitRespawnButtonOnPressed;
ScreenshotButton.OnPressed += ScreenshotButtonOnPressed; // Sunrise-edit
PlayerList.OnSelectionChanged += OnListOnOnSelectionChanged; PlayerList.OnSelectionChanged += OnListOnOnSelectionChanged;
} }
@ -36,8 +37,22 @@ namespace Content.Client.Administration.UI.Tabs.AdminTab
SubmitKickButton.Disabled = disableButtons; SubmitKickButton.Disabled = disableButtons;
SubmitAHelpButton.Disabled = disableButtons; SubmitAHelpButton.Disabled = disableButtons;
SubmitRespawnButton.Disabled = disableButtons; SubmitRespawnButton.Disabled = disableButtons;
ScreenshotButton.Disabled = disableButtons; // Sunrise-edit
} }
// Sunrise-start
private void ScreenshotButtonOnPressed(BaseButton.ButtonEventArgs obj)
{
if (_selectedPlayer == null)
return;
if (!AdminUIHelpers.TryConfirm(ScreenshotButton, _confirmations))
return;
IoCManager.Resolve<IClientConsoleHost>().ExecuteCommand($"screenshot \"{_selectedPlayer.Username}\"");
}
// Sunrise-end
private void SubmitKickButtonOnPressed(BaseButton.ButtonEventArgs obj) private void SubmitKickButtonOnPressed(BaseButton.ButtonEventArgs obj)
{ {
if (_selectedPlayer == null) if (_selectedPlayer == null)

View file

@ -1,3 +1,4 @@
using Content.Client._Sunrise.Proton; // Sunrise-Proton
using Content.Client._Sunrise.ServersHub; using Content.Client._Sunrise.ServersHub;
using Content.Client.Administration.Managers; using Content.Client.Administration.Managers;
using Content.Client.Changelog; using Content.Client.Changelog;
@ -73,6 +74,7 @@ namespace Content.Client.Entry
[Dependency] private readonly ContentReplayPlaybackManager _replayMan = default!; [Dependency] private readonly ContentReplayPlaybackManager _replayMan = default!;
[Dependency] private readonly DebugMonitorManager _debugMonitorManager = default!; [Dependency] private readonly DebugMonitorManager _debugMonitorManager = default!;
[Dependency] private readonly ServersHubManager _serversHubManager = default!; // Sunrise-Hub [Dependency] private readonly ServersHubManager _serversHubManager = default!; // Sunrise-Hub
[Dependency] private readonly ProtonManager _proton = default!; // Sunrise-Proton
public override void Init() public override void Init()
{ {
@ -137,6 +139,7 @@ namespace Content.Client.Entry
_playbackMan.Initialize(); _playbackMan.Initialize();
_serversHubManager.Initialize(); // Sunrise-Hub _serversHubManager.Initialize(); // Sunrise-Hub
_proton.Initialize(); // Sunrise-Proton
//AUTOSCALING default Setup! //AUTOSCALING default Setup!
_configManager.SetCVar("interface.resolutionAutoScaleUpperCutoffX", 1080); _configManager.SetCVar("interface.resolutionAutoScaleUpperCutoffX", 1080);

View file

@ -1,4 +1,5 @@
using Content.Client._Sunrise.ServersHub; using Content.Client._Sunrise.Proton; // Sunrise-Proton
using Content.Client._Sunrise.ServersHub;
using Content.Client.Administration.Managers; using Content.Client.Administration.Managers;
using Content.Client.Changelog; using Content.Client.Changelog;
using Content.Client.Chat.Managers; using Content.Client.Chat.Managers;
@ -56,6 +57,7 @@ namespace Content.Client.IoC
// Sunrise // Sunrise
collection.Register<ServersHubManager>(); // Sunrise-Hub collection.Register<ServersHubManager>(); // Sunrise-Hub
collection.Register<ProtonManager>(); // Sunrise-Proton
} }
} }
} }

View file

@ -0,0 +1,80 @@
using Content.Shared._Sunrise.Proton;
using Robust.Client.Graphics;
using Robust.Shared.ContentPack;
using Robust.Shared.Network;
using Robust.Shared.Utility;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
namespace Content.Client._Sunrise.Proton;
public sealed class ProtonManager
{
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IResourceManager _resourceManager = default!;
[Dependency] private readonly IClientNetManager _netManager = default!;
[Dependency] private readonly ILogManager _logManager = default!;
private ISawmill _sawmill = default!;
public static readonly ResPath ProtonPath = new ResPath("/Proton");
public void Initialize()
{
IoCManager.InjectDependencies(this);
_sawmill = _logManager.GetSawmill("proton");
_resourceManager.UserData.CreateDir(ProtonPath);
_sawmill.Debug($"Proton Manager successfully initialized");
// Registration of messages
_netManager.RegisterNetMessage<ProtonRequestScreenshotClient>(HandleRequestScreenshot);
_netManager.RegisterNetMessage<ProtonRequestScreenshotServer>();
_netManager.RegisterNetMessage<ProtonResponseScreenshotClient>();
_netManager.RegisterNetMessage<ProtonResponseScreenshotServer>(HandleResponseScreenshot);
}
private void HandleRequestScreenshot(ProtonRequestScreenshotClient request)
{
_clyde.Screenshot(ScreenshotType.Final, pixels => {HandleScreenshot(ref request, pixels);});
_sawmill.Debug($"Successfully handled screenshot request");
}
private void HandleScreenshot(ref ProtonRequestScreenshotClient request, Image<Rgb24> screenshot)
{
var response = new ProtonResponseScreenshotClient()
{
Screenshot = screenshot,
};
_netManager.ClientSendMessage(response);
_sawmill.Debug($"Successfully sent screenshot request");
}
private void HandleResponseScreenshot(ProtonResponseScreenshotServer response)
{
if (response.Screenshot == null)
{
_sawmill.Debug($"Received response with null screenshot");
return;
}
var path = ProtonPath / $"test-{response.Screenshot.GetHashCode()}.png";
using var stream = _resourceManager.UserData.OpenWrite(path);
response.Screenshot.SaveAsPng(stream);
_sawmill.Debug($"Successfully received screenshot and saved it to {path}");
stream.Close();
using var imageStream = _resourceManager.UserData.OpenRead(path);
var view = new ProtonScreenshotView(Texture.LoadFromPNGStream(imageStream, "screenshot"));
view.OpenCentered();
imageStream.Close();
_sawmill.Debug($"Opened window (centered) successfully");
}
}

View file

@ -0,0 +1,10 @@
<controls:FancyWindow
xmlns="https://spacestation14.io"
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:latejoin="clr-namespace:Content.Client._Sunrise.Proton"
Title="{Loc 'admin-player-actions-screenshot'}"
HorizontalExpand="True"
VerticalExpand="True">
<TextureRect Name="TextureRect"></TextureRect>
</controls:FancyWindow>

View file

@ -0,0 +1,20 @@
using System.Numerics;
using Content.Client.UserInterface.Controls;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.XAML;
namespace Content.Client._Sunrise.Proton;
[GenerateTypedNameReferences]
public sealed partial class ProtonScreenshotView : FancyWindow
{
public ProtonScreenshotView(Texture image)
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
TextureRect.Texture = image;
TextureRect.TextureScale = new Vector2(0.7f, 0.7f);
}
}

View file

@ -0,0 +1,28 @@
using Content.Shared._Sunrise.Proton;
using Robust.Shared.Console;
using Robust.Shared.Network;
namespace Content.Client._Sunrise.Proton;
public sealed class ScreenshotCommand : LocalizedCommands
{
[Dependency] private readonly IClientNetManager _netManager = default!;
public override string Command => "screenshot";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1)
{
shell.WriteLine($"You need to provide a player to screenshot. Try running 'screenshot (playername)' as an example");
return;
}
var name = args[0];
var message = new ProtonRequestScreenshotServer()
{
Target = name,
};
_netManager.ClientSendMessage(message);
}
}

View file

@ -1,4 +1,5 @@
using Content.Server._Sunrise.GuideGenerator; using Content.Server._Sunrise.GuideGenerator;
using Content.Server._Sunrise.Proton; // Sunrise-Proton
using Content.Server._Sunrise.ServersHub; using Content.Server._Sunrise.ServersHub;
using Content.Server._Sunrise.TTS; using Content.Server._Sunrise.TTS;
using Content.Server.Acz; using Content.Server.Acz;
@ -48,6 +49,7 @@ namespace Content.Server.Entry
private IVoteManager _voteManager = default!; private IVoteManager _voteManager = default!;
private ServerUpdateManager _updateManager = default!; private ServerUpdateManager _updateManager = default!;
private ServersHubManager _serversHubManager = default!; // Sunrise-Edit private ServersHubManager _serversHubManager = default!; // Sunrise-Edit
private ProtonManager _proton = default!; // Proton
private PlayTimeTrackingManager? _playTimeTracking; private PlayTimeTrackingManager? _playTimeTracking;
private IEntitySystemManager? _sysMan; private IEntitySystemManager? _sysMan;
private IServerDbManager? _dbManager; private IServerDbManager? _dbManager;
@ -95,6 +97,7 @@ namespace Content.Server.Entry
_voteManager = IoCManager.Resolve<IVoteManager>(); _voteManager = IoCManager.Resolve<IVoteManager>();
_updateManager = IoCManager.Resolve<ServerUpdateManager>(); _updateManager = IoCManager.Resolve<ServerUpdateManager>();
_serversHubManager = IoCManager.Resolve<ServersHubManager>(); // Sunrise-Edit _serversHubManager = IoCManager.Resolve<ServersHubManager>(); // Sunrise-Edit
_proton = IoCManager.Resolve<ProtonManager>(); // Proton
_playTimeTracking = IoCManager.Resolve<PlayTimeTrackingManager>(); _playTimeTracking = IoCManager.Resolve<PlayTimeTrackingManager>();
_sysMan = IoCManager.Resolve<IEntitySystemManager>(); _sysMan = IoCManager.Resolve<IEntitySystemManager>();
_dbManager = IoCManager.Resolve<IServerDbManager>(); _dbManager = IoCManager.Resolve<IServerDbManager>();
@ -114,6 +117,7 @@ namespace Content.Server.Entry
IoCManager.Resolve<ServerApi>().Initialize(); IoCManager.Resolve<ServerApi>().Initialize();
IoCManager.Resolve<ServersHubManager>().Initialize(); // Sunrise-Hub IoCManager.Resolve<ServersHubManager>().Initialize(); // Sunrise-Hub
IoCManager.Resolve<ProtonManager>().Initialize(); // Proton
_voteManager.Initialize(); _voteManager.Initialize();
_updateManager.Initialize(); _updateManager.Initialize();

View file

@ -1,3 +1,4 @@
using Content.Server._Sunrise.Proton; // Sunrise-Proton
using Content.Server._Sunrise.ServersHub; using Content.Server._Sunrise.ServersHub;
using Content.Server._Sunrise.TTS; using Content.Server._Sunrise.TTS;
using Content.Server.Administration; using Content.Server.Administration;
@ -74,6 +75,7 @@ namespace Content.Server.IoC
IoCManager.Register<MappingManager>(); IoCManager.Register<MappingManager>();
IoCManager.Register<ServersHubManager>(); // Sunrise-Hub IoCManager.Register<ServersHubManager>(); // Sunrise-Hub
IoCManager.Register<ProtonManager>(); // Sunrise-Proton
} }
} }
} }

View file

@ -0,0 +1,144 @@
using Content.Server.Administration;
using Content.Server.Administration.Managers;
using Content.Shared._Sunrise.Proton;
using Content.Shared.Administration;
using Content.Shared.Administration.Managers;
using Robust.Server.Console;
using Robust.Server.Player;
using Robust.Shared.Console;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Timing;
using Timer = Robust.Shared.Timing.Timer;
namespace Content.Server._Sunrise.Proton;
public struct ProtonRequest
{
public NetUserId Admin { get; set; }
public TimeSpan TimeStamp { get; set; }
}
public sealed class ProtonManager
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IServerNetManager _netManager = default!;
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly ISharedAdminManager _adminManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IBanManager _banManager = default!;
[Dependency] private readonly IPlayerLocator _locator = default!;
[Dependency] private readonly IServerConsoleHost _console = default!;
private ISawmill _sawmill = default!;
/// <summary>
/// Стэк реквестов,
/// ключ - кому был дан запрос
/// значение - от кого был запрос
/// </summary>
private Dictionary<NetUserId, ProtonRequest> _requests = new ();
public void Initialize()
{
IoCManager.InjectDependencies(this);
_sawmill = _logManager.GetSawmill("proton");
_sawmill.Debug($"Proton Manager successfully initialized");
// Registration of messages
_netManager.RegisterNetMessage<ProtonRequestScreenshotClient>();
_netManager.RegisterNetMessage<ProtonRequestScreenshotServer>(HandleScreenshotRequest);
_netManager.RegisterNetMessage<ProtonResponseScreenshotClient>(HandleScreenshotResponse);
_netManager.RegisterNetMessage<ProtonResponseScreenshotServer>();
}
private void HandleScreenshotRequest(ProtonRequestScreenshotServer request)
{
var admin = _playerManager.GetSessionById(request.MsgChannel.UserId);
if (!_adminManager.HasAdminFlag(admin, AdminFlags.Moderator))
{
_sawmill.Debug($"{request.MsgChannel.UserName} requested screenshot without proper admin flags");
return;
}
if (request.Target == null)
{
_sawmill.Debug($"requested screenshot for null player");
return;
}
var req = new ProtonRequestScreenshotClient();
if (!_playerManager.TryGetSessionByUsername(request.Target, out var targetSession))
{
_sawmill.Debug($"requested screenshot for unexisting player");
return;
}
if (_requests.ContainsKey(targetSession.UserId))
{
_sawmill.Error($"Multiple requests of one screenshot");
return;
}
_netManager.ServerSendMessage(req, targetSession.Channel);
_requests.Add(targetSession.UserId, new ProtonRequest() { Admin = admin.UserId, TimeStamp = _gameTiming.CurTime });
_sawmill.Debug($"{request.MsgChannel.UserName} successfully requested screenshot of {targetSession.Channel.UserName}");
Timer.Spawn(TimeSpan.FromSeconds(30), () => {CheckTimerOnFired(targetSession);});
}
private async void CheckTimerOnFired(ICommonSession session)
{
if (!_requests.ContainsKey(session.UserId))
return;
BanPlayer(session, "Это автоматический бан. Клиент не ответил на пакет в течении заданного времени");
_sawmill.Debug($"Successfully banned {session.UserId} for not answering a packet");
}
private void BanPlayer(ICommonSession session, string message)
{
_console.ExecuteCommand($"ban {session.UserId} \"{message}\" 0 high");
}
private void HandleScreenshotResponse(ProtonResponseScreenshotClient response)
{
var session = _playerManager.GetSessionById(response.MsgChannel.UserId);
if (!_requests.ContainsKey(response.MsgChannel.UserId))
{
_sawmill.Debug($"Received screenshot response without screenshot");
BanPlayer(session, "Это автоматический бан. Клиент отправил пустой пакет.");
return;
}
if (response.Screenshot == null)
{
_sawmill.Debug($"Received screenshot response with null screenshot");
BanPlayer(session, "Это автоматический бан. Клиент отправил пустой пакет.");
return;
}
if (response.Screenshot.Height > 5000 || response.Screenshot.Width > 5000)
{
_sawmill.Debug($"Received screenshot response with too big screenshot");
BanPlayer(session, "Это автоматический бан. Клиент отправил слишком большой пакет.");
return;
}
var entry = _requests[response.MsgChannel.UserId];
var resp = new ProtonResponseScreenshotServer()
{
Screenshot = response.Screenshot,
};
var admin = _playerManager.GetSessionById(entry.Admin);
_netManager.ServerSendMessage(resp, admin.Channel);
_requests.Remove(response.MsgChannel.UserId);
}
}

View file

@ -0,0 +1,42 @@
using Lidgren.Network;
using Robust.Shared.Network;
using Robust.Shared.Serialization;
namespace Content.Shared._Sunrise.Proton;
/// <summary>
/// Request client to send screenshot
/// </summary>
public sealed class ProtonRequestScreenshotClient : NetMessage
{
public override MsgGroups MsgGroup => MsgGroups.Core;
public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer)
{
}
public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer)
{
}
}
/// <summary>
/// Send request for a screenshot of screen of a specific player
/// </summary>
public sealed class ProtonRequestScreenshotServer : NetMessage
{
public override MsgGroups MsgGroup => MsgGroups.Command;
// Data sent
public string? Target;
public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer)
{
buffer.Write(Target);
}
public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer)
{
Target = buffer.ReadString();
}
}

View file

@ -0,0 +1,99 @@
using System.IO;
using Lidgren.Network;
using Robust.Shared.Network;
using Robust.Shared.Serialization;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
namespace Content.Shared._Sunrise.Proton;
// Maybe we could use one type of message to both get screenshot from client to server and send screenshot from server
// to admin, who requested this screenshot.
/// <summary>
/// Response, containing screenshot of client's window
/// </summary>
public sealed class ProtonResponseScreenshotClient : NetMessage
{
public override MsgGroups MsgGroup => MsgGroups.Command;
// Data sent
public Image<Rgb24>? Screenshot;
public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer)
{
var imageDataLength = buffer.ReadInt32();
if (imageDataLength == 0)
{
Screenshot = null;
return;
}
var imageData = buffer.ReadBytes(imageDataLength);
using var memoryStream = new MemoryStream(imageData);
Screenshot = Image.Load<Rgb24>(memoryStream);
}
public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer)
{
if (Screenshot == null)
{
buffer.Write(0);
return;
}
using var memoryStream = new MemoryStream();
Screenshot.SaveAsPng(memoryStream);
var imageData = memoryStream.ToArray();
buffer.Write(imageData.Length);
buffer.Write(imageData);
}
}
/// <summary>
/// Screenshot, sent from server to target admin
/// </summary>
public sealed class ProtonResponseScreenshotServer : NetMessage
{
public override MsgGroups MsgGroup => MsgGroups.Command;
// Data sent
public Image<Rgb24>? Screenshot;
public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer)
{
var imageDataLength = buffer.ReadInt32();
if (imageDataLength == 0)
{
Screenshot = null;
return;
}
var imageData = buffer.ReadBytes(imageDataLength);
using var memoryStream = new MemoryStream(imageData);
Screenshot = Image.Load<Rgb24>(memoryStream);
}
public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer)
{
if (Screenshot == null)
{
buffer.Write(0);
return;
}
using var memoryStream = new MemoryStream();
Screenshot.SaveAsPng(memoryStream);
var imageData = memoryStream.ToArray();
buffer.Write(imageData.Length);
buffer.Write(imageData);
}
}

View file

@ -0,0 +1 @@
admin-player-actions-screenshot = Screenshot

View file

@ -0,0 +1 @@
admin-player-actions-screenshot = Просмотр экрана