Fix S.T.Json holding integration instances live for long. (#13080)

This commit is contained in:
Pieter-Jan Briers
2022-12-19 03:09:50 +01:00
committed by GitHub
parent 4bfc644a03
commit 896ffec8d9
6 changed files with 36 additions and 4 deletions

View File

@@ -24,6 +24,7 @@ public sealed partial class AdminLogManager
foreach (var converter in _reflection.FindTypesWithAttribute<AdminLogConverterAttribute>()) foreach (var converter in _reflection.FindTypesWithAttribute<AdminLogConverterAttribute>())
{ {
var instance = _typeFactory.CreateInstance<JsonConverter>(converter); var instance = _typeFactory.CreateInstance<JsonConverter>(converter);
(instance as IAdminLogConverter)?.Init(_dependencies);
_jsonOptions.Converters.Add(instance); _jsonOptions.Converters.Add(instance);
} }

View File

@@ -24,6 +24,7 @@ public sealed partial class AdminLogManager : SharedAdminLogManager, IAdminLogMa
[Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IDynamicTypeFactory _typeFactory = default!; [Dependency] private readonly IDynamicTypeFactory _typeFactory = default!;
[Dependency] private readonly IReflectionManager _reflection = default!; [Dependency] private readonly IReflectionManager _reflection = default!;
[Dependency] private readonly IDependencyCollection _dependencies = default!;
public const string SawmillId = "admin.logs"; public const string SawmillId = "admin.logs";

View File

@@ -3,8 +3,17 @@ using System.Text.Json.Serialization;
namespace Content.Server.Administration.Logs.Converters; namespace Content.Server.Administration.Logs.Converters;
public abstract class AdminLogConverter<T> : JsonConverter<T> public interface IAdminLogConverter
{ {
void Init(IDependencyCollection dependencies);
}
public abstract class AdminLogConverter<T> : JsonConverter<T>, IAdminLogConverter
{
public virtual void Init(IDependencyCollection dependencies)
{
}
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
throw new NotSupportedException(); throw new NotSupportedException();

View File

@@ -4,6 +4,7 @@ namespace Content.Server.Administration.Logs.Converters;
[AttributeUsage(AttributeTargets.Class)] [AttributeUsage(AttributeTargets.Class)]
[BaseTypeRequired(typeof(AdminLogConverter<>))] [BaseTypeRequired(typeof(AdminLogConverter<>))]
[MeansImplicitUse]
public sealed class AdminLogConverterAttribute : Attribute public sealed class AdminLogConverterAttribute : Attribute
{ {
} }

View File

@@ -6,7 +6,14 @@ namespace Content.Server.Administration.Logs.Converters;
[AdminLogConverter] [AdminLogConverter]
public sealed class EntityUidConverter : AdminLogConverter<EntityUid> public sealed class EntityUidConverter : AdminLogConverter<EntityUid>
{ {
[Dependency] private readonly IEntityManager _entities = default!; // System.Text.Json actually keeps hold of your JsonSerializerOption instances in a cache on .NET 7.
// Use a weak reference to avoid holding server instances live too long in integration tests.
private WeakReference<IEntityManager> _entityManager = default!;
public override void Init(IDependencyCollection dependencies)
{
_entityManager = new WeakReference<IEntityManager>(dependencies.Resolve<IEntityManager>());
}
public static void Write(Utf8JsonWriter writer, EntityUid value, JsonSerializerOptions options, IEntityManager entities) public static void Write(Utf8JsonWriter writer, EntityUid value, JsonSerializerOptions options, IEntityManager entities)
{ {
@@ -29,6 +36,9 @@ public sealed class EntityUidConverter : AdminLogConverter<EntityUid>
public override void Write(Utf8JsonWriter writer, EntityUid value, JsonSerializerOptions options) public override void Write(Utf8JsonWriter writer, EntityUid value, JsonSerializerOptions options)
{ {
Write(writer, value, options, _entities); if (!_entityManager.TryGetTarget(out var entityManager))
throw new InvalidOperationException("EntityManager got garbage collected!");
Write(writer, value, options, entityManager);
} }
} }

View File

@@ -6,13 +6,23 @@ namespace Content.Server.Administration.Logs.Converters;
[AdminLogConverter] [AdminLogConverter]
public sealed class PlayerSessionConverter : AdminLogConverter<SerializablePlayer> public sealed class PlayerSessionConverter : AdminLogConverter<SerializablePlayer>
{ {
// System.Text.Json actually keeps hold of your JsonSerializerOption instances in a cache on .NET 7.
// Use a weak reference to avoid holding server instances live too long in integration tests.
private WeakReference<IEntityManager> _entityManager = default!;
public override void Init(IDependencyCollection dependencies)
{
_entityManager = new WeakReference<IEntityManager>(dependencies.Resolve<IEntityManager>());
}
public override void Write(Utf8JsonWriter writer, SerializablePlayer value, JsonSerializerOptions options) public override void Write(Utf8JsonWriter writer, SerializablePlayer value, JsonSerializerOptions options)
{ {
writer.WriteStartObject(); writer.WriteStartObject();
if (value.Player.AttachedEntity is {Valid: true} playerEntity) if (value.Player.AttachedEntity is {Valid: true} playerEntity)
{ {
var entityManager = IoCManager.Resolve<IEntityManager>(); if (!_entityManager.TryGetTarget(out var entityManager))
throw new InvalidOperationException("EntityManager got garbage collected!");
writer.WriteNumber("id", (int) value.Player.AttachedEntity); writer.WriteNumber("id", (int) value.Player.AttachedEntity);
writer.WriteString("name", entityManager.GetComponent<MetaDataComponent>(playerEntity).EntityName); writer.WriteString("name", entityManager.GetComponent<MetaDataComponent>(playerEntity).EntityName);