From 7efb3e3f3d04781c417e41ea1cb2326f521646bd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 24 May 2023 22:56:29 +0200 Subject: [PATCH] Add `SoftTypeReference` to scripting API for lazy-load type references (via typename) --- .../CustomEditors/Editors/TypeEditor.cs | 34 ++-- Source/Engine/Scripting/SoftTypeReference.cs | 87 ++++++++++ Source/Engine/Scripting/SoftTypeReference.h | 151 ++++++++++++++++++ Source/Engine/Serialization/JsonConverters.cs | 39 ++++- Source/Engine/Serialization/JsonSerializer.cs | 1 + 5 files changed, 298 insertions(+), 14 deletions(-) create mode 100644 Source/Engine/Scripting/SoftTypeReference.cs create mode 100644 Source/Engine/Scripting/SoftTypeReference.h diff --git a/Source/Editor/CustomEditors/Editors/TypeEditor.cs b/Source/Editor/CustomEditors/Editors/TypeEditor.cs index 722235088..9ee2c37a4 100644 --- a/Source/Editor/CustomEditors/Editors/TypeEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TypeEditor.cs @@ -393,11 +393,8 @@ namespace FlaxEditor.CustomEditors.Editors if (_element != null) { _element.CustomControl.ValueChanged += () => SetValue(_element.CustomControl.Value.Type); - if (_element.CustomControl.Type == ScriptType.Object) - { _element.CustomControl.Type = Values.Type.Type != typeof(object) || Values[0] == null ? ScriptType.Object : TypeUtils.GetObjectType(Values[0]); - } } } @@ -407,9 +404,7 @@ namespace FlaxEditor.CustomEditors.Editors base.Refresh(); if (!HasDifferentValues) - { _element.CustomControl.Value = new ScriptType(Values[0] as Type); - } } } @@ -425,9 +420,7 @@ namespace FlaxEditor.CustomEditors.Editors base.Initialize(layout); if (_element != null) - { _element.CustomControl.ValueChanged += () => SetValue(_element.CustomControl.Value); - } } /// @@ -436,9 +429,32 @@ namespace FlaxEditor.CustomEditors.Editors base.Refresh(); if (!HasDifferentValues) - { _element.CustomControl.Value = (ScriptType)Values[0]; - } + } + } + + /// + /// Default implementation of the inspector used to edit reference to the . Used to pick classes. + /// + [CustomEditor(typeof(SoftTypeReference)), DefaultEditor] + public class SoftTypeReferenceEditor : TypeEditorBase + { + /// + public override void Initialize(LayoutElementsContainer layout) + { + base.Initialize(layout); + + if (_element != null) + _element.CustomControl.ValueChanged += () => SetValue(new SoftTypeReference(_element.CustomControl.ValueTypeName)); + } + + /// + public override void Refresh() + { + base.Refresh(); + + if (!HasDifferentValues) + _element.CustomControl.ValueTypeName = ((SoftTypeReference)Values[0]).TypeName; } } diff --git a/Source/Engine/Scripting/SoftTypeReference.cs b/Source/Engine/Scripting/SoftTypeReference.cs new file mode 100644 index 000000000..f12f2bdb7 --- /dev/null +++ b/Source/Engine/Scripting/SoftTypeReference.cs @@ -0,0 +1,87 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +using System; + +namespace FlaxEngine +{ + /// + /// The soft reference to the scripting type contained in the scripting assembly. + /// + public struct SoftTypeReference : IComparable, IComparable + { + private string _typeName; + + /// + /// Gets or sets the type full name (eg. FlaxEngine.Actor). + /// + public string TypeName + { + get => _typeName; + set => _typeName = value; + } + + /// + /// Gets or sets the type (resolves soft reference). + /// + public Type Type + { + get => _typeName != null ? Type.GetType(_typeName) : null; + set => _typeName = value?.FullName; + } + + /// + /// Initializes a new instance of the . + /// + /// The type name. + public SoftTypeReference(string typeName) + { + _typeName = typeName; + } + + /// + /// Gets the soft type reference from full name. + /// + /// The type name. + /// The soft type reference. + public static implicit operator SoftTypeReference(string s) + { + return new SoftTypeReference { _typeName = s }; + } + + /// + /// Gets the soft type reference from runtime type. + /// + /// The type. + /// The soft type reference. + public static implicit operator SoftTypeReference(Type s) + { + return new SoftTypeReference { _typeName = s?.FullName }; + } + + /// + public override string ToString() + { + return _typeName; + } + + /// + public override int GetHashCode() + { + return _typeName?.GetHashCode() ?? 0; + } + + /// + public int CompareTo(object obj) + { + if (obj is SoftTypeReference other) + return CompareTo(other); + return 0; + } + + /// + public int CompareTo(SoftTypeReference other) + { + return string.Compare(_typeName, other._typeName, StringComparison.Ordinal); + } + } +} diff --git a/Source/Engine/Scripting/SoftTypeReference.h b/Source/Engine/Scripting/SoftTypeReference.h new file mode 100644 index 000000000..30508f639 --- /dev/null +++ b/Source/Engine/Scripting/SoftTypeReference.h @@ -0,0 +1,151 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Scripting.h" +#include "ScriptingObject.h" +#include "Engine/Core/Log.h" +#include "Engine/Core/Types/String.h" +#include "Engine/Serialization/SerializationFwd.h" + +/// +/// The soft reference to the scripting type contained in the scripting assembly. +/// +template +API_STRUCT(InBuild) struct SoftTypeReference +{ +protected: + StringAnsi _typeName; + +public: + SoftTypeReference() = default; + + SoftTypeReference(const SoftTypeReference& s) + : _typeName(s._typeName) + { + } + + SoftTypeReference(SoftTypeReference&& s) noexcept + : _typeName(MoveTemp(s._typeName)) + { + } + + SoftTypeReference(const StringView& s) + : _typeName(s) + { + } + + SoftTypeReference(const StringAnsiView& s) + : _typeName(s) + { + } + + SoftTypeReference(const char* s) + : _typeName(s) + { + } + +public: + FORCE_INLINE SoftTypeReference& operator=(SoftTypeReference&& s) noexcept + { + _typeName = MoveTemp(s._typeName); + return *this; + } + + FORCE_INLINE SoftTypeReference& operator=(StringAnsi&& s) noexcept + { + _typeName = MoveTemp(s); + return *this; + } + + FORCE_INLINE SoftTypeReference& operator=(const SoftTypeReference& s) + { + _typeName = s._typeName; + return *this; + } + + FORCE_INLINE SoftTypeReference& operator=(const StringAnsiView& s) + { + _typeName = s; + return *this; + } + + FORCE_INLINE bool operator==(const SoftTypeReference& other) const + { + return _typeName == other._typeName; + } + + FORCE_INLINE bool operator!=(const SoftTypeReference& other) const + { + return _typeName != other._typeName; + } + + FORCE_INLINE bool operator==(const StringAnsiView& other) const + { + return _typeName == other; + } + + FORCE_INLINE bool operator!=(const StringAnsiView& other) const + { + return _typeName != other; + } + + FORCE_INLINE operator bool() const + { + return _typeName.HasChars(); + } + +public: + // Gets the type full name (eg. FlaxEngine.Actor). + StringAnsiView GetTypeName() const + { + return StringAnsiView(_typeName); + } + + // Gets the type (resolves soft reference). + ScriptingTypeHandle GetType() const + { + return Scripting::FindScriptingType(_typeName); + } + + // Creates a new objects of that type (or of type T if failed to solve typename). + T* NewObject() const + { + const ScriptingTypeHandle type = Scripting::FindScriptingType(_typeName); + auto obj = ScriptingObject::NewObject(type); + if (!obj) + { + if (_typeName.HasChars()) + LOG(Error, "Unknown or invalid type {0}", String(_typeName)); + obj = ScriptingObject::NewObject(); + } + return obj; + } +}; + +template +uint32 GetHash(const SoftTypeReference& key) +{ + return GetHash(key.GetTypeName()); +} + +// @formatter:off +namespace Serialization +{ + template + bool ShouldSerialize(const SoftTypeReference& v, const void* otherObj) + { + return !otherObj || v != *(SoftTypeReference*)otherObj; + } + template + void Serialize(ISerializable::SerializeStream& stream, const SoftTypeReference& v, const void* otherObj) + { + stream.String(v.GetTypeName()); + } + template + void Deserialize(ISerializable::DeserializeStream& stream, SoftTypeReference& v, ISerializeModifier* modifier) + { + v = stream.GetTextAnsi(); + } +} +// @formatter:on diff --git a/Source/Engine/Serialization/JsonConverters.cs b/Source/Engine/Serialization/JsonConverters.cs index 07388a09b..40f9cb5a2 100644 --- a/Source/Engine/Serialization/JsonConverters.cs +++ b/Source/Engine/Serialization/JsonConverters.cs @@ -7,7 +7,7 @@ using Newtonsoft.Json; namespace FlaxEngine.Json { /// - /// Serialize references to the FlaxEngine.Object as Guid. + /// Serialize references to the as Guid. /// /// internal class FlaxObjectConverter : JsonConverter @@ -46,7 +46,7 @@ namespace FlaxEngine.Json } /// - /// Serialize SceneReference as Guid in internal format. + /// Serialize as Guid in internal format. /// /// internal class SceneReferenceConverter : JsonConverter @@ -79,7 +79,7 @@ namespace FlaxEngine.Json } /// - /// Serialize SoftObjectReference as Guid in internal format. + /// Serialize as Guid in internal format. /// /// internal class SoftObjectReferenceConverter : JsonConverter @@ -111,7 +111,36 @@ namespace FlaxEngine.Json } /// - /// Serialize SoftObjectReference as Guid in internal format. + /// Serialize as typename string in internal format. + /// + /// + internal class SoftTypeReferenceConverter : JsonConverter + { + /// + public override void WriteJson(JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer) + { + writer.WriteValue(((SoftTypeReference)value).TypeName); + } + + /// + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer) + { + var result = new SoftTypeReference(); + if (reader.TokenType == JsonToken.String) + result.TypeName = (string)reader.Value; + + return result; + } + + /// + public override bool CanConvert(Type objectType) + { + return objectType == typeof(SoftTypeReference); + } + } + + /// + /// Serialize as Guid in internal format. /// /// internal class MarginConverter : JsonConverter @@ -237,7 +266,7 @@ namespace FlaxEngine.Json } /// - /// Serialize LocalizedString as inlined text is not using localization (Id member is empty). + /// Serialize as inlined text is not using localization (Id member is empty). /// /// internal class LocalizedStringConverter : JsonConverter diff --git a/Source/Engine/Serialization/JsonSerializer.cs b/Source/Engine/Serialization/JsonSerializer.cs index 617768a83..30889fce0 100644 --- a/Source/Engine/Serialization/JsonSerializer.cs +++ b/Source/Engine/Serialization/JsonSerializer.cs @@ -123,6 +123,7 @@ namespace FlaxEngine.Json settings.Converters.Add(ObjectConverter); settings.Converters.Add(new SceneReferenceConverter()); settings.Converters.Add(new SoftObjectReferenceConverter()); + settings.Converters.Add(new SoftTypeReferenceConverter()); settings.Converters.Add(new MarginConverter()); settings.Converters.Add(new VersionConverter()); settings.Converters.Add(new LocalizedStringConverter());