1
This commit is contained in:
@@ -1,7 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace BITKit
|
||||
{
|
||||
public interface IAddressable
|
||||
{
|
||||
string AddressablePath { get; }
|
||||
|
||||
ulong AddressableId
|
||||
{
|
||||
get => ulong.MinValue;
|
||||
set
|
||||
{
|
||||
if (value <= 0) throw new ArgumentOutOfRangeException(nameof(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -155,7 +155,7 @@ namespace BITKit
|
||||
"FAE",
|
||||
"MK",
|
||||
"Lzf",
|
||||
|
||||
"MySql",
|
||||
};
|
||||
}
|
||||
#if NET5_0_OR_GREATER
|
||||
@@ -249,7 +249,6 @@ namespace BITKit
|
||||
Settings = settings??new AppSettings();
|
||||
CancellationTokenSource = new CancellationTokenSource();
|
||||
AppName = appName;
|
||||
ThreadHelper.LogCurrentThread();
|
||||
InitialTime = DateTime.Now;
|
||||
await Init();
|
||||
}
|
||||
@@ -270,9 +269,7 @@ namespace BITKit
|
||||
BIT4Log.Log<BITApp>("已加载全局appsettings");
|
||||
}
|
||||
//内部反射初始化
|
||||
ThreadHelper.InitAPP();
|
||||
await UniTask.SwitchToThreadPool();
|
||||
ThreadHelper.LogCurrentThread();
|
||||
Stopwatch stopwatch = new();
|
||||
stopwatch.Start();
|
||||
Stopwatch reflectionHelperWatch = new();
|
||||
@@ -284,11 +281,15 @@ namespace BITKit
|
||||
await BITCommands.InitializeAsync();
|
||||
commandWatch.Stop();
|
||||
|
||||
|
||||
Stopwatch binaryWatch = new();
|
||||
await BITBinary.Start();
|
||||
binaryWatch.Stop();
|
||||
|
||||
stopwatch.Stop();
|
||||
State = InitializationState.Initialized;
|
||||
BIT4Log.Log<BITApp>($"已完成初始化,耗时:{stopwatch.ElapsedMilliseconds}ms");
|
||||
BIT4Log.Log<BITApp>($"反射初始化耗时:{reflectionHelperWatch.ElapsedMilliseconds}ms");
|
||||
BIT4Log.Log<BITApp>($"初始化二进制序列化耗时:{binaryWatch.ElapsedMilliseconds}ms");
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
|
@@ -16,10 +16,15 @@ namespace BITKit.IO
|
||||
public DateTime LastModifiedTime;
|
||||
|
||||
// ReSharper disable once CollectionNeverQueried.Global
|
||||
public readonly List<string> Files = new List<string>();
|
||||
// ReSharper disable once FieldCanBeMadeReadOnly.Global
|
||||
public List<string> Files = new List<string>();
|
||||
}
|
||||
public class BITAssets : List<IAsset>
|
||||
{
|
||||
public static BITHeader ReadHeader(string path)
|
||||
{
|
||||
return Read<BITHeader>(path,"manifest.json");
|
||||
}
|
||||
public static void Build(string path, params IAsset[] assets)
|
||||
{
|
||||
List<IAsset> assetList = new();
|
||||
@@ -54,6 +59,7 @@ namespace BITKit.IO
|
||||
using var ms = new MemoryStream(x.Buffer);
|
||||
using var writer = entry.Open();
|
||||
ms.CopyTo(writer);
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
zipFile.Dispose();
|
||||
|
@@ -8,6 +8,10 @@ namespace BITKit
|
||||
public class ExcuteOnStartAttribute : Attribute { }
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class ExcuteOnStopAttribute : Attribute { }
|
||||
[AttributeUsage(AttributeTargets.Property|AttributeTargets.Field)]
|
||||
public class ReadOnlyAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
||||
public class ReadOnlyAttribute : Attribute
|
||||
{
|
||||
public bool HideLabel { get; set; }
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,17 @@ namespace BITKit
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class InjectAttribute : System.Attribute
|
||||
{
|
||||
public static void Clear(object obj)
|
||||
{
|
||||
var fields = obj.GetType().GetFields(ReflectionHelper.Flags);
|
||||
foreach (var field in fields)
|
||||
{
|
||||
if (field.GetCustomAttributes(typeof(InjectAttribute), true).Length > 0)
|
||||
{
|
||||
field.SetValue(obj, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
public readonly bool CanBeNull;
|
||||
public InjectAttribute()
|
||||
{
|
||||
@@ -20,21 +31,41 @@ namespace BITKit
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true, Inherited = true)]
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, AllowMultiple = true, Inherited = true)]
|
||||
public class CustomTypeAttribute : System.Attribute
|
||||
{
|
||||
public readonly Type Type;
|
||||
public readonly bool AsGlobal;
|
||||
public CustomTypeAttribute(Type type)
|
||||
{
|
||||
|
||||
Type = type;
|
||||
}
|
||||
public CustomTypeAttribute(Type type, bool asGlobal)
|
||||
{
|
||||
Type = type;
|
||||
AsGlobal = asGlobal;
|
||||
}
|
||||
public CustomTypeAttribute(bool asGlobal)
|
||||
{
|
||||
AsGlobal = asGlobal;
|
||||
}
|
||||
}
|
||||
#if UNITY_64
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true, Inherited = true)]
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||
public class ExportAttribute : System.Attribute
|
||||
{
|
||||
|
||||
public readonly string Name;
|
||||
public readonly IExportSetting Settings;
|
||||
public ExportAttribute(){}
|
||||
public ExportAttribute(IExportSetting settings)
|
||||
{
|
||||
Settings = settings;
|
||||
}
|
||||
public ExportAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
public interface IExportSetting{}
|
||||
#endif
|
||||
}
|
||||
|
@@ -45,7 +45,7 @@ namespace BITKit
|
||||
#if NET5_0_OR_GREATER
|
||||
Log($"[{DateTime.Now}]{typeof(T).Name}:{x}");
|
||||
#else
|
||||
Log($"{typeof(T).Name}:{x}");
|
||||
Log($"<color=#add8e6ff><b>{typeof(T).Name}</b></color>:{x}");
|
||||
#endif
|
||||
|
||||
currentType = typeof(T);
|
||||
@@ -69,7 +69,7 @@ namespace BITKit
|
||||
#endif
|
||||
public static void Warning<T>(object x)
|
||||
{
|
||||
Warning($"{typeof(T).Name}:{x}");
|
||||
Warning($"<color=#ffa500ff><b>{typeof(T).Name}</b></color>:{x}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -6,7 +6,9 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Microsoft.SqlServer.Server;
|
||||
#if NET5_0_OR_GREATER
|
||||
using Microsoft.SqlServer.Server;
|
||||
#endif
|
||||
@@ -17,9 +19,7 @@ namespace BITKit
|
||||
public class BITBinary
|
||||
{
|
||||
private static readonly Dictionary<string, INetMessageReader> netReaders = new();
|
||||
#if NET5_0_OR_GREATER
|
||||
public static readonly List<Type> serializableTypes = new();
|
||||
#endif
|
||||
|
||||
public static async UniTask Start()
|
||||
{
|
||||
@@ -27,30 +27,31 @@ namespace BITKit
|
||||
#if NET5_0_OR_GREATER
|
||||
serializableTypes.Clear();
|
||||
#endif
|
||||
var stringBuilder = new StringBuilder();
|
||||
foreach (var x in await ReflectionHelper.GetInstances<INetMessageReader>())
|
||||
{
|
||||
var typeName = x.GetMessageType().FullName;
|
||||
if (typeName == null) continue;
|
||||
netReaders.Add(typeName, x);
|
||||
BIT4Log.Log<BITBinary>($"已注册类型:{typeName}");
|
||||
stringBuilder.AppendLine(typeName);
|
||||
}
|
||||
// #if NET5_0_OR_GREATER
|
||||
// var serializes = await ReflectionHelper.GetInstances<IBinarySerialize>();
|
||||
// #else
|
||||
//
|
||||
// #endif
|
||||
// #if NET5_0_OR_GREATER
|
||||
// #else
|
||||
// serializes = serializes.Where(x => x is not UnityEngine.Object);
|
||||
// #endif
|
||||
// foreach (var x in serializes)
|
||||
// {
|
||||
// serializableTypes.Add(x.GetType());
|
||||
// BIT4Log.Log<BITBinary>($"已注册类型:{x.GetType().FullName}");
|
||||
// }
|
||||
|
||||
var serializes = await ReflectionHelper.GetInstances<IBinarySerialize>();
|
||||
#if NET5_0_OR_GREATER
|
||||
#else
|
||||
serializes = serializes.Where(x => x.GetType().IsAssignableFrom(typeof(UnityEngine.Object)) is false);
|
||||
#endif
|
||||
foreach (var x in serializes)
|
||||
{
|
||||
serializableTypes.Add(x.GetType());
|
||||
//BIT4Log.Log<BITBinary>($"已注册类型:{x.GetType().FullName}");
|
||||
stringBuilder.AppendLine(x.GetType().FullName);
|
||||
}
|
||||
|
||||
BIT4Log.Log<BITBinary>($"已注册类型:\n{stringBuilder}");
|
||||
}
|
||||
|
||||
public static object Read<T>(byte[] buffer) => (T)ReadAsValue(buffer);
|
||||
public static T Read<T>(byte[] buffer) => (T)ReadAsValue(buffer);
|
||||
public static object ReadAsValue(byte[] buffer)
|
||||
{
|
||||
using var ms = new MemoryStream(buffer);
|
||||
@@ -60,20 +61,35 @@ namespace BITKit
|
||||
|
||||
public static object Read(BinaryReader reader)
|
||||
{
|
||||
var isObjects = reader.ReadBoolean();
|
||||
if (isObjects)
|
||||
try
|
||||
{
|
||||
var objs = new object[reader.ReadInt32()];
|
||||
for (var i = 0; i < objs.Length; i++)
|
||||
if (reader.ReadBoolean())
|
||||
{
|
||||
objs[i] = Read(reader);
|
||||
var objs = new object[reader.ReadInt32()];
|
||||
for (var i = 0; i < objs.Length; i++)
|
||||
{
|
||||
objs[i] = Read(reader);
|
||||
}
|
||||
return objs.ToArray();
|
||||
}
|
||||
return objs.ToArray();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
var typeName = reader.ReadString();
|
||||
if (netReaders.TryGetValue(typeName, out var netReader))
|
||||
return netReader.ReadBinaryAsObject(reader);
|
||||
|
||||
var instance = System.Activator.CreateInstance(BITSharp.GetTypeFromFullName(typeName));
|
||||
if (instance is IBinarySerialize serialize)
|
||||
{
|
||||
serialize.Read(reader);
|
||||
return instance;
|
||||
}
|
||||
|
||||
var json = reader.ReadString();
|
||||
if (string.IsNullOrEmpty(json))
|
||||
{
|
||||
@@ -128,9 +144,22 @@ namespace BITKit
|
||||
{
|
||||
netReader.WriteBinaryAsObject(writer,value);
|
||||
}
|
||||
else if (value is IBinarySerialize serialize)
|
||||
{
|
||||
serialize.Write(writer);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(JsonConvert.SerializeObject(value));
|
||||
try
|
||||
{
|
||||
writer.Write(JsonConvert.SerializeObject(value));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
BIT4Log.Warning<BITBinary>(typeName);
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public static bool IsSupport(object obj) => IsSupport(obj.GetType().FullName);
|
||||
@@ -138,21 +167,9 @@ namespace BITKit
|
||||
public static bool IsSupport<T>() => IsSupport(typeof(T).FullName);
|
||||
public static bool IsSupport(string typeName)
|
||||
{
|
||||
if (netReaders.ContainsKey(typeName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#if NET5_0_OR_GREATER
|
||||
else if (serializableTypes.Any(x => x.FullName == typeName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
return netReaders.ContainsKey(typeName) || serializableTypes.Any(x => x.FullName == typeName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static bool TryWrite(object obj, out byte[] bytes)
|
||||
{
|
||||
bytes = null;
|
||||
|
@@ -17,9 +17,9 @@ namespace BITKit
|
||||
}
|
||||
public static void WriteFloat3(this BinaryWriter writer, float3 value)
|
||||
{
|
||||
value.x = MathB.Fix(value.x);
|
||||
value.y = MathB.Fix(value.y);
|
||||
value.z = MathB.Fix(value.z);
|
||||
//value.x = MathB.Fix(value.x);
|
||||
//value.y = MathB.Fix(value.y);
|
||||
//value.z = MathB.Fix(value.z);
|
||||
|
||||
writer.Write(value.x);
|
||||
writer.Write(value.y);
|
||||
@@ -40,10 +40,10 @@ namespace BITKit
|
||||
|
||||
public static void WriteFloat4(this BinaryWriter writer, float4 value)
|
||||
{
|
||||
value.x = MathB.Fix(value.x);
|
||||
value.y = MathB.Fix(value.y);
|
||||
value.z = MathB.Fix(value.z);
|
||||
value.w = MathB.Fix(value.z);
|
||||
//value.x = MathB.Fix(value.x);
|
||||
//value.y = MathB.Fix(value.y);
|
||||
//value.z = MathB.Fix(value.z);
|
||||
//value.w = MathB.Fix(value.z);
|
||||
|
||||
|
||||
writer.Write(value.x);
|
||||
|
8
Src/Core/Command Pattern.meta
Normal file
8
Src/Core/Command Pattern.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 242f24809addbc442a81534487aa2b17
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
52
Src/Core/Command Pattern/CommandPattern.cs
Normal file
52
Src/Core/Command Pattern/CommandPattern.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.SqlServer.Server;
|
||||
|
||||
namespace BITKit.CommandPattern
|
||||
{
|
||||
public interface ICommand
|
||||
{
|
||||
void Execute();
|
||||
void Undo();
|
||||
}
|
||||
public interface ICommandService
|
||||
{
|
||||
ICommand[] Commands { get; }
|
||||
event Action<ICommand> OnExecute;
|
||||
event Action<ICommand> OnClear;
|
||||
event Action<ICommand> OnUndo;
|
||||
event Action<ICommand[]> OnRelease;
|
||||
void Execute(ICommand command);
|
||||
void Undo();
|
||||
void Undo(int count);
|
||||
void Redo();
|
||||
void Trace(ICommand command);
|
||||
void Clear();
|
||||
void Rebuild();
|
||||
void Release();
|
||||
}
|
||||
|
||||
public sealed class CommandSequence:List<ICommand>,IBinarySerialize
|
||||
{
|
||||
public void Read(BinaryReader r)
|
||||
{
|
||||
Clear();
|
||||
var count = r.ReadInt32();
|
||||
for (var i = 0; i <count; i++)
|
||||
{
|
||||
Add(BITBinary.Read(r).As<ICommand>());
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter w)
|
||||
{
|
||||
w.Write(Count);
|
||||
foreach (var x in this)
|
||||
{
|
||||
BITBinary.Write(w,x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 80daba647c9a302418cae1bd15d9a078
|
||||
guid: 348d403129b862f46a56448b4e2e9528
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
@@ -1,11 +1,55 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using System.Text;
|
||||
|
||||
#if UNITY_64
|
||||
using UnityEngine;
|
||||
#endif
|
||||
namespace BITKit
|
||||
{
|
||||
#if UNITY_64
|
||||
[Serializable]
|
||||
public sealed class ExecuteCommandAction : IAction
|
||||
{
|
||||
[SerializeField] private string command;
|
||||
public void Execute()
|
||||
{
|
||||
BITCommands.Excute(command);
|
||||
}
|
||||
}
|
||||
[Serializable]
|
||||
public sealed class DataCondition : ICondition
|
||||
{
|
||||
[SerializeReference, SubclassSelector] private IReference key;
|
||||
public bool OnCheck()
|
||||
{
|
||||
return Data.Get<bool>(key.Value);
|
||||
}
|
||||
}
|
||||
[Serializable]
|
||||
public sealed class DataCommand : IAction,IConfigProvider
|
||||
{
|
||||
[SerializeReference, SubclassSelector] private IReference key;
|
||||
[SerializeReference, SubclassSelector] private IReference value = new Reference();
|
||||
public void Execute()
|
||||
{
|
||||
//Data.Set(key.Value, value.Value);
|
||||
DataParser.Set(key.Value, value.Value);
|
||||
}
|
||||
|
||||
public string GetConfig(params object[] args)
|
||||
{
|
||||
return $"{key.Value} {value.Value}";
|
||||
}
|
||||
|
||||
public void Configure(params object[] args)
|
||||
{
|
||||
Execute();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
public static class BITCommands
|
||||
{
|
||||
[BITCommand]
|
||||
|
@@ -5,7 +5,18 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace BITKit
|
||||
{
|
||||
public class DI
|
||||
public abstract class InjectFromDI<T>
|
||||
{
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return _value ??= DI.Get<T>();
|
||||
}
|
||||
}
|
||||
private T _value;
|
||||
}
|
||||
public sealed class DI
|
||||
{
|
||||
static readonly ConcurrentDictionary<Type, object> dictionary = new();
|
||||
[ExcuteOnStop]
|
||||
@@ -13,7 +24,7 @@ namespace BITKit
|
||||
{
|
||||
//dictionary.Clear();
|
||||
}
|
||||
|
||||
|
||||
public static void Register<Interface, Class>() where Class : new()
|
||||
{
|
||||
var type = typeof(Interface);
|
||||
@@ -86,28 +97,56 @@ namespace BITKit
|
||||
throw new NullReferenceException($"没有找到{typeof(T).Name}");
|
||||
}
|
||||
}
|
||||
public static bool TryGet<T>(out T value)
|
||||
{
|
||||
lock (dictionary)
|
||||
{
|
||||
if (dictionary.TryGetValue(typeof(T), out var obj))
|
||||
{
|
||||
if (obj is T i)
|
||||
{
|
||||
value = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
public static async Task<Interface> GetAsync<Interface>()
|
||||
{
|
||||
await TaskHelper.WaitUntil(() => dictionary.ContainsKey(typeof(Interface)) && dictionary[typeof(Interface) ]is not null);
|
||||
|
||||
return Get<Interface>();
|
||||
}
|
||||
/// <summary>
|
||||
/// 自动依赖注入,将所有带有<see cref="InjectAttribute"/>的字段注入
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
public static void Inject(object obj)
|
||||
{
|
||||
foreach (var field in obj.GetType().GetFields())
|
||||
foreach (var field in obj.GetType().GetFields(ReflectionHelper.Flags))
|
||||
{
|
||||
if (field.GetCustomAttribute<ObsoleteAttribute>() is null &&
|
||||
field.GetCustomAttribute<InjectAttribute>() is not null)
|
||||
try
|
||||
{
|
||||
lock (dictionary)
|
||||
if (field.GetCustomAttribute<ObsoleteAttribute>() is null &&
|
||||
field.GetCustomAttribute<InjectAttribute>() is not null)
|
||||
{
|
||||
if(dictionary!.TryGetValue(field.FieldType,out var value))
|
||||
lock (dictionary)
|
||||
{
|
||||
BIT4Log.Log<DI>($"已为{obj.GetType().Name}.{field.Name}注入{value.GetType().Name}");
|
||||
field.SetValue(obj,value);
|
||||
if(dictionary!.TryGetValue(field.FieldType,out var value))
|
||||
{
|
||||
BIT4Log.Log<DI>($"已为{obj.GetType().Name}.{field.Name}注入{value.GetType().Name}");
|
||||
field.SetValue(obj,value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
BIT4Log.LogException(e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
21
Src/Core/ECS/EntityBinarySystem.cs
Normal file
21
Src/Core/ECS/EntityBinarySystem.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace BITKit.Entities
|
||||
{
|
||||
public interface IEntityBinaryComponent
|
||||
{
|
||||
int Id { get; }
|
||||
void Serialize(BinaryWriter writer);
|
||||
void Deserialize(BinaryReader reader);
|
||||
}
|
||||
public interface IEntityBinaryHeader
|
||||
{
|
||||
int Id { get; }
|
||||
IDictionary<int,IEntityBinaryComponent> ComponentDictionary { get; }
|
||||
void Serialize(BinaryWriter writer);
|
||||
void Deserialize(BinaryReader reader);
|
||||
}
|
||||
}
|
11
Src/Core/ECS/EntityBinarySystem.cs.meta
Normal file
11
Src/Core/ECS/EntityBinarySystem.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 41391256f70b92848918e355d5d43b78
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -11,6 +11,10 @@ namespace BITKit.Entities
|
||||
/// </summary>
|
||||
public interface IEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 等待初始化完成,通常用于其他系统需要等待实体初始化完成
|
||||
/// </summary>
|
||||
void WaitForInitializationComplete();
|
||||
ulong Id { get; }
|
||||
CancellationToken CancellationToken { get; }
|
||||
bool TryGetComponent<T>(out T component);
|
||||
@@ -78,6 +82,17 @@ namespace BITKit.Entities
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
IEntity Get(ulong id);
|
||||
/// <summary>
|
||||
/// 尝试通过Id获取Entity
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
bool TryGetEntity(ulong id, out IEntity entity);
|
||||
/// <summary>
|
||||
/// 通过Id获取或添加Entity
|
||||
/// </summary>
|
||||
IEntity GetOrAdd(ulong id,Func<ulong,IEntity> factory);
|
||||
|
||||
/// <summary>
|
||||
/// 查询Entity,例如
|
||||
|
9
Src/Core/Exceptions/MyException.cs
Normal file
9
Src/Core/Exceptions/MyException.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BITKit
|
||||
{
|
||||
|
||||
}
|
||||
|
11
Src/Core/Exceptions/MyException.cs.meta
Normal file
11
Src/Core/Exceptions/MyException.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2dd41043546fb0e41a82e57749c0994a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -6,6 +6,10 @@ namespace BITKit
|
||||
{
|
||||
public static class FuncExtensions
|
||||
{
|
||||
public static IEnumerable<Func<T>> CastAsFunc<T>(this Func<T> self)
|
||||
{
|
||||
return self is null ? Array.Empty<Func<T>>() : self?.GetInvocationList().Cast<Func<T>>();
|
||||
}
|
||||
public static IEnumerable<Func<T0, T1>> CastAsFunc<T0, T1>(this Func<T0, T1> self)
|
||||
{
|
||||
return self is null ? Array.Empty<Func<T0,T1>>() : self?.GetInvocationList().Cast<Func<T0, T1>>();
|
||||
|
@@ -19,5 +19,6 @@ namespace BITKit
|
||||
}
|
||||
return cacheName;
|
||||
}
|
||||
public static int Hash => typeof(T).GetHashCode();
|
||||
}
|
||||
}
|
||||
|
@@ -1,17 +1,174 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace BITKit
|
||||
{
|
||||
public static partial class Extensions
|
||||
public sealed class CacheList<T> : IList<T>
|
||||
{
|
||||
public static IDictionary<TKey, TValue> CreateOrAddIfEmety<TKey, TValue>(this IDictionary<TKey, TValue> self, TKey key, Func<TValue> createFactory)
|
||||
public T[] ValueArray
|
||||
{
|
||||
if (self.ContainsKey(key) is false)
|
||||
get
|
||||
{
|
||||
self.Add(key, createFactory.Invoke());
|
||||
CheckChanges();
|
||||
return _array;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
private readonly IList<T> _listImplementation = new List<T>();
|
||||
private T[] _array = Array.Empty<T>();
|
||||
private IEnumerator<T> _enumerable=Array.Empty<T>().AsEnumerable().GetEnumerator();
|
||||
private bool _isDirty = true;
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
CheckChanges();
|
||||
return _enumerable;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
CheckChanges();
|
||||
return _enumerable;
|
||||
}
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
_listImplementation.Add(item);
|
||||
_isDirty = true;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_isDirty = false;
|
||||
_listImplementation.Clear();
|
||||
_array = Array.Empty<T>();
|
||||
}
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
CheckChanges();
|
||||
return _listImplementation.Contains(item);
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
CheckChanges();
|
||||
_listImplementation.CopyTo(array, arrayIndex);
|
||||
_isDirty = true;
|
||||
}
|
||||
|
||||
public bool Remove(T item)
|
||||
{
|
||||
_isDirty = true;
|
||||
return _listImplementation.Remove(item);
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
|
||||
get
|
||||
{
|
||||
CheckChanges();
|
||||
return _listImplementation.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckChanges();
|
||||
return _listImplementation.IsReadOnly;
|
||||
}
|
||||
}
|
||||
|
||||
public int IndexOf(T item)
|
||||
{
|
||||
CheckChanges();
|
||||
return _listImplementation.IndexOf(item);
|
||||
}
|
||||
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
CheckChanges();
|
||||
_listImplementation.Insert(index, item);
|
||||
_isDirty = true;
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
CheckChanges();
|
||||
_listImplementation.RemoveAt(index);
|
||||
_isDirty = true;
|
||||
}
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckChanges();
|
||||
return _listImplementation[index];
|
||||
}
|
||||
set
|
||||
{
|
||||
_isDirty = true;
|
||||
_listImplementation[index] = value;
|
||||
CheckChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CheckChanges()
|
||||
{
|
||||
if (!_isDirty) return false;
|
||||
_array = new T[_listImplementation.Count];
|
||||
_listImplementation.CopyTo(_array, 0);
|
||||
_enumerable = _array.AsEnumerable().GetEnumerator();
|
||||
_isDirty = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PrioritySelector<T>
|
||||
{
|
||||
public T Current { get; private set; }
|
||||
public event Action<T> OnRelease;
|
||||
private readonly SortedDictionary<int,T> _sortedDictionary = new();
|
||||
public void Set(int priority, T item)
|
||||
{
|
||||
_sortedDictionary.Set(priority,item);
|
||||
CheckChanges();
|
||||
}
|
||||
public void Remove(int priority)
|
||||
{
|
||||
_sortedDictionary.Remove(priority);
|
||||
CheckChanges();
|
||||
}
|
||||
|
||||
private void CheckChanges()
|
||||
{
|
||||
if (_sortedDictionary.Count is 0)
|
||||
return;
|
||||
var _current = _sortedDictionary.Last().Value;
|
||||
switch (_current, Current)
|
||||
{
|
||||
case (not null, null):
|
||||
Current = _current;
|
||||
break;
|
||||
case (null, not null):
|
||||
Current = default;
|
||||
break;
|
||||
case (not null, not null):
|
||||
if (Current.Equals(_current) is false)
|
||||
{
|
||||
Current = _current;
|
||||
}
|
||||
break;
|
||||
case (null, null):
|
||||
break;
|
||||
}
|
||||
OnRelease?.Invoke(Current);
|
||||
}
|
||||
}
|
||||
}
|
@@ -94,7 +94,7 @@ namespace BITKit
|
||||
}
|
||||
public static bool IsValid<T>(this IEnumerable<T> e)
|
||||
{
|
||||
return e is not null && e.Count() > 0;
|
||||
return e is not null && e.Any();
|
||||
}
|
||||
public static bool TryAdd<T>(this IList<T> self, T t)
|
||||
{
|
||||
|
@@ -107,6 +107,7 @@ namespace BITKit
|
||||
if (index is not -1 && list.TryGetElementAt(index, out var nextElement))
|
||||
{
|
||||
nextElement.IsEntered = true;
|
||||
OnEntry?.Invoke(nextElement);
|
||||
nextElement.Entry();
|
||||
try
|
||||
{
|
||||
@@ -114,7 +115,7 @@ namespace BITKit
|
||||
nextElement.Entered();
|
||||
}
|
||||
catch (OperationCanceledException){}
|
||||
OnEntry?.Invoke(nextElement);
|
||||
|
||||
}
|
||||
}
|
||||
completed = true;
|
||||
|
@@ -36,11 +36,38 @@ namespace BITKit.StateMachine
|
||||
bool Enabled { get; set; }
|
||||
T CurrentState { get; set; }
|
||||
event Action<T,T> OnStateChanged;
|
||||
event Action<T> OnStateRegistered;
|
||||
event Action<T> OnStateUnRegistered;
|
||||
|
||||
IDictionary<Type, T> StateDictionary { get; }
|
||||
void Initialize();
|
||||
void UpdateState(float deltaTime);
|
||||
void DisposeState();
|
||||
void TransitionState<State>() where State : T;
|
||||
void TransitionState(T state);
|
||||
void Register(T newState) => throw new NotImplementedException("未实现的接口");
|
||||
void UnRegister(T newState) => throw new NotImplementedException("未实现的接口");
|
||||
void InvokeOnStateRegistered(T state){}
|
||||
void InvokeOnStateUnRegistered(T state){}
|
||||
}
|
||||
|
||||
public static class StateMachineUtils
|
||||
{
|
||||
public static void Register<T>(IStateMachine<T> stateMachine, T newState) where T : IState
|
||||
{
|
||||
if (stateMachine.StateDictionary.ContainsKey(newState.GetType()))
|
||||
{
|
||||
throw new ArgumentException($"State {newState.GetType().Name} already registered");
|
||||
}
|
||||
stateMachine.StateDictionary.Add(newState.GetType(), newState);
|
||||
newState.Initialize();
|
||||
stateMachine.InvokeOnStateRegistered(newState);
|
||||
}
|
||||
public static void UnRegister<T>(this IStateMachine<T> stateMachine, T newState) where T : IState
|
||||
{
|
||||
if (!stateMachine.StateDictionary.ContainsKey(newState.GetType())) return;
|
||||
stateMachine.StateDictionary.Remove(newState.GetType());
|
||||
stateMachine.InvokeOnStateUnRegistered(newState);
|
||||
}
|
||||
}
|
||||
}
|
@@ -40,7 +40,7 @@ namespace BITKit
|
||||
/// <summary>
|
||||
/// 基础物品
|
||||
/// </summary>
|
||||
public interface IBasicItem :IPropertable
|
||||
public interface IBasicItem :IPropertable,ICloneable,IAddressable
|
||||
{
|
||||
/// <summary>
|
||||
/// 唯一Id
|
||||
@@ -50,10 +50,10 @@ namespace BITKit
|
||||
/// 物品名,一般用于查找物品的主键
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
/// <summary>
|
||||
/// 可寻址路径,该路径用于查找物品
|
||||
/// </summary>
|
||||
string AddressablePath { get; }
|
||||
// /// <summary>
|
||||
// /// 可寻址路径,该路径用于查找物品
|
||||
// /// </summary>
|
||||
// string AddressablePath { get; }
|
||||
/// <summary>
|
||||
/// 物品描述
|
||||
/// </summary>
|
||||
@@ -76,7 +76,7 @@ namespace BITKit
|
||||
/// 被托管的物品
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public record ManagedItem : IBasicItem
|
||||
public class ManagedItem : IBasicItem
|
||||
{
|
||||
#region 字段
|
||||
public int Id;
|
||||
@@ -135,8 +135,14 @@ namespace BITKit
|
||||
{
|
||||
return property.CopyPropertiesFrom(propertable);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public object Clone()
|
||||
{
|
||||
var item = MemberwiseClone() as ManagedItem;
|
||||
item!.Id = Id;
|
||||
return item;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
@@ -19,18 +19,24 @@ namespace BITKit.Net
|
||||
public event Action OnDisconnected;
|
||||
public event Action OnConnectedFailed;
|
||||
public bool IsConnected => client.connected;
|
||||
public int Ping => -1;
|
||||
|
||||
public bool ManualTick { get; set; }
|
||||
|
||||
public int Ping { get; private set; }
|
||||
public int Id { get; private set; } = -1;
|
||||
private readonly KcpClient client;
|
||||
|
||||
private readonly Queue<byte[]> commandQueue = new();
|
||||
|
||||
private DateTime _lastPingTime = DateTime.Now;
|
||||
|
||||
private readonly Timer _timer = new(100)
|
||||
{
|
||||
AutoReset = true
|
||||
};
|
||||
|
||||
private readonly GenericEvent _events = new();
|
||||
|
||||
public KcpNetClient()
|
||||
{
|
||||
client = new KcpClient(
|
||||
@@ -50,15 +56,8 @@ namespace BITKit.Net
|
||||
|
||||
private void Tick(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
//await UniTask.SwitchToThreadPool();
|
||||
while (commandQueue.TryDequeue(out var bytes))
|
||||
{
|
||||
client.Send(bytes, KcpChannel.Reliable);
|
||||
}
|
||||
//for (var i = 0; i < 32; i++)
|
||||
{
|
||||
client.Tick();
|
||||
}
|
||||
if (!ManualTick)
|
||||
Tick();
|
||||
}
|
||||
|
||||
public async void Disconnect()
|
||||
@@ -145,6 +144,9 @@ namespace BITKit.Net
|
||||
case NetCommandType.Heartbeat:
|
||||
client.Send(new[] { (byte)NetCommandType.Heartbeat }, KcpChannel.Reliable);
|
||||
break;
|
||||
case NetCommandType.Ping:
|
||||
Ping = (int)(DateTime.Now - _lastPingTime).TotalMilliseconds;
|
||||
break;
|
||||
default:
|
||||
BIT4Log.Log<KcpClient>($"未知消息类型:{type},字节:{(byte)type}");
|
||||
if (bytes.Array != null)
|
||||
@@ -250,7 +252,29 @@ namespace BITKit.Net
|
||||
client.Send(bytes, KcpChannel.Reliable);
|
||||
}
|
||||
|
||||
public void Tick() => client.Tick();
|
||||
#if UNITY_EDITOR
|
||||
private readonly IntervalUpdate _pingInterval = new(1);
|
||||
#endif
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
while (commandQueue.TryDequeue(out var bytes))
|
||||
{
|
||||
client.Send(bytes, KcpChannel.Reliable);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (_pingInterval.AllowUpdate)
|
||||
{
|
||||
_lastPingTime = DateTime.Now;
|
||||
client.Send(new[] { (byte)NetCommandType.Ping }, KcpChannel.Reliable);
|
||||
}
|
||||
#endif
|
||||
|
||||
client.Tick();
|
||||
|
||||
}
|
||||
|
||||
public void HandShake()
|
||||
{
|
||||
// send client to server
|
||||
|
@@ -14,6 +14,7 @@ namespace BITKit.Net
|
||||
{
|
||||
public class KCPNetServer:INetServer,INetProvider
|
||||
{
|
||||
public bool ManualTick { get; set; }
|
||||
public event Action<int> OnClientConnected;
|
||||
public event Action<int> OnClientDisconnected;
|
||||
public event Action OnStartServer;
|
||||
@@ -24,7 +25,7 @@ namespace BITKit.Net
|
||||
{
|
||||
AutoReset = true
|
||||
};
|
||||
|
||||
|
||||
public KCPNetServer()
|
||||
{
|
||||
server = new KcpServer(
|
||||
@@ -40,21 +41,38 @@ namespace BITKit.Net
|
||||
|
||||
private void Tick(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
if (server.IsActive() is false) return;
|
||||
if (server.IsActive() is false || ManualTick) return;
|
||||
server.Tick();
|
||||
}
|
||||
|
||||
public void StartServer(ushort port = 27014)
|
||||
{
|
||||
OnStartServer?.Invoke();
|
||||
server.Start(port);
|
||||
_timer.Start();
|
||||
if (IsRunningServer is false)
|
||||
{
|
||||
OnStartServer?.Invoke();
|
||||
server.Start(port);
|
||||
_timer.Start();
|
||||
BIT4Log.Log<KCPNetServer>($"已启动KCP服务器:{port}");
|
||||
}
|
||||
else
|
||||
{
|
||||
BIT4Log.Warning<KCPNetServer>($"KCP服务器已经启动,忽略此次请求");
|
||||
}
|
||||
|
||||
}
|
||||
public void StopServer(bool dispose = false)
|
||||
{
|
||||
server.Stop();
|
||||
OnStopServer?.Invoke();
|
||||
_timer.Stop();
|
||||
if (IsRunningServer)
|
||||
{
|
||||
server.Stop();
|
||||
OnStopServer?.Invoke();
|
||||
_timer.Stop();
|
||||
BIT4Log.Log<KCPNetServer>($"已停止KCP服务器");
|
||||
}
|
||||
else
|
||||
{
|
||||
BIT4Log.Warning<KCPNetServer>($"KCP服务器未运行,忽略此次请求");
|
||||
}
|
||||
}
|
||||
public bool IsRunningServer => server.IsActive();
|
||||
public void SendMessageToClient(int id, string message)
|
||||
@@ -140,6 +158,9 @@ namespace BITKit.Net
|
||||
var targetId = reader.ReadInt32();
|
||||
server.Send(targetId,bytes,channel);
|
||||
break;
|
||||
case NetCommandType.Ping:
|
||||
server.Send(Id,new byte[]{(byte)NetCommandType.Ping},channel);
|
||||
break;
|
||||
default:
|
||||
BIT4Log.Log<KCPNetServer>($"未知消息类型:{type},字节:{(byte)type}");
|
||||
BIT4Log.Log<KCPNetServer>($"已收到:({Id}, {BitConverter.ToString(bytes.Array, bytes.Offset, bytes.Count)} @ {channel})");
|
||||
@@ -235,5 +256,6 @@ namespace BITKit.Net
|
||||
.Build();
|
||||
server.Send(id,bytes, KcpChannel.Reliable);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -7,6 +7,10 @@ namespace BITKit
|
||||
{
|
||||
public static class MathE
|
||||
{
|
||||
public static bool IndexInRange<T>(this IEnumerable<T> self, int index)
|
||||
{
|
||||
return index >= 0 && index < self.Count();
|
||||
}
|
||||
public static bool Equals<T>(params T[] objs)
|
||||
{
|
||||
var a = objs.FirstOrDefault();
|
||||
@@ -25,7 +29,6 @@ namespace BITKit
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Subtract<T>(IEnumerable<T> a, IEnumerable<T> b) where T : IEnumerable
|
||||
{
|
||||
List<T> list = new();
|
||||
@@ -61,7 +64,8 @@ namespace BITKit
|
||||
{
|
||||
foreach (var x in b)
|
||||
{
|
||||
if (a.Contains(x))
|
||||
var enumerable = a as T[] ?? a.ToArray();
|
||||
if (enumerable.Contains(x))
|
||||
{
|
||||
|
||||
}
|
||||
@@ -73,6 +77,7 @@ namespace BITKit
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerable<T[]> Combinations<T>(IEnumerable<T> source)
|
||||
{
|
||||
|
8
Src/Core/Mod.meta
Normal file
8
Src/Core/Mod.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 237df241127e9db4e8945efe90ffe83a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
14
Src/Core/Mod/ModCommands.cs
Normal file
14
Src/Core/Mod/ModCommands.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BITKit.Mod
|
||||
{
|
||||
public struct LoadFromFolderCommand
|
||||
{
|
||||
public string FolderPath;
|
||||
}
|
||||
public struct UninstallPackageCommand
|
||||
{
|
||||
public string PackageName;
|
||||
}
|
||||
}
|
11
Src/Core/Mod/ModCommands.cs.meta
Normal file
11
Src/Core/Mod/ModCommands.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5db9ede675f865f4bb8572fada2ec7ab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
60
Src/Core/Mod/ModPackage.cs
Normal file
60
Src/Core/Mod/ModPackage.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BITKit.Mod
|
||||
{
|
||||
[Serializable]
|
||||
public sealed record ModPackage
|
||||
{
|
||||
public const string DefaultFileName = "package.json";
|
||||
/// <summary>
|
||||
/// ModPackage名称,可以重复
|
||||
/// </summary>
|
||||
public string Name;
|
||||
/// <summary>
|
||||
/// 唯一包名
|
||||
/// </summary>
|
||||
public string PackageName;
|
||||
/// <summary>
|
||||
/// 描述
|
||||
/// </summary>
|
||||
public string Description;
|
||||
/// <summary>
|
||||
/// 版本号,建议调试时为0.0.0
|
||||
/// </summary>
|
||||
public string Version;
|
||||
/// <summary>
|
||||
/// 依赖包,例如: "com.bitkit.mymod":"0.0.0"
|
||||
/// </summary>
|
||||
public Dictionary<string, string> Dependencies;
|
||||
/// <summary>
|
||||
/// 引用DLL,如果丢失则会导致Mod无法加载,例如: "BITKit.dll"
|
||||
/// </summary>
|
||||
public List<string> Dlls;
|
||||
/// <summary>
|
||||
/// 标签,用于过滤和搜索
|
||||
/// </summary>
|
||||
public string[] Tags=Array.Empty<string>();
|
||||
/// <summary>
|
||||
/// 下载直链,通常托管于其他平台
|
||||
/// </summary>
|
||||
public string DownloadLink;
|
||||
/// <summary>
|
||||
/// 程序入口,类似指向.cs脚本或者.version资源文件
|
||||
/// </summary>
|
||||
public string EntryPoint;
|
||||
/// <summary>
|
||||
/// 当前路径,由中间件自动填充
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string WorkDirectory;
|
||||
/// <summary>
|
||||
/// 描述文件路径,由中间件自动填充
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string PackagePath;
|
||||
}
|
||||
|
||||
}
|
11
Src/Core/Mod/ModPackage.cs.meta
Normal file
11
Src/Core/Mod/ModPackage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b340f148374c7a4ab84a5835089faf6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
516
Src/Core/Mod/ModService.cs
Normal file
516
Src/Core/Mod/ModService.cs
Normal file
@@ -0,0 +1,516 @@
|
||||
using System;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BITKit.IO;
|
||||
using BITKit.UX;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Microsoft.CSharp;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BITKit.Mod
|
||||
{
|
||||
/// <summary>
|
||||
/// Mod接口,需要实现所有方法
|
||||
/// </summary>
|
||||
public interface IMod
|
||||
{
|
||||
string FolderPath { get; set; }
|
||||
/// <summary>
|
||||
/// 唯一Id
|
||||
/// </summary>
|
||||
public Guid Id => new Guid("3E5AF780-FAB1-40B7-B8EF-62938F2340CB");
|
||||
/// <summary>
|
||||
/// Mod名称,可以重复
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
/// <summary>
|
||||
/// 包名,不可重复
|
||||
/// </summary>
|
||||
public string PackageName { get; }
|
||||
/// <summary>
|
||||
/// 描述,可为空
|
||||
/// </summary>
|
||||
public string Description { get; }
|
||||
/// <summary>
|
||||
/// 版本,调试时Mod版本需要高于以前的版本或者为0.0.0
|
||||
/// </summary>
|
||||
public string Version { get; }
|
||||
/// <summary>
|
||||
/// 开发者
|
||||
/// </summary>
|
||||
public string Author { get; }
|
||||
/// <summary>
|
||||
/// 联系邮箱,或者为其他联系方式
|
||||
/// </summary>
|
||||
public string Email { get; }
|
||||
/// <summary>
|
||||
/// Mod主页
|
||||
/// </summary>
|
||||
public string Url { get; }
|
||||
/// <summary>
|
||||
/// Mod标签,通常用于过滤和搜索
|
||||
/// </summary>
|
||||
public string[] Tags { get; }
|
||||
/// <summary>
|
||||
/// Mod属性,例如图标,背景或者其他资产包
|
||||
/// </summary>
|
||||
public object[] Properties { get; }
|
||||
/// <summary>
|
||||
/// 初始化时
|
||||
/// </summary>
|
||||
void OnInitialize();
|
||||
/// <summary>
|
||||
/// 初始化时的异步方法
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
UniTask OnInitializedAsync(CancellationToken cancellationToken);
|
||||
/// <summary>
|
||||
/// 初始化完成后
|
||||
/// </summary>
|
||||
void OnInitialized();
|
||||
/// <summary>
|
||||
/// 被释放时
|
||||
/// </summary>
|
||||
void OnDispose();
|
||||
/// <summary>
|
||||
/// 被释放时的异步方法
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
UniTask OnDisposeAsync(CancellationToken cancellationToken);
|
||||
/// <summary>
|
||||
/// 被释放后
|
||||
/// </summary>
|
||||
void OnDisposed();
|
||||
}
|
||||
public class MyMod : IMod
|
||||
{
|
||||
public string FolderPath { get; set; }
|
||||
public virtual string Name { get; set; } = nameof(MyMod);
|
||||
// ReSharper disable once StringLiteralTypo
|
||||
public virtual string PackageName { get; set; } = "com.bitkit.mymod";
|
||||
public virtual string Description{ get; set; } = "Empty mod for test function";
|
||||
public virtual string Version { get; set; } = "initial 0.0.1";
|
||||
public virtual string Author { get; set; } = nameof(BITKit);
|
||||
public virtual string Email { get; set; } = "root@bitfall.icu";
|
||||
public virtual string Url { get; set; } = "https://bitfall.icu";
|
||||
public virtual string[] Tags { get; set; } = Array.Empty<string>();
|
||||
public virtual object[] Properties{ get; set; } = Array.Empty<object>();
|
||||
public virtual void OnInitialize()
|
||||
{
|
||||
}
|
||||
public virtual UniTask OnInitializedAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
public virtual void OnInitialized()
|
||||
{
|
||||
}
|
||||
public virtual void OnDispose()
|
||||
{
|
||||
}
|
||||
public virtual UniTask OnDisposeAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
public virtual void OnDisposed()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class ModService
|
||||
{
|
||||
public static async UniTask<ModPackage[]> SearchPackages()
|
||||
{
|
||||
DI.TryGet<IUXWaiting>(out var waiting);
|
||||
var handle = waiting?.Get();
|
||||
handle?.SetMessage("正在搜索Mod包");
|
||||
|
||||
var list=new List<ModPackage>();
|
||||
var path = Path.Combine(Environment.CurrentDirectory, "Mods");
|
||||
var dir = new DirectoryInfo(path);
|
||||
dir.Create();
|
||||
foreach (var x in dir.GetDirectories())
|
||||
{
|
||||
var file = Path.Combine(x.FullName,ModPackage.DefaultFileName);
|
||||
if(File.Exists(file) is false)continue;
|
||||
var package = JsonConvert.DeserializeObject<ModPackage>(await File.ReadAllTextAsync(file))!;
|
||||
package.PackagePath = file;
|
||||
package.WorkDirectory = x.FullName;
|
||||
list.Add(package);
|
||||
}
|
||||
|
||||
waiting?.Release(handle);
|
||||
return list.ToArray();
|
||||
}
|
||||
public static async UniTask Reload()
|
||||
{
|
||||
OnReload?.Invoke();
|
||||
var mods = Mods;
|
||||
foreach (var x in Mods)
|
||||
{
|
||||
UnLoad(x);
|
||||
UnInstall(x);
|
||||
}
|
||||
|
||||
foreach (var x in await SearchPackages())
|
||||
{
|
||||
var path = x.PackagePath;
|
||||
if (File.Exists(path) is false)
|
||||
{
|
||||
BIT4Log.Warning<ModService>($"未找到{x.PackageName}的描述文件:{path}");
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
await LoadFromPackage(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
BIT4Log.LogException(e);
|
||||
}
|
||||
}
|
||||
OnReloaded?.Invoke();
|
||||
}
|
||||
|
||||
public static IMod[] Mods { get; private set; }=Array.Empty<IMod>();
|
||||
|
||||
public static bool IsLocked
|
||||
{
|
||||
get => _IsLocked;
|
||||
set
|
||||
{
|
||||
if (_IsLocked == value) return;
|
||||
_IsLocked = value;
|
||||
OnLocked?.Invoke(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static event Action<ModPackage> OnPackageLoad;
|
||||
public static event Action<ModPackage> OnPackageLoaded;
|
||||
|
||||
public static event Action<IMod> OnModLoad;
|
||||
public static event Action<IMod> OnModLoaded;
|
||||
public static event Action<IMod> OnModUnLoad;
|
||||
public static event Action<IMod> OnModUnLoaded;
|
||||
public static event Action<IMod> OnModInstalled;
|
||||
public static event Action<IMod> OnModUnInstalled;
|
||||
|
||||
public static event Action OnReload;
|
||||
public static event Action OnReloaded;
|
||||
|
||||
public static event Action<bool> OnLocked;
|
||||
|
||||
public static event Func<IMod,UniTask> OnModLoadAsync;
|
||||
public static event Func<IMod,UniTask> OnModUnloadAsync;
|
||||
|
||||
private static CancellationTokenSource _CancellationTokenSource;
|
||||
private static readonly ConcurrentQueue<IMod> _RegisterQueue=new();
|
||||
private static readonly ConcurrentQueue<IMod> _UnRegisterQueue = new();
|
||||
private static readonly List<IMod> _CacheMods = new();
|
||||
private static readonly ConcurrentDictionary<string,IMod> _InstalledMods=new();
|
||||
private static Thread _Thread;
|
||||
private static bool _IsRunning;
|
||||
private static bool _IsLocked;
|
||||
private static AppDomain _ModDomain;
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
BIT4Log.Log<ModService>("Mod服务已启动");
|
||||
_IsRunning = true;
|
||||
_CancellationTokenSource = new CancellationTokenSource();
|
||||
_Thread = new Thread(InternalInitialize);
|
||||
_Thread.Start();
|
||||
|
||||
return;
|
||||
|
||||
async void InternalInitialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
_ModDomain = AppDomain.CreateDomain("ModDomain");
|
||||
|
||||
var modPath = Path.Combine(Environment.CurrentDirectory, "Mods\\");
|
||||
PathHelper.EnsureDirectoryCreated(modPath);
|
||||
var directoryInfo = new DirectoryInfo(modPath);
|
||||
foreach (var fileInfo in directoryInfo.GetFiles())
|
||||
{
|
||||
switch (fileInfo.Extension)
|
||||
{
|
||||
case ".dll":
|
||||
{
|
||||
var assembly = Assembly.LoadFile(fileInfo.FullName);
|
||||
await Load(assembly);
|
||||
continue;
|
||||
}
|
||||
#if UNITY_64
|
||||
case ".cs":
|
||||
{
|
||||
var code = await File.ReadAllTextAsync(fileInfo.FullName);
|
||||
var assembly = BITSharp.Compile(code);
|
||||
await Load(assembly, fileInfo.DirectoryName);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
BIT4Log.Warning<ModService>("自动加载Mod失败");
|
||||
BIT4Log.LogException(e);
|
||||
}
|
||||
|
||||
|
||||
while (_IsRunning)
|
||||
{
|
||||
DI.TryGet<IUXWaiting>(out var waiting);
|
||||
|
||||
_CacheMods.Clear();
|
||||
|
||||
while (_UnRegisterQueue.TryDequeue(out var mod))
|
||||
{
|
||||
var handle = waiting?.Get();
|
||||
handle?.SetMessage($":正在卸载{mod.PackageName}");
|
||||
|
||||
mod.OnDispose();
|
||||
_CacheMods.Add(mod);
|
||||
OnModUnLoad?.Invoke(mod);
|
||||
|
||||
waiting?.Release(handle);
|
||||
}
|
||||
|
||||
foreach (var mod in _CacheMods)
|
||||
{
|
||||
await mod.OnDisposeAsync(_CancellationTokenSource.Token);
|
||||
foreach (var x in OnModUnloadAsync.CastAsFunc())
|
||||
{
|
||||
await x.Invoke(mod);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var mod in _CacheMods)
|
||||
{
|
||||
mod.OnDisposed();
|
||||
OnModUnLoaded?.Invoke(mod);
|
||||
BIT4Log.Log<ModService>($"卸载Mod:{mod.GetType().FullName}");
|
||||
}
|
||||
|
||||
_CacheMods.Clear();
|
||||
|
||||
while (_RegisterQueue.TryDequeue(out var mod))
|
||||
{
|
||||
var handle = waiting?.Get();
|
||||
|
||||
handle?.SetMessage($"正在加载:{mod.PackageName}");
|
||||
|
||||
_CacheMods.Add(mod);
|
||||
mod.OnInitialize();
|
||||
OnModLoad?.Invoke(mod);
|
||||
BIT4Log.Log<ModService>($"加载Mod:{mod.GetType().FullName}");
|
||||
|
||||
waiting?.Release(handle);
|
||||
}
|
||||
|
||||
foreach (var mod in _CacheMods)
|
||||
{
|
||||
var handle = waiting?.Get();
|
||||
handle?.SetMessage($"正在初始化:{mod.PackageName}");
|
||||
|
||||
await mod.OnInitializedAsync(_CancellationTokenSource.Token);
|
||||
foreach (var x in OnModLoadAsync.CastAsFunc())
|
||||
{
|
||||
await x.Invoke(mod);
|
||||
}
|
||||
|
||||
waiting?.Release(handle);
|
||||
}
|
||||
|
||||
foreach (var mod in _CacheMods)
|
||||
{
|
||||
var handle = waiting?.Get();
|
||||
handle?.SetMessage($":正在完成初始化中{mod.PackageName}");
|
||||
|
||||
mod.OnInitialized();
|
||||
OnModLoaded?.Invoke(mod);
|
||||
|
||||
waiting?.Release(handle);
|
||||
}
|
||||
|
||||
_CacheMods.Clear();
|
||||
|
||||
|
||||
//Thread.Sleep(1000);
|
||||
#if UNITY_64
|
||||
await UniTask.Delay(1000);
|
||||
#else
|
||||
await Task.Delay(1000);
|
||||
#endif
|
||||
IsLocked = false;
|
||||
}
|
||||
|
||||
BIT4Log.Log<ModService>("Mod服务已停止");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
BIT4Log.LogException(e);
|
||||
BIT4Log.Warning<ModService>("Mod服务遇到了错误,已停止");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Dispose()
|
||||
{_IsRunning = false;
|
||||
_CancellationTokenSource.Cancel();
|
||||
try
|
||||
{
|
||||
_Thread.Join(100);
|
||||
_RegisterQueue.Clear();
|
||||
_UnRegisterQueue.Clear();
|
||||
Mods = Array.Empty<IMod>();
|
||||
_InstalledMods.Clear();
|
||||
AppDomain.Unload(_ModDomain);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
BIT4Log.LogException(e);
|
||||
}
|
||||
|
||||
}
|
||||
public static UniTask Load(Assembly assembly,string folderPath=null)
|
||||
{
|
||||
BIT4Log.Log<ModService>($"加载程序集:{assembly.FullName}");
|
||||
|
||||
try
|
||||
{
|
||||
//_ModDomain.Load(assembly.FullName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
BIT4Log.LogException(e);
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
var types = new List<Type>();
|
||||
|
||||
try
|
||||
{
|
||||
types.AddRange(assembly.GetTypes());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
BIT4Log.Warning<ModService>($"{assembly.FullName}遇到了错误");
|
||||
BIT4Log.LogException(e);
|
||||
}
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
//if (type.IsAssignableFrom(typeof(IMod)) is false)
|
||||
if(typeof(IMod).IsAssignableFrom(type) is false)
|
||||
{
|
||||
//BIT4Log.Log<ModService>($"跳过类型:{type.FullName}");
|
||||
continue;
|
||||
}
|
||||
var mod = Activator.CreateInstance(type).As<IMod>();
|
||||
DI.Inject(mod);
|
||||
mod.FolderPath =folderPath;
|
||||
if(_InstalledMods.ContainsKey(mod.PackageName))
|
||||
{
|
||||
BIT4Log.Log<ModService>($"Mod已安装,跳过加载:{mod.PackageName}");
|
||||
continue;
|
||||
}
|
||||
BIT4Log.Log<ModService>($"加载Mod:{mod.GetType().FullName},Folder:{folderPath}");
|
||||
|
||||
Install(mod);
|
||||
Load(mod);
|
||||
}
|
||||
|
||||
BIT4Log.Log<ModService>($"<color=green>程序集加载完成:{assembly.FullName}</color>");
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
public static async UniTask LoadFromPackage(string path)
|
||||
{
|
||||
await UniTask.Yield();
|
||||
if(File.Exists(path) is false) throw new FileNotFoundException(path);
|
||||
var package = JsonConvert.DeserializeObject<ModPackage>(await File.ReadAllTextAsync(path))!;
|
||||
BIT4Log.Log<ModService>($"加载Mod包:{package.PackageName}");
|
||||
if(package.EntryPoint is null) throw new InvalidOperationException("空入口,无法识别类型");
|
||||
path = Path.Combine(Path.GetDirectoryName(path)!, package.EntryPoint);
|
||||
if(File.Exists(path) is false) throw new InvalidOperationException($"未找到入口文件:{path}");
|
||||
|
||||
OnPackageLoad?.Invoke(package);
|
||||
|
||||
foreach (var name in package.Dlls)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
var fileInfo = new FileInfo(path);
|
||||
switch (fileInfo.Extension)
|
||||
{
|
||||
case ".dll":
|
||||
{
|
||||
var assembly = Assembly.LoadFile(fileInfo.FullName);
|
||||
await Load(assembly, fileInfo.DirectoryName);
|
||||
break;
|
||||
}
|
||||
#if UNITY_64
|
||||
case ".cs":
|
||||
{
|
||||
var code = await File.ReadAllTextAsync(fileInfo.FullName);
|
||||
var assembly = BITSharp.Compile(code);
|
||||
await Load(assembly, fileInfo.DirectoryName);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
OnPackageLoaded?.Invoke(package);
|
||||
}
|
||||
public static void Load(IMod mod)
|
||||
{
|
||||
IsLocked = true;
|
||||
_RegisterQueue.Enqueue(mod);
|
||||
}
|
||||
|
||||
public static void UnLoad(IMod mod)
|
||||
{
|
||||
IsLocked = true;
|
||||
_UnRegisterQueue.Enqueue(mod);
|
||||
}
|
||||
|
||||
public static void Install(IMod mod)
|
||||
{
|
||||
if (_InstalledMods.ContainsKey(mod.PackageName))
|
||||
{
|
||||
throw new ArgumentException("Mod已安装");
|
||||
}
|
||||
_InstalledMods.TryAdd(mod.PackageName,mod);
|
||||
Mods = _InstalledMods.Values.ToArray();
|
||||
OnModInstalled?.Invoke(mod);
|
||||
}
|
||||
public static void UnInstall(IMod mod)
|
||||
{
|
||||
if(_InstalledMods.ContainsKey(mod.PackageName) is false) return;
|
||||
_InstalledMods.TryRemove(mod.PackageName);
|
||||
Mods = _InstalledMods.Values.ToArray();
|
||||
OnModUnInstalled?.Invoke(mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
11
Src/Core/Mod/ModService.cs.meta
Normal file
11
Src/Core/Mod/ModService.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e5da31958eda074e87068926f79dd11
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
9
Src/Core/Mod/ModTypes.cs
Normal file
9
Src/Core/Mod/ModTypes.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BITKit.Mod
|
||||
{
|
||||
public interface IAssetMod{}
|
||||
public interface IScriptMod{}
|
||||
public interface IPackageMod{}
|
||||
}
|
11
Src/Core/Mod/ModTypes.cs.meta
Normal file
11
Src/Core/Mod/ModTypes.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55e47ab2418c5894b9c1349e43415cb3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
@@ -50,6 +51,12 @@ namespace BITKit
|
||||
{
|
||||
return self.ToString();
|
||||
}
|
||||
#if NET5_0_OR_GREATER
|
||||
public static implicit operator HttpContent(ContextModel self)
|
||||
{
|
||||
return new StringContent(self, System.Text.Encoding.UTF8, "application/json");
|
||||
}
|
||||
#endif
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
@@ -57,7 +64,7 @@ namespace BITKit
|
||||
}
|
||||
|
||||
[JsonProperty("status_code")]
|
||||
public int StatusCode;
|
||||
public int StatusCode=200;
|
||||
[JsonProperty("message")]
|
||||
public string Message=string.Empty;
|
||||
[JsonProperty("data")]
|
||||
|
@@ -5,6 +5,7 @@ namespace BITKit
|
||||
public record Package
|
||||
{
|
||||
public string Name=nameof(Package);
|
||||
|
||||
public string DirectoryPath;
|
||||
public string GitUrl;
|
||||
public string Version;
|
||||
@@ -12,5 +13,6 @@ namespace BITKit
|
||||
public string ProcessName;
|
||||
public string ProductName;
|
||||
public DateTime LastUpdateTime;
|
||||
|
||||
}
|
||||
}
|
||||
|
8
Src/Core/Modification.meta
Normal file
8
Src/Core/Modification.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8845089e2cd9eb44b27dabc6859da55
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
117
Src/Core/Modification/IModification.cs
Normal file
117
Src/Core/Modification/IModification.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace BITKit.Modification
|
||||
{
|
||||
/// <summary>
|
||||
/// 不兼容的改装
|
||||
/// </summary>
|
||||
public class IncompatibleModifyException : InGameException
|
||||
{
|
||||
public IncompatibleModifyException(IModifyElement[] errorElements) : base()
|
||||
{
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 没有前置改装
|
||||
/// </summary>
|
||||
public class NotRequireModifyException : InGameException
|
||||
{
|
||||
public NotRequireModifyException(IModifyElement[] errorElements) : base()
|
||||
{
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 不支持的改装
|
||||
/// </summary>
|
||||
public class NotSupportModifyException : InGameException
|
||||
{
|
||||
public NotSupportModifyException(IModifyElement[] errorElements) : base()
|
||||
{
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 改装元素
|
||||
/// </summary>
|
||||
public interface IModifyElement
|
||||
{
|
||||
IModifyElement[] Require { get; }
|
||||
IModifyElement[] Incompatible { get; }
|
||||
}
|
||||
/// <summary>
|
||||
/// 改装管理器
|
||||
/// </summary>
|
||||
public interface IModifyManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取所有改装
|
||||
/// </summary>
|
||||
IDictionary<IModifyElement,object> Modifies { get; }
|
||||
/// <summary>
|
||||
/// 获取所有已改装
|
||||
/// </summary>
|
||||
IDictionary<IModifyElement,object> Modified { get; }
|
||||
/// <summary>
|
||||
/// 添加改装
|
||||
/// </summary>
|
||||
/// <param name="modify"></param>
|
||||
void Add(IModifyElement modify,object obj);
|
||||
/// <summary>
|
||||
/// 移除改装
|
||||
/// </summary>
|
||||
/// <param name="modify"></param>
|
||||
void Remove(IModifyElement modify,out object obj);
|
||||
}
|
||||
public class ModifyManager : IModifyManager
|
||||
{
|
||||
public virtual IDictionary<IModifyElement, object> Modifies { get; } =new Dictionary<IModifyElement,object>();
|
||||
public virtual IDictionary<IModifyElement, object> Modified { get; } = new Dictionary<IModifyElement, object>();
|
||||
|
||||
public virtual void Add(IModifyElement modify, object obj)
|
||||
{
|
||||
if(Modified.ContainsKey(modify))
|
||||
throw new NotSupportModifyException(new[]{modify});
|
||||
|
||||
var list = new List<IModifyElement>();
|
||||
list.AddRange(Modified.Keys);
|
||||
list.Add(modify);
|
||||
|
||||
|
||||
foreach (var x in list)
|
||||
{
|
||||
if(x.Require is null or {Length:0})continue;
|
||||
|
||||
foreach (var _x in x.Require)
|
||||
{
|
||||
if (list.Contains(_x) is false)
|
||||
throw new NotRequireModifyException(x.Require);
|
||||
}
|
||||
}
|
||||
|
||||
//你知道怎么做,帮我做一下
|
||||
|
||||
|
||||
var incompatible = list.Where(x=>x.Incompatible is not null).SelectMany(x => x.Incompatible).ToArray();
|
||||
if(MathE.Contains(incompatible,list))
|
||||
throw new IncompatibleModifyException(incompatible);
|
||||
|
||||
Modified.Add(modify,obj);
|
||||
}
|
||||
|
||||
public virtual void Remove(IModifyElement modify,out object obj)
|
||||
{
|
||||
var list = new List<IModifyElement>();
|
||||
list.AddRange(Modified.Keys);
|
||||
list.Remove(modify);
|
||||
|
||||
var requires = list.Where(x=>x.Require is not null).SelectMany(x => x.Require).ToArray();
|
||||
if(requires.Contains(modify))
|
||||
throw new NotRequireModifyException(requires);
|
||||
|
||||
obj = Modified[modify];
|
||||
Modified.Remove(modify);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
11
Src/Core/Modification/IModification.cs.meta
Normal file
11
Src/Core/Modification/IModification.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 014832ce48d3bce4b85ecc2518c02667
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -11,6 +11,7 @@ namespace BITKit.Net.Http
|
||||
public int Port { get; set; }
|
||||
private readonly HttpListener _httpListener = new();
|
||||
public event Func<HttpListenerRequest,HttpContent> OnRequest;
|
||||
public event Action<byte[]> OnRequestBytes;
|
||||
private readonly CancellationTokenSource _cancellationTokenSource = new();
|
||||
private CancellationToken _cancellationToken=>_cancellationTokenSource.Token;
|
||||
|
||||
@@ -69,7 +70,6 @@ namespace BITKit.Net.Http
|
||||
|
||||
var response = context.Response;
|
||||
|
||||
|
||||
var output = response.OutputStream;
|
||||
|
||||
if (request.RawUrl is "/favicon.ico")
|
||||
@@ -83,11 +83,16 @@ namespace BITKit.Net.Http
|
||||
response.Headers.Add("Access-Control-Allow-Origin", "*"); // 允许任何来源访问,生产环境应更具体设置
|
||||
response.Headers.Add("Access-Control-Allow-Methods", "*"); // 允许的HTTP方法
|
||||
response.Headers.Add("Access-Control-Allow-Headers", "*"); // 允许的标头
|
||||
|
||||
|
||||
var content = OnRequest?.Invoke(request);
|
||||
//添加返回的文本为utf-8
|
||||
response.ContentType = "application/json;charset=utf-8";
|
||||
response.ContentEncoding = System.Text.Encoding.UTF8;
|
||||
|
||||
|
||||
|
||||
var buffer = StringHelper.GetBytes(ContextModel.Error("没有注册请求事件"));
|
||||
|
||||
var content = OnRequest?.Invoke(request);
|
||||
if (content is not null)
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
|
@@ -3,11 +3,11 @@ using System.Net;
|
||||
|
||||
namespace BITKit.Net.LAN
|
||||
{
|
||||
public interface ILANBroadcaster
|
||||
public interface ILANBroadcaster:IDisposable
|
||||
{
|
||||
int Port { get; set; }
|
||||
byte[] Buffer { get; set; }
|
||||
event Action<EndPoint, string> OnReceive;
|
||||
event Action<EndPoint, byte[]> OnReceive;
|
||||
void StartBroadcast();
|
||||
void StopBroadcast();
|
||||
void StartListen();
|
||||
|
@@ -15,22 +15,26 @@ namespace BITKit.Net.LAN
|
||||
public byte[] Buffer { get; set; } = StringHelper.GetBytes("Hello World");
|
||||
private bool _isBroadcasting;
|
||||
private bool _isListening;
|
||||
private int head;
|
||||
private int revHead;
|
||||
|
||||
private Thread _thread;
|
||||
|
||||
public UdpBasedLanBroadcaster()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public event Action<EndPoint, string> OnReceive;
|
||||
public event Action<EndPoint, byte[]> OnReceive;
|
||||
|
||||
public void StartBroadcast()
|
||||
{
|
||||
_isBroadcasting = true;
|
||||
Thread thread = new(Process)
|
||||
_thread = new(Process)
|
||||
{
|
||||
IsBackground = true
|
||||
};
|
||||
thread.Start();
|
||||
_thread.Start();
|
||||
BIT4Log.Log<ILANBroadcaster>($"开始广播端口{Port}");
|
||||
}
|
||||
|
||||
@@ -42,23 +46,24 @@ namespace BITKit.Net.LAN
|
||||
|
||||
public void StartListen()
|
||||
{
|
||||
if (_isListening) return;
|
||||
_isListening = true;
|
||||
var thread = new Thread(ReceiveThread)
|
||||
_thread = new Thread(ReceiveThread)
|
||||
{
|
||||
IsBackground = true
|
||||
};
|
||||
thread.Start();
|
||||
_thread.Start();
|
||||
BIT4Log.Log<ILANBroadcaster>($"开始监听端口:{Port}");
|
||||
}
|
||||
|
||||
public void StopListen()
|
||||
{
|
||||
if (_isListening is false) return;
|
||||
_isListening = false;
|
||||
_thread?.Abort();
|
||||
BIT4Log.Log<ILANBroadcaster>($"停止监听端口{Port}");
|
||||
}
|
||||
|
||||
|
||||
private void Process()
|
||||
public void Process()
|
||||
{
|
||||
var udpClient = new UdpClient(new IPEndPoint(IPAddress.Any, 0));
|
||||
|
||||
@@ -68,9 +73,14 @@ namespace BITKit.Net.LAN
|
||||
//IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse("255.255.255.255"), 8080);
|
||||
try
|
||||
{
|
||||
var tempBuffer = new byte[Buffer.Length + 1];
|
||||
tempBuffer[0] = (byte)head++;
|
||||
Buffer.CopyTo(tempBuffer, 1);
|
||||
udpClient.Send(tempBuffer, tempBuffer.Length, endpoint);
|
||||
Thread.Sleep(1000);
|
||||
while (_isBroadcasting)
|
||||
{
|
||||
udpClient.Send(Buffer, Buffer.Length, endpoint);
|
||||
udpClient.Send(tempBuffer, tempBuffer.Length, endpoint);
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
@@ -87,22 +97,44 @@ namespace BITKit.Net.LAN
|
||||
{
|
||||
var udpClient = new UdpClient(new IPEndPoint(IPAddress.Any, Port));
|
||||
var endpoint = new IPEndPoint(IPAddress.Any, 0);
|
||||
while (_isListening)
|
||||
while (_isListening && BITApp.CancellationToken.IsCancellationRequested is false)
|
||||
{
|
||||
var buf = udpClient.Receive(ref endpoint);
|
||||
var msg = Encoding.Default.GetString(buf);
|
||||
byte[] buf;
|
||||
try
|
||||
{
|
||||
buf = udpClient.Receive(ref endpoint);
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
udpClient.Dispose();
|
||||
return;
|
||||
}
|
||||
var packageHead = buf[0];
|
||||
if(packageHead<revHead)
|
||||
continue;
|
||||
revHead = packageHead;
|
||||
buf = buf[1..];
|
||||
if (OnReceive is not null)
|
||||
OnReceive(endpoint, msg);
|
||||
OnReceive(endpoint, buf);
|
||||
else
|
||||
BIT4Log.Log<ILANBroadcaster>($"Receive From {endpoint}:\t{msg}");
|
||||
BIT4Log.Log<ILANBroadcaster>($"Receive From {endpoint}:\t{buf}");
|
||||
Thread.Sleep(500);
|
||||
}
|
||||
udpClient.Dispose();
|
||||
}
|
||||
catch(ThreadAbortException){}
|
||||
catch (Exception e)
|
||||
{
|
||||
BIT4Log.LogException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
StopBroadcast();
|
||||
StopListen();
|
||||
OnReceive = null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -28,6 +28,7 @@ namespace BITKit
|
||||
AllClientCommand=5,
|
||||
Message=6,
|
||||
Heartbeat=7,
|
||||
Ping=8,
|
||||
}
|
||||
/// <summary>
|
||||
/// 网络提供服务,包括了基础网络服务,e.g
|
||||
@@ -43,6 +44,7 @@ namespace BITKit
|
||||
/// </summary>
|
||||
public interface INetProvider
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 向服务端发送指令
|
||||
/// </summary>
|
||||
@@ -138,6 +140,14 @@ namespace BITKit
|
||||
/// </summary>
|
||||
public interface INetServer
|
||||
{
|
||||
/// <summary>
|
||||
/// 源物体,用于通过代理直接访问
|
||||
/// </summary>
|
||||
public object Source => this;
|
||||
/// <summary>
|
||||
/// 手动Tick
|
||||
/// </summary>
|
||||
public bool ManualTick { get; set; }
|
||||
/// <summary>
|
||||
/// 回调:当客户端连接时
|
||||
/// </summary>
|
||||
@@ -207,6 +217,11 @@ namespace BITKit
|
||||
/// </summary>
|
||||
public interface INetClient
|
||||
{
|
||||
/// <summary>
|
||||
/// 源物体,用于通过代理直接访问
|
||||
/// </summary>
|
||||
public object Source => this;
|
||||
|
||||
//基本客户端回调
|
||||
public event Action OnStartConnect;
|
||||
public event Action OnConnected;
|
||||
@@ -217,6 +232,10 @@ namespace BITKit
|
||||
/// </summary>
|
||||
bool IsConnected { get; }
|
||||
/// <summary>
|
||||
/// 手动Tick
|
||||
/// </summary>
|
||||
bool ManualTick { get; set; }
|
||||
/// <summary>
|
||||
/// 连接服务端的延迟
|
||||
/// </summary>
|
||||
int Ping { get; }
|
||||
|
@@ -4,11 +4,14 @@ namespace BITKit
|
||||
{
|
||||
public interface ICondition
|
||||
{
|
||||
public bool Allow => OnCheck();
|
||||
public string Reason=> "Not Implemented";
|
||||
bool OnCheck();
|
||||
}
|
||||
[Serializable]
|
||||
public struct AllowCondition : ICondition
|
||||
{
|
||||
|
||||
public bool OnCheck() => true;
|
||||
}
|
||||
}
|
@@ -37,6 +37,12 @@ namespace BITKit
|
||||
[System.Serializable]
|
||||
public class Optional<T> : IOptional<T>
|
||||
{
|
||||
public Optional(){}
|
||||
public Optional(T value, bool allow=false)
|
||||
{
|
||||
this.allow = allow;
|
||||
this.value = value;
|
||||
}
|
||||
#if NET5_0_OR_GREATER
|
||||
bool allow;
|
||||
T value;
|
||||
|
@@ -11,7 +11,6 @@ namespace BITKit
|
||||
/// </summary>
|
||||
public interface IProperty
|
||||
{
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 属性接口
|
||||
@@ -71,11 +70,7 @@ namespace BITKit
|
||||
{
|
||||
foreach (var x in factory)
|
||||
{
|
||||
properties.Add(x.GetType()!.FullName, x);
|
||||
foreach (var att in x.GetType().GetCustomAttributes<CustomTypeAttribute>())
|
||||
{
|
||||
properties.Add(att.Type!.FullName, x);
|
||||
}
|
||||
AddPropertyInternal(x);
|
||||
}
|
||||
}
|
||||
Dictionary<string, object> properties=new();
|
||||
@@ -104,7 +99,8 @@ namespace BITKit
|
||||
}
|
||||
else
|
||||
{
|
||||
properties.Add(typeof(T).FullName, x = addFactory.Invoke());
|
||||
AddPropertyInternal(x =addFactory.Invoke());
|
||||
//properties.Add(typeof(T).FullName, x = addFactory.Invoke());
|
||||
return (T)x;
|
||||
}
|
||||
}
|
||||
@@ -132,17 +128,24 @@ namespace BITKit
|
||||
}
|
||||
public bool TrySetProperty<T>(T value)
|
||||
{
|
||||
if (properties.TryGetValue(typeof(T).FullName, out var x))
|
||||
bool result = false;
|
||||
foreach (var pair in properties.Where(x=>x.Value is T).ToArray())
|
||||
{
|
||||
properties[typeof(T).FullName] = value;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
properties[pair.Key] = value;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
// if (properties.TryGetValue(typeof(T).FullName, out var x))
|
||||
// {
|
||||
// properties[typeof(T).FullName] = value;
|
||||
// return true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
}
|
||||
public object[] GetProperties()=>properties.Values.ToArray();
|
||||
public object[] GetProperties()=>properties.Values.Distinct().ToArray();
|
||||
|
||||
public void Read(BinaryReader r)
|
||||
{
|
||||
@@ -168,9 +171,21 @@ namespace BITKit
|
||||
ClearProperties();
|
||||
foreach (var x in propertable.GetProperties())
|
||||
{
|
||||
properties.Add(x.GetType().FullName, x);
|
||||
AddPropertyInternal(x);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private void AddPropertyInternal(object value)
|
||||
{
|
||||
if (value is ICloneable cloneable)
|
||||
{
|
||||
value = cloneable.Clone();
|
||||
}
|
||||
properties.Set(value.GetType().FullName, value);
|
||||
foreach (var att in value.GetType().GetCustomAttributes<CustomTypeAttribute>())
|
||||
{
|
||||
properties.Set(att.Type!.FullName, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
8
Src/Core/Selection.meta
Normal file
8
Src/Core/Selection.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a9ce048cccddfd42972d7502f096e77
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
34
Src/Core/Selection/ISelectable.cs
Normal file
34
Src/Core/Selection/ISelectable.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
namespace BITKit
|
||||
{
|
||||
public enum SelectionState
|
||||
{
|
||||
None,
|
||||
Hover,
|
||||
Active,
|
||||
Inactive,
|
||||
Focus,
|
||||
Selected,
|
||||
Enabled,
|
||||
Checked,
|
||||
Root,
|
||||
}
|
||||
public interface ISelectable
|
||||
{
|
||||
#if UNITY_64
|
||||
UnityEngine.Transform Transform { get; }
|
||||
#endif
|
||||
void SetSelectionState(SelectionState state);
|
||||
event Action OnNone;
|
||||
event Action OnHover;
|
||||
event Action OnActive;
|
||||
event Action OnInactive;
|
||||
event Action OnFocus;
|
||||
event Action OnSelected;
|
||||
event Action OnEnabled;
|
||||
event Action OnChecked;
|
||||
event Action OnRoot;
|
||||
}
|
||||
}
|
11
Src/Core/Selection/ISelectable.cs.meta
Normal file
11
Src/Core/Selection/ISelectable.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb2d5a09faa2e9a41858ff98770b423c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
20
Src/Core/Selection/ISelector.cs
Normal file
20
Src/Core/Selection/ISelector.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BITKit.Selection
|
||||
{
|
||||
public interface ISelector
|
||||
{
|
||||
bool TryGetCurrentSelectable(out ISelectable selectable);
|
||||
event Action<ISelectable> OnNone;
|
||||
event Action<ISelectable> OnHover;
|
||||
event Action<ISelectable> OnActive;
|
||||
event Action<ISelectable> OnInactive;
|
||||
event Action<ISelectable> OnFocus;
|
||||
event Action<ISelectable> OnSelected;
|
||||
event Action<ISelectable> OnEnabled;
|
||||
event Action<ISelectable> OnChecked;
|
||||
event Action<ISelectable> OnRoot;
|
||||
}
|
||||
}
|
11
Src/Core/Selection/ISelector.cs.meta
Normal file
11
Src/Core/Selection/ISelector.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 119f6a0b25fc0cc47b6f950cd9902aba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,38 +1,228 @@
|
||||
using System;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using BITKit.Mod;
|
||||
|
||||
#if UNITY_64
|
||||
using System.Collections;
|
||||
using BITKit.UX;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using static BITKit.Mod.DotNetSdkRoslynService;
|
||||
#endif
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace BITKit
|
||||
{
|
||||
public class BITSharp
|
||||
{
|
||||
#if UNITY_64
|
||||
[RuntimeInitializeOnLoadMethod]
|
||||
private static void Reload()
|
||||
{
|
||||
ReferencedAssemblies.Clear();
|
||||
Dictionary.Clear();
|
||||
assemblies = null;
|
||||
|
||||
}
|
||||
#endif
|
||||
public static readonly List<string> ReferencedAssemblies = new();
|
||||
private static readonly ConcurrentDictionary<string, Type> Dictionary = new();
|
||||
static Assembly[] assemblies;
|
||||
public static bool TryGetTypeFromFullName(string fullClassName,out Type type)
|
||||
private static Assembly[] assemblies;
|
||||
|
||||
public static bool TryGetTypeFromFullName(string fullClassName, out Type type)
|
||||
{
|
||||
type = GetTypeFromFullName(fullClassName);
|
||||
return type is not null ? true : false;
|
||||
}
|
||||
|
||||
public static Type GetTypeFromFullName(string fullClassName)
|
||||
{
|
||||
return Dictionary.GetOrAdd(fullClassName, SearchType);
|
||||
}
|
||||
static Type SearchType(string fullName)
|
||||
|
||||
private static Type SearchType(string fullName)
|
||||
{
|
||||
assemblies = assemblies??AppDomain.CurrentDomain.GetAssemblies();
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
var type = assembly.GetType(fullName,false);
|
||||
if (type is not null)
|
||||
{
|
||||
return type;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
assemblies ??= AppDomain.CurrentDomain.GetAssemblies();
|
||||
return assemblies.Select(assembly => assembly.GetType(fullName, false)).FirstOrDefault(type => type is not null);
|
||||
}
|
||||
#if UNITY_64
|
||||
|
||||
public static Assembly Compile(params string[] codes)
|
||||
{
|
||||
var outputPath = PathHelper.GetTempFilePath();
|
||||
|
||||
DI.TryGet<IUXWaiting>(out var waiting);
|
||||
|
||||
var handle = waiting?.Get();
|
||||
handle?.SetMessage("正在编译");
|
||||
try
|
||||
{
|
||||
if (Installed is false)
|
||||
{
|
||||
throw new FileNotFoundException($"未从当前路径找到Roslyn:\n{CSCPath}");
|
||||
}
|
||||
|
||||
var argumentBuilder = new StringBuilder();
|
||||
|
||||
var tempList = new List<string> { outputPath };
|
||||
|
||||
foreach (var code in codes)
|
||||
{
|
||||
var temp = PathHelper.GetTempFilePath();
|
||||
|
||||
File.WriteAllText(temp,code);
|
||||
|
||||
argumentBuilder.Append(" ");
|
||||
argumentBuilder.Append(temp);
|
||||
|
||||
tempList.Add(temp);
|
||||
}
|
||||
|
||||
argumentBuilder.Append(" -target:library");
|
||||
|
||||
argumentBuilder.Append(" /unsafe");
|
||||
|
||||
foreach (var x in ReferencedAssemblies)
|
||||
{
|
||||
argumentBuilder.Append(" ");
|
||||
argumentBuilder.Append("-r:");
|
||||
argumentBuilder.Append(x);
|
||||
//argumentBuilder.Append(Path.Combine(dllPath, x));
|
||||
}
|
||||
|
||||
argumentBuilder.Append(" ");
|
||||
argumentBuilder.Append($"-out:{outputPath}");
|
||||
|
||||
BIT4Log.Log<BITSharp>("已创建编译参数:");
|
||||
BIT4Log.Log(argumentBuilder.ToString());
|
||||
|
||||
BIT4Log.Log<BITSharp>($"dotnet {CSCPath} {argumentBuilder}");
|
||||
|
||||
//Original
|
||||
// var StartInfo = new ProcessStartInfo
|
||||
// {
|
||||
// //FileName = MCSPath,
|
||||
// FileName = "dotnet",
|
||||
// Arguments =$"{CSCPath} {argumentBuilder}" ,
|
||||
// UseShellExecute = true,
|
||||
// CreateNoWindow = false,
|
||||
// };
|
||||
|
||||
var StartInfo = new ProcessStartInfo
|
||||
{
|
||||
//FileName = MCSPath,
|
||||
FileName = "dotnet",
|
||||
Arguments =$"{CSCPath} {argumentBuilder}" ,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
StandardErrorEncoding = System.Text.Encoding.GetEncoding("gb2312"),
|
||||
StandardInputEncoding = System.Text.Encoding.GetEncoding("gb2312"),
|
||||
StandardOutputEncoding = System.Text.Encoding.GetEncoding("gb2312"),
|
||||
};
|
||||
|
||||
|
||||
var process = new Process()
|
||||
{
|
||||
StartInfo = StartInfo,
|
||||
};
|
||||
process.OutputDataReceived += (sender, args) =>
|
||||
{
|
||||
BIT4Log.Log<BITSharp>(args.Data);
|
||||
};
|
||||
process.ErrorDataReceived += (sender, args) =>
|
||||
{
|
||||
BIT4Log.Warning<BITSharp>(args.Data);
|
||||
};
|
||||
|
||||
process.Start();
|
||||
BIT4Log.Log<BITSharp>("已启动");
|
||||
|
||||
process.BeginErrorReadLine();
|
||||
process.BeginOutputReadLine();
|
||||
|
||||
while (process.HasExited is false)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
if (BITApp.CancellationToken.IsCancellationRequested) throw new OperationCanceledException("程序已退出,取消编译");
|
||||
}
|
||||
|
||||
var bytes = File.ReadAllBytes(outputPath);
|
||||
|
||||
if(process.ExitCode is not 0)
|
||||
{
|
||||
BIT4Log.LogException(new Exception($"编译失败:{process.ExitCode}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var x in tempList)
|
||||
{
|
||||
File.Delete(x);
|
||||
}
|
||||
}
|
||||
|
||||
waiting?.Release(handle);
|
||||
|
||||
return Assembly.Load(bytes);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
waiting?.Release(handle);
|
||||
|
||||
throw new Exception($"编译失败:{e}");
|
||||
}
|
||||
|
||||
// var codeProvider = new CSharpCodeProvider();
|
||||
// var parameters = new CompilerParameters
|
||||
// {
|
||||
// GenerateInMemory = true,
|
||||
// CompilerOptions = "/unsafe",
|
||||
// OutputAssembly = outputPath,
|
||||
// };
|
||||
// parameters.ReferencedAssemblies.Add("System.dll");
|
||||
// foreach (var x in ReferencedAssemblies)
|
||||
// {
|
||||
// parameters.ReferencedAssemblies.Add(x);
|
||||
// BIT4Log.Log<BITSharp>($"添加引用:");
|
||||
// BIT4Log.Log<BITSharp>(x);
|
||||
// }
|
||||
//
|
||||
// BIT4Log.Log<BITSharp>($"程序集输出路径:{outputPath}");
|
||||
//
|
||||
// var results = codeProvider.CompileAssemblyFromSource(parameters, codes);
|
||||
//
|
||||
// if (results.Errors.Count <= 0)
|
||||
// {
|
||||
// BIT4Log.Log<BITSharp>($"编译成功:{results.CompiledAssembly.FullName}");
|
||||
//
|
||||
// codeProvider.Dispose();
|
||||
// return results.CompiledAssembly;
|
||||
// }
|
||||
//
|
||||
// foreach (CompilerError error in results.Errors)
|
||||
// {
|
||||
// var sb = new StringBuilder();
|
||||
// sb.AppendLine(error.FileName);
|
||||
// sb.AppendLine($"Error ({error.ErrorNumber}): {error.ErrorText}");
|
||||
// sb.AppendLine($"Line: {error.Line}, Column: {error.Column}");
|
||||
// sb.AppendLine($"Is Warning: {error.IsWarning}");
|
||||
// BIT4Log.LogException(new Exception(sb.ToString()));
|
||||
// }
|
||||
//
|
||||
// throw new Exception("编译失败");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
8
Src/Core/Storage.meta
Normal file
8
Src/Core/Storage.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f4e804e88433c74392144bb5a48db7c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
226
Src/Core/Storage/ApplicationFile.cs
Normal file
226
Src/Core/Storage/ApplicationFile.cs
Normal file
@@ -0,0 +1,226 @@
|
||||
#if UNITY_64
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
|
||||
using AnotherFileBrowser.Windows;
|
||||
using UnityEngine;
|
||||
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
|
||||
|
||||
namespace BITKit.IO
|
||||
{
|
||||
public interface IApplicationFile
|
||||
{
|
||||
string Extension { get; set; }
|
||||
IStorageFile Current { get; }
|
||||
event Func<(string, byte[])> DataHandle;
|
||||
event Action<IStorageFile, IStorageFile> OnPathChanged;
|
||||
void Save();
|
||||
void SaveAs(string path=null);
|
||||
void Load(IStorageFile file=null);
|
||||
void Reload();
|
||||
void AddListener(string key, Action<byte[]> action);
|
||||
void RemoveListener(string key, Action<byte[]> action);
|
||||
}
|
||||
[Serializable]
|
||||
public sealed class ApplicationFile:IApplicationFile
|
||||
{
|
||||
private const string DefaultPath = nameof(ApplicationFile)+"."+nameof(DefaultPath);
|
||||
|
||||
#if UNITY_64
|
||||
[UnityEngine.RuntimeInitializeOnLoadMethod]
|
||||
private static void Reload()
|
||||
{
|
||||
Current = null;
|
||||
DataHandle = null;
|
||||
_genericEvent.Clear();
|
||||
_OpenFileCancellationTokenSource = new();
|
||||
}
|
||||
#endif
|
||||
public static IStorageFile Current
|
||||
{
|
||||
get => _current;
|
||||
set
|
||||
{
|
||||
OnPathChanged?.Invoke(_current,value);
|
||||
_current = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static IStorageFile _current;
|
||||
public static event Func<(string, byte[])> DataHandle;
|
||||
public static event Action<IStorageFile, IStorageFile> OnPathChanged;
|
||||
private static readonly GenericEvent _genericEvent = new();
|
||||
private static CancellationTokenSource _OpenFileCancellationTokenSource = new();
|
||||
private static string Extension { get; set; } = "zip";
|
||||
|
||||
private static BrowserProperties _browserProperties
|
||||
{
|
||||
get
|
||||
{
|
||||
var bp = new BrowserProperties();
|
||||
#if UNITY_64
|
||||
if (PlayerPrefs.HasKey(DefaultPath))
|
||||
{
|
||||
bp.initialDir = PlayerPrefs.GetString(DefaultPath);
|
||||
}
|
||||
#endif
|
||||
if (string.IsNullOrEmpty(Extension) is false)
|
||||
{
|
||||
bp.filter = $"{Extension} files (*.{Extension})|*.{Extension}";
|
||||
}
|
||||
return bp;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Save()
|
||||
{
|
||||
if (Current is null)
|
||||
{
|
||||
var bp = _browserProperties;
|
||||
|
||||
new Thread(() =>
|
||||
{
|
||||
new FileBrowser().SaveFileBrowser(bp, nameof(ApplicationFile), $".{Extension}", async path =>
|
||||
{
|
||||
await BITApp.SwitchToMainThread();
|
||||
|
||||
Current = new FileProvider(path);
|
||||
|
||||
Save();
|
||||
});
|
||||
}).Start();
|
||||
|
||||
BIT4Log.Log<IApplicationFile>($"无已加载的数据,等待选择保存地址");
|
||||
return;
|
||||
}
|
||||
|
||||
if (DataHandle is null)
|
||||
{
|
||||
BIT4Log.Log<IApplicationFile>("无任何可保存的数据");
|
||||
}
|
||||
|
||||
List<IAsset> assets = new();
|
||||
foreach (var func in DataHandle.CastAsFunc())
|
||||
{
|
||||
(string name,byte[] bytes) value = func.Invoke();
|
||||
var asset = new BITAsset(value.name,value.bytes);
|
||||
assets.Add(asset);
|
||||
}
|
||||
BITAssets.Build(Current.GetPath(),assets.ToArray());
|
||||
|
||||
BIT4Log.Log<IApplicationFile>($"已保存为:{Current.GetPath()}");
|
||||
}
|
||||
|
||||
public static async void SaveAs(string path)
|
||||
{
|
||||
if (path is null)
|
||||
{
|
||||
var bp = _browserProperties;
|
||||
|
||||
new Thread(() =>
|
||||
{
|
||||
new FileBrowser().SaveFileBrowser(bp, nameof(ApplicationFile), $".{Extension}", async path =>
|
||||
{
|
||||
await BITApp.SwitchToMainThread();
|
||||
|
||||
Current = new FileProvider(path);
|
||||
|
||||
SaveAs(path);
|
||||
});
|
||||
}).Start();
|
||||
|
||||
return;
|
||||
}
|
||||
Current = new FileProvider(path);
|
||||
Save();
|
||||
}
|
||||
public void AddListener(string key, Action<byte[]> action)
|
||||
{
|
||||
_genericEvent.AddListener<byte[]>(key,action);
|
||||
}
|
||||
|
||||
public void RemoveListener(string key, Action<byte[]> action)
|
||||
{
|
||||
_genericEvent.RemoveListener<byte[]>(key,action);
|
||||
}
|
||||
|
||||
public static void ReloadFile()
|
||||
{
|
||||
Load(Current);
|
||||
}
|
||||
public static void Load(IStorageFile file=null)
|
||||
{
|
||||
if (file is null)
|
||||
{
|
||||
_OpenFileCancellationTokenSource.Cancel();
|
||||
_OpenFileCancellationTokenSource = new();
|
||||
|
||||
BIT4Log.Log<IApplicationFile>("正在选择文件,跳过该次载入");
|
||||
|
||||
var bp = _browserProperties;
|
||||
new Thread(() =>
|
||||
{
|
||||
new FileBrowser().OpenFileBrowser(bp, Filepath);
|
||||
return;
|
||||
async void Filepath(string path)
|
||||
{
|
||||
await BITApp.SwitchToMainThread();
|
||||
Current = new FileProvider(path);
|
||||
Load(Current);
|
||||
}
|
||||
}).Start();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Current = file;
|
||||
|
||||
var path = file.GetPath();
|
||||
var header = BITAssets.ReadHeader(path);
|
||||
foreach (var name in header.Files)
|
||||
{
|
||||
var asset = BITAssets.ReadAsset(path, name);
|
||||
|
||||
_genericEvent.Invoke(asset.Name,asset.Buffer);
|
||||
}
|
||||
BIT4Log.Log<IApplicationFile>($"已加载{header.Files.Count}个数据");
|
||||
}
|
||||
string IApplicationFile.Extension
|
||||
{
|
||||
get => Extension;
|
||||
set => Extension = value;
|
||||
}
|
||||
IStorageFile IApplicationFile.Current => Current;
|
||||
|
||||
event Func<(string, byte[])> IApplicationFile.DataHandle
|
||||
{
|
||||
add => DataHandle += value;
|
||||
remove => DataHandle -= value;
|
||||
}
|
||||
event Action<IStorageFile,IStorageFile> IApplicationFile.OnPathChanged
|
||||
{
|
||||
add => OnPathChanged += value;
|
||||
remove=>OnPathChanged -= value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void IApplicationFile.Save() => Save();
|
||||
|
||||
void IApplicationFile.SaveAs(string path) => SaveAs(path);
|
||||
|
||||
void IApplicationFile.Load(IStorageFile file) => Load(file);
|
||||
void IApplicationFile.Reload()=>ReloadFile();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
11
Src/Core/Storage/ApplicationFile.cs.meta
Normal file
11
Src/Core/Storage/ApplicationFile.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f898a39f5757fbe4c8c63532611d5542
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -4,11 +4,10 @@ namespace BITKit
|
||||
{
|
||||
public interface ITicker
|
||||
{
|
||||
ulong TickCount { get; }
|
||||
void Add(Action action);
|
||||
void AddAsUpdate(Action<float> action);
|
||||
void AddAsFixedUpdate(Action<float> action);
|
||||
void RemoveAsUpdate(Action<float> action);
|
||||
void RemoveAsFixedUpdate(Action<float> action);
|
||||
void Add(Action<float> action);
|
||||
void Remove(Action<float> action);
|
||||
}
|
||||
public interface IMainTicker : ITicker { }
|
||||
public interface IThreadTicker : ITicker { }
|
||||
|
@@ -1,107 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace BITKit
|
||||
{
|
||||
public class ThreadHelper : IThreadTicker
|
||||
{
|
||||
internal static void InitAPP()
|
||||
{
|
||||
ThreadHelper helper = new();
|
||||
singleton = helper;
|
||||
DI.Register<ThreadHelper>(helper);
|
||||
DI.Register<IThreadTicker>(helper);
|
||||
}
|
||||
static ThreadHelper singleton;
|
||||
public static void LogCurrentThread()
|
||||
{
|
||||
var currentTheadID = Thread.CurrentThread.ManagedThreadId.ToString();
|
||||
}
|
||||
private readonly List<Action<float>> Updates = new();
|
||||
private readonly List<Action<float>> FixedUpdates = new();
|
||||
private readonly CancellationToken CancellationToken=new();
|
||||
private readonly List<Timer> timers = new();
|
||||
public void AddAsUpdate(Action<float> action)
|
||||
{
|
||||
Updates.Add(action);
|
||||
}
|
||||
public void AddAsFixedUpdate(Action<float> action)
|
||||
{
|
||||
FixedUpdates.Add(action);
|
||||
}
|
||||
public void RemoveAsUpdate(Action<float> action)
|
||||
{
|
||||
Updates.Remove(action);
|
||||
}
|
||||
public void RemoveAsFixedUpdate(Action<float> action)
|
||||
{
|
||||
FixedUpdates.Remove(action);
|
||||
}
|
||||
public void Add(Action action)
|
||||
{
|
||||
try
|
||||
{
|
||||
new Thread(action.Invoke).Start();
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
BIT4Log.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
[ExcuteOnAwake]
|
||||
public static void Start()
|
||||
{
|
||||
var ticker = DI.Get<ThreadHelper>();
|
||||
singleton = ticker;
|
||||
new Thread(singleton.Init).Start();
|
||||
}
|
||||
[ExcuteOnStop]
|
||||
public static void Stop()
|
||||
{
|
||||
foreach (var timer in singleton.timers)
|
||||
{
|
||||
timer.Stop();
|
||||
timer.Dispose();
|
||||
}
|
||||
singleton.Updates.Clear();
|
||||
singleton.FixedUpdates.Clear();
|
||||
}
|
||||
void Init()
|
||||
{
|
||||
CreateTimer(Updates, 1 / 64f);
|
||||
CreateTimer(FixedUpdates, 1 / 32f);
|
||||
}
|
||||
Timer CreateTimer(List<Action<float>> actions, float deltaTime)
|
||||
{
|
||||
Timer timer = new();
|
||||
timer.Interval = TimeSpan.FromSeconds(deltaTime).Milliseconds;
|
||||
timer.Elapsed += (x, y) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
CancellationToken.ThrowIfCancellationRequested();
|
||||
foreach (var action in actions.ToArray())
|
||||
{
|
||||
action.Invoke(deltaTime);
|
||||
}
|
||||
}
|
||||
catch (System.OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
BIT4Log.LogException(e);
|
||||
}
|
||||
};
|
||||
timer.AutoReset = true;
|
||||
timer.Enabled = true;
|
||||
timer.Start();
|
||||
timers.Add(timer);
|
||||
return timer;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,3 +1,6 @@
|
||||
using System;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace BITKit
|
||||
{
|
||||
public enum TransformMode : int
|
||||
@@ -7,4 +10,13 @@ namespace BITKit
|
||||
Rotate,
|
||||
Scale,
|
||||
}
|
||||
public interface ITransform:IDisposable
|
||||
{
|
||||
float3 LocalPosition { get; set; }
|
||||
float3 Position { get; set; }
|
||||
quaternion LocalRotation { get; set; }
|
||||
quaternion Rotation { get; set; }
|
||||
float3 LocalScale { get; set; }
|
||||
float4x4 Matrix { get; set; }
|
||||
}
|
||||
}
|
8
Src/Core/Tween.meta
Normal file
8
Src/Core/Tween.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7541124b3e1e214fa3ba67ab31fccae
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
68
Src/Core/Tween/BITween.cs
Normal file
68
Src/Core/Tween/BITween.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace BITKit.Tween
|
||||
{
|
||||
|
||||
public static class BITween
|
||||
{
|
||||
public class TweenSequence
|
||||
{
|
||||
internal TweenSequence(){}
|
||||
private readonly List<UniTask> tasks = new();
|
||||
public TweenSequence Append(UniTask task)
|
||||
{
|
||||
tasks.Add(task);
|
||||
return this;
|
||||
}
|
||||
|
||||
public async UniTask Play(CancellationToken cancellationToken=default)
|
||||
{
|
||||
foreach (var task in tasks)
|
||||
{
|
||||
await task;
|
||||
if (cancellationToken.IsCancellationRequested) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
public static TweenSequence CreateSequence()
|
||||
{
|
||||
return new TweenSequence();
|
||||
}
|
||||
|
||||
public static async UniTask MoveToForward(
|
||||
Action<float> setter,
|
||||
float from,
|
||||
float to,
|
||||
float duration = 1,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var t = 0f;
|
||||
var delta = 1 / duration;
|
||||
setter(from);
|
||||
//BIT4Log.Log<TweenSequence>($"已创建Tween,from:[{from}]to:[{to}]duration:[{duration}]delta:[{delta}]");
|
||||
while (t < 1 && cancellationToken.IsCancellationRequested is false)
|
||||
{
|
||||
t = math.clamp(t + delta*BITApp.Time.DeltaTime, 0, 1);
|
||||
var next = math.lerp(from, to, t);
|
||||
//BIT4Log.Log<TweenSequence>($"当前进度:[{t}]next:[{next}]");
|
||||
|
||||
#if UNITY_64
|
||||
await UniTask.NextFrame(cancellationToken);
|
||||
await UniTask.SwitchToMainThread(cancellationToken);
|
||||
#else
|
||||
await UniTask.Yield();
|
||||
#endif
|
||||
setter(next);
|
||||
}
|
||||
if (cancellationToken.IsCancellationRequested is false)
|
||||
setter(to);
|
||||
}
|
||||
}
|
||||
}
|
11
Src/Core/Tween/BITween.cs.meta
Normal file
11
Src/Core/Tween/BITween.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f84a6de7ba54554bbbb179c4afac11e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
13
Src/Core/UX/IUXBarService.cs
Normal file
13
Src/Core/UX/IUXBarService.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BITKit.UX
|
||||
{
|
||||
|
||||
public interface IUXBarService
|
||||
{
|
||||
void SetOrCreate(int id,string name,float value,object data = null);
|
||||
void Remove(int id);
|
||||
}
|
||||
}
|
||||
|
11
Src/Core/UX/IUXBarService.cs.meta
Normal file
11
Src/Core/UX/IUXBarService.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4a419b02b7a18a449a9598b8788a670f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
11
Src/Core/UX/IUXDialogue.cs
Normal file
11
Src/Core/UX/IUXDialogue.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BITKit.UX
|
||||
{
|
||||
public interface IUXDialogue
|
||||
{
|
||||
void Show(string content,string title = "Alert",Action confirmAction=null,Action<bool> onChoose=null);
|
||||
}
|
||||
}
|
11
Src/Core/UX/IUXDialogue.cs.meta
Normal file
11
Src/Core/UX/IUXDialogue.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 69687081ecdbf81469da6af131fab9da
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
10
Src/Core/UX/IUXPopUp.cs
Normal file
10
Src/Core/UX/IUXPopUp.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BITKit.UX
|
||||
{
|
||||
public interface IUXPopup
|
||||
{
|
||||
void Popup(string content,float duration=3f);
|
||||
}
|
||||
}
|
11
Src/Core/UX/IUXPopUp.cs.meta
Normal file
11
Src/Core/UX/IUXPopUp.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 437d4ac1db96ac14bb41ff06527de651
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
19
Src/Core/UX/IUXWaiting.cs
Normal file
19
Src/Core/UX/IUXWaiting.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BITKit.UX
|
||||
{
|
||||
public interface IUXWaitingHandle
|
||||
{
|
||||
string Message { get; set; }
|
||||
object Container { get; }
|
||||
public void SetMessage(string message)=>Message=message;
|
||||
}
|
||||
public interface IUXWaiting
|
||||
{
|
||||
IUXWaitingHandle Get();
|
||||
void Release(IUXWaitingHandle handle);
|
||||
}
|
||||
}
|
||||
|
11
Src/Core/UX/IUXWaiting.cs.meta
Normal file
11
Src/Core/UX/IUXWaiting.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3eb1539e554cd824d96fbdc602b8fd81
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,4 +1,6 @@
|
||||
namespace BITKit
|
||||
using System;
|
||||
|
||||
namespace BITKit
|
||||
{
|
||||
|
||||
public interface IAwake { void OnAwake(); }
|
||||
@@ -23,7 +25,11 @@
|
||||
public interface IFixedUpdate { void OnFixedUpdate(float deltaTime); }
|
||||
public interface ILateUpdate { void OnLateUpdate(float deltaTime); }
|
||||
public interface IDestroy { void OnDestroyComponent(); }
|
||||
|
||||
[Serializable]
|
||||
public struct EmptyAction : IAction
|
||||
{
|
||||
public void Execute() { }
|
||||
}
|
||||
|
||||
public class BehaviorUpdater
|
||||
{
|
||||
|
@@ -34,6 +34,15 @@ namespace BITKit
|
||||
private readonly Dictionary<string, List<object>> events = new();
|
||||
private readonly Dictionary<string, object> dictionary = new();
|
||||
public const string defaultEventName = nameof(GenericEvent);
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_directDirection.Clear();
|
||||
events.Clear();
|
||||
dictionary.Clear();
|
||||
}
|
||||
|
||||
|
||||
public void AddListener<T>(Action<T> action) =>
|
||||
AddListener<T>(defaultEventName, action);
|
||||
public void Invoke<T>(T value) =>
|
||||
@@ -182,5 +191,7 @@ namespace BITKit
|
||||
{
|
||||
return dictionary.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@@ -12,4 +12,5 @@
|
||||
T Get();
|
||||
void Set(T t);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,5 +1,11 @@
|
||||
namespace BITKit
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace BITKit
|
||||
{
|
||||
|
||||
|
||||
public interface IReference
|
||||
{
|
||||
string Get();
|
||||
@@ -7,6 +13,15 @@
|
||||
string Replace(string value) => Get().Replace("{x}",value);
|
||||
}
|
||||
|
||||
public static class IReferenceExtensions
|
||||
{
|
||||
public static string[] Cast(this IEnumerable<IReference> self)
|
||||
{
|
||||
return self.Select(Get).ToArray();
|
||||
string Get(IReference x) => x.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IReference<T>
|
||||
{
|
||||
T Get();
|
||||
|
@@ -22,6 +22,11 @@ namespace BITKit
|
||||
[CustomType(typeof(IValidHandle))]
|
||||
public sealed class ValidHandle: IValidHandle
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Allow:{enableHandle}\nElements:{string.Join("\n",objs)}\nDisableElements:{string.Join("\n",disableObjs)}";
|
||||
}
|
||||
|
||||
public ValidHandle() {}
|
||||
public ValidHandle(Action<bool> boolDelegate)
|
||||
{
|
||||
@@ -36,11 +41,18 @@ namespace BITKit
|
||||
public bool Allow => this;
|
||||
|
||||
private bool enableHandle;
|
||||
private readonly List<object> objs = new List<object>();
|
||||
private readonly List<object> disableObjs = new List<object>();
|
||||
/// <summary>
|
||||
/// ⚠️Dont operate this field directly
|
||||
/// </summary>
|
||||
public readonly List<object> objs = new List<object>();
|
||||
/// <summary>
|
||||
/// ⚠️Dont operate this field directly
|
||||
/// </summary>
|
||||
public readonly List<object> disableObjs = new List<object>();
|
||||
private bool tempEnable;
|
||||
private Action<bool> EventOnEnableChanged;
|
||||
|
||||
|
||||
public void AddElement(object obj)
|
||||
{
|
||||
if (objs.Contains(obj))
|
||||
|
@@ -50,6 +50,17 @@ namespace BITKit
|
||||
key = Generic<T>.GetVariable(key);
|
||||
events.Get(key).Remove(action);
|
||||
}
|
||||
|
||||
public static void Set<T>(string key, Func<T> factory)
|
||||
{
|
||||
key = Generic<T>.GetVariable(key);
|
||||
dictionary.AddOrUpdate(key,factory, AddOrUpdate);
|
||||
return;
|
||||
Func<T> AddOrUpdate(string _key,object current)
|
||||
{
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
public static void Set<T>(string key, T value)
|
||||
{
|
||||
key = Generic<T>.GetVariable(key);
|
||||
@@ -91,9 +102,12 @@ namespace BITKit
|
||||
key = Generic<T>.GetVariable(key);
|
||||
if (dictionary.TryGetValue(key, out var value))
|
||||
{
|
||||
if (value is T t)
|
||||
switch (value)
|
||||
{
|
||||
return t;
|
||||
case T t:
|
||||
return t;
|
||||
case Func<T> func:
|
||||
return func.Invoke();
|
||||
}
|
||||
}
|
||||
return default;
|
||||
|
@@ -25,10 +25,12 @@ namespace BITKit
|
||||
else if (bool.TryParse(value, out var boolResult))
|
||||
{
|
||||
Data.Set(key, boolResult);
|
||||
Data.Set(key, boolResult ? 1 : 0);
|
||||
}
|
||||
else if (int.TryParse(value, out var intResult))
|
||||
{
|
||||
Data.Set(key, intResult);
|
||||
Data.Set(key, intResult is 1);
|
||||
}
|
||||
else if (float.TryParse(value, out var floatResult))
|
||||
{
|
||||
|
@@ -35,7 +35,7 @@ namespace BITKit
|
||||
public static void EnsureDirectoryCreated(string path)
|
||||
{
|
||||
path = Path.GetDirectoryName(path);
|
||||
if (Directory.Exists(path) is true) return;
|
||||
if (Directory.Exists(path)) return;
|
||||
if (path != null) Directory.CreateDirectory(path);
|
||||
}
|
||||
|
||||
@@ -44,21 +44,17 @@ namespace BITKit
|
||||
var path = GetPath(paths);
|
||||
var dictionaryPath = Path.GetDirectoryName(path);
|
||||
Directory.CreateDirectory(dictionaryPath);
|
||||
if (File.Exists(path) is false)
|
||||
{
|
||||
using (var fs = File.Create(path))
|
||||
{
|
||||
fs.Close();
|
||||
fs.Dispose();
|
||||
}
|
||||
}
|
||||
if (File.Exists(path)) return path;
|
||||
using var fs = File.Create(path);
|
||||
fs.Close();
|
||||
fs.Dispose();
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static string GetTempFilePath(string fileName = null)
|
||||
{
|
||||
var path = GetFilePath("Temps", fileName is null ? Guid.NewGuid().ToString() : fileName);
|
||||
var path = GetFilePath("Temps", fileName ?? Guid.NewGuid().ToString());
|
||||
return path;
|
||||
}
|
||||
|
||||
|
@@ -12,13 +12,14 @@ namespace BITKit
|
||||
{
|
||||
public class ReflectionHelper
|
||||
{
|
||||
public static BindingFlags Flags => BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
public static InitializationState State = InitializationState.None;
|
||||
private static Type[] types = Type.EmptyTypes;
|
||||
private static IEnumerable<MethodInfo> methods = new List<MethodInfo>();
|
||||
private static IEnumerable<FieldInfo> fields = new List<FieldInfo>();
|
||||
private readonly static Dictionary<Type, MethodInfo[]> methodsCache = new();
|
||||
private readonly static Dictionary<Type, FieldInfo[]> fieldsCache = new();
|
||||
private readonly static Dictionary<Attribute, Attribute[]> attributes = new();
|
||||
private static readonly Dictionary<Type, MethodInfo[]> methodsCache = new();
|
||||
private static readonly Dictionary<Type, FieldInfo[]> fieldsCache = new();
|
||||
private static readonly Dictionary<Attribute, Attribute[]> attributes = new();
|
||||
public static async Task<IEnumerable<MethodInfo>> GetMethods<Att>() where Att : Attribute
|
||||
{
|
||||
await EnsureConfig();
|
||||
@@ -145,7 +146,7 @@ namespace BITKit
|
||||
|
||||
var loadedAssemblies = BITApp.Assemblies.IsValid()
|
||||
? BITApp.Assemblies
|
||||
: AppDomain.CurrentDomain.GetAssemblies();
|
||||
: GetAllAssemblies();
|
||||
BIT4Log.Log<ReflectionHelper>($"已加载程序集:{loadedAssemblies.Length}个");
|
||||
var result = new List<Type>();
|
||||
for (var i = 0; i < loadedAssemblies.Length; i++)
|
||||
@@ -226,6 +227,23 @@ namespace BITKit
|
||||
State = InitializationState.Initialized;
|
||||
BIT4Log.Log<ReflectionHelper>("已完成初始化");
|
||||
}
|
||||
|
||||
private static Assembly[] GetAllAssemblies()
|
||||
{
|
||||
return AppDomain.CurrentDomain.GetAssemblies();
|
||||
//return AppDomain.CurrentDomain.GetReferanceAssemblies().ToArray();
|
||||
// var assemblies = new List<Assembly>();
|
||||
// foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
// {
|
||||
// assemblies.Add(assembly);
|
||||
// foreach (var assemblyName in assembly.GetReferencedAssemblies())
|
||||
// {
|
||||
// assemblies.Add(Assembly.Load(assemblyName));
|
||||
// }
|
||||
// }
|
||||
// return assemblies.Distinct().ToArray();
|
||||
}
|
||||
|
||||
[ExcuteOnStop]
|
||||
public static void Reload()
|
||||
{
|
||||
@@ -237,4 +255,36 @@ namespace BITKit
|
||||
methodsCache.Clear();
|
||||
}
|
||||
}
|
||||
public static class Extents
|
||||
{
|
||||
public static List<Assembly> GetReferanceAssemblies(this AppDomain domain)
|
||||
{
|
||||
var list = new List<Assembly>();
|
||||
domain.GetAssemblies().ForEach(i =>
|
||||
{
|
||||
GetReferanceAssemblies(i, list);
|
||||
});
|
||||
return list;
|
||||
}
|
||||
static void GetReferanceAssemblies(Assembly assembly, List<Assembly> list)
|
||||
{
|
||||
assembly.GetReferencedAssemblies().ForEach(i =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var ass = Assembly.Load(i);
|
||||
if (!list.Contains(ass))
|
||||
{
|
||||
list.Add(ass);
|
||||
GetReferanceAssemblies(ass, list);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user