BS 0.1 基础构建完成 / 0.2 Visual 同为Unity UI控件部分
This commit is contained in:
417
Convention/[Runtime]/Architecture.cs
Normal file
417
Convention/[Runtime]/Architecture.cs
Normal file
@@ -0,0 +1,417 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.VisualBasic;
|
||||
|
||||
namespace Convention
|
||||
{
|
||||
// Interface
|
||||
|
||||
public interface ISignal
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public interface IModel
|
||||
{
|
||||
string Save();
|
||||
void Load(string data);
|
||||
}
|
||||
|
||||
public interface IConvertable<T>
|
||||
{
|
||||
T ConvertTo();
|
||||
}
|
||||
|
||||
public interface IConvertModel<T>
|
||||
: IModel, IConvertable<T>
|
||||
{
|
||||
}
|
||||
|
||||
// Instance
|
||||
|
||||
public class SingletonModel<T>: IModel
|
||||
{
|
||||
private static T InjectInstance = default;
|
||||
|
||||
public static T Instance
|
||||
{
|
||||
get => InjectInstance;
|
||||
set
|
||||
{
|
||||
if (value == null && InjectInstance == null)
|
||||
return;
|
||||
if (InjectInstance == null || InjectInstance.Equals(value) == false)
|
||||
{
|
||||
InjectInstance = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public T Data => InjectInstance;
|
||||
|
||||
void IModel.Load(string data)
|
||||
{
|
||||
if(typeof(T).GetInterfaces().Contains(typeof(IModel)))
|
||||
{
|
||||
typeof(T).GetMethod(nameof(IModel.Load))!.Invoke(Instance, new object[] { data });
|
||||
}
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
string IModel.Save()
|
||||
{
|
||||
if (typeof(T).GetInterfaces().Contains(typeof(IModel)))
|
||||
{
|
||||
return (string)typeof(T).GetMethod(nameof(IModel.Save))!.Invoke(Instance, Array.Empty<object>())!;
|
||||
}
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public static implicit operator T(SingletonModel<T> _) => InjectInstance;
|
||||
}
|
||||
|
||||
public class DependenceModel
|
||||
: IConvertModel<bool>, IEnumerable<IConvertModel<bool>>
|
||||
{
|
||||
private readonly IConvertModel<bool>[] queries;
|
||||
|
||||
public DependenceModel(params IConvertModel<bool>[] queries)
|
||||
{
|
||||
this.queries = queries;
|
||||
}
|
||||
public DependenceModel(IEnumerable<IConvertModel<bool>> queries)
|
||||
{
|
||||
this.queries = queries.ToArray();
|
||||
}
|
||||
|
||||
public bool ConvertTo()
|
||||
{
|
||||
foreach (var query in queries)
|
||||
{
|
||||
if (query.ConvertTo() == false)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public IEnumerator<IConvertModel<bool>> GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<IConvertModel<bool>>)this.queries).GetEnumerator();
|
||||
}
|
||||
|
||||
public virtual void Load(string data)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual string Save()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return this.queries.GetEnumerator();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Architecture
|
||||
{
|
||||
public static string FormatType(Type type)
|
||||
{
|
||||
return type.Assembly + "::" + type.FullName;
|
||||
}
|
||||
|
||||
public static Type LoadFromFormat(string data)
|
||||
{
|
||||
var keys = data.Split("::");
|
||||
Assembly asm = null;
|
||||
try
|
||||
{
|
||||
asm = Assembly.LoadFrom(keys[0]);
|
||||
return asm.GetType(keys[1]);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Type LoadFromFormat(string data, out Exception exception)
|
||||
{
|
||||
exception = null;
|
||||
var keys = data.Split("::");
|
||||
Assembly asm = null;
|
||||
try
|
||||
{
|
||||
asm = Assembly.LoadFrom(keys[0]);
|
||||
return asm.GetType(keys[1]);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void InternalReset()
|
||||
{
|
||||
// Register System
|
||||
RegisterHistory.Clear();
|
||||
UncompleteTargets.Clear();
|
||||
Completer.Clear();
|
||||
Dependences.Clear();
|
||||
Childs.Clear();
|
||||
// Event Listener
|
||||
SignalListener.Clear();
|
||||
// Linear Chain for Dependence
|
||||
TimelineQuenes.Clear();
|
||||
TimelineContentID = 0;
|
||||
}
|
||||
|
||||
#region Objects Registered
|
||||
|
||||
private class TypeQuery
|
||||
: IConvertModel<bool>
|
||||
{
|
||||
private Type queryType;
|
||||
|
||||
public TypeQuery(Type queryType)
|
||||
{
|
||||
this.queryType = queryType;
|
||||
}
|
||||
|
||||
public bool ConvertTo()
|
||||
{
|
||||
return Architecture.Childs.ContainsKey(queryType);
|
||||
}
|
||||
|
||||
public void Load(string data)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public string Save()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly HashSet<Type> RegisterHistory = new();
|
||||
private static readonly Dictionary<Type, object> UncompleteTargets = new();
|
||||
private static readonly Dictionary<Type, Action> Completer = new();
|
||||
private static readonly Dictionary<Type, DependenceModel> Dependences = new();
|
||||
private static readonly Dictionary<Type, object> Childs = new();
|
||||
|
||||
public class Registering : IConvertModel<bool>
|
||||
{
|
||||
private readonly Type registerSlot;
|
||||
|
||||
public Registering(Type registerSlot)
|
||||
{
|
||||
this.registerSlot = registerSlot;
|
||||
}
|
||||
|
||||
public bool ConvertTo()
|
||||
{
|
||||
return Architecture.Childs.ContainsKey(registerSlot);
|
||||
}
|
||||
|
||||
public void Load(string data)
|
||||
{
|
||||
throw new InvalidOperationException($"Cannt use {nameof(Registering)} to load type");
|
||||
}
|
||||
|
||||
public string Save()
|
||||
{
|
||||
return $"{FormatType(registerSlot)}[{ConvertTo()}]";
|
||||
}
|
||||
}
|
||||
|
||||
private static bool InternalRegisteringComplete(out HashSet<Type> InternalUpdateBuffer)
|
||||
{
|
||||
InternalUpdateBuffer = new();
|
||||
bool result = false;
|
||||
foreach (var dependence in Dependences)
|
||||
{
|
||||
if (dependence.Value.ConvertTo())
|
||||
{
|
||||
InternalUpdateBuffer.Add(dependence.Key);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void InternalRegisteringUpdate(HashSet<Type> InternalUpdateBuffer)
|
||||
{
|
||||
foreach (var complete in InternalUpdateBuffer)
|
||||
{
|
||||
Dependences.Remove(complete);
|
||||
}
|
||||
foreach (var complete in InternalUpdateBuffer)
|
||||
{
|
||||
Completer[complete]();
|
||||
Completer.Remove(complete);
|
||||
}
|
||||
foreach (var complete in InternalUpdateBuffer)
|
||||
{
|
||||
Childs.Add(complete, UncompleteTargets[complete]);
|
||||
UncompleteTargets.Remove(complete);
|
||||
}
|
||||
}
|
||||
|
||||
public static Registering Register(Type slot, object target, Action completer, params Type[] dependences)
|
||||
{
|
||||
if (RegisterHistory.Add(slot) == false)
|
||||
{
|
||||
throw new InvalidOperationException("Illegal duplicate registrations");
|
||||
}
|
||||
Completer[slot] = completer;
|
||||
UncompleteTargets[slot] = target;
|
||||
Dependences[slot] = new DependenceModel(from dependence in dependences where dependence != slot select new TypeQuery(dependence));
|
||||
while (InternalRegisteringComplete(out var buffer))
|
||||
InternalRegisteringUpdate(buffer);
|
||||
return new Registering(slot);
|
||||
}
|
||||
|
||||
public static Registering Register<T>(T target, Action completer, params Type[] dependences) => Register(typeof(T), target!, completer, dependences);
|
||||
|
||||
public static bool Contains(Type type) => Childs.ContainsKey(type);
|
||||
|
||||
public static bool Contains<T>() => Contains(typeof(T));
|
||||
|
||||
public static object InternalGet(Type type) => Childs[type];
|
||||
|
||||
public static object Get(Type type) => InternalGet(type);
|
||||
|
||||
public static T Get<T>() => (T)Get(typeof(T));
|
||||
|
||||
#endregion
|
||||
|
||||
#region Signal & Update
|
||||
|
||||
private static readonly Dictionary<Type, HashSet<Action<ISignal>>> SignalListener = new();
|
||||
|
||||
public class Listening
|
||||
{
|
||||
private readonly Action<ISignal> action;
|
||||
private readonly Type type;
|
||||
|
||||
public Listening(Action<ISignal> action, Type type)
|
||||
{
|
||||
this.action = action;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public void StopListening()
|
||||
{
|
||||
if (SignalListener.TryGetValue(type, out var actions))
|
||||
actions.Remove(action);
|
||||
}
|
||||
}
|
||||
|
||||
public static Listening AddListener<Signal>(Type slot, Action<Signal> listener) where Signal : ISignal
|
||||
{
|
||||
if (SignalListener.ContainsKey(slot) == false)
|
||||
SignalListener.Add(slot, new());
|
||||
void action(ISignal x)
|
||||
{
|
||||
if (x is Signal signal)
|
||||
listener(signal);
|
||||
}
|
||||
Listening result = new(action, slot);
|
||||
SignalListener[slot].Add(action);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void SendMessage(Type slot, ISignal signal)
|
||||
{
|
||||
if(SignalListener.TryGetValue(slot,out var actions))
|
||||
{
|
||||
foreach (var action in actions)
|
||||
{
|
||||
action(signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SendMessage<Signal>(Signal signal) where Signal : ISignal => SendMessage(signal.GetType(), signal);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Timeline/Chain & Update
|
||||
|
||||
private class TimelineQueneEntry
|
||||
{
|
||||
public Func<bool> predicate;
|
||||
public List<Action> actions = new();
|
||||
}
|
||||
|
||||
private class Timeline
|
||||
{
|
||||
public Dictionary<Func<bool>, int> PredicateMapper = new();
|
||||
public List<TimelineQueneEntry> Quene = new();
|
||||
public int Context = 0;
|
||||
}
|
||||
|
||||
private static Dictionary<int, Timeline> TimelineQuenes = new();
|
||||
private static int TimelineContentID = 0;
|
||||
|
||||
public static int CreateTimeline()
|
||||
{
|
||||
TimelineQuenes.Add(TimelineContentID++, new());
|
||||
return TimelineQuenes.Count;
|
||||
}
|
||||
|
||||
public static void AddStep(int timelineId, Func<bool> predicate,params Action[] actions)
|
||||
{
|
||||
var timeline = TimelineQuenes[timelineId];
|
||||
if (timeline.PredicateMapper.TryGetValue(predicate, out var time))
|
||||
{
|
||||
timeline.Quene[time].actions.AddRange(actions);
|
||||
}
|
||||
else
|
||||
{
|
||||
time = timeline.Quene.Count;
|
||||
timeline.PredicateMapper.Add(predicate, time);
|
||||
timeline.Quene.Add(new()
|
||||
{
|
||||
predicate = predicate,
|
||||
actions = actions.ToList()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateTimeline()
|
||||
{
|
||||
for (bool stats = true; stats;)
|
||||
{
|
||||
stats = false;
|
||||
foreach (var pair in TimelineQuenes)
|
||||
{
|
||||
var timeline = pair.Value;
|
||||
if (timeline.Quene[timeline.Context].predicate())
|
||||
{
|
||||
stats = true;
|
||||
foreach (var action in timeline.Quene[timeline.Context].actions)
|
||||
{
|
||||
action();
|
||||
}
|
||||
timeline.Context++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ResetTimelineContext(int timelineId)
|
||||
{
|
||||
TimelineQuenes[timelineId].Context = 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
11
Convention/[Runtime]/Architecture.cs.meta
Normal file
11
Convention/[Runtime]/Architecture.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05ba12419404f2945a5de57b43fb4342
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
400
Convention/[Runtime]/Config.cs
Normal file
400
Convention/[Runtime]/Config.cs
Normal file
@@ -0,0 +1,400 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Convention
|
||||
{
|
||||
public static class PlatformIndicator
|
||||
{
|
||||
#if DEBUG
|
||||
public static bool IsRelease => false;
|
||||
#else
|
||||
public static bool IsRelease => true;
|
||||
#endif
|
||||
public static bool IsPlatformWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||
public static bool IsPlatformLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
|
||||
public static bool IsPlatformOsx => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
|
||||
public static bool IsPlatformX64 => System.Environment.Is64BitOperatingSystem;
|
||||
|
||||
static PlatformIndicator()
|
||||
{
|
||||
MainThreadID = Thread.CurrentThread.ManagedThreadId;
|
||||
}
|
||||
|
||||
public static int MainThreadID { get; private set; }
|
||||
public static bool CurrentThreadIsMainThread()
|
||||
{
|
||||
return MainThreadID == Thread.CurrentThread.ManagedThreadId;
|
||||
}
|
||||
|
||||
public static string CompanyName = Application.companyName;
|
||||
|
||||
public static string ProductName = Application.productName;
|
||||
|
||||
public static string ApplicationPath => throw new NotSupportedException("Not support to get ApplicationPath");
|
||||
|
||||
public static string StreamingAssetsPath => Application.streamingAssetsPath;
|
||||
|
||||
public static string PersistentDataPath => Application.persistentDataPath;
|
||||
|
||||
public static string DataPath => Application.dataPath;
|
||||
}
|
||||
|
||||
public static partial class Utility
|
||||
{
|
||||
public static string ConvertString(object obj)
|
||||
{
|
||||
return Convert.ToString(obj);
|
||||
}
|
||||
public static T ConvertValue<T>(string str)
|
||||
{
|
||||
Type type = typeof(T);
|
||||
var parse_method = type.GetMethod("Parse");
|
||||
if (parse_method != null &&
|
||||
(parse_method.ReturnType.IsSubclassOf(type) || parse_method.ReturnType == type) &&
|
||||
parse_method.GetParameters().Length == 1 &&
|
||||
parse_method.GetParameters()[0].ParameterType == typeof(string))
|
||||
{
|
||||
return (T)parse_method.Invoke(null, new object[] { str });
|
||||
}
|
||||
|
||||
throw new InvalidCastException($"\"{str}\" is cannt convert to type<{type}>");
|
||||
}
|
||||
|
||||
|
||||
public static object SeekValue(object obj, string name, BindingFlags flags, out bool isSucceed)
|
||||
{
|
||||
Type type = obj.GetType();
|
||||
var field = type.GetField(name, flags);
|
||||
isSucceed = true;
|
||||
if (field != null)
|
||||
{
|
||||
return field.GetValue(obj);
|
||||
}
|
||||
var property = type.GetProperty(name, flags);
|
||||
if (property != null)
|
||||
{
|
||||
return property.GetValue(obj);
|
||||
}
|
||||
isSucceed = false;
|
||||
return null;
|
||||
}
|
||||
public static object SeekValue(object obj, string name, BindingFlags flags)
|
||||
{
|
||||
Type type = obj.GetType();
|
||||
var field = type.GetField(name, flags);
|
||||
if (field != null)
|
||||
{
|
||||
return field.GetValue(obj);
|
||||
}
|
||||
var property = type.GetProperty(name, flags);
|
||||
if (property != null)
|
||||
{
|
||||
return property.GetValue(obj);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static object SeekValue(object obj, string name, Type valueType, BindingFlags flags, out bool isSucceed)
|
||||
{
|
||||
Type type = obj.GetType();
|
||||
var field = type.GetField(name, flags);
|
||||
isSucceed = true;
|
||||
if (field != null && field.FieldType == valueType)
|
||||
{
|
||||
return field.GetValue(obj);
|
||||
}
|
||||
var property = type.GetProperty(name, flags);
|
||||
if (property != null && property.PropertyType == valueType)
|
||||
{
|
||||
return property.GetValue(obj);
|
||||
}
|
||||
isSucceed = false;
|
||||
return null;
|
||||
}
|
||||
public static object SeekValue(object obj, string name, Type valueType, BindingFlags flags)
|
||||
{
|
||||
Type type = obj.GetType();
|
||||
var field = type.GetField(name, flags);
|
||||
if (field != null && field.FieldType == valueType)
|
||||
{
|
||||
return field.GetValue(obj);
|
||||
}
|
||||
var property = type.GetProperty(name, flags);
|
||||
if (property != null && property.PropertyType == valueType)
|
||||
{
|
||||
return property.GetValue(obj);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static bool PushValue(object obj, object value, string name, BindingFlags flags)
|
||||
{
|
||||
Type type = obj.GetType();
|
||||
var field = type.GetField(name, flags);
|
||||
if (field != null)
|
||||
{
|
||||
field.SetValue(obj, value);
|
||||
return true;
|
||||
}
|
||||
var property = type.GetProperty(name, flags);
|
||||
if (property != null)
|
||||
{
|
||||
property.SetValue(obj, value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<Type> SeekType(Predicate<Type> pr, IEnumerable<Assembly> assemblys = null, int findCount = -1)
|
||||
{
|
||||
List<Type> types = new List<Type>();
|
||||
if (assemblys == null)
|
||||
assemblys = AppDomain.CurrentDomain.GetAssemblies();
|
||||
foreach (var assembly in assemblys)
|
||||
{
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
if (pr(type))
|
||||
types.Add(type);
|
||||
if (types.Count == findCount)
|
||||
return types;
|
||||
}
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
public static List<MemberInfo> GetMemberInfos(Type type, IEnumerable<Type> cutOffType = null, bool isGetNotPublic = false, bool isGetStatic = false)
|
||||
{
|
||||
Type current = type;
|
||||
BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
|
||||
List<MemberInfo> result = new();
|
||||
if (isGetNotPublic)
|
||||
flags |= BindingFlags.NonPublic;
|
||||
if (isGetStatic)
|
||||
flags |= BindingFlags.Static;
|
||||
while ((cutOffType != null && !cutOffType.Contains(current)) && current != null)
|
||||
{
|
||||
result.AddRange(current.GetFields(flags));
|
||||
result.AddRange(current.GetProperties(flags));
|
||||
result.AddRange(current.GetMethods(flags));
|
||||
current = current.BaseType;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool IsNumber([In] object data)
|
||||
{
|
||||
if (data == null) return false;
|
||||
var type = data.GetType();
|
||||
return IsNumber(type);
|
||||
}
|
||||
public static bool IsString([In] object data)
|
||||
{
|
||||
if (data == null) return false;
|
||||
var type = data.GetType();
|
||||
return IsString(type);
|
||||
}
|
||||
public static bool IsBinary([In] object data)
|
||||
{
|
||||
if (data == null) return false;
|
||||
var type = data.GetType();
|
||||
return IsBinary(type);
|
||||
}
|
||||
public static bool IsArray([In] object data)
|
||||
{
|
||||
if (data == null) return false;
|
||||
var type = data.GetType();
|
||||
return IsArray(type);
|
||||
}
|
||||
public static bool IsBool([In] object data)
|
||||
{
|
||||
if (data == null) return false;
|
||||
return IsBool(data.GetType());
|
||||
}
|
||||
|
||||
public static bool IsNumber([In] Type type)
|
||||
{
|
||||
return
|
||||
type == typeof(double) ||
|
||||
type == typeof(float) ||
|
||||
type == typeof(int) ||
|
||||
type == typeof(long) ||
|
||||
type == typeof(sbyte) ||
|
||||
type == typeof(short) ||
|
||||
type == typeof(ushort) ||
|
||||
type == typeof(uint) ||
|
||||
type == typeof(ulong) ||
|
||||
type == typeof(char);
|
||||
}
|
||||
public static bool IsString([In] Type type)
|
||||
{
|
||||
return type == typeof(string) || type == typeof(char[]);
|
||||
}
|
||||
public static bool IsBinary([In] Type type)
|
||||
{
|
||||
return
|
||||
type == typeof(byte) ||
|
||||
type == typeof(sbyte) ||
|
||||
type == typeof(byte[]) ||
|
||||
type == typeof(sbyte[]);
|
||||
}
|
||||
public static bool IsArray([In] Type type)
|
||||
{
|
||||
return type.IsArray;
|
||||
}
|
||||
public static bool IsBool([In] Type type)
|
||||
{
|
||||
return type == typeof(bool);
|
||||
}
|
||||
public static bool IsEnum([In] Type type)
|
||||
{
|
||||
return type.IsEnum;
|
||||
}
|
||||
|
||||
public static bool HasCustomAttribute(MemberInfo member, IEnumerable<Type> attrs)
|
||||
{
|
||||
foreach (var attr in attrs)
|
||||
{
|
||||
if (member.GetCustomAttribute(attr, true) != null)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public static Type GetMemberValueType(MemberInfo member)
|
||||
{
|
||||
if (member is FieldInfo field)
|
||||
{
|
||||
return field.FieldType;
|
||||
}
|
||||
else if (member is PropertyInfo property)
|
||||
{
|
||||
return property.PropertyType;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static bool GetMemberValueType(MemberInfo member, out Type type)
|
||||
{
|
||||
if (member is FieldInfo field)
|
||||
{
|
||||
type = field.FieldType;
|
||||
return true;
|
||||
}
|
||||
else if (member is PropertyInfo property)
|
||||
{
|
||||
type = property.PropertyType;
|
||||
return true;
|
||||
}
|
||||
type = null;
|
||||
return false;
|
||||
}
|
||||
public static void PushValue(object target, object value, MemberInfo info)
|
||||
{
|
||||
if (info is FieldInfo field)
|
||||
{
|
||||
if (value.GetType().IsSubclassOf(field.FieldType))
|
||||
field.SetValue(target, value);
|
||||
else
|
||||
{
|
||||
field.SetValue(target, field.FieldType.GetMethod(nameof(float.Parse)).Invoke(target, new object[] { value }));
|
||||
}
|
||||
}
|
||||
else if (info is PropertyInfo property)
|
||||
{
|
||||
property.SetValue(target, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("info is unsupport");
|
||||
}
|
||||
}
|
||||
public static object SeekValue(object target, MemberInfo info)
|
||||
{
|
||||
if (info is FieldInfo field)
|
||||
{
|
||||
return field.GetValue(target);
|
||||
}
|
||||
else if (info is PropertyInfo property)
|
||||
{
|
||||
return property.GetValue(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("info is unsupport");
|
||||
}
|
||||
}
|
||||
public static bool TrySeekValue(object target, MemberInfo info, out object value)
|
||||
{
|
||||
if (info is FieldInfo field)
|
||||
{
|
||||
value = field.GetValue(target);
|
||||
return true;
|
||||
}
|
||||
else if (info is PropertyInfo property)
|
||||
{
|
||||
value = property.GetValue(target);
|
||||
return true;
|
||||
}
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<MemberInfo> SeekMemberInfo(object target, IEnumerable<Type> attrs, IEnumerable<Type> types, Type untilBase = null)
|
||||
{
|
||||
Type _CurType = target.GetType();
|
||||
List<MemberInfo> result = new();
|
||||
result.AddRange(_CurType.GetMembers(BindingFlags.Public | BindingFlags.Instance));
|
||||
while (_CurType != null && _CurType != typeof(object) && _CurType != untilBase)
|
||||
{
|
||||
result.AddRange(
|
||||
from info in _CurType.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
where attrs == null || HasCustomAttribute(info, attrs)
|
||||
where types == null || (GetMemberValueType(info, out var type) && types.Contains(type))
|
||||
select info
|
||||
);
|
||||
_CurType = _CurType.BaseType;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public static List<MemberInfo> SeekMemberInfo(object target, IEnumerable<string> names, BindingFlags flags = BindingFlags.Default)
|
||||
{
|
||||
Type _CurType = target.GetType();
|
||||
List<MemberInfo> result = _CurType.GetMembers(flags).ToList();
|
||||
HashSet<string> nameSet = names.ToHashSet();
|
||||
result.RemoveAll(x => nameSet.Contains(x.Name) == false);
|
||||
return result;
|
||||
}
|
||||
public static object InvokeMember(MemberInfo member, object target, params object[] parameters)
|
||||
{
|
||||
if (member is MethodInfo method)
|
||||
{
|
||||
return method.Invoke(target, parameters);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static bool TryInvokeMember(MemberInfo member, object target, out object returnValue, params object[] parameters)
|
||||
{
|
||||
returnValue = null;
|
||||
if (member is MethodInfo method)
|
||||
{
|
||||
returnValue = method.Invoke(target, parameters);
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
public static T Shared<T>(T target, out T value)
|
||||
{
|
||||
value = target;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
public static string NowFormat(string format = "yyyy-MM-dd_HH-mm-ss")
|
||||
{
|
||||
return DateTime.Now.ToString(format);
|
||||
}
|
||||
}
|
||||
}
|
11
Convention/[Runtime]/Config.cs.meta
Normal file
11
Convention/[Runtime]/Config.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 85887203e7df0bf44b324c63e3e6e317
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
1207
Convention/[Runtime]/File.cs
Normal file
1207
Convention/[Runtime]/File.cs
Normal file
File diff suppressed because it is too large
Load Diff
11
Convention/[Runtime]/File.cs.meta
Normal file
11
Convention/[Runtime]/File.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 213daa024b1d15e4595ef7b17e684165
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
244
Convention/[Runtime]/GlobalConfig.cs
Normal file
244
Convention/[Runtime]/GlobalConfig.cs
Normal file
@@ -0,0 +1,244 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Convention
|
||||
{
|
||||
public class GlobalConfig : IEnumerable<KeyValuePair<string, object>>
|
||||
{
|
||||
public static string ConstConfigFile = "config.json";
|
||||
|
||||
public static void InitExtensionEnv()
|
||||
{
|
||||
ConstConfigFile = "config.json";
|
||||
ProjectConfig.InitExtensionEnv();
|
||||
}
|
||||
|
||||
public static void GenerateEmptyConfigJson(ToolFile file)
|
||||
{
|
||||
file.SaveAsRawJson<Dictionary<string, object>>(new()
|
||||
{
|
||||
{ "properties",new Dictionary<string, object>() }
|
||||
});
|
||||
}
|
||||
|
||||
private int configLogging_tspace = "Property not found".Length;
|
||||
|
||||
private ToolFile DataDir;
|
||||
private Dictionary<string, object> data_pair = new();
|
||||
|
||||
public GlobalConfig(string dataDir, bool isTryCreateDataDir = false, bool isLoad = true)
|
||||
: this(new ToolFile(dataDir), isTryCreateDataDir, isLoad) { }
|
||||
public GlobalConfig(ToolFile dataDir, bool isTryCreateDataDir = false, bool isLoad = true)
|
||||
{
|
||||
// build up data folder
|
||||
dataDir ??= new ToolFile("./");
|
||||
this.DataDir = dataDir;
|
||||
if (this.DataDir.IsDir() == false)
|
||||
this.DataDir.BackToParentDir();
|
||||
if (this.DataDir.Exists() == false)
|
||||
{
|
||||
if (isTryCreateDataDir)
|
||||
this.DataDir.MustExistsPath();
|
||||
else
|
||||
throw new Exception($"Data dir not found: {this.DataDir}");
|
||||
}
|
||||
// build up init data file
|
||||
var configFile = this.ConfigFile;
|
||||
if (configFile.Exists() == false)
|
||||
GenerateEmptyConfigJson(configFile);
|
||||
else if (isLoad)
|
||||
this.LoadProperties();
|
||||
}
|
||||
~GlobalConfig()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ToolFile GetConfigFile() => DataDir | ConstConfigFile;
|
||||
public ToolFile ConfigFile => GetConfigFile();
|
||||
|
||||
public ToolFile GetFile(string path, bool isMustExist = false)
|
||||
{
|
||||
var file = DataDir | path;
|
||||
if (isMustExist)
|
||||
file.MustExistsPath();
|
||||
return file;
|
||||
}
|
||||
public bool EraseFile(string path)
|
||||
{
|
||||
var file = DataDir | path;
|
||||
if (file.Exists())
|
||||
{
|
||||
file.Delete();
|
||||
file.Create();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public bool RemoveFile(string path)
|
||||
{
|
||||
var file = DataDir | path;
|
||||
if (file.Exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
file.Delete();
|
||||
return true;
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public bool CreateFile(string path)
|
||||
{
|
||||
var file = DataDir | path;
|
||||
if (file.Exists())
|
||||
return false;
|
||||
if (file.GetParentDir().Exists() == false)
|
||||
return false;
|
||||
file.Create();
|
||||
return true;
|
||||
}
|
||||
|
||||
public object this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
return data_pair[key];
|
||||
}
|
||||
set
|
||||
{
|
||||
data_pair[key] = value;
|
||||
}
|
||||
}
|
||||
public bool Contains(string key) => data_pair.ContainsKey(key);
|
||||
public bool Remove(string key)
|
||||
{
|
||||
if (data_pair.ContainsKey(key))
|
||||
{
|
||||
data_pair.Remove(key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<KeyValuePair<string, object>>)this.data_pair).GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable)this.data_pair).GetEnumerator();
|
||||
}
|
||||
public int DataSize() => data_pair.Count;
|
||||
|
||||
public GlobalConfig SaveProperties()
|
||||
{
|
||||
var configFile = this.ConfigFile;
|
||||
configFile.SaveAsRawJson<Dictionary<string, Dictionary<string, object>>>(new()
|
||||
{
|
||||
{ "properties", data_pair }
|
||||
});
|
||||
return this;
|
||||
}
|
||||
public GlobalConfig LoadProperties()
|
||||
{
|
||||
var configFile = this.ConfigFile;
|
||||
if (configFile.Exists() == false)
|
||||
{
|
||||
data_pair = new();
|
||||
}
|
||||
else
|
||||
{
|
||||
var data = configFile.LoadAsRawJson<Dictionary<string, Dictionary<string, object>>>();
|
||||
if (data.TryGetValue("properties", out data_pair) == false)
|
||||
{
|
||||
throw new Exception($"Can't find properties not found in config file");
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ToolFile GetLogFile()
|
||||
{
|
||||
return this.GetFile(ConfigFile.GetName(true) + "_log.txt", true);
|
||||
}
|
||||
public ToolFile LogFile => GetLogFile();
|
||||
|
||||
private Action<string> MyDefaultLogger;
|
||||
public Action<string> DefaultLogger
|
||||
{
|
||||
get
|
||||
{
|
||||
return MyDefaultLogger ?? Console.WriteLine;
|
||||
}
|
||||
set
|
||||
{
|
||||
MyDefaultLogger = value;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Log(string messageType, string message, Action<string> logger)
|
||||
{
|
||||
configLogging_tspace = Math.Max(configLogging_tspace, messageType.Length);
|
||||
(logger ?? DefaultLogger)($"[{Utility.NowFormat()}]{new string(' ', configLogging_tspace / 2)}{messageType}{new string(' ', configLogging_tspace - configLogging_tspace / 2)}: {message}");
|
||||
}
|
||||
public void Log(string messageType, string message) => Log(messageType, message, null);
|
||||
public void LogPropertyNotFound(string message, Action<string> logger, object @default = null)
|
||||
{
|
||||
if (@default != null)
|
||||
{
|
||||
message = $"{message} (default: {@default})";
|
||||
}
|
||||
Log("Property not found", message);
|
||||
}
|
||||
public void LogPropertyNotFound(string message, object @default = null)
|
||||
{
|
||||
if (@default != null)
|
||||
{
|
||||
message = $"{message} (default: {@default})";
|
||||
}
|
||||
Log("Property not found", message);
|
||||
}
|
||||
public void LogMessageOfPleaseCompleteConfiguration()
|
||||
{
|
||||
var message = "Please complete configuration";
|
||||
Log("Error", message);
|
||||
}
|
||||
|
||||
public object FindItem(string key, object @default = null)
|
||||
{
|
||||
if (Contains(key))
|
||||
{
|
||||
return this[key];
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPropertyNotFound(key, @default);
|
||||
return @default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ProjectConfig : GlobalConfig
|
||||
{
|
||||
private static string ProjectConfigFileFocus = "Assets/";
|
||||
|
||||
public static new void InitExtensionEnv()
|
||||
{
|
||||
ProjectConfigFileFocus = "Assets/";
|
||||
}
|
||||
|
||||
public ProjectConfig(bool isLoad = true) : base(ProjectConfigFileFocus, true, isLoad) { }
|
||||
|
||||
public static void SetProjectConfigFileFocus(string path)
|
||||
{
|
||||
ProjectConfigFileFocus = path;
|
||||
}
|
||||
public static string GetProjectConfigFileFocus()
|
||||
{
|
||||
return ProjectConfigFileFocus;
|
||||
}
|
||||
}
|
||||
}
|
11
Convention/[Runtime]/GlobalConfig.cs.meta
Normal file
11
Convention/[Runtime]/GlobalConfig.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f52809f177921c46ab08b68e2e2fe87
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Convention/[Runtime]/Plugins.meta
Normal file
8
Convention/[Runtime]/Plugins.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1004e4f82453e9f46941974d015f2ae2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
241
Convention/[Runtime]/Plugins/Plugin.cs
Normal file
241
Convention/[Runtime]/Plugins/Plugin.cs
Normal file
@@ -0,0 +1,241 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Convention
|
||||
{
|
||||
public static class PluginExtenion
|
||||
{
|
||||
public static string[] SelectMultipleFiles(string filter = "所有文件|*.*", string title = "选择文件")
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
return WindowsKit.SelectMultipleFiles(filter, title);
|
||||
#else
|
||||
throw new NotImplementedException();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string SelectFile(string filter = "所有文件|*.*", string title = "选择文件")
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
var results = WindowsKit.SelectMultipleFiles(filter, title);
|
||||
if (results != null && results.Length > 0)
|
||||
return results[0];
|
||||
#else
|
||||
throw new NotImplementedException();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string SaveFile(string filter = "保存文件|*.*", string title = "选择文件")
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
return WindowsKit.SaveFile(filter, title);
|
||||
#else
|
||||
throw new NotImplementedException();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string SelectFolder(string description = "请选择文件夹")
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
return WindowsKit.SelectFolder(description);
|
||||
#else
|
||||
throw new NotImplementedException();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public class PriorityQueue<T> where T : IComparable<T>
|
||||
{
|
||||
private int _size;
|
||||
private int _capacity;
|
||||
private T[] _elements;
|
||||
public readonly IComparer<T> _comparer = null;
|
||||
public readonly Func<T, T, int> _comparer_func = null;
|
||||
public readonly Comparator _comparator = Comparator.less;
|
||||
|
||||
public int Size => _size;
|
||||
public int Capacity => _capacity;
|
||||
public int Count => _size;
|
||||
public bool IsEmpty => _size == 0;
|
||||
public T Top => _elements[0];
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Array.Clear(_elements, 0, _size);
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
public PriorityQueue(Comparator comparator = Comparator.less, int capacity = 1)
|
||||
{
|
||||
_size = 0;
|
||||
_capacity = Math.Max(1, capacity);
|
||||
_comparator = comparator;
|
||||
_elements = new T[_capacity];
|
||||
}
|
||||
public PriorityQueue(IComparer<T> comparer, int capacity = 1)
|
||||
{
|
||||
_size = 0;
|
||||
_capacity = Math.Max(1, capacity);
|
||||
_comparer = comparer;
|
||||
_elements = new T[_capacity];
|
||||
}
|
||||
public PriorityQueue(Func<T, T, int> comparer, int capacity = 1)
|
||||
{
|
||||
_size = 0;
|
||||
_capacity = Math.Max(1, capacity);
|
||||
_comparer_func = comparer;
|
||||
_elements = new T[_capacity];
|
||||
}
|
||||
|
||||
private int Compare(T x, T y)
|
||||
{
|
||||
if (_comparer != null)
|
||||
{
|
||||
return _comparer.Compare(x, y) * (int)_comparator;
|
||||
}
|
||||
else if (_comparer_func != null)
|
||||
{
|
||||
return _comparer_func(x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
return x.CompareTo(y) * (int)_comparator;
|
||||
}
|
||||
}
|
||||
|
||||
private void ShiftDown()
|
||||
{
|
||||
int cur = 0;
|
||||
int child = 1;
|
||||
while (child < _size)
|
||||
{
|
||||
if (child + 1 < _size && Compare(_elements[child + 1], _elements[child]) < 0)
|
||||
child++;
|
||||
if (Compare(_elements[child], _elements[cur]) < 0)
|
||||
{
|
||||
Swap(ref _elements[child], ref _elements[cur]);
|
||||
cur = child;
|
||||
child = 2 * cur + 1;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ShiftUp()
|
||||
{
|
||||
int cur = _size - 1;
|
||||
int parent = (cur - 1) / 2;
|
||||
while (cur > 0)
|
||||
{
|
||||
if (Compare(_elements[cur], _elements[parent]) < 0)
|
||||
{
|
||||
Swap(ref _elements[cur], ref _elements[parent]);
|
||||
cur = parent;
|
||||
parent = (cur - 1) / 2;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ExpandCapacity()
|
||||
{
|
||||
int newCapacity = Math.Max(_capacity * 2, 4);
|
||||
T[] temp = new T[newCapacity];
|
||||
Array.Copy(_elements, temp, _size);
|
||||
_elements = temp;
|
||||
_capacity = newCapacity;
|
||||
}
|
||||
|
||||
public void EnsureCapacity(int minCapacity)
|
||||
{
|
||||
if (_capacity < minCapacity)
|
||||
{
|
||||
int newCapacity = Math.Max(_capacity * 2, minCapacity);
|
||||
T[] temp = new T[newCapacity];
|
||||
Array.Copy(_elements, temp, _size);
|
||||
_elements = temp;
|
||||
_capacity = newCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
public T Peek()
|
||||
{
|
||||
if (_size == 0)
|
||||
throw new InvalidOperationException("Queue is empty");
|
||||
return _elements[0];
|
||||
}
|
||||
|
||||
public T Dequeue()
|
||||
{
|
||||
if (_size == 0)
|
||||
throw new InvalidOperationException("Queue is empty");
|
||||
|
||||
T result = _elements[0];
|
||||
Swap(ref _elements[0], ref _elements[_size - 1]);
|
||||
_size--;
|
||||
ShiftDown();
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool TryDequeue(out T result)
|
||||
{
|
||||
if (_size == 0)
|
||||
{
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
result = Dequeue();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Enqueue(T value)
|
||||
{
|
||||
if (_size == _capacity)
|
||||
ExpandCapacity();
|
||||
_elements[_size++] = value;
|
||||
ShiftUp();
|
||||
}
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
for (int i = 0; i < _size; i++)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(_elements[i], item))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public T[] ToArray()
|
||||
{
|
||||
T[] result = new T[_size];
|
||||
Array.Copy(_elements, result, _size);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void TrimExcess()
|
||||
{
|
||||
if (_size < _capacity * 0.9)
|
||||
{
|
||||
T[] temp = new T[_size];
|
||||
Array.Copy(_elements, temp, _size);
|
||||
_elements = temp;
|
||||
_capacity = _size;
|
||||
}
|
||||
}
|
||||
|
||||
private void Swap(ref T a, ref T b)
|
||||
{
|
||||
T temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
|
||||
public enum Comparator
|
||||
{
|
||||
less = -1,
|
||||
equal = 0,
|
||||
greater = 1
|
||||
}
|
||||
}
|
||||
}
|
11
Convention/[Runtime]/Plugins/Plugin.cs.meta
Normal file
11
Convention/[Runtime]/Plugins/Plugin.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3f940668acbb414bad6df4108cc7296
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Convention/[Runtime]/Plugins/Windows.meta
Normal file
8
Convention/[Runtime]/Plugins/Windows.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a6905999a8fa084f8f367ab0a94426d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
129
Convention/[Runtime]/Plugins/Windows/WindowsKit.cs
Normal file
129
Convention/[Runtime]/Plugins/Windows/WindowsKit.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Convention
|
||||
{
|
||||
public static class WindowsKit
|
||||
{
|
||||
public static string current_initialDir = "";
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
||||
public class OpenFileName
|
||||
{
|
||||
public int structSize = 0;
|
||||
public IntPtr dlgOwner = IntPtr.Zero;
|
||||
public IntPtr instance = IntPtr.Zero;
|
||||
public string filter = null;
|
||||
public string customFilter = null;
|
||||
public int maxCustFilter = 0;
|
||||
public int filterIndex = 0;
|
||||
public string file = null;
|
||||
public int maxFile = 0;
|
||||
public string fileTitle = null;
|
||||
public int maxFileTitle = 0;
|
||||
public string initialDir = null;
|
||||
public string title = null;
|
||||
public int flags = 0;
|
||||
public short fileOffset = 0;
|
||||
public short fileExtension = 0;
|
||||
public string defExt = null;
|
||||
public IntPtr custData = IntPtr.Zero;
|
||||
public IntPtr hook = IntPtr.Zero;
|
||||
public string templateName = null;
|
||||
public IntPtr reservedPtr = IntPtr.Zero;
|
||||
public int reservedInt = 0;
|
||||
public int flagsEx = 0;
|
||||
}
|
||||
|
||||
[DllImport("Comdlg32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]
|
||||
public static extern bool GetOpenFileName([In, Out] OpenFileName ofn);
|
||||
|
||||
[DllImport("Comdlg32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]
|
||||
public static extern bool GetSaveFileName([In, Out] OpenFileName ofn);
|
||||
|
||||
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern IntPtr SHBrowseForFolder(ref BROWSEINFO lpbi);
|
||||
|
||||
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern bool SHGetPathFromIDList(IntPtr pidl, IntPtr pszPath);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
||||
public struct BROWSEINFO
|
||||
{
|
||||
public IntPtr hwndOwner;
|
||||
public IntPtr pidlRoot;
|
||||
public string pszDisplayName;
|
||||
public string lpszTitle;
|
||||
public uint ulFlags;
|
||||
public IntPtr lpfn;
|
||||
public IntPtr lParam;
|
||||
public int iImage;
|
||||
}
|
||||
|
||||
public static string SelectFolder(string description = "请选择文件夹")
|
||||
{
|
||||
BROWSEINFO bi = new BROWSEINFO();
|
||||
bi.lpszTitle = description;
|
||||
bi.ulFlags = 0x00000040; // BIF_NEWDIALOGSTYLE
|
||||
bi.hwndOwner = IntPtr.Zero;
|
||||
|
||||
IntPtr pidl = SHBrowseForFolder(ref bi);
|
||||
if (pidl != IntPtr.Zero)
|
||||
{
|
||||
IntPtr pathPtr = Marshal.AllocHGlobal(260);
|
||||
if (SHGetPathFromIDList(pidl, pathPtr))
|
||||
{
|
||||
string path = Marshal.PtrToStringAuto(pathPtr);
|
||||
Marshal.FreeHGlobal(pathPtr);
|
||||
current_initialDir = path;
|
||||
return path;
|
||||
}
|
||||
Marshal.FreeHGlobal(pathPtr);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string[] SelectMultipleFiles(string filter = "所有文件|*.*", string title = "选择文件")
|
||||
{
|
||||
OpenFileName ofn = new OpenFileName();
|
||||
ofn.structSize = Marshal.SizeOf(ofn);
|
||||
ofn.filter = filter.Replace("|", "\0") + "\0";
|
||||
ofn.file = new string(new char[256]);
|
||||
ofn.maxFile = ofn.file.Length;
|
||||
ofn.fileTitle = new string(new char[64]);
|
||||
ofn.maxFileTitle = ofn.fileTitle.Length;
|
||||
ofn.initialDir = current_initialDir;
|
||||
ofn.title = title;
|
||||
ofn.flags = 0x00080000 | 0x00001000 | 0x00000800 | 0x00000008 | 0x00000200; // OFN_ALLOWMULTISELECT
|
||||
|
||||
if (GetOpenFileName(ofn))
|
||||
{
|
||||
current_initialDir = Path.GetDirectoryName(ofn.file);
|
||||
return ofn.file.Split('\0');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string SaveFile(string filter = "所有文件|*.*", string title = "保存文件")
|
||||
{
|
||||
OpenFileName ofn = new OpenFileName();
|
||||
ofn.structSize = Marshal.SizeOf(ofn);
|
||||
ofn.filter = filter.Replace("|", "\0") + "\0";
|
||||
ofn.file = new string(new char[256]);
|
||||
ofn.maxFile = ofn.file.Length;
|
||||
ofn.fileTitle = new string(new char[64]);
|
||||
ofn.maxFileTitle = ofn.fileTitle.Length;
|
||||
ofn.initialDir = current_initialDir;
|
||||
ofn.title = title;
|
||||
ofn.flags = 0x00080000 | 0x00001000 | 0x00000800 | 0x00000008 | 0x00000002; // OFN_OVERWRITEPROMPT
|
||||
|
||||
if (GetSaveFileName(ofn))
|
||||
{
|
||||
current_initialDir = Path.GetDirectoryName(ofn.file);
|
||||
return ofn.file;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
11
Convention/[Runtime]/Plugins/Windows/WindowsKit.cs.meta
Normal file
11
Convention/[Runtime]/Plugins/Windows/WindowsKit.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 885b4479ac61fbd40ad648656379c0c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
355
Convention/[Runtime]/Web.cs
Normal file
355
Convention/[Runtime]/Web.cs
Normal file
@@ -0,0 +1,355 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace Convention
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class ToolURL
|
||||
{
|
||||
private string url;
|
||||
private static readonly HttpClient httpClient = new();
|
||||
private object data;
|
||||
|
||||
public ToolURL(string url)
|
||||
{
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.url;
|
||||
}
|
||||
|
||||
#region HTTP Methods
|
||||
|
||||
public async Task<bool> GetAsync(Action<HttpResponseMessage> callback)
|
||||
{
|
||||
if (!IsValid)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
var response = await httpClient.GetAsync(this.url);
|
||||
callback(response);
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
catch
|
||||
{
|
||||
callback(null);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Get(Action<HttpResponseMessage> callback)
|
||||
{
|
||||
return GetAsync(callback).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public async Task<bool> PostAsync(Action<HttpResponseMessage> callback, Dictionary<string, string> formData = null)
|
||||
{
|
||||
if (!IsValid)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
HttpContent content = null;
|
||||
if (formData != null)
|
||||
{
|
||||
content = new FormUrlEncodedContent(formData);
|
||||
}
|
||||
|
||||
var response = await httpClient.PostAsync(this.url, content);
|
||||
callback(response);
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
catch
|
||||
{
|
||||
callback(null);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Post(Action<HttpResponseMessage> callback, Dictionary<string, string> formData = null)
|
||||
{
|
||||
return PostAsync(callback, formData).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region URL Properties
|
||||
|
||||
public string FullURL => this.url;
|
||||
public static implicit operator string(ToolURL data) => data.FullURL;
|
||||
|
||||
public string GetFullURL()
|
||||
{
|
||||
return this.url;
|
||||
}
|
||||
|
||||
public string GetFilename()
|
||||
{
|
||||
if (string.IsNullOrEmpty(this.url))
|
||||
return "";
|
||||
|
||||
Uri uri = new Uri(this.url);
|
||||
string path = uri.AbsolutePath;
|
||||
return Path.GetFileName(path);
|
||||
}
|
||||
|
||||
public string GetExtension()
|
||||
{
|
||||
string filename = GetFilename();
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
return "";
|
||||
|
||||
return Path.GetExtension(filename);
|
||||
}
|
||||
|
||||
public bool ExtensionIs(params string[] extensions)
|
||||
{
|
||||
string el = GetExtension().ToLower();
|
||||
string eln = el.Length > 1 ? el[1..] : null;
|
||||
foreach (string extension in extensions)
|
||||
if (el == extension || eln == extension)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Validation
|
||||
|
||||
public bool IsValid => ValidateURL();
|
||||
|
||||
public bool ValidateURL()
|
||||
{
|
||||
if (string.IsNullOrEmpty(this.url))
|
||||
return false;
|
||||
|
||||
return Uri.TryCreate(this.url, UriKind.Absolute, out Uri uriResult)
|
||||
&& (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps);
|
||||
}
|
||||
|
||||
public static implicit operator bool(ToolURL url) => url.IsValid;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Load Methods
|
||||
|
||||
public async Task<string> LoadAsTextAsync()
|
||||
{
|
||||
if (!IsValid)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var response = await httpClient.GetAsync(this.url);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
this.data = await response.Content.ReadAsStringAsync();
|
||||
return (string)this.data;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 请求失败
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public string LoadAsText()
|
||||
{
|
||||
return LoadAsTextAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public async Task<byte[]> LoadAsBinaryAsync()
|
||||
{
|
||||
if (!IsValid)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var response = await httpClient.GetAsync(this.url);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
this.data = await response.Content.ReadAsByteArrayAsync();
|
||||
return (byte[])this.data;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 请求失败
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[] LoadAsBinary()
|
||||
{
|
||||
return LoadAsBinaryAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public T LoadAsJson<T>()
|
||||
{
|
||||
string jsonText = LoadAsText();
|
||||
if (string.IsNullOrEmpty(jsonText))
|
||||
return default(T);
|
||||
|
||||
try
|
||||
{
|
||||
T result = JsonUtility.FromJson<T>(jsonText);
|
||||
this.data = result;
|
||||
return result;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<T> LoadAsJsonAsync<T>()
|
||||
{
|
||||
string jsonText = await LoadAsTextAsync();
|
||||
if (string.IsNullOrEmpty(jsonText))
|
||||
return default(T);
|
||||
|
||||
try
|
||||
{
|
||||
T result = JsonUtility.FromJson<T>(jsonText);
|
||||
this.data = result;
|
||||
return result;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Save Methods
|
||||
|
||||
public void Save(string localPath = null)
|
||||
{
|
||||
if (IsText)
|
||||
SaveAsText(localPath);
|
||||
else if (IsJson)
|
||||
SaveAsJson(localPath);
|
||||
else
|
||||
SaveAsBinary(localPath);
|
||||
}
|
||||
|
||||
public void SaveAsText(string localPath = null)
|
||||
{
|
||||
if (localPath == null)
|
||||
{
|
||||
localPath = Path.Combine(Path.GetTempPath(), GetFilename());
|
||||
}
|
||||
|
||||
if (this.data is string text)
|
||||
{
|
||||
File.WriteAllText(localPath, text);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveAsJson(string localPath = null)
|
||||
{
|
||||
if (localPath == null)
|
||||
{
|
||||
localPath = Path.Combine(Path.GetTempPath(), GetFilename());
|
||||
}
|
||||
|
||||
if (this.data != null)
|
||||
{
|
||||
string jsonText = JsonUtility.ToJson(this.data);
|
||||
File.WriteAllText(localPath, jsonText);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveAsBinary(string localPath = null)
|
||||
{
|
||||
if (localPath == null)
|
||||
{
|
||||
localPath = Path.Combine(Path.GetTempPath(), GetFilename());
|
||||
}
|
||||
|
||||
if (this.data is byte[] bytes)
|
||||
{
|
||||
File.WriteAllBytes(localPath, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region URL Types
|
||||
|
||||
public bool IsText => ExtensionIs("txt", "html", "htm", "css", "js", "xml", "csv");
|
||||
public bool IsJson => ExtensionIs("json");
|
||||
public bool IsImage => ExtensionIs("jpg", "jpeg", "png", "gif", "bmp", "svg");
|
||||
public bool IsDocument => ExtensionIs("pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
public static ToolURL operator |(ToolURL left, string rightPath)
|
||||
{
|
||||
string baseUrl = left.GetFullURL();
|
||||
if (baseUrl.EndsWith('/'))
|
||||
{
|
||||
return new ToolURL(baseUrl + rightPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ToolURL(baseUrl + "/" + rightPath);
|
||||
}
|
||||
}
|
||||
|
||||
public ToolURL Open(string url)
|
||||
{
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
|
||||
public async Task<ToolURL> DownloadAsync(string localPath = null)
|
||||
{
|
||||
if (!IsValid)
|
||||
return this;
|
||||
|
||||
if (localPath == null)
|
||||
{
|
||||
localPath = Path.Combine(Path.GetTempPath(), GetFilename());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var response = await httpClient.GetAsync(this.url);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var bytes = await response.Content.ReadAsByteArrayAsync();
|
||||
await File.WriteAllBytesAsync(localPath, bytes);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 下载失败
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ToolURL Download(string localPath = null)
|
||||
{
|
||||
return DownloadAsync(localPath).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
11
Convention/[Runtime]/Web.cs.meta
Normal file
11
Convention/[Runtime]/Web.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9ec6a6437ba8c2499b966c24528761c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user