mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
* UIControllerManager Implemented UI Controller Manager * added fetch function * added note * Hiding some internal stuff * Implemented event on gamestate switch for ui * Fix serialization field assigner emit * fixing issues with ILEmitter stuff * AHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH Blame Smug * fixing nullref * Add checking for no backing field / property for ui system dependencies * fixes Gamestate detection * Implemented event on UIControllers on system load * Updated systemload/unload listeners * Had this backwards lol * Fix nulling systems before calling OnSystemUnloaded, broke InventoryUIController.Hands.cs * Created UI Window management system - A manager that allows for easy creation and access of popup or gamestate windows * Changing to use basewindow instead of default window * Implemented UI Theming that isn't ass * Updated default theme loading and validation * Added path validation for themes * Implemented UI Themes * Implemented UI Theme prototypes * Implementing theming for texture buttons and Texturerects * fixing server error * Remove IUILink * Implemented default theme overriding and theme colors * Fixing sandbox lul * Added error for not finding UITheme * fixing setting default theme in content * Move entity and tile spawn window logic to controllers * Add 2 TODOs * Merge fixes * Add IOnStateChanged for UI controllers * Fix inventory window being slow to open Caches resources when the UI theme is changed * Remove caching on theme change The real fix was fixing the path for resources * Remove test method * Fix crash when controllers implement non generic interfaces * Add controllers frame update * Split UserInterfaceManager into partials - Created UI screen * Converted more UI managers into partials * Setup UIScreen manager system * Added some widget utility funcs updated adding widgets * Started removing HUDManager * Moved UiController Manager to Partials Finished moving all UIController code to UIManager * Fixed screen loading * Fixed Screen scaling * Fixed Screen scaling cleanup * wat * IwantToDie * Fixed resolving ResourceCache instead of IResourceCache * Split IOnStateChanged into IOnStateEntered and IOnStateExited * Implemented helpers for adjusting UIAutoscale for screens * Fixed autoscale, removed archiving from autoscale * Implemented popups and adjusted some stuff * Fixing some popup related shinanegans * Fixing some draw order issues * fixing dumb shit * Fix indentation in UserInterfaceManager.Input.cs * Moved screen setup to post init (run after content) * Fix updating theme * Merge fixes * Fix resolving sprite system on control creation * Fix min size of tile spawn window * Add UIController.Initialize method * https://tenor.com/view/minor-spelling-mistake-gif-21179057 * Add doc comment to UIController * Split UIController.cs and UISystemDependency.cs into their own files * Add more documentation to ui controllers * Add AttributeUsage to UISystemDependencyAttribute * Fix method naming * Add documentation for assigners * Return casted widgets where relevant * Fix entity spawner scroll (#1) * Add CloseOnClick and CloseOnEscape for popups * Remove named windows and popups * Cleanup controller code * Add IOnStateChanged, IOnSystemChanged, IOnSystemLoaded, IOnSystemUnloaded * Add more docs to state and system change interfaces * Fixing Window issues * Fixing some window fuckery * Added OnOpen event to windows, updated sandbox window Sandbox windows now persist values and positions * Recurse through controls to register widgets (#2) * Allow path to be folder * Fix local player shutdown * Fixing escape menu * Fix backing field in DataDefinition.Emitters * Ent+Tile spawn no crash * Skip no-spawn in entity spawn menu Co-authored-by: Jezithyr <jmaster9999@gmail.com> Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com> Co-authored-by: Jezithyr <Jezithyr@gmail.com> Co-authored-by: wrexbe <81056464+wrexbe@users.noreply.github.com> Co-authored-by: Flipp Syder <76629141+vulppine@users.noreply.github.com> Co-authored-by: wrexbe <wrexbe@protonmail.com>
321 lines
13 KiB
C#
321 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using Robust.Shared.IoC;
|
|
using Robust.Shared.Log;
|
|
using Robust.Shared.Serialization.Manager.Attributes;
|
|
using Robust.Shared.Serialization.Markdown.Mapping;
|
|
using Robust.Shared.Serialization.Markdown.Sequence;
|
|
using Robust.Shared.Serialization.Markdown.Validation;
|
|
using Robust.Shared.Serialization.Markdown.Value;
|
|
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
|
using Robust.Shared.Utility;
|
|
using YamlDotNet.Serialization.NamingConventions;
|
|
using static Robust.Shared.Serialization.Manager.SerializationManager;
|
|
|
|
namespace Robust.Shared.Serialization.Manager.Definition
|
|
{
|
|
public sealed partial class DataDefinition
|
|
{
|
|
private readonly struct FieldInterfaceInfo
|
|
{
|
|
public readonly (bool Value, bool Sequence, bool Mapping) Reader;
|
|
public readonly bool Writer;
|
|
public readonly bool Copier;
|
|
|
|
public FieldInterfaceInfo((bool Value, bool Sequence, bool Mapping) reader, bool writer, bool copier)
|
|
{
|
|
Reader = reader;
|
|
Writer = writer;
|
|
Copier = copier;
|
|
}
|
|
}
|
|
|
|
private readonly PopulateDelegateSignature _populate;
|
|
private readonly SerializeDelegateSignature _serialize;
|
|
private readonly CopyDelegateSignature _copy;
|
|
|
|
internal DataDefinition(Type type, IDependencyCollection collection, InstantiationDelegate<object> instantiator, bool isRecord)
|
|
{
|
|
Type = type;
|
|
IsRecord = isRecord;
|
|
|
|
var fieldDefs = GetFieldDefinitions(instantiator, isRecord);
|
|
|
|
var dataFields = fieldDefs
|
|
.Select(f => f.Attribute)
|
|
.OfType<DataFieldAttribute>().ToArray();
|
|
|
|
Duplicates = dataFields
|
|
.Where(f =>
|
|
dataFields.Count(df => df.Tag == f.Tag) > 1)
|
|
.Select(f => f.Tag)
|
|
.Distinct()
|
|
.ToArray();
|
|
|
|
var fields = fieldDefs;
|
|
|
|
fields.Sort((a, b) => b.Attribute.Priority.CompareTo(a.Attribute.Priority));
|
|
|
|
BaseFieldDefinitions = fields.ToImmutableArray();
|
|
DefaultValues = fieldDefs.Select(f => f.DefaultValue).ToArray();
|
|
|
|
_populate = EmitPopulateDelegate(collection);
|
|
_serialize = EmitSerializeDelegate(collection);
|
|
_copy = EmitCopyDelegate();
|
|
|
|
var fieldAccessors = new AccessField<object, object?>[BaseFieldDefinitions.Length];
|
|
var fieldAssigners = new AssignField<object, object?>[BaseFieldDefinitions.Length];
|
|
var interfaceInfos = new FieldInterfaceInfo[BaseFieldDefinitions.Length];
|
|
|
|
for (var i = 0; i < BaseFieldDefinitions.Length; i++)
|
|
{
|
|
var fieldDefinition = BaseFieldDefinitions[i];
|
|
|
|
fieldAccessors[i] = EmitFieldAccessor(fieldDefinition);
|
|
fieldAssigners[i] = EmitFieldAssigner<object>(Type, fieldDefinition.FieldType, fieldDefinition.BackingField);
|
|
|
|
if (fieldDefinition.Attribute.CustomTypeSerializer != null)
|
|
{
|
|
//reader (value, sequence, mapping), writer, copier
|
|
var reader = (false, false, false);
|
|
var writer = false;
|
|
var copier = false;
|
|
foreach (var @interface in fieldDefinition.Attribute.CustomTypeSerializer.GetInterfaces())
|
|
{
|
|
var genericTypedef = @interface.GetGenericTypeDefinition();
|
|
if (genericTypedef == typeof(ITypeWriter<>))
|
|
{
|
|
if (@interface.GenericTypeArguments[0].IsAssignableTo(fieldDefinition.FieldType))
|
|
{
|
|
writer = true;
|
|
}
|
|
}
|
|
else if (genericTypedef == typeof(ITypeCopier<>))
|
|
{
|
|
if (@interface.GenericTypeArguments[0].IsAssignableTo(fieldDefinition.FieldType))
|
|
{
|
|
copier = true;
|
|
}
|
|
}
|
|
else if (genericTypedef == typeof(ITypeReader<,>))
|
|
{
|
|
if (@interface.GenericTypeArguments[0].IsAssignableTo(fieldDefinition.FieldType))
|
|
{
|
|
if (@interface.GenericTypeArguments[1] == typeof(ValueDataNode))
|
|
{
|
|
reader.Item1 = true;
|
|
}else if (@interface.GenericTypeArguments[1] == typeof(SequenceDataNode))
|
|
{
|
|
reader.Item2 = true;
|
|
}else if (@interface.GenericTypeArguments[1] == typeof(MappingDataNode))
|
|
{
|
|
reader.Item3 = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!reader.Item1 && !reader.Item2 && !reader.Item3 && !writer && !copier)
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"Could not find any fitting implementation of ITypeReader, ITypeWriter or ITypeCopier for field {fieldDefinition}({fieldDefinition.FieldType}) on type {type} on CustomTypeSerializer {fieldDefinition.Attribute.CustomTypeSerializer}");
|
|
}
|
|
|
|
interfaceInfos[i] = new FieldInterfaceInfo(reader, writer, copier);
|
|
}
|
|
}
|
|
|
|
FieldAccessors = fieldAccessors.ToImmutableArray();
|
|
FieldAssigners = fieldAssigners.ToImmutableArray();
|
|
FieldInterfaceInfos = interfaceInfos.ToImmutableArray();
|
|
}
|
|
|
|
public Type Type { get; }
|
|
|
|
private string[] Duplicates { get; }
|
|
private object?[] DefaultValues { get; }
|
|
|
|
private ImmutableArray<AccessField<object, object?>> FieldAccessors { get; }
|
|
private ImmutableArray<AssignField<object, object?>> FieldAssigners { get; }
|
|
|
|
internal ImmutableArray<FieldDefinition> BaseFieldDefinitions { get; }
|
|
private ImmutableArray<FieldInterfaceInfo> FieldInterfaceInfos { get; }
|
|
|
|
internal bool IsRecord { get; }
|
|
|
|
public object Populate(
|
|
object target,
|
|
MappingDataNode mapping,
|
|
ISerializationManager serialization,
|
|
ISerializationContext? context,
|
|
bool skipHook)
|
|
{
|
|
return _populate(target, mapping, serialization, context, skipHook, DefaultValues);
|
|
}
|
|
|
|
public MappingDataNode Serialize(
|
|
object obj,
|
|
ISerializationManager serialization,
|
|
ISerializationContext? context,
|
|
bool alwaysWrite)
|
|
{
|
|
return _serialize(obj, serialization, context, alwaysWrite, DefaultValues);
|
|
}
|
|
|
|
public object Copy(
|
|
object source,
|
|
object target,
|
|
ISerializationManager serialization,
|
|
ISerializationContext? context)
|
|
{
|
|
return _copy(source, target, serialization, context);
|
|
}
|
|
|
|
public ValidationNode Validate(
|
|
ISerializationManager serialization,
|
|
MappingDataNode mapping,
|
|
ISerializationContext? context)
|
|
{
|
|
var validatedMapping = new Dictionary<ValidationNode, ValidationNode>();
|
|
|
|
foreach (var (key, val) in mapping.Children)
|
|
{
|
|
if (key is not ValueDataNode valueDataNode)
|
|
{
|
|
validatedMapping.Add(new ErrorNode(key, "Key not ValueDataNode."), new InconclusiveNode(val));
|
|
continue;
|
|
}
|
|
|
|
var field = BaseFieldDefinitions.FirstOrDefault(f => f.Attribute is DataFieldAttribute dataFieldAttribute && dataFieldAttribute.Tag == valueDataNode.Value);
|
|
if (field == null)
|
|
{
|
|
var error = new ErrorNode(
|
|
key,
|
|
$"Field \"{valueDataNode.Value}\" not found in \"{Type}\".",
|
|
false);
|
|
|
|
validatedMapping.Add(error, new InconclusiveNode(val));
|
|
continue;
|
|
}
|
|
|
|
var keyValidated = serialization.ValidateNode(typeof(string), key, context);
|
|
ValidationNode valValidated = field.Attribute.CustomTypeSerializer != null
|
|
? serialization.ValidateNodeWith(field.FieldType,
|
|
field.Attribute.CustomTypeSerializer, val, context)
|
|
: serialization.ValidateNode(field.FieldType, val, context);
|
|
|
|
validatedMapping.Add(keyValidated, valValidated);
|
|
}
|
|
|
|
return new ValidatedMappingNode(validatedMapping);
|
|
}
|
|
|
|
public bool CanCallWith(object obj) => Type.IsInstanceOfType(obj);
|
|
|
|
public bool TryGetDuplicates([NotNullWhen(true)] out string[] duplicates)
|
|
{
|
|
duplicates = Duplicates;
|
|
return duplicates.Length > 0;
|
|
}
|
|
|
|
private bool GatherFieldData(AbstractFieldInfo fieldInfo, out DataFieldBaseAttribute? dataFieldBaseAttribute,
|
|
[NotNullWhen(true)]out AbstractFieldInfo? backingField, out InheritanceBehavior inheritanceBehavior)
|
|
{
|
|
dataFieldBaseAttribute = null;
|
|
backingField = fieldInfo;
|
|
inheritanceBehavior = InheritanceBehavior.Default;
|
|
|
|
if (fieldInfo.HasAttribute<AlwaysPushInheritanceAttribute>(true))
|
|
{
|
|
inheritanceBehavior = InheritanceBehavior.Always;
|
|
}
|
|
else if (fieldInfo.HasAttribute<NeverPushInheritanceAttribute>(true))
|
|
{
|
|
inheritanceBehavior = InheritanceBehavior.Never;
|
|
}
|
|
|
|
if (fieldInfo is SpecificPropertyInfo propertyInfo)
|
|
{
|
|
// We only want the most overriden instance of a property for the type we are working with
|
|
if (!propertyInfo.IsMostOverridden(Type))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (propertyInfo.PropertyInfo.GetMethod == null)
|
|
{
|
|
Logger.ErrorS(LogCategory, $"Property {propertyInfo} is annotated with DataFieldAttribute but has no getter");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!fieldInfo.TryGetAttribute<DataFieldAttribute>(out var dataFieldAttribute, true))
|
|
{
|
|
if (!fieldInfo.TryGetAttribute<IncludeDataFieldAttribute>(out var includeDataFieldAttribute, true))
|
|
{
|
|
return true;
|
|
}
|
|
dataFieldBaseAttribute = includeDataFieldAttribute;
|
|
}
|
|
else
|
|
{
|
|
dataFieldBaseAttribute = dataFieldAttribute;
|
|
|
|
if (fieldInfo is SpecificPropertyInfo property && !dataFieldAttribute.ReadOnly && property.PropertyInfo.SetMethod == null)
|
|
{
|
|
if (!property.TryGetBackingField(out var backingFieldInfo))
|
|
{
|
|
Logger.ErrorS(LogCategory, $"Property {property} in type {property.DeclaringType} is annotated with DataFieldAttribute as non-readonly but has no auto-setter");
|
|
return false;
|
|
}
|
|
|
|
backingField = backingFieldInfo;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private List<FieldDefinition> GetFieldDefinitions(InstantiationDelegate<object> instantiator, bool isRecord)
|
|
{
|
|
var dummyObject = instantiator();
|
|
var fieldDefinitions = new List<FieldDefinition>();
|
|
|
|
foreach (var abstractFieldInfo in Type.GetAllPropertiesAndFields())
|
|
{
|
|
if (abstractFieldInfo.IsBackingField())
|
|
continue;
|
|
|
|
if (isRecord && abstractFieldInfo.IsAutogeneratedRecordMember())
|
|
continue;
|
|
|
|
if (!GatherFieldData(abstractFieldInfo, out var dataFieldBaseAttribute, out var backingField,
|
|
out var inheritanceBehavior))
|
|
continue;
|
|
|
|
if (dataFieldBaseAttribute == null)
|
|
{
|
|
if (!isRecord)
|
|
continue;
|
|
|
|
dataFieldBaseAttribute = new DataFieldAttribute(CamelCaseNamingConvention.Instance.Apply(abstractFieldInfo.Name));
|
|
}
|
|
|
|
var fieldDefinition = new FieldDefinition(
|
|
dataFieldBaseAttribute,
|
|
abstractFieldInfo.GetValue(dummyObject),
|
|
abstractFieldInfo,
|
|
backingField,
|
|
inheritanceBehavior);
|
|
|
|
fieldDefinitions.Add(fieldDefinition);
|
|
}
|
|
|
|
return fieldDefinitions;
|
|
}
|
|
}
|
|
}
|