Files
RobustToolbox/Robust.Shared/Serialization/Manager/Definition/DataDefinition.cs
Jezithyr 710371d7d1 UI refactor and UITheme implementations (#2712)
* 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>
2022-09-04 16:10:54 -07:00

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;
}
}
}