using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using ILRuntime.CLR.Utils;
using ILRuntime.Mono.Cecil.Pdb;
using ILRuntime.Reflection;
using ILRuntime.Runtime.Intepreter;
using JEngine.Core;
using Malee.List;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
using AppDomain = ILRuntime.Runtime.Enviorment.AppDomain;
using Object = UnityEngine.Object;
namespace JEngine.Editor
{
[CustomEditor(typeof(ClassBind))]
internal class ClassBindEditor : UnityEditor.Editor
{
private ReorderableList _classBinds;
void OnEnable()
{
_classBinds = new ReorderableList(serializedObject.FindProperty("scriptsToBind"))
{
elementNameProperty = "className",
sortable = true,
multipleSelection = true,
};
}
public override void OnInspectorGUI()
{
serializedObject.Update();
_classBinds.DoLayoutList();
serializedObject.ApplyModifiedProperties();
GUILayout.Space(5);
Setting.MakeHorizontal(50, () =>
{
if (GUILayout.Button(Setting.GetString(SettingString.ClassBindGetAllField), GUILayout.Height(30)))
{
DoConvert(target as ClassBind);
}
});
GUILayout.Space(5);
Setting.MakeHorizontal(50, () =>
{
if (GUILayout.Button(Setting.GetString(SettingString.ClassBindGetAllType), GUILayout.Height(30)))
{
DoFieldType(target as ClassBind);
}
});
GUILayout.Space(5);
Setting.MakeHorizontal(50, () =>
{
if (GUILayout.Button(Setting.GetString(SettingString.ClassBindRearrangeTitle), GUILayout.Height(30)))
{
CleanFields(target as ClassBind);
}
});
GUILayout.Space(5);
Setting.MakeHorizontal(50,
() => { EditorGUILayout.HelpBox(Setting.GetString(SettingString.ClassBindInfo), MessageType.Info); });
GUILayout.Space(15);
}
///
/// 清理/排序/删除
///
///
///
public static async void CleanFields(ClassBind instance, bool toast = true)
{
int affectCounts = 0;
foreach (var data in instance.scriptsToBind) //遍历
{
string className =
$"{data.classNamespace + (string.IsNullOrEmpty(data.classNamespace) ? "" : ".")}{data.className}";
Type t = JEngine.Core.Tools.GetHotType(className); //加载热更类
if (t == null)
{
EditorUtility.DisplayDialog(Setting.GetString(SettingString.ClassBindErrorTitle),
String.Format(Setting.GetString(SettingString.ClassBindErrorContent),
className), "OK");
return;
}
var flag =
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance |
BindingFlags.Static;
if (Setting.ClassBindGetFromBase)
{
flag |= BindingFlags.FlattenHierarchy;
}
else
{
flag |= BindingFlags.DeclaredOnly;
}
for (int i = 0; i < data.fields.Count; i++)
{
var field = data.fields[i];
var fieldType = t.GetField(field.fieldName, flag)?.FieldType ??
t.GetProperty(field.fieldName, flag)?.PropertyType;
if (fieldType == null)
{
Log.PrintError(String.Format(Setting.GetString(SettingString.ClassBindInvalidFieldDeleted),
className, field.fieldName));
data.fields.RemoveAt(i);
i--;
continue;
}
affectCounts++;
EditorUtility.DisplayProgressBar(Setting.GetString(SettingString.ClassBindProgress),
String.Format(Setting.GetString(SettingString.ClassBindRearrange),
field.fieldName, data.fields.IndexOf(field), data.fields.Length),
data.fields.IndexOf(field) / (float)data.fields.Length);
await Task.Delay(50); //延迟一下,动画更丝滑
}
var f = data.fields.OrderBy(s => s.fieldName);
FieldList newF = new FieldList();
foreach (var cf in f)
{
newF.Add(cf);
}
data.fields = newF;
}
EditorUtility.ClearProgressBar();
TrySave(instance, toast, String.Format(Setting.GetString(SettingString.ClassBindRearrangeResult),
affectCounts, instance.name), Setting.GetString(SettingString.ClassBindResultTitle),
Setting.GetString(SettingString.Done));
}
///
/// 修改类型
///
///
///
public static async void DoFieldType(ClassBind instance, bool toast = true)
{
int affectCounts = 0;
foreach (var data in instance.scriptsToBind) //遍历
{
string className =
$"{data.classNamespace + (string.IsNullOrEmpty(data.classNamespace) ? "" : ".")}{data.className}";
Type t = JEngine.Core.Tools.GetHotType(className); //加载热更类
if (t == null)
{
EditorUtility.DisplayDialog(Setting.GetString(SettingString.ClassBindErrorTitle),
String.Format(Setting.GetString(SettingString.ClassBindErrorContent),
className), "OK");
return;
}
var flag =
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance |
BindingFlags.Static;
if (Setting.ClassBindGetFromBase)
{
flag |= BindingFlags.FlattenHierarchy;
}
else
{
flag |= BindingFlags.DeclaredOnly;
}
foreach (var field in data.fields)
{
var fieldType = t.GetField(field.fieldName, flag)?.FieldType ??
t.GetProperty(field.fieldName, flag)?.PropertyType;
if (fieldType == null)
{
Log.PrintError(String.Format(Setting.GetString(SettingString.ClassBindInvalidField),
className, field.fieldName));
}
SetType(field, fieldType);
affectCounts++;
EditorUtility.DisplayProgressBar(Setting.GetString(SettingString.ClassBindProgress),
String.Format(Setting.GetString(SettingString.ClassBindProgressContentForGetField),
field.fieldName, data.fields.IndexOf(field), data.fields.Length),
data.fields.IndexOf(field) / (float)data.fields.Length);
await Task.Delay(50); //延迟一下,动画更丝滑
}
}
EditorUtility.ClearProgressBar();
TrySave(instance, toast, String.Format(Setting.GetString(SettingString.ClassBindResultContentForGetType),
affectCounts, instance.name), Setting.GetString(SettingString.ClassBindResultTitle),
Setting.GetString(SettingString.Done));
}
///
/// 自动匹配
///
///
///
public static async void DoConvert(ClassBind instance, bool toast = true)
{
int affectCounts = 0;
foreach (var data in instance.scriptsToBind) //遍历
{
string className =
$"{data.classNamespace + (string.IsNullOrEmpty(data.classNamespace) ? "" : ".")}{data.className}";
Type t = JEngine.Core.Tools.GetHotType(className); //加载热更类
if (t == null)
{
EditorUtility.DisplayDialog(Setting.GetString(SettingString.ClassBindErrorTitle),
String.Format(Setting.GetString(SettingString.ClassBindErrorContent),
className), "OK");
return;
}
var fieldsInCb = data.fields.Select(f => f.fieldName).ToList(); //全部已经设置的字段
var flag = BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.SetProperty;
var flag4Private = BindingFlags.Public | BindingFlags.SetProperty;
if (Setting.ClassBindGetFromBase)
{
flag |= BindingFlags.FlattenHierarchy;
flag4Private |= BindingFlags.FlattenHierarchy;
}
else
{
flag |= BindingFlags.DeclaredOnly;
flag4Private |= BindingFlags.DeclaredOnly;
}
var members = new List(0);
//忽略有ClassBindIgnore标签的
var type = t;
while (type != null && type.FullName != null && !type.FullName.Contains(typeof(System.Object).FullName)
&& !type.FullName.Contains(typeof(MonoBehaviour).FullName)
&& !type.FullName.Contains("JEngine.Core.JBehaviour"))
{
var fs = type.GetFields(flag).ToList()
.FindAll(x => !Attribute.IsDefined(x, typeof(ClassBindIgnoreAttribute), true));
var ps = type.GetProperties(flag).ToList()
.FindAll(x => !Attribute.IsDefined(x, typeof(ClassBindIgnoreAttribute), true));
if (Setting.ClassBindIgnorePrivate)
{
members.AddRange(fs.FindAll(x => !x.IsPrivate));
members.AddRange(type.GetProperties(flag4Private).ToList().FindAll(x =>
!Attribute.IsDefined(x, typeof(ClassBindIgnoreAttribute), true)));
}
else
{
members.AddRange(fs);
members.AddRange(ps);
}
type = type.BaseType;
}
foreach (var field in members)
{
//跳过HideInInspector标签的
if (Setting.ClassBindIgnoreHideInInspector)
{
var attr = field.GetCustomAttributes(typeof(HideInInspector), false);
if (attr.Length > 0)
{
continue;
}
}
//遍历字段
EditorUtility.DisplayProgressBar(Setting.GetString(SettingString.ClassBindProgress),
String.Format(Setting.GetString(SettingString.ClassBindProgressContentForGetType),
$"{t.Name}:{field.Name}",
members.ToList().IndexOf(field), members.Count),
members.ToList().IndexOf(field) / (float)members.Count);
if (!fieldsInCb.Contains(field.Name))
{
ClassField cf = new ClassField();
string fieldName = field.Name;
cf.fieldName = fieldName;
Type fieldType = (field is PropertyInfo)
? ((PropertyInfo)field).PropertyType
: ((FieldInfo)field).FieldType;
SetType(cf, fieldType);
SetVal(ref cf, field, instance.gameObject);
data.fields.Add(cf);
affectCounts++;
}
await Task.Delay(10); //延迟一下,动画更丝滑
}
}
await Task.Delay(50); //延迟一下,动画更丝滑
EditorUtility.ClearProgressBar();
TrySave(instance, toast, String.Format(Setting.GetString(SettingString.ClassBindResultContentForSetField),
affectCounts, instance.name), Setting.GetString(SettingString.ClassBindResultTitle),
Setting.GetString(SettingString.Done));
}
private static void TrySave(ClassBind instance, bool toast, string text, string title = "", string ok = "")
{
EditorUtility.SetDirty(instance);
//转换后保存场景
try
{
PrefabUtility.SavePrefabAsset(instance.gameObject, out _);
}
catch
{
try
{
EditorSceneManager.SaveOpenScenes();
}
catch
{
try
{
var scene = SceneManager.GetActiveScene();
EditorSceneManager.SaveScene(scene, scene.path);
}
catch
{
//ignored
}
}
}
AssetDatabase.SaveAssets();
EditorUtility.ClearProgressBar();
if (toast)
{
EditorUtility.DisplayDialog(title, text, ok);
}
else
{
Log.Print(text);
}
}
private static void SetType(ClassField cf, Type type)
{
type =
type is ILRuntimeWrapperType wrapperType
? wrapperType.RealType
: type;
if (type == typeof(GameObject))
{
cf.fieldType = ClassField.FieldType.GameObject;
}
else if (type == typeof(Component) || type.IsSubclassOf(typeof(MonoBehaviour)) ||
JEngine.Core.Tools.IsJBehaviourType(type))
{
cf.fieldType = ClassField.FieldType.UnityComponent;
}
else if (type.IsSubclassOf(typeof(Object)))
{
if (type == typeof(AudioClip) || type == typeof(Sprite) || type == typeof(TextAsset) ||
type == typeof(Material))
{
cf.fieldType = ClassField.FieldType.HotUpdateResource;
}
else
{
cf.fieldType = ClassField.FieldType.UnityComponent;
}
}
else
{
var numType = new[]
{
typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long),
typeof(ulong),
typeof(float), typeof(decimal), typeof(double)
};
if (numType.Contains(type))
{
cf.fieldType = ClassField.FieldType.Number;
}
else if (type == typeof(string))
{
cf.fieldType = ClassField.FieldType.String;
}
else if (type == typeof(bool))
{
cf.fieldType = ClassField.FieldType.Bool;
}
else if (JEngine.Core.Tools.HasHotType(type.FullName))
{
cf.fieldType = ClassField.FieldType.UnityComponent;
}
else
{
cf.fieldType = ClassField.FieldType.NotSupported;
}
}
}
private static void SetVal(ref ClassField cf, MemberInfo field,
GameObject instance)
{
if (cf.fieldType == ClassField.FieldType.UnityComponent) return;
object value;
var type = (field is PropertyInfo)
? ((PropertyInfo)field).PropertyType
: ((FieldInfo)field).FieldType;
value = type.IsValueType ? Activator.CreateInstance(type) : null;
SetVal(ref cf, type, value, instance);
}
private static void SetVal(ref ClassField cf, Type type, object value, GameObject instance)
{
if (type is ILRuntimeType) return;
if (type != typeof(Object) ||
!JEngine.Core.Tools.IsJBehaviourType(type))
{
try
{
if (type == typeof(String) || cf.fieldType == ClassField.FieldType.String)
{
value = "";
}
cf.value = value.ToString();
}
catch
{
Log.PrintWarning(String.Format(Setting.GetString(SettingString.ClassBindUnableSetFieldValue),
instance.name, type.Name, cf.fieldName));
}
}
}
}
}