Editor/TypeLibraryExtensions.cs
using Facepunch.ActionGraphs;
using Sandbox;
using Sandbox.Internal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
namespace ExtendedEditor;
public static class TypeLibraryExtensions
{
extension(TypeLibrary typeLibrary)
{
public SerializedProperty CreateProperty(Type type, string title, Func<object> getter,
Action<object> setter, Attribute[]? attributes = null, SerializedObject? parent = null)
{
var property = new ActionBasedSerializedProperty(type, title, title, "", getter, setter, attributes, parent);
property.PropertyToObject = PropertyToObject!;
return property;
}
}
private static SerializedObject? PropertyToObject(SerializedProperty property)
{
var value = property.GetValue<object>();
var result = value.GetSerialized();
result?.ParentProperty = property;
return result;
}
private class ActionBasedSerializedProperty : SerializedProperty
{
public Func<SerializedProperty, SerializedObject>? PropertyToObject;
private readonly Type _type;
private readonly string _name = string.Empty;
private readonly string _title = string.Empty;
private readonly string _description = string.Empty;
private readonly string _groupName = string.Empty;
private readonly string _sourceFile = string.Empty;
private readonly int _sourceLine = -1;
private readonly Func<object> _get;
private readonly Action<object> _set;
private readonly List<Attribute> _attributes;
private readonly SerializedObject? _parent;
public ActionBasedSerializedProperty(Type type, string name, string title, string description, Func<object> get,
Action<object> set, Attribute[]? attributes, SerializedObject? parent)
{
_type = type;
_name = name;
_title = title;
_description = description;
_get = get;
_set = set;
_attributes = [.. attributes ?? []];
_parent = parent;
_groupName = _attributes.OfType<IGroupAttribute>().FirstOrDefault()?.Value ?? _groupName;
_groupName = _attributes.OfType<ICategoryProvider>().FirstOrDefault()?.Value ?? _groupName;
_description = _attributes.OfType<IDescriptionAttribute>().FirstOrDefault()?.Value ?? _description;
_title = _attributes.OfType<ITitleProvider>().FirstOrDefault()?.Value ?? _title;
_sourceFile = _attributes.OfType<ISourcePathProvider>().FirstOrDefault()?.Path ?? _sourceFile;
_sourceLine = _attributes.OfType<ISourceLineProvider>().FirstOrDefault()?.Line ?? _sourceLine;
}
public override SerializedObject? Parent => _parent;
public override bool IsMethod => false;
public override string Name => _name;
public override string DisplayName => _title;
public override string Description => _description;
public override string GroupName => _groupName;
public override bool IsEditable => true;
public override int Order => 0;
public override Type PropertyType => _type;
public override string SourceFile => _sourceFile;
public override int SourceLine => _sourceLine;
public override bool HasChanges => false;
public override ref AsAccessor As => ref base.As;
public override U GetValue<U>(U defaultValue = default!) => ValueToType<U>(_get());
public override void SetValue<U>(U value) => _set(ValueToType(_type, value)!);
public override IEnumerable<Attribute> GetAttributes() => _attributes;
public override bool TryGetAsObject(out SerializedObject obj)
{
obj = PropertyToObject?.Invoke(this) ?? null!;
return obj is not null;
}
private static object? ValueToType(Type type, object value, object defaultValue = default!)
{
try
{
if(value is null)
return defaultValue;
if(value.GetType().IsAssignableTo(type))
return value;
if(type == typeof(string))
return (object)$"{value}";
if(value.GetType() == typeof(string))
return JsonSerializer.Deserialize((string)value, type);
// Convert.ChangeType doesn't support long to enum
if(type.IsEnum && value is IConvertible)
{
try
{
return Enum.ToObject(type, Convert.ToInt64(value));
}
catch
{
return defaultValue;
}
}
var converted = Convert.ChangeType(value, type);
if(converted is not null)
return converted;
var jsonElement = JsonSerializer.SerializeToElement(value);
return jsonElement.Deserialize(type);
}
catch(System.Exception)
{
return defaultValue;
}
}
}
}