This commit is contained in:
CortexCore 2024-11-03 16:38:17 +08:00
parent 056e2cada5
commit 4ba741408d
4682 changed files with 2343 additions and 5341 deletions

View File

@ -7,6 +7,8 @@ using Cysharp.Threading.Tasks;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
// ReSharper disable StringLiteralTypo // ReSharper disable StringLiteralTypo
#if NET5_0_OR_GREATER #if NET5_0_OR_GREATER
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -18,10 +20,9 @@ namespace BITKit
{ {
public override string Message => "Application Is Not Playing"; public override string Message => "Application Is Not Playing";
} }
// ReSharper disable once InconsistentNaming
public class BITApp public class BITApp
{ {
public static int Count => _count++;
private static int _count;
public static async UniTask SwitchToMainThread() public static async UniTask SwitchToMainThread()
{ {
#if UNITY_5_3_OR_NEWER #if UNITY_5_3_OR_NEWER
@ -168,7 +169,6 @@ namespace BITKit
"MySql", "MySql",
}; };
} }
#if NET5_0_OR_GREATER
/// <summary> /// <summary>
/// 依赖服务集合 /// 依赖服务集合
/// </summary> /// </summary>
@ -176,13 +176,18 @@ namespace BITKit
/// <summary> /// <summary>
/// 依赖服务提供接口 /// 依赖服务提供接口
/// </summary> /// </summary>
public static ServiceProvider ServiceProvider { get; internal set; } public static ServiceProvider ServiceProvider { get; private set; }
public static ServiceProvider BuildServiceProvider()
{
var value = ServiceProvider = ServiceCollection.BuildServiceProvider();
OnServiceProviderBuild?.Invoke(value);
return value;
}
/// <summary> /// <summary>
/// 服务创建后的回调 /// 服务创建后的回调
/// </summary> /// </summary>
public static Action<ServiceProvider> OnServiceProviderBuilded; public static Action<ServiceProvider> OnServiceProviderBuild;
#endif
/// <summary> /// <summary>
/// 主线程 /// 主线程
/// </summary> /// </summary>
@ -254,7 +259,6 @@ namespace BITKit
private static DateTime InitialTime { get; set; }=DateTime.Now; private static DateTime InitialTime { get; set; }=DateTime.Now;
public static async UniTask Start(string appName = nameof(BITApp),AppSettings settings=default) public static async UniTask Start(string appName = nameof(BITApp),AppSettings settings=default)
{ {
_count = 0;
Time.TimeAsDouble = 0; Time.TimeAsDouble = 0;
Time.DeltaTime = 1 / 60f; Time.DeltaTime = 1 / 60f;

View File

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

View File

@ -2,7 +2,7 @@
{ {
public static class BITMapper public static class BITMapper
{ {
public static void Map<T>(T source, T target) where T : class public static T Map<T>(object source, T target) where T : class
{ {
foreach (var info in source.GetType().GetProperties(ReflectionHelper.Flags)) foreach (var info in source.GetType().GetProperties(ReflectionHelper.Flags))
{ {
@ -23,6 +23,7 @@
targetInfo?.SetValue(target, value); targetInfo?.SetValue(target, value);
} }
} }
return target;
} }
} }
} }

View File

@ -137,50 +137,58 @@ namespace BITKit
} }
public static void Write(BinaryWriter writer, object value) public static void Write(BinaryWriter writer, object value)
{ {
if (value is not string && value is IList enumerable) try
{ {
var pars = enumerable.Cast<object>().ToArray(); if (value is not string && value is IList enumerable)
writer.Write(true);
writer.Write(value.GetType().GetElementType()!.FullName!);
writer.Write(pars.Length);
foreach (var obj in pars)
{ {
WriteInterel(writer, obj); var pars = enumerable.Cast<object>().ToArray();
} writer.Write(true);
} writer.Write(value.GetType().GetElementType()!.FullName!);
else writer.Write(pars.Length);
{ foreach (var obj in pars)
writer.Write(false); {
WriteInterel(writer,value); WriteInterel(writer, obj);
} }
return;
void WriteInterel(BinaryWriter writer, object value)
{
var typeName = value.GetType().FullName;
writer.Write(typeName!);
if (netReaders.TryGetValue(typeName, out var netReader))
{
netReader.WriteBinaryAsObject(writer,value);
}
else if (value is IBinarySerialize serialize)
{
serialize.Write(writer);
} }
else else
{ {
try writer.Write(false);
{ WriteInterel(writer,value);
writer.Write(JsonConvert.SerializeObject(value));
}
catch (Exception)
{
BIT4Log.Warning<BITBinary>(typeName);
throw;
}
} }
return;
void WriteInterel(BinaryWriter writer, object value)
{
var typeName = value.GetType().FullName;
writer.Write(typeName!);
if (netReaders.TryGetValue(typeName, out var netReader))
{
netReader.WriteBinaryAsObject(writer,value);
}
else if (value is IBinarySerialize serialize)
{
serialize.Write(writer);
}
else
{
try
{
writer.Write(JsonConvert.SerializeObject(value));
}
catch (Exception)
{
BIT4Log.Warning<BITBinary>(typeName);
throw;
}
}
}
} }
catch (Exception e)
{
throw;
}
} }
public static bool IsSupport(object obj) => IsSupport(obj.GetType().FullName); public static bool IsSupport(object obj) => IsSupport(obj.GetType().FullName);
public static bool IsSupport(Type type) => IsSupport(type.FullName); public static bool IsSupport(Type type) => IsSupport(type.FullName);

View File

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

View File

@ -28,7 +28,7 @@ namespace BITKit.CommandPattern
void Release(); void Release();
} }
public sealed class CommandSequence:List<ICommand>,IBinarySerialize public sealed class CommandSequence:List<ICommand>,Microsoft.SqlServer.Server.IBinarySerialize
{ {
public void Read(BinaryReader r) public void Read(BinaryReader r)
{ {

View File

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

View File

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

View File

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

View File

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

View File

@ -3,5 +3,6 @@ namespace BITKit
public interface IDescription public interface IDescription
{ {
public string Name { get; } public string Name { get; }
public string Description { get; }
} }
} }

View File

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

View File

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

View File

@ -54,79 +54,87 @@ namespace BITKit.Entities
public T[] QueryComponents<T>() public T[] QueryComponents<T>()
{ {
return _queryCache.GetOrAdd(typeof(T), type => throw new NotImplementedException();
{ // return _queryCache.GetOrAdd(typeof(T), type =>
return _entities.Values.Where(entity => entity.TryGetComponent(out T component)).ToArray(); // {
}).Cast<T>().ToArray(); // return _entities.Values.Where(entity => entity.TryGetComponent(out T component)).ToArray();
// }).Cast<T>().ToArray();
} }
public (T, T1)[] QueryComponents<T, T1>() public (T, T1)[] QueryComponents<T, T1>()
{ {
List<(T, T1)> list = new(); throw new NotImplementedException();
foreach (var entity in _entities.Values) // List<(T, T1)> list = new();
{ // foreach (var entity in _entities.Values)
if (entity.TryGetComponent(out T t) && entity.TryGetComponent(out T1 t1)) // {
list.Add((t, t1)); // if (entity.TryGetComponent(out T t) && entity.TryGetComponent(out T1 t1))
} // list.Add((t, t1));
return list.ToArray(); // }
// return list.ToArray();
} }
public (T, T1, T2)[] QueryComponents<T, T1, T2>() public (T, T1, T2)[] QueryComponents<T, T1, T2>()
{ {
List<(T, T1, T2)> list = new(); throw new NotImplementedException();
foreach (var entity in _entities.Values) // List<(T, T1, T2)> list = new();
{ // foreach (var entity in _entities.Values)
if (entity.TryGetComponent(out T t) && entity.TryGetComponent(out T1 t1) && entity.TryGetComponent(out T2 t2)) // {
list.Add((t, t1, t2)); // if (entity.TryGetComponent(out T t) && entity.TryGetComponent(out T1 t1) && entity.TryGetComponent(out T2 t2))
} // list.Add((t, t1, t2));
return list.ToArray(); // }
// return list.ToArray();
} }
public (T, T1, T2, T3)[] QueryComponents<T, T1, T2, T3>() public (T, T1, T2, T3)[] QueryComponents<T, T1, T2, T3>()
{ {
List<(T, T1, T2, T3)> list = new(); throw new NotImplementedException();
foreach (var entity in _entities.Values) // List<(T, T1, T2, T3)> list = new();
{ // foreach (var entity in _entities.Values)
if (entity.TryGetComponent(out T t) && entity.TryGetComponent(out T1 t1) && entity.TryGetComponent(out T2 t2) && entity.TryGetComponent(out T3 t3)) // {
list.Add((t, t1, t2, t3)); // if (entity.TryGetComponent(out T t) && entity.TryGetComponent(out T1 t1) && entity.TryGetComponent(out T2 t2) && entity.TryGetComponent(out T3 t3))
} // list.Add((t, t1, t2, t3));
return list.ToArray(); // }
// return list.ToArray();
} }
public (T, T1, T2, T3, T4)[] QueryComponents<T, T1, T2, T3, T4>() public (T, T1, T2, T3, T4)[] QueryComponents<T, T1, T2, T3, T4>()
{ {
List<(T, T1, T2, T3, T4)> list = new(); throw new NotImplementedException();
foreach (var entity in _entities.Values) // List<(T, T1, T2, T3, T4)> list = new();
{ // foreach (var entity in _entities.Values)
if (entity.TryGetComponent(out T t) && entity.TryGetComponent(out T1 t1) && entity.TryGetComponent(out T2 t2) && entity.TryGetComponent(out T3 t3) && entity.TryGetComponent(out T4 t4)) // {
list.Add((t, t1, t2, t3, t4)); // if (entity.TryGetComponent(out T t) && entity.TryGetComponent(out T1 t1) && entity.TryGetComponent(out T2 t2) && entity.TryGetComponent(out T3 t3) && entity.TryGetComponent(out T4 t4))
} // list.Add((t, t1, t2, t3, t4));
return list.ToArray(); // }
// return list.ToArray();
} }
public (T, T1, T2, T3, T4, T5)[] QueryComponents<T, T1, T2, T3, T4, T5>() public (T, T1, T2, T3, T4, T5)[] QueryComponents<T, T1, T2, T3, T4, T5>()
{ {
List<(T, T1, T2, T3, T4, T5)> list = new(); throw new NotImplementedException();
foreach (var entity in _entities.Values) // List<(T, T1, T2, T3, T4, T5)> list = new();
{ // foreach (var entity in _entities.Values)
if (entity.TryGetComponent(out T t) && entity.TryGetComponent(out T1 t1) && entity.TryGetComponent(out T2 t2) && entity.TryGetComponent(out T3 t3) && entity.TryGetComponent(out T4 t4) && entity.TryGetComponent(out T5 t5)) // {
list.Add((t, t1, t2, t3, t4, t5)); // if (entity.TryGetComponent(out T t) && entity.TryGetComponent(out T1 t1) && entity.TryGetComponent(out T2 t2) && entity.TryGetComponent(out T3 t3) && entity.TryGetComponent(out T4 t4) && entity.TryGetComponent(out T5 t5))
} // list.Add((t, t1, t2, t3, t4, t5));
return list.ToArray(); // }
// return list.ToArray();
} }
public (T, T1, T2, T3, T4, T5, T6)[] QueryComponents<T, T1, T2, T3, T4, T5, T6>() public (T, T1, T2, T3, T4, T5, T6)[] QueryComponents<T, T1, T2, T3, T4, T5, T6>()
{ {
List<(T, T1, T2, T3, T4, T5, T6)> list = new(); throw new NotImplementedException();
foreach (var entity in _entities.Values) // List<(T, T1, T2, T3, T4, T5, T6)> list = new();
{ // foreach (var entity in _entities.Values)
if (entity.TryGetComponent(out T t) && entity.TryGetComponent(out T1 t1) && entity.TryGetComponent(out T2 t2) && entity.TryGetComponent(out T3 t3) && entity.TryGetComponent(out T4 t4) && entity.TryGetComponent(out T5 t5) && entity.TryGetComponent(out T6 t6)) // {
list.Add((t, t1, t2, t3, t4, t5, t6)); // if (entity.TryGetComponent(out T t) && entity.TryGetComponent(out T1 t1) && entity.TryGetComponent(out T2 t2) && entity.TryGetComponent(out T3 t3) && entity.TryGetComponent(out T4 t4) && entity.TryGetComponent(out T5 t5) && entity.TryGetComponent(out T6 t6))
} // list.Add((t, t1, t2, t3, t4, t5, t6));
return list.ToArray(); // }
// return list.ToArray();
} }
public ValueTuple<T, T1, T2, T3, T4, T5, T6, TRest>[] QueryComponents<T, T1, T2, T3, T4, T5, T6, TRest>() where TRest : struct public ValueTuple<T, T1, T2, T3, T4, T5, T6, TRest>[] QueryComponents<T, T1, T2, T3, T4, T5, T6, TRest>() where TRest : struct
{ {
throw new NotImplementedException(); throw new NotImplementedException();
// throw new NotImplementedException();
} }
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -7,9 +8,25 @@ namespace BITKit.Entities
{ {
public class Entity : IEntity, IDisposable public class Entity : IEntity, IDisposable
{ {
private class EntityServiceProvider : IServiceProvider
{
public ServiceProvider ServiceProvider;
public readonly List<Object> Services = new();
public object GetService(Type serviceType)
{
var value = ServiceProvider.GetService(serviceType);
if (value != null)
{
Services.TryAdd(value);
}
return value;
}
}
public Entity() public Entity()
{ {
RegisterComponent<IEntity>(this); ServiceCollection.AddSingleton<IEntity>(this);
} }
public void WaitForInitializationComplete() public void WaitForInitializationComplete()
{ {
@ -19,51 +36,26 @@ namespace BITKit.Entities
public CancellationToken CancellationToken => _cancellationTokenSource.Token; public CancellationToken CancellationToken => _cancellationTokenSource.Token;
private readonly CancellationTokenSource _cancellationTokenSource = new(); private readonly CancellationTokenSource _cancellationTokenSource = new();
public bool TryGetComponent<T>(out T component) public IServiceProvider ServiceProvider
{ {
var value = ServiceProvider.GetService<T>(); get
if (value != null)
{ {
component = value; if (_serviceProvider is not null)
return true; {
return _serviceProvider;
}
var value = new EntityServiceProvider()
{
ServiceProvider = ServiceCollection.BuildServiceProvider()
};
_serviceProvider = value;
return _serviceProvider;
} }
component = default!;
return false;
} }
public bool TryGetComponent(Type type, out IEntityComponent component)
{
var value = ServiceProvider.GetService(type);
if (value != null)
{
component = (IEntityComponent)value;
return true;
}
component = default!;
return false;
}
public IEntityComponent[] Components => ServiceCollection.OfType<IEntityComponent>().ToArray();
public bool RegisterComponent<T>(T component)
{
_services.Add(component);
ServiceCollection.AddSingleton(typeof(T), component);
return true;
}
public IServiceProvider ServiceProvider => _serviceProvider ??= ServiceCollection.BuildServiceProvider();
public IServiceCollection ServiceCollection { get; } = new ServiceCollection(); public IServiceCollection ServiceCollection { get; } = new ServiceCollection();
private IServiceProvider _serviceProvider; private EntityServiceProvider _serviceProvider;
private readonly CacheList<object> _services = new(); public object[] GetServices()=> _serviceProvider.Services.ToArray();
public object[] GetServices()
{
return _services.ToArray();
}
public void Inject(object obj) public void Inject(object obj)
{ {
foreach (var fieldInfo in obj.GetType().GetFields(ReflectionHelper.Flags)) foreach (var fieldInfo in obj.GetType().GetFields(ReflectionHelper.Flags))
@ -78,14 +70,10 @@ namespace BITKit.Entities
public void Dispose() public void Dispose()
{ {
_cancellationTokenSource.Cancel(); _cancellationTokenSource.Cancel();
_cancellationTokenSource.Dispose(); _serviceProvider.ServiceProvider.Dispose();
foreach (var x in GetServices().OfType<IDisposable>())
foreach (var service in _services)
{ {
if (service is IDisposable disposable) x.Dispose();
{
disposable.Dispose();
}
} }
} }
} }

View File

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

View File

@ -1,6 +1,7 @@
using System.Threading; using System.Threading;
using System; using System;
using System.ComponentModel.Design; using System.ComponentModel.Design;
using Microsoft.Extensions.DependencyInjection;
#if NET5_0_OR_GREATER #if NET5_0_OR_GREATER
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
#endif #endif
@ -11,44 +12,14 @@ namespace BITKit.Entities
/// </summary> /// </summary>
public interface IEntity public interface IEntity
{ {
/// <summary>
/// 等待初始化完成,通常用于其他系统需要等待实体初始化完成
/// </summary>
void WaitForInitializationComplete();
int Id { get; } int Id { get; }
CancellationToken CancellationToken { get; } CancellationToken CancellationToken { get; }
bool TryGetComponent<T>(out T component);
bool TryGetComponent(Type type, out IEntityComponent component);
IEntityComponent[] Components { get; }
bool RegisterComponent<T>(T component);
IServiceProvider ServiceProvider { get; } IServiceProvider ServiceProvider { get; }
#if NET5_0_OR_GREATER
IServiceCollection ServiceCollection { get; } IServiceCollection ServiceCollection { get; }
object[] GetServices(); object[] GetServices();
#endif
void Inject(object obj); void Inject(object obj);
} }
/// <summary> /// <summary>
/// 基本实体组件
/// </summary>
public interface IEntityComponent
{
IEntity Entity { get; set; }
#if NET5_0_OR_GREATER
void BuildService(IServiceCollection serviceCollection);
#endif
}
public interface IEntityBehavior:IEntityComponent
{
void Initialize(IEntity _entity);
void OnAwake();
void OnStart();
void OnUpdate(float deltaTime);
void OnFixedUpdate(float deltaTime);
void OnLateUpdate(float deltaTime);
void OnDestroyComponent();
}
/// <summary>
/// 基本实体服务 /// 基本实体服务
/// </summary> /// </summary>
public interface IEntitiesService public interface IEntitiesService

View File

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

View File

@ -8,7 +8,8 @@ namespace BITKit
{ {
public static IEnumerable<Func<T>> CastAsFunc<T>(this Func<T> self) public static IEnumerable<Func<T>> CastAsFunc<T>(this Func<T> self)
{ {
return self is null ? Array.Empty<Func<T>>() : self?.GetInvocationList().Cast<Func<T>>(); var value = self;
return value 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) public static IEnumerable<Func<T0, T1>> CastAsFunc<T0, T1>(this Func<T0, T1> self)
{ {

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -186,6 +187,10 @@ namespace BITKit
self.Add(key, value); self.Add(key, value);
} }
} }
public static TValue Get<TKey,TValue>(this IDictionary<Type,TValue> self)
{
return self[typeof(TKey)];
}
public static TValue Get<TKey, TValue>(this IDictionary<TKey, TValue> self, TKey key) where TValue : new() public static TValue Get<TKey, TValue>(this IDictionary<TKey, TValue> self, TKey key) where TValue : new()
{ {
lock (self) lock (self)

View File

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

View File

@ -31,7 +31,7 @@ namespace BITKit.StateMachine
void OnStateExit(IState old, IState newState); void OnStateExit(IState old, IState newState);
} }
public interface IStateMachine<T> public interface IStateMachine<T> where T:IState
{ {
bool Enabled { get; set; } bool Enabled { get; set; }
T CurrentState { get; set; } T CurrentState { get; set; }
@ -53,7 +53,7 @@ namespace BITKit.StateMachine
public static class StateMachineUtils public static class StateMachineUtils
{ {
public static void Register<T>(IStateMachine<T> stateMachine, T newState) where T : IState public static void Register<T>(this IStateMachine<T> stateMachine, T newState) where T : IState
{ {
if (stateMachine.StateDictionary.ContainsKey(newState.GetType())) if (stateMachine.StateDictionary.ContainsKey(newState.GetType()))
{ {

View File

@ -1,155 +1,161 @@
using System; using System;
using System.IO; using System.Collections.Generic;
namespace BITKit namespace BITKit
{ {
#region #region
public enum ItemQuality public enum ItemQuality
{ {
/// <summary> /// <summary>
/// 常见的 /// 常见的
/// </summary> /// </summary>
Common, Common,
/// <summary> /// <summary>
/// 罕见的 /// 罕见的
/// </summary> /// </summary>
Uncommon, Uncommon,
/// <summary> /// <summary>
/// 稀有的 /// 稀有的
/// </summary> /// </summary>
Rare, Rare,
/// <summary> /// <summary>
/// 史诗的 /// 史诗的
/// </summary> /// </summary>
Epic, Epic,
/// <summary> /// <summary>
/// 传奇的 /// 传奇的
/// </summary> /// </summary>
Legendary, Legendary,
/// <summary> /// <summary>
/// 神话的 /// 神话的
/// </summary> /// </summary>
Mythical, Mythical,
/// <summary> /// <summary>
/// 开发者 /// 开发者
/// </summary> /// </summary>
Develop, Develop,
} }
#endregion #endregion
#region #region
/// <summary>
/// 物品属性
/// </summary>
public interface IScriptableItemProperty
{
}
/// <summary> /// <summary>
/// 基础物品 /// 基础物品
/// </summary> /// </summary>
public interface IBasicItem :IPropertable,ICloneable public interface IScriptableItem
{ {
/// <summary> /// <summary>
/// 唯一Id /// 唯一Id
/// </summary> /// </summary>
int Id { get; } int Id { get; }
/// <summary> /// <summary>
/// 物品名,一般用于查找物品的主键 /// 物品名,一般用于查找物品的主键
/// </summary> /// </summary>
string Name { get; } string Name { get; }
// /// <summary>
// /// 可寻址路径,该路径用于查找物品
// /// </summary>
// string AddressablePath { get; }
/// <summary> /// <summary>
/// 物品描述 /// 物品描述
/// </summary> /// </summary>
string Description { get; } string Description { get; }
/// <summary>
/// 最大堆叠数量
/// </summary>
public int MaxStack { get; }
/// <summary> /// <summary>
/// 物品品质 /// 物品品质
/// </summary> /// </summary>
ItemQuality Quality { get; } ItemQuality Quality { get; }
bool CopyItemsFrom(IBasicItem item);
/// <summary>
/// 属性
/// </summary>
IReadOnlyDictionary<Type, IScriptableItemProperty> Properties { get; }
/// <summary> /// <summary>
/// 价值 /// 价值
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
int Value=>10; int Value => 10;
} }
/// <summary>
/// 可配置化物品,通常用于配置 public interface IRuntimeItem : ICloneable
/// </summary>
public interface IAssetableItem: IBasicItem
{ {
/// <summary>
/// 运行时Id
/// </summary>
public int Id { get; }
/// <summary>
/// 配置Id
/// </summary>
public int ScriptableId { get; }
/// <summary>
/// 数量
/// </summary>
public int Amount { get; }
/// <summary>
/// 运行时属性
/// </summary>
IDictionary<Type, IScriptableItemProperty> RuntimeProperties { get; }
/// <summary>
/// 当运行时属性改变时
/// </summary>
// ReSharper disable once EventNeverInvoked.Global
event Action<IRuntimeItem> OnRuntimePropertiesChanged;
} }
#endregion #endregion
#region #region
/// <summary> /// <summary>
/// 被托管的物品 /// 被托管的物品
/// </summary> /// </summary>
[Serializable] [Serializable]
public class ManagedItem : IBasicItem public record RuntimeItem : IRuntimeItem
{ {
#region public int Id { get; set; } = new Random().Next();
public int Id; public int ScriptableId { get; set; }
public string Name; public int Amount { get; set; }
public string AddressablePath { get; set; }
public string Description;
public ItemQuality Quality;
public int Value { get; set; }
/// <summary>
/// 本地属性
/// </summary>
private Property property = new();
#endregion
#region
int IBasicItem.Id => Id;
string IBasicItem.Name => Name;
string IBasicItem.Description => Description;
ItemQuality IBasicItem.Quality => Quality;
#endregion
#region
public bool Contains<T>() => property.Contains<T>();
public T GetOrAddProperty<T>(Func<T> addFactory) => property.GetOrAddProperty<T>(addFactory); public IDictionary<Type, IScriptableItemProperty> RuntimeProperties { get; set; } =
new Dictionary<Type, IScriptableItemProperty>();
public T GetOrCreateProperty<T>() => property.GetOrCreateProperty<T>(); public event Action<IRuntimeItem> OnRuntimePropertiesChanged;
public object[] GetProperties() => property.GetProperties(); object ICloneable.Clone()
public bool TryGetProperty<T>(out T value) => property.TryGetProperty<T>(out value);
public bool TryRemoveProperty<T>() => property.TryRemoveProperty<T>();
public bool TrySetProperty<T>(T value) => property.TrySetProperty(value);
public void Read(BinaryReader r)
{ {
throw new NotImplementedException(); return new RuntimeItem
} {
Id = new Random().Next(),
public void Write(BinaryWriter w) ScriptableId = ScriptableId,
{ Amount = Amount,
throw new NotImplementedException(); RuntimeProperties = new Dictionary<Type, IScriptableItemProperty>(RuntimeProperties),
} OnRuntimePropertiesChanged = null,
};
public bool CopyItemsFrom(IBasicItem item)
{
Value = item.Value;
Id=item.Id;
Name = item.Name;
//AddressablePath = item.AddressablePath;
Description = item.Description;
Quality=item.Quality;
CopyPropertiesFrom(item);
return true;
}
public bool ClearProperties()=>property.ClearProperties();
public bool CopyPropertiesFrom(IPropertable propertable)
{
return property.CopyPropertiesFrom(propertable);
}
#endregion
public object Clone()
{
var item = MemberwiseClone() as ManagedItem;
item!.Id = Id;
return item;
} }
} }
#endregion #endregion
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using Microsoft.SqlServer.Server;
namespace BITKit namespace BITKit
{ {
@ -7,89 +8,63 @@ namespace BITKit
/// 支持属性 /// 支持属性
/// 支持回调 /// 支持回调
/// </summary> /// </summary>
public interface IBasicItemContainer public interface IRuntimeItemContainer:IBinarySerialize
{ {
/// <summary> /// <summary>
/// 物品容器的唯一Id /// 物品容器的唯一Id
/// </summary> /// </summary>
int Id { get; } int Id { get; }
/// <summary> /// <summary>
/// 尝试获取指定Item
/// </summary>
bool TryGetItem(Func<IBasicItem, bool> func, out IBasicItem item);
/// <summary>
/// 获取所有Item的只读副本 /// 获取所有Item的只读副本
/// </summary> /// </summary>
IBasicItem[] GetItems(); IRuntimeItem[] GetItems();
/// <summary> /// <summary>
/// 添加物品的接口 /// 添加物品的接口
/// </summary> /// </summary>
bool Add(IBasicItem item); bool Add(IRuntimeItem item);
/// <summary>
/// 通过Item本身进行移除
/// </summary>
bool Remove(IBasicItem item);
/// <summary> /// <summary>
/// 通过Id移除物品(推荐) /// 通过Id移除物品(推荐)
/// </summary> /// </summary>
bool Remove(int id); bool Remove(int id);
/// <summary> /// <summary>
/// 通过工厂方法移除物品
/// </summary>
bool Remove(Func<IBasicItem, bool> removeFactory);
/// <summary>
/// 通过通过Id丢下物品 /// 通过通过Id丢下物品
/// </summary> /// </summary>
bool Drop(int Id); bool Drop(int id);
bool DropOrSpawn(IBasicItem item);
/// <summary> /// <summary>
/// 注册添加物品的工厂方法, /// 注册添加物品的工厂方法,
/// </summary> /// </summary>
event Func<IBasicItem, bool> AddFactory; event Func<IRuntimeItem, bool> AddFactory;
/// <summary> /// <summary>
/// 注册移除物品的工厂方法 /// 注册移除物品的工厂方法
/// </summary> /// </summary>
event Func<IBasicItem, bool> RemoveFactory; event Func<IRuntimeItem, bool> RemoveFactory;
/// <summary> /// <summary>
/// 注册丢下物品的工厂方法 /// 注册丢下物品的工厂方法
/// </summary> /// </summary>
event Func<IBasicItem, bool> DropFactory; event Func<IRuntimeItem, bool> DropFactory;
/// <summary> /// <summary>
/// 已添加Item的回调 /// 已添加Item的回调
/// </summary> /// </summary>
event Action<IBasicItem> OnAdd; event Action<IRuntimeItem> OnAdd;
/// <summary> /// <summary>
/// 已移除Item的回调 /// 已移除Item的回调
/// </summary> /// </summary>
event Action<IBasicItem> OnRemove; event Action<IRuntimeItem> OnRemove;
/// <summary> /// <summary>
/// 已设置Item的回调 /// 已设置Item的回调
/// </summary> /// </summary>
event Action<IBasicItem> OnSet; event Action<IRuntimeItem> OnSet;
/// <summary> /// <summary>
/// 已丢下Item的回调 /// 已丢下Item的回调
/// </summary> /// </summary>
event Action<IBasicItem> OnDrop; event Action<IRuntimeItem> OnDrop;
/// <summary> /// <summary>
/// 已重构Items的回调 /// 已重构Items的回调
/// </summary> /// </summary>
event Action<IBasicItemContainer> OnRebuild; event Action<IRuntimeItemContainer> OnRebuild;
/// <summary> /// <summary>
/// 是否已完成物品交换,例如false就是开始交换物品true就是已完成交换物品,可以处理物品了 /// 是否已完成物品交换,例如false就是开始交换物品true就是已完成交换物品,可以处理物品了
/// </summary> /// </summary>
event Action<bool> OnRelease; event Action<bool> OnRelease;
/// <summary>
/// 添加挂起句柄
/// </summary>
/// <param name="id"></param>
void AddHandle(int id);
/// <summary>
/// 移除挂起句柄
/// </summary>
/// <param name="id"></param>
void RemoveHandle(int id);
} }
} }

View File

@ -1,13 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace BITKit
{
public interface IWorldItemObject
{
public IBasicItem Item { get; set; }
public event Action<IBasicItem> OnSetItem;
}
}

View File

@ -14,6 +14,7 @@ using System.Numerics;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using BITKit.Net.Examples; using BITKit.Net.Examples;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
using Unity.Mathematics; using Unity.Mathematics;
@ -21,6 +22,26 @@ namespace BITKit.Net
{ {
public class KcpNetClient:INetClient,INetProvider public class KcpNetClient:INetClient,INetProvider
{ {
private readonly ILogger<KcpNetClient> _logger;
public KcpNetClient(ILogger<KcpNetClient> logger)
{
_logger = logger;
_client = new KcpClient(
OnConnectedInternal,
OnData,
OnDisconnectInternal,
OnError,
KCPNet.Config
);
_timer.Elapsed += Tick;
_logger.LogInformation("已创建KCP客户端");
AddCommandListener<NetClientAllocateIdCommand>(x =>
{
Id = x.Id;
});
_isConnected.AddListener(ConnectionCallback);
}
public event Action OnStartConnect; public event Action OnStartConnect;
public event Action OnConnected; public event Action OnConnected;
public event Action OnDisconnected; public event Action OnDisconnected;
@ -29,14 +50,14 @@ namespace BITKit.Net
public bool IsConnecting { get; private set; } public bool IsConnecting { get; private set; }
public double RpcTimeOut { get; set; } = 5; public double RpcTimeOut { get; set; } = 5;
public bool AutoReconnect { get; set; } = true; public bool AutoReconnect { get; set; } = true;
public float2 Traffic { get; set; } public float2 Traffic { get; private set; }
public bool ManualTick { get; set; } public bool ManualTick { get; set; }
private readonly IntervalUpdate _reconnectInterval = new(1); private readonly IntervalUpdate _reconnectInterval = new(1);
public int Ping { get; private set; } public int Ping { get; private set; }
public int Id { get; private set; } = -1; public int Id { get; private set; } = -1;
private readonly KcpClient client; private readonly KcpClient _client;
private readonly ConcurrentQueue<byte[]> _commandQueue = new(); private readonly ConcurrentQueue<byte[]> _commandQueue = new();
@ -50,7 +71,7 @@ namespace BITKit.Net
private readonly GenericEvent _events = new(); private readonly GenericEvent _events = new();
private readonly ValidHandle _isConnected = new(); private readonly ValidHandle _isConnected = new();
private bool _userConnected;
private int _index = int.MinValue; private int _index = int.MinValue;
private readonly ConcurrentDictionary<int, object> _p2p = new(); private readonly ConcurrentDictionary<int, object> _p2p = new();
private readonly ConcurrentDictionary<string,Func<object,UniTask<object>>> _rpc = new(); private readonly ConcurrentDictionary<string,Func<object,UniTask<object>>> _rpc = new();
@ -64,23 +85,7 @@ namespace BITKit.Net
private ushort _connectedPort = 27014; private ushort _connectedPort = 27014;
private readonly byte[] _heartBeat = new byte[] { (byte)NetCommandType.Heartbeat }; private readonly byte[] _heartBeat = new byte[] { (byte)NetCommandType.Heartbeat };
public KcpNetClient()
{
client = new KcpClient(
OnConnectedInternal,
OnData,
OnDisconnectInternal,
OnError,
KCPNet.Config
);
_timer.Elapsed += Tick;
BIT4Log.Log<KcpNetClient>("已创建KCP客户端");
AddCommandListener<NetClientAllocateIdCommand>(x =>
{
Id = x.Id;
});
_isConnected.AddListener(ConnectionCallback);
}
private async void ConnectionCallback(bool x) private async void ConnectionCallback(bool x)
{ {
@ -88,12 +93,12 @@ namespace BITKit.Net
if (x) if (x)
{ {
OnConnected?.Invoke(); OnConnected?.Invoke();
BIT4Log.Log<KcpNetClient>("连接成功"); _logger.LogInformation("连接成功");
} }
else else
{ {
OnDisconnected?.Invoke(); OnDisconnected?.Invoke();
BIT4Log.Log<KcpNetClient>("连接已断开"); _logger.LogInformation("连接已断开");
} }
} }
@ -104,9 +109,14 @@ namespace BITKit.Net
} }
public async void Disconnect() public async void Disconnect()
{
_userConnected = false;
DisconnectInternal();
}
private async void DisconnectInternal()
{ {
IsConnecting = false; IsConnecting = false;
client.Disconnect(); _client.Disconnect();
_isConnected.RemoveElement(this); _isConnected.RemoveElement(this);
_timer.Stop(); _timer.Stop();
try try
@ -117,6 +127,7 @@ namespace BITKit.Net
catch (OperationCanceledException){} catch (OperationCanceledException){}
} }
private string _lastHostName;
public async UniTask<bool> Connect(string address = "127.0.0.1", ushort port = 27014) public async UniTask<bool> Connect(string address = "127.0.0.1", ushort port = 27014)
{ {
if (IsConnecting) if (IsConnecting)
@ -124,7 +135,7 @@ namespace BITKit.Net
BIT4Log.Warning<KcpNetClient>("正在连接中"); BIT4Log.Warning<KcpNetClient>("正在连接中");
return false; return false;
} }
_userConnected = true;
//如果address是域名,解析为Ip //如果address是域名,解析为Ip
if (address.Contains(".")) if (address.Contains("."))
{ {
@ -132,7 +143,11 @@ namespace BITKit.Net
if (ip.Length > 0) if (ip.Length > 0)
{ {
address = ip[0].ToString(); address = ip[0].ToString();
BIT4Log.Log<KcpNetClient>($"解析域名:{address}"); if (_lastHostName != address)
{
_logger.LogInformation($"解析域名:{address},IP:{ip}");
_lastHostName = address;
}
} }
} }
@ -143,7 +158,7 @@ namespace BITKit.Net
IsConnecting = true; IsConnecting = true;
if (client.connected) return false; if (_client.connected) return false;
await BITApp.SwitchToMainThread(); await BITApp.SwitchToMainThread();
OnStartConnect?.Invoke(); OnStartConnect?.Invoke();
await UniTask.SwitchToThreadPool(); await UniTask.SwitchToThreadPool();
@ -151,7 +166,7 @@ namespace BITKit.Net
{ {
_lastHeartbeat = DateTime.Now; _lastHeartbeat = DateTime.Now;
client.Connect(address, port); _client.Connect(address, port);
_timer.Start(); _timer.Start();
_interval = TimeSpan.FromMilliseconds(_timer.Interval); _interval = TimeSpan.FromMilliseconds(_timer.Interval);
@ -163,13 +178,13 @@ namespace BITKit.Net
for (var i = 0; i < 5; i++) for (var i = 0; i < 5; i++)
{ {
client.Send(new[] { (byte)NetCommandType.Heartbeat }, KcpChannel.Reliable); _client.Send(new[] { (byte)NetCommandType.Heartbeat }, KcpChannel.Reliable);
Traffic += new float2(1, 0); Traffic += new float2(1, 0);
client.Tick(); _client.Tick();
await Task.Delay(100); await Task.Delay(100);
} }
if (client.connected) if (_client.connected)
{ {
SendServerMessage(Environment.MachineName); SendServerMessage(Environment.MachineName);
@ -177,11 +192,11 @@ namespace BITKit.Net
_connectedAddress = address; _connectedAddress = address;
_connectedPort = port; _connectedPort = port;
return client.connected; return _client.connected;
} }
OnConnectedFailed?.Invoke(); OnConnectedFailed?.Invoke();
Disconnect(); DisconnectInternal();
IsConnecting = false; IsConnecting = false;
return false; return false;
@ -220,7 +235,7 @@ namespace BITKit.Net
switch (type) switch (type)
{ {
case NetCommandType.Message: case NetCommandType.Message:
BIT4Log.Log<KcpNetClient>($"已收到消息:{reader.ReadString()}"); _logger.LogInformation($"已收到消息:{reader.ReadString()}");
break; break;
case NetCommandType.AllClientCommand: case NetCommandType.AllClientCommand:
case NetCommandType.Command: case NetCommandType.Command:
@ -424,7 +439,7 @@ namespace BITKit.Net
if (eventDelegate is null) if (eventDelegate is null)
{ {
BIT4Log.Warning<KcpNetClient>($"未找到对应的事件:{rpcName}"); //BIT4Log.Warning<KcpNetClient>($"未找到对应的事件:{rpcName}");
} }
else else
{ {
@ -443,9 +458,9 @@ namespace BITKit.Net
} }
break; break;
default: default:
BIT4Log.Log<KcpNetClient>($"未知消息类型:{type},字节:{(byte)type}"); _logger.LogInformation($"未知消息类型:{type},字节:{(byte)type}");
if (bytes.Array != null) if (bytes.Array != null)
BIT4Log.Log<KcpNetClient>( _logger.LogInformation(
$"已收到:({Id}, {BitConverter.ToString(bytes.Array, bytes.Offset, bytes.Count)} @ {channel})"); $"已收到:({Id}, {BitConverter.ToString(bytes.Array, bytes.Offset, bytes.Count)} @ {channel})");
break; break;
} }
@ -456,17 +471,17 @@ namespace BITKit.Net
// if (BITApp.SynchronizationContext is not null) // if (BITApp.SynchronizationContext is not null)
// await UniTask.SwitchToSynchronizationContext(BITApp.SynchronizationContext); // await UniTask.SwitchToSynchronizationContext(BITApp.SynchronizationContext);
// OnConnected?.Invoke(); // OnConnected?.Invoke();
// BIT4Log.Log<KcpNetClient>("已连接"); // _logger.LogInformation("已连接");
} }
private async void OnDisconnectInternal() private async void OnDisconnectInternal()
{ {
//BIT4Log.Log<KcpNetClient>("连接被断开"); // _logger.LogInformation("连接被断开");
Disconnect(); DisconnectInternal();
} }
private void OnError(ErrorCode errorCode, string message) private void OnError(ErrorCode errorCode, string message)
{ {
BIT4Log.Log<KcpNetClient>($"{client.remoteEndPoint}异常:{errorCode},{message}"); _logger.LogInformation($"{_client.remoteEndPoint}异常:{errorCode},{message}");
} }
public void ServerCommand<T>(T command = default) public void ServerCommand<T>(T command = default)
@ -500,6 +515,10 @@ namespace BITKit.Net
public async UniTask<T> GetFromServer<T>(string path = default,params object[] pars) public async UniTask<T> GetFromServer<T>(string path = default,params object[] pars)
{ {
if (IsConnected is false)
{
throw new NetOfflineException();
}
//await UniTask.SwitchToThreadPool(); //await UniTask.SwitchToThreadPool();
var id = _index++; var id = _index++;
var ms = new MemoryStream(); var ms = new MemoryStream();
@ -528,6 +547,10 @@ namespace BITKit.Net
while (true) while (true)
{ {
if (IsConnected is false)
{
throw new NetOfflineException();
}
if ((_now - startTime).TotalSeconds > RpcTimeOut) if ((_now - startTime).TotalSeconds > RpcTimeOut)
{ {
await BITApp.SwitchToMainThread(); await BITApp.SwitchToMainThread();
@ -573,24 +596,25 @@ namespace BITKit.Net
foreach (var methodInfo in rpcHandle.GetType().GetMethods()) foreach (var methodInfo in rpcHandle.GetType().GetMethods())
{ {
var att = methodInfo.GetCustomAttribute<NetRpcAttribute>(); var att = methodInfo.GetCustomAttribute<NetRpcAttribute>();
if(att is null)continue; if (att is null) continue;
_rpcMethods.AddOrUpdate(methodInfo.Name, methodInfo, (s, info) => methodInfo); _rpcMethods.AddOrUpdate(methodInfo.Name, methodInfo, (s, info) => methodInfo);
_rpcHandles.AddOrUpdate(methodInfo.Name, rpcHandle, (s, o) => rpcHandle); _rpcHandles.AddOrUpdate(methodInfo.Name, rpcHandle, (s, o) => rpcHandle);
reportBuilder.AppendLine($"Add [{methodInfo.Name}] as MethodInfo"); reportBuilder.AppendLine($"Add [{methodInfo.Name}] as MethodInfo");
} }
foreach (var eventInfo in rpcHandle.GetType().GetEvents()) foreach (var eventInfo in rpcHandle.GetType().GetEvents())
{ {
var att = eventInfo.GetCustomAttribute<NetRpcAttribute>(); var att = eventInfo.GetCustomAttribute<NetRpcAttribute>();
if(att is null)continue; if (att is null) continue;
_rpcEvents.TryAdd(eventInfo.Name, eventInfo); _rpcEvents.TryAdd(eventInfo.Name, eventInfo);
_rpcHandles.TryAdd(eventInfo.Name, rpcHandle); _rpcHandles.TryAdd(eventInfo.Name, rpcHandle);
reportBuilder.AppendLine($"Add [{eventInfo.Name}] as EventInfo"); reportBuilder.AppendLine($"Add [{eventInfo.Name}] as EventInfo");
} }
BIT4Log.Log<KcpNetClient>(reportBuilder); _logger.LogInformation(reportBuilder.ToString());
} }
public void AddCommandListener<T>(Action<T> handle) public void AddCommandListener<T>(Action<T> handle)
@ -621,6 +645,10 @@ namespace BITKit.Net
public void SendRT(string rpcName, params object[] pars) public void SendRT(string rpcName, params object[] pars)
{ {
if (IsConnected is false)
{
throw new NetOfflineException();
}
using var ms = new MemoryStream(); using var ms = new MemoryStream();
using var writer = new BinaryWriter(ms); using var writer = new BinaryWriter(ms);
writer.Write((byte)NetCommandType.GetFromServer); writer.Write((byte)NetCommandType.GetFromServer);
@ -650,36 +678,36 @@ namespace BITKit.Net
.Write(message) .Write(message)
.Build(); .Build();
Traffic+=new float2(bytes.Length,0); Traffic+=new float2(bytes.Length,0);
client.Send(bytes, KcpChannel.Reliable); _client.Send(bytes, KcpChannel.Reliable);
} }
#if UNITY_EDITOR #if UNITY_EDITOR
private readonly IntervalUpdate _pingInterval = new(1); private readonly IntervalUpdate _pingInterval = new(1);
#endif
#endif
public void Tick() public void Tick()
{ {
_now = DateTime.Now;
if(_userConnected is false)return;
try try
{ {
_now = DateTime.UtcNow; if (_client.connected)
if (client.connected)
{ {
if (DateTime.Now - _lastHeartbeat > TimeSpan.FromSeconds(5)) if (DateTime.Now - _lastHeartbeat > TimeSpan.FromSeconds(5))
{ {
BIT4Log.Warning<KcpNetClient>("心跳超时,自动断开"); BIT4Log.Warning<KcpNetClient>("心跳超时,自动断开");
Disconnect(); DisconnectInternal();
_commandQueue.Clear(); _commandQueue.Clear();
return; return;
} }
while (_commandQueue.TryDequeue(out var bytes)) while (_commandQueue.TryDequeue(out var bytes))
{ {
Traffic += new float2(bytes.Length, 0); Traffic += new float2(bytes.Length, 0);
client.Send(bytes, KcpChannel.Reliable); _client.Send(bytes, KcpChannel.Reliable);
} }
Traffic+=new float2(1,0); Traffic+=new float2(1,0);
client.Send(_heartBeat, KcpChannel.Unreliable); _client.Send(_heartBeat, KcpChannel.Unreliable);
} }
else else
{ {
@ -696,16 +724,7 @@ namespace BITKit.Net
//BIT4Log.Warning<KcpNetClient>("连接已断开,清空指令队列"); //BIT4Log.Warning<KcpNetClient>("连接已断开,清空指令队列");
} }
} }
_client.Tick();
#if UNITY_EDITOR
if (_pingInterval.AllowUpdate)
{
_lastPingTime = DateTime.Now;
client.Send(new[] { (byte)NetCommandType.Ping }, KcpChannel.Reliable);
}
#endif
client.Tick();
} }
catch (Exception e) catch (Exception e)
{ {
@ -718,7 +737,7 @@ namespace BITKit.Net
{ {
// send client to server // send client to server
Traffic+=new float2(2,0); Traffic+=new float2(2,0);
client.Send(new byte[]{0x01, 0x02}, KcpChannel.Reliable); _client.Send(new byte[]{0x01, 0x02}, KcpChannel.Reliable);
} }
} }
} }

View File

@ -101,7 +101,7 @@ namespace BITKit.Net
BIT4Log.Log<KCPNetServer>($"{Name}:链接{id}超时,已断开"); BIT4Log.Log<KCPNetServer>($"{Name}:链接{id}超时,已断开");
} }
if (server.IsActive() is false || ManualTick) return; if (server.IsActive() is false) return;
server.Tick(); server.Tick();
@ -150,9 +150,11 @@ namespace BITKit.Net
_timer.Interval = 1000f / TickRate; _timer.Interval = 1000f / TickRate;
_interval = TimeSpan.FromSeconds(1.0 / TickRate); _interval = TimeSpan.FromSeconds(1.0 / TickRate);
} }
OnStartServer?.Invoke(); OnStartServer?.Invoke();
server.Start(port); server.Start(port);
_timer.Start(); if (ManualTick is false)
_timer.Start();
_isStarted = true; _isStarted = true;
BIT4Log.Log<KCPNetServer>($"已启动KCP服务器:{port}"); BIT4Log.Log<KCPNetServer>($"已启动KCP服务器:{port}");
} }
@ -551,14 +553,14 @@ namespace BITKit.Net
{ {
foreach (var methodInfo in rpcHandle.GetType().GetMethods()) foreach (var methodInfo in rpcHandle.GetType().GetMethods())
{ {
var att = methodInfo.GetCustomAttribute<NetRpcAttribute>(); var att = methodInfo.GetCustomAttribute<NetRpcAttribute>(true);
if(att is null)continue; if(att is null)continue;
_rpcMethods.TryAdd(methodInfo.Name, methodInfo); _rpcMethods.TryAdd(methodInfo.Name, methodInfo);
_rpcHandles.TryAdd(methodInfo.Name, rpcHandle); _rpcHandles.TryAdd(methodInfo.Name, rpcHandle);
} }
foreach (var eventInfo in rpcHandle.GetType().GetEvents()) foreach (var eventInfo in rpcHandle.GetType().GetEvents())
{ {
var att = eventInfo.GetCustomAttribute<NetRpcAttribute>(); var att = eventInfo.GetCustomAttribute<NetRpcAttribute>(true);
if(att is null)continue; if(att is null)continue;
_rpcEvents.TryAdd(eventInfo.Name, eventInfo); _rpcEvents.TryAdd(eventInfo.Name, eventInfo);

View File

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

View File

@ -147,6 +147,21 @@ namespace BITKit
} }
} }
public static IEnumerable<T> SelectManyPro<T>(this IEnumerable<T> self, Func<T, IEnumerable<T>> factory)
{
return Get(self);
IEnumerable<T> Get(IEnumerable<T> list)
{
var newList = new List<T>();
foreach (var x in list)
{
newList.Add(x);
newList.AddRange(Get(factory.Invoke(x)));
}
return newList.Distinct();
}
}
/// <summary> /// <summary>
/// 获取集合中所有的组合,每个组合中的元素个数为输入集合的元素个数,每个元素只出现一次 /// 获取集合中所有的组合,每个组合中的元素个数为输入集合的元素个数,每个元素只出现一次
/// </summary> /// </summary>

View File

@ -126,7 +126,7 @@ namespace BITKit.Mod
} }
} }
public class ModService public partial class ModService
{ {
public static async UniTask<ModPackage[]> SearchPackages() public static async UniTask<ModPackage[]> SearchPackages()
{ {

View File

@ -0,0 +1,26 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
namespace BITKit.Mod
{
public partial class ModService
{
public static Func<string, UniTask<object>> LoadAssetAsyncFactory;
public static async UniTask<T> LoadAsset<T>(string location) where T : class
{
foreach (var func in LoadAssetAsyncFactory.CastAsFunc())
{
var value = await func.Invoke(location);
if (value is T value1)
{
return value1;
}
}
throw new Exception($"Asset not found: {location}");
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: e0adbb35ce4784342a0ed37ac4a56200 guid: c63d2ab8d0ff2304e980bb1e0585c903
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@ -1,5 +1,7 @@
using System; using System;
using System.IO;
using System.Net.Http; using System.Net.Http;
using Microsoft.SqlServer.Server;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@ -7,84 +9,103 @@ using Newtonsoft.Json.Linq;
namespace BITKit namespace BITKit
// ReSharper restore CheckNamespace // ReSharper restore CheckNamespace
{ {
public record ContextModel public record ContextModel:IBinarySerialize
{ {
public static ContextModel Get(object data) public static ContextModel Get(object data)
{
return new ContextModel()
{ {
return new ContextModel() StatusCode = 200,
{ Message = "success",
StatusCode = 200, Data = data
Message = "success", };
Data = data }
};
}
public static ContextModel Error(string message)
{
return new ContextModel()
{
StatusCode = 500,
Message = message,
Data = false
};
}
public static ContextModel GetFromJson(string json)
{
try
{
var result = new ContextModel();
var jObject = JObject.Parse(json);
if(jObject.TryGetValue("status_code",out var statusCode))
result.StatusCode = statusCode.Value<int>();
if(jObject.TryGetValue("message",out var message))
result.Message = message.Value<string>();
if (jObject.TryGetValue("data", out var data))
result.Data = data;
return JsonConvert.DeserializeObject<ContextModel>(json);
}
catch (Exception)
{
BIT4Log.Warning(json);
throw;
}
}
public static implicit operator string(ContextModel self)
{
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() public static ContextModel Error(string message)
{
return new ContextModel()
{ {
return JsonConvert.SerializeObject(this); StatusCode = 500,
} Message = message,
Data = false
};
}
[JsonProperty("status_code")] public static ContextModel GetFromJson(string json)
public int StatusCode=200; {
[JsonProperty("message")] try
public string Message=string.Empty;
[JsonProperty("data")]
public object Data=string.Empty;
[JsonIgnore]
public bool IsSuccess => StatusCode is 200 or 0;
public bool TryAs<T>(out T value)
{ {
switch (Data) var result = new ContextModel();
{ var jObject = JObject.Parse(json);
case T t: if (jObject.TryGetValue("status_code", out var statusCode))
value = t; result.StatusCode = statusCode.Value<int>();
return true; if (jObject.TryGetValue("message", out var message))
case JToken jToken: result.Message = message.Value<string>();
value = jToken.ToObject<T>(); if (jObject.TryGetValue("data", out var data))
return true; result.Data = data;
default: return JsonConvert.DeserializeObject<ContextModel>(json);
value = default; }
return false; catch (Exception)
} {
BIT4Log.Warning(json);
throw;
} }
} }
public static implicit operator string(ContextModel self)
{
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()
{
return JsonConvert.SerializeObject(this);
}
[JsonProperty("status_code")] public int StatusCode = 200;
[JsonProperty("message")] public string Message = string.Empty;
[JsonProperty("data")] public object Data = string.Empty;
[JsonIgnore] public bool IsSuccess => StatusCode is 200 or 0;
public bool TryAs<T>(out T value)
{
switch (Data)
{
case T t:
value = t;
return true;
case JToken jToken:
value = jToken.ToObject<T>();
return true;
default:
value = default;
return false;
}
}
public void Read(BinaryReader r)
{
StatusCode = r.ReadInt32();
Message = r.ReadString();
Data = r.ReadBoolean() ? BITBinary.Read(r) : null;
}
public void Write(BinaryWriter w)
{
w.Write(StatusCode);
w.Write(Message);
w.Write(Data is not null);
if (Data is not null)
{
BITBinary.Write(w, Data);
}
}
}
} }

24
Src/Core/Net/Exception.cs Normal file
View File

@ -0,0 +1,24 @@
namespace BITKit
{
public class NetOfflineException : System.Exception
{
public NetOfflineException() : base("Client is not connected") { }
}
public class NetAuthorizeException : System.Exception
{
public NetAuthorizeException() : base("Client is not authorized") { }
}
public abstract class NetAuthorityException : System.Exception
{
protected NetAuthorityException(string message =null) : base(string.IsNullOrEmpty(message)?"Authority is not valid":message) { }
}
public class NetClientOnlyException : NetAuthorityException
{
public NetClientOnlyException() : base("This method is only available on client") { }
}
public class NetServerOnlyException : NetAuthorityException
{
public NetServerOnlyException() : base("This method is only available on server") { }
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 618a48f7c780dd148ae1fa0fba95b583 guid: d38fad9bff7582b4b8287df7c47a47b7
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@ -18,7 +18,14 @@ namespace BITKit
[AttributeUsage(AttributeTargets.Method|AttributeTargets.Event)] [AttributeUsage(AttributeTargets.Method|AttributeTargets.Event)]
public sealed class NetRpcAttribute : Attribute public sealed class NetRpcAttribute : Attribute
{ {
public readonly bool AddTypeNamePrefix;
public NetRpcAttribute()
{
}
public NetRpcAttribute(bool addTypeNamePrefix)
{
AddTypeNamePrefix = addTypeNamePrefix;
}
} }
/// <summary> /// <summary>
/// 帮助类 /// 帮助类
@ -84,7 +91,6 @@ namespace BITKit
/// </summary> /// </summary>
public interface INetProvider public interface INetProvider
{ {
/// <summary> /// <summary>
/// 向服务端发送指令 /// 向服务端发送指令
/// </summary> /// </summary>
@ -192,6 +198,10 @@ namespace BITKit
/// </summary> /// </summary>
public interface INetServer public interface INetServer
{ {
/// <summary>
/// 通信接口
/// </summary>
public INetProvider NetProvider=>this as INetProvider;
/// <summary> /// <summary>
/// 源物体,用于通过代理直接访问 /// 源物体,用于通过代理直接访问
/// </summary> /// </summary>
@ -274,6 +284,10 @@ namespace BITKit
/// </summary> /// </summary>
public interface INetClient public interface INetClient
{ {
/// <summary>
/// 通讯接口
/// </summary>
public INetProvider NetProvider=>this as INetProvider;
/// <summary> /// <summary>
/// 源物体,用于通过代理直接访问 /// 源物体,用于通过代理直接访问
/// </summary> /// </summary>
@ -318,85 +332,4 @@ namespace BITKit
/// <param name="message">消息</param> /// <param name="message">消息</param>
void SendServerMessage(string message); void SendServerMessage(string message);
} }
#if UNITY
/// <summary>
/// 有关NetProvider的Unity代理
/// </summary>
[Serializable]
public class NetProviderProxy : INetProvider
{
[SerializeField] private MonoBehaviour monoBehaviour;
INetProvider netProvider => monoBehaviour as INetProvider;
void INetProvider.ServerCommand<T>(T command) => netProvider.ServerCommand(command);
void INetProvider.RpcClientCommand<T>(T command) => netProvider.RpcClientCommand(command);
void INetProvider.ClientCommand<T>(int id, T command) => netProvider.ClientCommand(id, command);
async UniTask<T> INetProvider.GetFromServer<T>(string addressablePath) =>
await netProvider.GetFromServer<T>(addressablePath);
UniTask<T> INetProvider.GetFromClient<T>(int id, string addressablePath) =>
netProvider.GetFromClient<T>(id, addressablePath);
void INetProvider.AddRpcHandle(object rpcHandle) => netProvider.AddRpcHandle(rpcHandle);
void INetProvider.AddCommandListener<T>(Action<T> handle) => netProvider.AddCommandListener<T>(handle);
void INetProvider.RemoveCommandListener<T>(Action<T> handle) => netProvider.RemoveCommandListener<T>(handle);
void INetProvider.SendRT(string rpcName, params object[] pars) => netProvider.SendRT(rpcName, pars);
void INetProvider.SendTargetRT(int id, string rpcName, params object[] pars) =>
netProvider.SendTargetRT(id, rpcName, pars);
void INetProvider.SendAllRT(string rpcName, params object[] pars) => netProvider.SendAllRT(rpcName, pars);
}
/// <summary>
/// 有关Unity的NetClient的代理
/// </summary>
[Serializable]
public class GDNetClientProxy : INetClient
{
[SerializeField] private MonoBehaviour monoBehaviour;
private INetClient _netClientImplementation=>monoBehaviour as INetClient;
event Action INetClient.OnStartConnect
{
add => _netClientImplementation.OnStartConnect += value;
remove => _netClientImplementation.OnStartConnect -= value;
}
event Action INetClient.OnConnected
{
add => _netClientImplementation.OnConnected += value;
remove => _netClientImplementation.OnConnected -= value;
}
event Action INetClient.OnDisconnected
{
add => _netClientImplementation.OnDisconnected += value;
remove => _netClientImplementation.OnDisconnected -= value;
}
event Action INetClient.OnConnectedFailed
{
add => _netClientImplementation.OnConnectedFailed += value;
remove => _netClientImplementation.OnConnectedFailed -= value;
}
UniTask<bool> INetClient.Connect(string address, ushort port)
{
return _netClientImplementation.Connect(address, port);
}
bool INetClient.IsConnected => _netClientImplementation.IsConnected;
int INetClient.Ping => _netClientImplementation.Ping;
public int Id => _netClientImplementation.Id;
void INetClient.Disconnect()
{
_netClientImplementation.Disconnect();
}
}
#endif
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,178 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
namespace BITKit
{
/// <summary>
/// 属性接口,用于序列化引用
/// </summary>
public interface IProperty
{
}
/// <summary>
/// 属性接口
/// GetOrCreate(√)
/// GetOrAdd(√)
/// TryGet(√)
/// TryRemove(√)
/// TrySet(√)
/// GetProperties(√)
/// </summary>
public interface IPropertable
{
/// <summary>
/// 是否拥有属性
/// </summary>
bool Contains<T>();
/// <summary>
/// 获取或创建属性
/// </summary>
T GetOrCreateProperty<T>();
/// <summary>
/// 获取或通过工厂方法添加属性
/// </summary>
T GetOrAddProperty<T>(Func<T> addFactory);
/// <summary>
/// 尝试获取属性
/// </summary>
bool TryGetProperty<T>(out T value);
/// <summary>
/// 尝试移除属性
/// </summary>
bool TryRemoveProperty<T>();
/// <summary>
/// 尝试设置属性
/// </summary>
bool TrySetProperty<T>(T value);
/// <summary>
/// 获取所有属性
/// </summary>
object[] GetProperties();
/// <summary>
/// 清除所有属性
/// </summary>
/// <returns></returns>
bool ClearProperties();
/// <summary>
/// 从其他对象复制属性
/// </summary>
/// <param name="propertable"></param>
/// <returns></returns>
bool CopyPropertiesFrom(IPropertable propertable);
}
public class Property : IPropertable
{
public Property() { }
public Property(IEnumerable<object> factory)
{
foreach (var x in factory)
{
properties.Add(x);
}
}
private readonly List<object> properties=new();
public T GetProperty<T>()
{
return properties.OfType<T>().First();
}
public void SetProperty<T>(T value)
{
for (var i = 0; i < properties.Count; i++)
{
if (properties[i] is T)
properties[i] = value;
}
}
public bool Contains<T>()
{
return properties.OfType<T>().FirstOrDefault() is not null;
}
public T GetOrCreateProperty<T>()
{
return GetOrAddProperty(Activator.CreateInstance<T>);
}
public T GetOrAddProperty<T>(Func<T> addFactory)
{
foreach (var obj in properties)
{
if (obj is T t) return t;
}
T x;
properties.Add(x = addFactory.Invoke());
return x;
}
public bool TryGetProperty<T>(out T value)
{
try
{
value = properties.OfType<T>().First();
return true;
}
catch (InvalidOperationException)
{
value = default;
return false;
}
}
public bool TryRemoveProperty<T>()
{
var removed=false;
foreach (var value in properties.OfType<T>().ToArray())
{
properties.Remove(value);
removed = true;
}
// if(properties.TryGetValue(typeof(T).FullName, out var x))
// {
// properties.Remove(typeof(T).Name);
// return true;
// }
return removed;
}
public bool TrySetProperty<T>(T value)
{
var current = properties.OfType<T>().FirstOrDefault();
if (current is not null)
{
properties.Remove(current);
return true;
}
properties.Add(value);
return false;
}
public object[] GetProperties() => properties.ToArray();
public void Read(BinaryReader r)
{
foreach (var x in properties)
{
}
}
public void Write(BinaryWriter w)
{
throw new NotImplementedException();
}
public bool ClearProperties()
{
properties.Clear();
return true;
}
public bool CopyPropertiesFrom(IPropertable propertable)
{
ClearProperties();
properties.AddRange( propertable.GetProperties());;
return true;
}
}
}

View File

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

View File

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

View File

@ -1,34 +0,0 @@
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_5_3_OR_NEWER
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

@ -1,20 +0,0 @@
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

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
namespace BITKit namespace BITKit
{ {
@ -26,6 +27,11 @@ namespace BITKit
/// </summary> /// </summary>
/// <param name="action"></param> /// <param name="action"></param>
void Remove(Action<float> action); void Remove(Action<float> action);
/// <summary>
/// 手动调用循环
/// </summary>
/// <param name="delta"></param>
void ManualTick(float delta);
} }
/// <summary> /// <summary>
/// 主线程循环 /// 主线程循环
@ -35,4 +41,40 @@ namespace BITKit
/// 线程池循环 /// 线程池循环
/// </summary> /// </summary>
public interface IThreadTicker : ITicker { } public interface IThreadTicker : ITicker { }
#if UNITY_5_3_OR_NEWER
/// <summary>
/// 最后执行的循环,通常用于旋转、位移等
/// </summary>
public interface IAfterTicker : ITicker{}
/// <summary>
/// Unity专用固定循环
/// </summary>
public interface IFixedTicker : ITicker{}
#endif
public class Ticker : ITicker
{
private readonly Queue<Action> _queue = new();
private event Action<float> TickEvents;
public ulong TickCount { get; private set; }
public void Add(Action action)
{
_queue.Enqueue(action);
}
public void Add(Action<float> action)=>TickEvents += action;
public void Remove(Action<float> action)=>TickEvents -= action;
public void ManualTick(float delta)
{
TickCount++;
while (_queue.TryDequeue(out var action))
{
action.Invoke();
}
TickEvents?.Invoke(delta);
}
}
} }

View File

@ -0,0 +1,70 @@
using System;
using BITKit;
using Timer = System.Timers.Timer;
namespace BITKit
{
public class TimerTicker:ITicker
{
public TimerTicker()
{
_timer = new Timer();
_timer.Elapsed += (sender, args) =>
{
OnTick();
if (_timer.Enabled && _timer.AutoReset is false)
{
_timer.Start();
}
};
}
public int TickRate { get; set; }
public ulong TickCount { get; private set; }
private readonly Timer _timer;
private float _deltaTime;
private event Action<float> Tick;
public void Add(Action action)
{
throw new NotImplementedException();
}
public void Add(Action<float> action)
{
Tick += action;
}
public void Remove(Action<float> action)
{
Tick -= action;
}
public void ManualTick(float delta)
{
throw new NotImplementedException();
}
public void Start()
{
_deltaTime = 1f / TickRate;
_timer.Interval = TimeSpan.FromSeconds(_deltaTime).TotalMilliseconds;
_timer.Enabled = true;
}
private void OnTick()
{
try
{
TickCount++;
Tick?.Invoke(_deltaTime);
}
catch (Exception e)
{
BIT4Log.LogException(e);
}
}
public void Stop()
{
_timer.Enabled = false;
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: eb2d5a09faa2e9a41858ff98770b423c guid: e07078010581a9b4481480587c1f6c64
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

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

View File

@ -19,10 +19,6 @@ namespace BITKit.UX
/// </summary> /// </summary>
bool IsWindow { get; } bool IsWindow { get; }
/// <summary> /// <summary>
/// 该面板是否有效,用于检查该面板是否已经被销毁
/// </summary>
//bool IsValid { get; }
/// <summary>
/// 该面板的索引(入口,Key) /// 该面板的索引(入口,Key)
/// </summary> /// </summary>
string Index { get; } string Index { get; }
@ -38,77 +34,15 @@ namespace BITKit.UX
/// 事件回调,当面板被打开时触发 /// 事件回调,当面板被打开时触发
/// </summary> /// </summary>
event Action OnEntry; event Action OnEntry;
event Func<UniTask> OnEntryAsync;
event Action OnEntryCompleted;
/// <summary> /// <summary>
/// 事件回调,当面板被关闭时触发 /// 事件回调,当面板被关闭时触发
/// </summary> /// </summary>
event Action OnExit; event Action OnExit;
void OnUpdate(float deltaTime); event Func<UniTask> OnExitAsync;
} event Action OnExitCompleted;
public abstract class UXPanelImplement:IUXPanel void OnTick(float deltaTime);
{
protected abstract IUXPanel service { get; }
private IUXPanel _iuxPanelImplementation => service;
public bool IsWindow=>_iuxPanelImplementation.IsWindow;
//public bool IsValid => _iuxPanelImplementation.IsValid;
public string Index => _iuxPanelImplementation.Index;
public bool AllowCursor => _iuxPanelImplementation.AllowCursor;
public bool AllowInput => _iuxPanelImplementation.AllowInput;
public bool IsEntered
{
get => service.IsEntered;
set => service.IsEntered = value;
}
public void Entry()
{
_iuxPanelImplementation.Entry();
}
public UniTask EntryAsync()
{
return service.EntryAsync();
}
public void Entered()
{
service.Entered();
}
public void Exit()
{
_iuxPanelImplementation.Exit();
}
public UniTask ExitAsync()
{
return service.ExitAsync();
}
public void Exited()
{
service.Exited();
}
public event Action OnEntry
{
add => _iuxPanelImplementation.OnEntry += value;
remove => _iuxPanelImplementation.OnEntry -= value;
}
public event Action OnExit
{
add => _iuxPanelImplementation.OnExit += value;
remove => _iuxPanelImplementation.OnExit -= value;
}
public void OnUpdate(float deltaTime)
{
service.OnUpdate(deltaTime);
}
} }
} }

View File

@ -1,10 +1,19 @@
using System;
using Cysharp.Threading.Tasks;
namespace BITKit.UX namespace BITKit.UX
{ {
/// <summary> /// <summary>
/// 基本UX服务(GUI管理器),主要通过加载叠加面板实现 /// 基本UX服务(GUI管理器),主要通过加载叠加面板实现
/// </summary> /// </summary>
public interface IUXService public interface IUXService:IDisposable
{ {
object Root { get; }
/// <summary>
/// 初始化
/// </summary>
/// <returns></returns>
UniTask InitializeAsync();
/// <summary> /// <summary>
/// 注册面板,加入注册队列 /// 注册面板,加入注册队列
/// </summary> /// </summary>

View File

@ -8,7 +8,11 @@ namespace BITKit.UX
{ {
string Message { get; set; } string Message { get; set; }
object Container { get; } object Container { get; }
public void SetMessage(string message)=>Message=message; public IUXWaitingHandle SetMessage(string message)
{
Message=message;
return this;
}
} }
public interface IUXWaiting public interface IUXWaiting
{ {

View File

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

View File

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

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 87bc33909c0bd1c49987c7095fb608c2 guid: 6a9ecef037d512f40886666749c60d48
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

View File

@ -1,12 +1,10 @@
{ {
"name": "BITKit.MotionMatching", "name": "BITKit.WorldNode",
"rootNamespace": "", "rootNamespace": "",
"references": [ "references": [
"GUID:14fe60d984bf9f84eac55c6ea033a8f4", "GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:d525ad6bd40672747bde77962f1c401e",
"GUID:49b49c76ee64f6b41bf28ef951cb0e50", "GUID:49b49c76ee64f6b41bf28ef951cb0e50",
"GUID:e34a5702dd353724aa315fb8011f08c3", "GUID:d8b63aba1907145bea998dd612889d6b"
"GUID:296866320aab85a42a0403bf684bac59"
], ],
"includePlatforms": [], "includePlatforms": [],
"excludePlatforms": [], "excludePlatforms": [],

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 9f93c330bf016d047a49b0c29307ef42 guid: d750d221812bb1d48baff92e6ef73e28
AssemblyDefinitionImporter: AssemblyDefinitionImporter:
externalObjects: {} externalObjects: {}
userData: userData:

View File

@ -0,0 +1,19 @@
using System.Collections;
using System.Collections.Generic;
namespace BITKit.WorldNode
{
/// <summary>
/// 世界节点
/// </summary>
public interface IWorldNode
{
public int Id { get; set; }
public object WorldObject { get; set; }
public void Initialize()
{
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 119f6a0b25fc0cc47b6f950cd9902aba guid: 051c8f58c08cb14449b4da1aca114caf
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@ -0,0 +1,41 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace BITKit.WorldNode
{
/// <summary>
/// 世界节点服务,所有动态世界节点通过此接口注册
/// </summary>
public interface IWorldNodeService
{
public IReadOnlyDictionary<int, IWorldNode> WorldNodes { get; }
public void RegisterNode(IWorldNode node);
public event Action<IWorldNode> OnNodeRegistered;
}
/// <summary>
/// 世界节点默认实现
/// </summary>
[Serializable]
public class WorldNodeService : IWorldNodeService,IDisposable
{
public static event Action<IWorldNode> OnNodeRegistered;
IReadOnlyDictionary<int, IWorldNode> IWorldNodeService.WorldNodes => WorldNodes;
private static readonly ConcurrentDictionary<int, IWorldNode> WorldNodes = new();
public void RegisterNode(IWorldNode node)
{
OnNodeRegistered?.Invoke(node);
WorldNodes.TryAdd(node.Id, node);
}
event Action<IWorldNode> IWorldNodeService.OnNodeRegistered
{
add=>OnNodeRegistered+=value;
remove=>OnNodeRegistered-=value;
}
public void Dispose()
{
WorldNodes.Clear();
}
}
}

View File

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

View File

@ -0,0 +1,53 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
#if UNITY_5_3_OR_NEWER
using UnityEngine;
#endif
namespace BITKit.WorldNode
{
[Serializable]
public struct WorldInfoNode : IWorldNode
{
// ReSharper disable once InconsistentNaming
#if UNITY_5_3_OR_NEWER
[SerializeReference,SubclassSelector]
#endif
private IReference name;
// ReSharper disable once InconsistentNaming
#if UNITY_5_3_OR_NEWER
[SerializeReference,SubclassSelector]
#endif
private IReference description;
public int Id { get; set; }
public object WorldObject { get; set; }
public string Name => name?.Value;
public string Description => description?.Value;
}
public sealed class WorldInfoNodeService : IDisposable
{
public IReadOnlyDictionary<int, WorldInfoNode> WorldInfoNodes => _infoNodes;
private readonly IWorldNodeService _worldNodeService;
private readonly ConcurrentDictionary<int, WorldInfoNode> _infoNodes = new();
public WorldInfoNodeService(IWorldNodeService worldNodeService)
{
_worldNodeService = worldNodeService;
_worldNodeService.OnNodeRegistered += OnNodeRegistered;
}
private void OnNodeRegistered(IWorldNode obj)
{
if (obj is not WorldInfoNode infoNode) return;
_infoNodes.TryAdd(obj.Id, infoNode);
}
public void Dispose()
{
_infoNodes.Clear();
}
}
}

View File

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

View File

@ -0,0 +1,18 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace BITKit.WorldNode
{
[Serializable]
public struct WorldInfoPlayerStart:IWorldNode
{
public static WorldInfoPlayerStart Current { get; set; }
public int Id { get; set; }
public object WorldObject { get; set; }
public void Initialize()
{
Current = this;
}
}
}

View File

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

View File

@ -0,0 +1,26 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
#if UNITY_5_3_OR_NEWER
using UnityEngine;
#endif
namespace BITKit.WorldNode
{
[Serializable]
public struct WorldPortalNode:IWorldNode
{
/// <summary>
/// 目标地图名称,为空则保留该地图
/// </summary>
#if UNITY_5_3_OR_NEWER
[SerializeReference,SubclassSelector]
#endif
public IReference MapName;
public float3 Position;
public float3 EulerAngle;
public int Id { get; set; }
public object WorldObject { get; set; }
}
}

View File

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

View File

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

View File

Before

Width:  |  Height:  |  Size: 307 B

After

Width:  |  Height:  |  Size: 307 B

View File

Before

Width:  |  Height:  |  Size: 583 B

After

Width:  |  Height:  |  Size: 583 B

View File

Before

Width:  |  Height:  |  Size: 187 B

After

Width:  |  Height:  |  Size: 187 B

View File

Before

Width:  |  Height:  |  Size: 208 B

After

Width:  |  Height:  |  Size: 208 B

View File

Before

Width:  |  Height:  |  Size: 111 B

After

Width:  |  Height:  |  Size: 111 B

View File

Before

Width:  |  Height:  |  Size: 128 B

After

Width:  |  Height:  |  Size: 128 B

View File

Before

Width:  |  Height:  |  Size: 197 B

After

Width:  |  Height:  |  Size: 197 B

View File

Before

Width:  |  Height:  |  Size: 201 B

After

Width:  |  Height:  |  Size: 201 B

View File

Before

Width:  |  Height:  |  Size: 145 B

After

Width:  |  Height:  |  Size: 145 B

View File

Before

Width:  |  Height:  |  Size: 150 B

After

Width:  |  Height:  |  Size: 150 B

View File

Before

Width:  |  Height:  |  Size: 151 B

After

Width:  |  Height:  |  Size: 151 B

Some files were not shown because too many files have changed in this diff Show More