Большие фиксы

This commit is contained in:
Pupchansky
2024-09-30 21:50:43 +05:00
parent ea6bb47c44
commit 5397ccc28f
6 changed files with 237 additions and 77 deletions

View File

@@ -3,6 +3,7 @@ using System.Numerics;
using System.Threading;
using System.Threading.Tasks;
using Content.Client.Administration.Managers;
using Content.Shared.Chat.TypingIndicator;
using Content.Shared.Verbs;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
@@ -25,11 +26,12 @@ public sealed class ContentSpriteSystem : EntitySystem
[Dependency] private readonly IUserInterfaceManager _ui = default!;
//WL-Changes-start
[Dependency] private readonly ILogManager _logMan = default!;
[Dependency] private readonly AppearanceSystem _appearance = default!;
private ISawmill _sawmill = default!;
//WL-Changes-end
private readonly ContentSpriteControl<Rgba32> _control = new();
private ContentSpriteControl<Rgba32> _control = default!;
public static readonly ResPath Exports = new ResPath("/Exports");
@@ -39,6 +41,7 @@ public sealed class ContentSpriteSystem : EntitySystem
//WL-Changes-start
_sawmill = _logMan.GetSawmill("sprite.export");
_control = new(_appearance);
//WL-Changes-end
_resManager.UserData.CreateDir(Exports);
@@ -92,6 +95,8 @@ public sealed class ContentSpriteSystem : EntitySystem
Action<ContentSpriteControl<Rgba32>.QueueEntry, Image<Rgba32>> action,
CancellationToken cancelToken = default)
{
const string speechPath = "/Textures/Effects/speech.rsi"; //Я ебал вычислять ЕБУЧИЕ TypingIndicator-ы СУКАААА. легче так
if (!_timing.IsFirstTimePredicted)
return;
@@ -101,12 +106,26 @@ public sealed class ContentSpriteSystem : EntitySystem
// Don't want to wait for engine pr
var size = Vector2i.Zero;
foreach (var layer in spriteComp.AllLayers)
var comp_scale = spriteComp.Scale;
var offset = spriteComp.Offset;
foreach (var layer_ in spriteComp.AllLayers)
{
if (layer_ is not SpriteComponent.Layer layer)
continue;
if (!layer.Visible)
continue;
size = Vector2i.ComponentMax(size, layer.PixelSize);
var pixel = layer.PixelSize;
var scale = layer.Scale;
var new_x = (int)MathF.Ceiling((float)pixel.X * scale.X * comp_scale.X + offset.X);
var new_y = (int)MathF.Ceiling((float)pixel.Y * scale.Y * comp_scale.Y + offset.Y);
var new_size = new Vector2i(new_x, new_y);
size = Vector2i.ComponentMax(size, new_size);
}
// Stop asserts
@@ -195,14 +214,22 @@ public sealed class ContentSpriteSystem : EntitySystem
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly ILogManager _logMan = default!;
internal Queue<QueueEntry> _queuedTextures = new();
private readonly AppearanceSystem _appearance;
internal readonly Queue<QueueEntry> _queuedTextures;
private readonly Queue<QueueEntry> _defferedTextures;
private ISawmill _sawmill;
public ContentSpriteControl()
public ContentSpriteControl(AppearanceSystem appearance)
{
IoCManager.InjectDependencies(this);
_sawmill = _logMan.GetSawmill("sprite.export");
_appearance = appearance;
_queuedTextures = new();
_defferedTextures = new();
}
protected override void Draw(DrawingHandleScreen handle)
@@ -214,35 +241,75 @@ public sealed class ContentSpriteSystem : EntitySystem
if (queued.Tcs.Task.IsCanceled)
continue;
try
if (ShouldBeDeffered(queued))
{
if (!_entManager.TryGetComponent(queued.Entity, out MetaDataComponent? metadata))
continue;
var result = queued;
handle.RenderInRenderTarget(queued.Texture, () =>
{
handle.DrawEntity(result.Entity, result.Texture.Size / 2, Vector2.One, Angle.Zero,
overrideDirection: result.Direction);
}, Color.Transparent);
queued.Texture.CopyPixelsToMemory<T>(image =>
{
queued.Action.Invoke(queued, image);
});
queued.Tcs.SetResult();
_defferedTextures.Enqueue(queued);
continue;
}
catch (Exception exc)
HandleQueue(queued, handle);
}
while (_defferedTextures.TryDequeue(out var dequeue))
{
if (dequeue.Tcs.Task.IsCanceled)
continue;
if (ShouldBeDeffered(dequeue))
{
queued.Texture.Dispose();
if (!string.IsNullOrEmpty(exc.StackTrace))
_sawmill.Fatal(exc.StackTrace);
queued.Tcs.SetException(exc);
_queuedTextures.Enqueue(dequeue);
continue;
}
HandleQueue(dequeue, handle);
}
}
private bool ShouldBeDeffered(QueueEntry entry)
{
var entity = entry.Entity;
if (_appearance.TryGetData<TypingIndicatorState>(entity, TypingIndicatorVisuals.State, out var state))
{
if (state is not TypingIndicatorState.None)
{
return true;
}
}
return false;
}
private void HandleQueue(QueueEntry queued, DrawingHandleScreen handle)
{
try
{
if (!_entManager.TryGetComponent(queued.Entity, out MetaDataComponent? metadata))
return;
var result = queued;
handle.RenderInRenderTarget(queued.Texture, () =>
{
handle.DrawEntity(result.Entity, result.Texture.Size / 2, Vector2.One, Angle.Zero,
overrideDirection: result.Direction);
}, Color.Transparent);
queued.Texture.CopyPixelsToMemory<T>(image =>
{
queued.Action.Invoke(queued, image);
});
queued.Tcs.SetResult();
}
catch (Exception exc)
{
queued.Texture.Dispose();
if (!string.IsNullOrEmpty(exc.StackTrace))
_sawmill.Fatal(exc.StackTrace);
queued.Tcs.SetException(exc);
}
}

View File

@@ -23,34 +23,40 @@ namespace Content.Client._WL.Poly
SubscribeNetworkEvent<PolyServerQueryEvent>(OnServerQuery);
}
private void OnServerQuery(PolyServerQueryEvent args)
private async void OnServerQuery(PolyServerQueryEvent args)
{
var ent = GetEntity(args.Entity);
#pragma warning disable CS4014
_contentSpriteSystem.Export(ent, Direction.South, (queue, image) =>
try
{
try
await _contentSpriteSystem.Export(ent, Direction.South, (queue, image) =>
{
using var stream = new MemoryStream();
try
{
//TODO: проверить захватывает ли GC потоки, кхм
using var stream = new MemoryStream(1024);
image.SaveAsPng(stream);
image.SaveAsPng(stream);
using var reader = new StreamReader(stream);
stream.Position = 0;
var str = Convert.ToBase64String(stream.GetBuffer());
stream.Position = 0;
var str = reader.ReadToEnd();
var ev = new PolyClientResponseEvent(str, args.QueryId);
var ev = new PolyClientResponseEvent(str, args.QueryId);
_sawmill.Info($"Запрос от Поли успешно обработан! Сущность: {ToPrettyString(args.Entity)}");
RaiseNetworkEvent(ev);
}
catch (Exception)
{
_sawmill.Error("Неизвестная ошибка при рендере фотографии для Поли!");
}
});
#pragma warning restore CS4014
RaiseNetworkEvent(ev);
}
catch (Exception ex)
{
_sawmill.Error($"Неизвестная ошибка при рендере фотографии для Поли! {ex.Message}");
}
});
}
catch (Exception exc)
{
_sawmill.Error($"Error: {exc.Message}");
}
}
}
}

View File

@@ -45,6 +45,20 @@ public sealed partial class ServerApi
HttpMethod method,
string exactPath,
Func<IStatusHandlerContext, Actor, Dictionary<string, string>, Task> handler)
{
RegisterParameterizedHandler(method, exactPath, async (context, maps) =>
{
if (await CheckActor(context) is not { } actor)
return;
await handler(context, actor, maps);
});
}
private void RegisterParameterizedHandler(
HttpMethod method,
string exactPath,
Func<IStatusHandlerContext, Dictionary<string, string>, Task> handler)
{
_statusHost.AddHandler(async context =>
{
@@ -56,14 +70,11 @@ public sealed partial class ServerApi
if (!await CheckAccess(context))
return true;
if (await CheckActor(context) is not { } actor)
return true;
var formatted_maps = CheckPathes(absolute_path, exactPath);
if (formatted_maps.Count == 0)
return true;
await handler(context, actor, formatted_maps);
await handler(context, formatted_maps);
return true;
});
}
@@ -80,13 +91,13 @@ public sealed partial class ServerApi
if (!match.Success)
continue;
var to_replace = match.Groups[0].Value;
var name = match.Groups[1].Value;
var to_replace = match.Groups[1].Value;
var name = match.Groups[2].Value;
var inner_regex = new Regex(predictedPath.Replace(to_replace, "(.*)"));
var inner_match = inner_regex.Match(realPath).Groups[0].Value;
var inner_match = inner_regex.Match(realPath).Groups[1].Value;
dict.Add(name, inner_match);
dict.Add(name.Trim(), inner_match.Trim());
}
return dict;

View File

@@ -6,7 +6,6 @@ using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using Content.Server._WL.DiscordAuth;
using Content.Server._WL.Poly;
@@ -32,7 +31,6 @@ using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace Content.Server.Administration;
@@ -119,6 +117,8 @@ public sealed partial class ServerApi : IPostInjectInit
RegisterActorHandler(HttpMethod.Patch, "/admin/actions/server/shutdown", ShutdownServer);
RegisterHandler(HttpMethod.Get, "/admin/info/poly/random_message", PolyMessage);
RegisterParameterizedHandler(HttpMethod.Get, $"/admin/info/poly/images/{{${Constants.PolyMapImage}}}.png", PolyImage);
//wL-Changes-end
}
@@ -163,17 +163,55 @@ public sealed partial class ServerApi : IPostInjectInit
if (entry == null)
{
var is_ready = poly_system.IsReadyToPick();
var how_long = poly_system.HowLongBeforeReady();
var msg = is_ready
? "Поли ожидает подходящего сообщения!"
: $"Поли устала! До готовности: {how_long}";
await RespondError(
context,
ErrorCode.None,
HttpStatusCode.InternalServerError,
$"Поли ещё не выбрала подходящего сообщения! До готовности: {poly_system.HowLongBeforeReady()}");
msg);
return;
}
await context.RespondJsonAsync(entry.Value);
}
private async Task PolyImage(IStatusHandlerContext context, Dictionary<string, string> maps)
{
var poly_system = _entitySystemManager.GetEntitySystem<PolySystem>();
if (!maps.TryGetValue(Constants.PolyMapImage, out var map))
{
await RespondError(
context,
ErrorCode.None,
HttpStatusCode.InternalServerError,
"Ошибка при получении ссылки!");
return;
}
using var stream = poly_system.PickImage(map);
if (stream == null)
{
await RespondError(
context,
ErrorCode.None,
HttpStatusCode.InternalServerError,
"Изображение не было найдено!");
return;
}
await using var resp_stream = await context.RespondStreamAsync();
stream.CopyTo(resp_stream);
}
private async Task ShutdownServer(IStatusHandlerContext context, Actor actor)
{
if (!await IsAdmin(actor.Record.UserId))
@@ -990,4 +1028,11 @@ public sealed partial class ServerApi : IPostInjectInit
}
#endregion
//WL-Changes-start
private static class Constants
{
public const string PolyMapImage = "image";
}
//WL-Changes-end
}

View File

@@ -35,10 +35,8 @@ using Robust.Shared.Toolshed;
using Robust.Shared.Utility;
using System.Linq;
using Content.Server.Silicons.Laws;
using Content.Shared.Silicons.Laws;
using Content.Shared.Silicons.Laws.Components;
using Robust.Server.Player;
using Content.Shared.Mind;
using Robust.Shared.Physics.Components;
using static Content.Shared.Configurable.ConfigurationComponent;
using Content.Shared.Humanoid.Prototypes;

View File

@@ -12,6 +12,7 @@ using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using System.IO;
using System.Linq;
namespace Content.Server._WL.Poly
@@ -24,6 +25,9 @@ namespace Content.Server._WL.Poly
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IConfigurationManager _configMan = default!;
[Dependency] private readonly IPlayerManager _playMan = default!;
[Dependency] private readonly ILogManager _logMan = default!;
private ISawmill _sawmill = default!;
private List<MessageEntry> _messages = default!;
@@ -33,6 +37,7 @@ namespace Content.Server._WL.Poly
private bool _neededCleanup = false;
private Dictionary<string, ChatMessage> _queriedEntities = default!;
private Dictionary<string, string?> _handledImages = default!;
private const int MAX_QUERIES_PER_PLAYER = 20;
@@ -42,6 +47,9 @@ namespace Content.Server._WL.Poly
_messages = new();
_queriedEntities = new();
_handledImages = new();
_sawmill = _logMan.GetSawmill("poly.server");
_readyToPick = _configMan.GetCVar(WLCVars.PolyNeededRoundEndCleanup);
_chooseInterval = TimeSpan.FromSeconds(_configMan.GetCVar(WLCVars.PolyMessageChooseCooldown));
@@ -49,8 +57,6 @@ namespace Content.Server._WL.Poly
Subs.CVar(_configMan, WLCVars.PolyMessageChooseCooldown, (new_value) => _chooseInterval = TimeSpan.FromSeconds(new_value), true);
Subs.CVar(_configMan, WLCVars.PolyNeededRoundEndCleanup, (needed) => _neededCleanup = needed);
UpdatesOutsidePrediction = false;
SubscribeLocalEvent<RoundRestartCleanupEvent>((_) =>
{
if (!_neededCleanup)
@@ -76,8 +82,10 @@ namespace Content.Server._WL.Poly
if (!ShouldMessageBeChosen(msg))
return;
_readyToPick = false;
QueryAddMessage(msg);
ResetTimer();
_readyToPick = false;
};
_playMan.PlayerStatusChanged += (sender, args) =>
@@ -85,7 +93,7 @@ namespace Content.Server._WL.Poly
if (_playMan.Sessions.Length - 1 != 0)
return;
if (args.NewStatus != SessionStatus.Connected)
if (args.NewStatus is not SessionStatus.Connected or SessionStatus.InGame)
return;
HandleQueries();
@@ -103,7 +111,7 @@ namespace Content.Server._WL.Poly
if (_time >= _chooseInterval)
{
_time = TimeSpan.Zero;
ResetTimer();
_readyToPick = true;
}
}
@@ -121,19 +129,22 @@ namespace Content.Server._WL.Poly
{
var queried = args.QueryId;
if (!_queriedEntities.TryGetValue(queried, out var msg)) //Если событие было выслано всем клиентам, то каждый клиент отправит ответ,
return; //И тогда от каждого клиента добавится сообщение, эта проверка нужна, чтоб избежать этого
if (!_queriedEntities.TryGetValue(queried, out var msg))
return;
_queriedEntities.Remove(queried);
var entry = MessageToEntry(msg, args.Stream);
var entry = MessageToEntry(msg, queried);
_messages.Add(entry);
_handledImages.Add(queried, args.Stream);
}
private void HandleQueries()
{
var sessions = _playMan.Sessions.ToDictionary(k => k, v => 0);
var sessions = _playMan.Sessions
.Where(s => s.Status == SessionStatus.InGame)
.ToDictionary(k => k, v => 0);
if (sessions.Count == 0)
return;
@@ -143,11 +154,11 @@ namespace Content.Server._WL.Poly
if (session_pair == null)
return;
var session = session_pair.Value.Key;
var queries = session_pair.Value.Value;
foreach (var item in _queriedEntities)
{
var session = session_pair.Value.Key;
var queries = session_pair.Value.Value;
if (queries > MAX_QUERIES_PER_PLAYER)
{
session_pair = PickSession();
@@ -179,6 +190,28 @@ namespace Content.Server._WL.Poly
}
#region Public
public void ResetTimer()
{
_time = TimeSpan.Zero;
}
public Stream? PickImage(string id)
{
if (!_handledImages.TryGetValue(id, out var stream_string) || stream_string == null)
{
_handledImages.Remove(id);
return null;
}
_handledImages.Remove(id);
var bytes = Convert.FromBase64String(stream_string);
var stream = new MemoryStream(bytes);
return stream;
}
public void Clean()
{
_messages.Clear();
@@ -190,7 +223,7 @@ namespace Content.Server._WL.Poly
return _readyToPick;
}
public MessageEntry MessageToEntry(ChatMessage msg, string? pngBase64 = null)
public MessageEntry MessageToEntry(ChatMessage msg, string id)
{
var sender_ent = GetEntity(msg.SenderEntity);
@@ -201,7 +234,7 @@ namespace Content.Server._WL.Poly
SenderEntityName = Name(sender_ent),
RoundId = _ticker.RoundId,
IsRoundFlow = _ticker.RunLevel == GameRunLevel.InRound,
PngBase64 = pngBase64
ID = id
};
}
@@ -226,7 +259,7 @@ namespace Content.Server._WL.Poly
ChatChannel.Damage |
ChatChannel.Visual |
ChatChannel.Notifications |
ChatChannel.AdminRelated |
//ChatChannel.AdminRelated | //я всегда буду злодеем >:3
ChatChannel.Unspecified;
if (unnecessary_flags.HasFlag(msg.Channel))
@@ -248,5 +281,5 @@ namespace Content.Server._WL.Poly
string SenderEntityName,
int RoundId,
bool IsRoundFlow,
string? PngBase64 = null);
string ID);
}