This commit is contained in:
CortexCore
2024-03-31 23:31:00 +08:00
parent e179d2eb53
commit b7b89ee71a
641 changed files with 31286 additions and 22134 deletions

View File

@@ -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));
}
}
}
}

View File

@@ -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)
{

View File

@@ -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();

View File

@@ -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; }
}
}

View File

@@ -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
}

View File

@@ -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}");
}
}
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 242f24809addbc442a81534487aa2b17
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 80daba647c9a302418cae1bd15d9a078
guid: 348d403129b862f46a56448b4e2e9528
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -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]

View File

@@ -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);
}
}
}
}

View 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);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 41391256f70b92848918e355d5d43b78
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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,例如

View File

@@ -0,0 +1,9 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace BITKit
{
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2dd41043546fb0e41a82e57749c0994a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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>>();

View File

@@ -19,5 +19,6 @@ namespace BITKit
}
return cacheName;
}
public static int Hash => typeof(T).GetHashCode();
}
}

View File

@@ -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);
}
}
}

View File

@@ -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)
{

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -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
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 237df241127e9db4e8945efe90ffe83a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5db9ede675f865f4bb8572fada2ec7ab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}

View 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
View 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);
}
}
}

View 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
View File

@@ -0,0 +1,9 @@
using System.Collections;
using System.Collections.Generic;
namespace BITKit.Mod
{
public interface IAssetMod{}
public interface IScriptMod{}
public interface IPackageMod{}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 55e47ab2418c5894b9c1349e43415cb3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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")]

View File

@@ -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;
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e8845089e2cd9eb44b27dabc6859da55
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 014832ce48d3bce4b85ecc2518c02667
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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

View File

@@ -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();

View File

@@ -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;
}
}
}

View File

@@ -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; }

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0a9ce048cccddfd42972d7502f096e77
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eb2d5a09faa2e9a41858ff98770b423c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 119f6a0b25fc0cc47b6f950cd9902aba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2f4e804e88433c74392144bb5a48db7c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f898a39f5757fbe4c8c63532611d5542
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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 { }

View File

@@ -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;
}
}
}

View File

@@ -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
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f7541124b3e1e214fa3ba67ab31fccae
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

68
Src/Core/Tween/BITween.cs Normal file
View 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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9f84a6de7ba54554bbbb179c4afac11e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4a419b02b7a18a449a9598b8788a670f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}

View 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
View 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);
}
}

View 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
View 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);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3eb1539e554cd824d96fbdc602b8fd81
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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
{

View File

@@ -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);
}
}
}

View File

@@ -12,4 +12,5 @@
T Get();
void Set(T t);
}
}

View File

@@ -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();

View File

@@ -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))

View File

@@ -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;

View File

@@ -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))
{

View File

@@ -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;
}

View File

@@ -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
}
});
}
}
}