|
@ -7,6 +7,8 @@ using Cysharp.Threading.Tasks;
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
// ReSharper disable StringLiteralTypo
|
||||
#if NET5_0_OR_GREATER
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
@ -18,10 +20,9 @@ namespace BITKit
|
|||
{
|
||||
public override string Message => "Application Is Not Playing";
|
||||
}
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public class BITApp
|
||||
{
|
||||
public static int Count => _count++;
|
||||
private static int _count;
|
||||
public static async UniTask SwitchToMainThread()
|
||||
{
|
||||
#if UNITY_5_3_OR_NEWER
|
||||
|
@ -168,7 +169,6 @@ namespace BITKit
|
|||
"MySql",
|
||||
};
|
||||
}
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// 依赖服务集合
|
||||
/// </summary>
|
||||
|
@ -176,13 +176,18 @@ namespace BITKit
|
|||
/// <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>
|
||||
public static Action<ServiceProvider> OnServiceProviderBuilded;
|
||||
#endif
|
||||
public static Action<ServiceProvider> OnServiceProviderBuild;
|
||||
/// <summary>
|
||||
/// 主线程
|
||||
/// </summary>
|
||||
|
@ -254,7 +259,6 @@ namespace BITKit
|
|||
private static DateTime InitialTime { get; set; }=DateTime.Now;
|
||||
public static async UniTask Start(string appName = nameof(BITApp),AppSettings settings=default)
|
||||
{
|
||||
_count = 0;
|
||||
Time.TimeAsDouble = 0;
|
||||
Time.DeltaTime = 1 / 60f;
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ef5795e833a88cd4b8c1485d4b50ebfd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -2,7 +2,7 @@
|
|||
{
|
||||
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))
|
||||
{
|
||||
|
@ -23,6 +23,7 @@
|
|||
targetInfo?.SetValue(target, value);
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,50 +137,58 @@ namespace BITKit
|
|||
}
|
||||
public static void Write(BinaryWriter writer, object value)
|
||||
{
|
||||
if (value is not string && value is IList enumerable)
|
||||
try
|
||||
{
|
||||
var pars = enumerable.Cast<object>().ToArray();
|
||||
writer.Write(true);
|
||||
writer.Write(value.GetType().GetElementType()!.FullName!);
|
||||
writer.Write(pars.Length);
|
||||
foreach (var obj in pars)
|
||||
if (value is not string && value is IList enumerable)
|
||||
{
|
||||
WriteInterel(writer, obj);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(false);
|
||||
WriteInterel(writer,value);
|
||||
}
|
||||
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);
|
||||
var pars = enumerable.Cast<object>().ToArray();
|
||||
writer.Write(true);
|
||||
writer.Write(value.GetType().GetElementType()!.FullName!);
|
||||
writer.Write(pars.Length);
|
||||
foreach (var obj in pars)
|
||||
{
|
||||
WriteInterel(writer, obj);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
writer.Write(JsonConvert.SerializeObject(value));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
BIT4Log.Warning<BITBinary>(typeName);
|
||||
throw;
|
||||
}
|
||||
|
||||
writer.Write(false);
|
||||
WriteInterel(writer,value);
|
||||
}
|
||||
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(Type type) => IsSupport(type.FullName);
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: de545a8f794806c468d90c634c53582a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -28,7 +28,7 @@ namespace BITKit.CommandPattern
|
|||
void Release();
|
||||
}
|
||||
|
||||
public sealed class CommandSequence:List<ICommand>,IBinarySerialize
|
||||
public sealed class CommandSequence:List<ICommand>,Microsoft.SqlServer.Server.IBinarySerialize
|
||||
{
|
||||
public void Read(BinaryReader r)
|
||||
{
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fa98aca39a218e24cafafaf926cafa1c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 11c54e9541abd79499031d2fd92af224
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4273bb14e8bf75943bcac37ef781c9fd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8016d8e2215fd544b81c945f11090744
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -3,5 +3,6 @@ namespace BITKit
|
|||
public interface IDescription
|
||||
{
|
||||
public string Name { get; }
|
||||
public string Description { get; }
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 549453866297ec84f8fcb28c9da7b1f0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7f0710f16fd119f44a922b5e83c7d57b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -54,79 +54,87 @@ namespace BITKit.Entities
|
|||
|
||||
public T[] QueryComponents<T>()
|
||||
{
|
||||
return _queryCache.GetOrAdd(typeof(T), type =>
|
||||
{
|
||||
return _entities.Values.Where(entity => entity.TryGetComponent(out T component)).ToArray();
|
||||
}).Cast<T>().ToArray();
|
||||
throw new NotImplementedException();
|
||||
// return _queryCache.GetOrAdd(typeof(T), type =>
|
||||
// {
|
||||
// return _entities.Values.Where(entity => entity.TryGetComponent(out T component)).ToArray();
|
||||
// }).Cast<T>().ToArray();
|
||||
}
|
||||
public (T, T1)[] QueryComponents<T, T1>()
|
||||
{
|
||||
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));
|
||||
}
|
||||
return list.ToArray();
|
||||
throw new NotImplementedException();
|
||||
// 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));
|
||||
// }
|
||||
// return list.ToArray();
|
||||
}
|
||||
public (T, T1, T2)[] QueryComponents<T, T1, T2>()
|
||||
{
|
||||
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));
|
||||
}
|
||||
return list.ToArray();
|
||||
throw new NotImplementedException();
|
||||
// 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));
|
||||
// }
|
||||
// return list.ToArray();
|
||||
}
|
||||
|
||||
public (T, T1, T2, T3)[] QueryComponents<T, T1, T2, T3>()
|
||||
{
|
||||
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));
|
||||
}
|
||||
return list.ToArray();
|
||||
throw new NotImplementedException();
|
||||
// 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));
|
||||
// }
|
||||
// return list.ToArray();
|
||||
}
|
||||
|
||||
public (T, T1, T2, T3, T4)[] QueryComponents<T, T1, T2, T3, T4>()
|
||||
{
|
||||
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));
|
||||
}
|
||||
return list.ToArray();
|
||||
throw new NotImplementedException();
|
||||
// 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));
|
||||
// }
|
||||
// return list.ToArray();
|
||||
}
|
||||
|
||||
public (T, T1, T2, T3, T4, T5)[] QueryComponents<T, T1, T2, T3, T4, T5>()
|
||||
{
|
||||
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));
|
||||
}
|
||||
return list.ToArray();
|
||||
throw new NotImplementedException();
|
||||
// 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));
|
||||
// }
|
||||
// return list.ToArray();
|
||||
}
|
||||
|
||||
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();
|
||||
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));
|
||||
}
|
||||
return list.ToArray();
|
||||
throw new NotImplementedException();
|
||||
// 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));
|
||||
// }
|
||||
// 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
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
@ -7,9 +8,25 @@ namespace BITKit.Entities
|
|||
{
|
||||
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()
|
||||
{
|
||||
RegisterComponent<IEntity>(this);
|
||||
ServiceCollection.AddSingleton<IEntity>(this);
|
||||
}
|
||||
public void WaitForInitializationComplete()
|
||||
{
|
||||
|
@ -19,51 +36,26 @@ namespace BITKit.Entities
|
|||
public CancellationToken CancellationToken => _cancellationTokenSource.Token;
|
||||
private readonly CancellationTokenSource _cancellationTokenSource = new();
|
||||
|
||||
public bool TryGetComponent<T>(out T component)
|
||||
public IServiceProvider ServiceProvider
|
||||
{
|
||||
var value = ServiceProvider.GetService<T>();
|
||||
if (value != null)
|
||||
get
|
||||
{
|
||||
component = value;
|
||||
return true;
|
||||
if (_serviceProvider is not null)
|
||||
{
|
||||
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();
|
||||
private IServiceProvider _serviceProvider;
|
||||
private readonly CacheList<object> _services = new();
|
||||
|
||||
public object[] GetServices()
|
||||
{
|
||||
return _services.ToArray();
|
||||
}
|
||||
|
||||
private EntityServiceProvider _serviceProvider;
|
||||
public object[] GetServices()=> _serviceProvider.Services.ToArray();
|
||||
public void Inject(object obj)
|
||||
{
|
||||
foreach (var fieldInfo in obj.GetType().GetFields(ReflectionHelper.Flags))
|
||||
|
@ -78,14 +70,10 @@ namespace BITKit.Entities
|
|||
public void Dispose()
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
_cancellationTokenSource.Dispose();
|
||||
|
||||
foreach (var service in _services)
|
||||
_serviceProvider.ServiceProvider.Dispose();
|
||||
foreach (var x in GetServices().OfType<IDisposable>())
|
||||
{
|
||||
if (service is IDisposable disposable)
|
||||
{
|
||||
disposable.Dispose();
|
||||
}
|
||||
x.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 881c97aa8732ace4796d97874c8f8769
|
||||
guid: 881c97aa8732ace4796d97874c8f8769
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System.Threading;
|
||||
using System;
|
||||
using System.ComponentModel.Design;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
#if NET5_0_OR_GREATER
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
#endif
|
||||
|
@ -11,44 +12,14 @@ namespace BITKit.Entities
|
|||
/// </summary>
|
||||
public interface IEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 等待初始化完成,通常用于其他系统需要等待实体初始化完成
|
||||
/// </summary>
|
||||
void WaitForInitializationComplete();
|
||||
int Id { 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; }
|
||||
#if NET5_0_OR_GREATER
|
||||
IServiceCollection ServiceCollection { get; }
|
||||
object[] GetServices();
|
||||
#endif
|
||||
void Inject(object obj);
|
||||
}
|
||||
/// <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>
|
||||
public interface IEntitiesService
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6ca7db14bd1dc33459de160d09804e8b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -8,7 +8,8 @@ namespace BITKit
|
|||
{
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
|
@ -186,6 +187,10 @@ namespace BITKit
|
|||
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()
|
||||
{
|
||||
lock (self)
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 07743e56508d72f4bbfa9a075927d42c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -31,7 +31,7 @@ namespace BITKit.StateMachine
|
|||
void OnStateExit(IState old, IState newState);
|
||||
}
|
||||
|
||||
public interface IStateMachine<T>
|
||||
public interface IStateMachine<T> where T:IState
|
||||
{
|
||||
bool Enabled { get; set; }
|
||||
T CurrentState { get; set; }
|
||||
|
@ -53,7 +53,7 @@ namespace BITKit.StateMachine
|
|||
|
||||
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()))
|
||||
{
|
||||
|
|
|
@ -1,155 +1,161 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BITKit
|
||||
{
|
||||
#region 物品枚举
|
||||
|
||||
public enum ItemQuality
|
||||
{
|
||||
/// <summary>
|
||||
/// 常见的
|
||||
/// </summary>
|
||||
Common,
|
||||
|
||||
/// <summary>
|
||||
/// 罕见的
|
||||
/// </summary>
|
||||
Uncommon,
|
||||
|
||||
/// <summary>
|
||||
/// 稀有的
|
||||
/// </summary>
|
||||
Rare,
|
||||
|
||||
/// <summary>
|
||||
/// 史诗的
|
||||
/// </summary>
|
||||
Epic,
|
||||
|
||||
/// <summary>
|
||||
/// 传奇的
|
||||
/// </summary>
|
||||
Legendary,
|
||||
|
||||
/// <summary>
|
||||
/// 神话的
|
||||
/// </summary>
|
||||
Mythical,
|
||||
|
||||
/// <summary>
|
||||
/// 开发者
|
||||
/// </summary>
|
||||
Develop,
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 物品接口
|
||||
|
||||
/// <summary>
|
||||
/// 物品属性
|
||||
/// </summary>
|
||||
public interface IScriptableItemProperty
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 基础物品
|
||||
/// </summary>
|
||||
public interface IBasicItem :IPropertable,ICloneable
|
||||
public interface IScriptableItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 唯一Id
|
||||
/// </summary>
|
||||
int Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 物品名,一般用于查找物品的主键
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
// /// <summary>
|
||||
// /// 可寻址路径,该路径用于查找物品
|
||||
// /// </summary>
|
||||
// string AddressablePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 物品描述
|
||||
/// </summary>
|
||||
string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 最大堆叠数量
|
||||
/// </summary>
|
||||
public int MaxStack { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 物品品质
|
||||
/// </summary>
|
||||
ItemQuality Quality { get; }
|
||||
bool CopyItemsFrom(IBasicItem item);
|
||||
|
||||
/// <summary>
|
||||
/// 属性
|
||||
/// </summary>
|
||||
IReadOnlyDictionary<Type, IScriptableItemProperty> Properties { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 价值
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
int Value=>10;
|
||||
int Value => 10;
|
||||
}
|
||||
/// <summary>
|
||||
/// 可配置化物品,通常用于配置
|
||||
/// </summary>
|
||||
public interface IAssetableItem: IBasicItem
|
||||
|
||||
public interface IRuntimeItem : ICloneable
|
||||
{
|
||||
/// <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
|
||||
|
||||
#region 物品实现
|
||||
|
||||
/// <summary>
|
||||
/// 被托管的物品
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ManagedItem : IBasicItem
|
||||
public record RuntimeItem : IRuntimeItem
|
||||
{
|
||||
#region 字段
|
||||
public int Id;
|
||||
public string Name;
|
||||
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 int Id { get; set; } = new Random().Next();
|
||||
public int ScriptableId { get; set; }
|
||||
public int Amount { get; set; }
|
||||
|
||||
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();
|
||||
|
||||
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)
|
||||
object ICloneable.Clone()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter w)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
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;
|
||||
return new RuntimeItem
|
||||
{
|
||||
Id = new Random().Next(),
|
||||
ScriptableId = ScriptableId,
|
||||
Amount = Amount,
|
||||
RuntimeProperties = new Dictionary<Type, IScriptableItemProperty>(RuntimeProperties),
|
||||
OnRuntimePropertiesChanged = null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using Microsoft.SqlServer.Server;
|
||||
|
||||
namespace BITKit
|
||||
{
|
||||
|
@ -7,89 +8,63 @@ namespace BITKit
|
|||
/// 支持属性
|
||||
/// 支持回调
|
||||
/// </summary>
|
||||
public interface IBasicItemContainer
|
||||
public interface IRuntimeItemContainer:IBinarySerialize
|
||||
{
|
||||
/// <summary>
|
||||
/// 物品容器的唯一Id
|
||||
/// </summary>
|
||||
int Id { get; }
|
||||
/// <summary>
|
||||
/// 尝试获取指定Item
|
||||
/// </summary>
|
||||
bool TryGetItem(Func<IBasicItem, bool> func, out IBasicItem item);
|
||||
/// <summary>
|
||||
/// 获取所有Item的只读副本
|
||||
/// </summary>
|
||||
IBasicItem[] GetItems();
|
||||
IRuntimeItem[] GetItems();
|
||||
/// <summary>
|
||||
/// 添加物品的接口
|
||||
/// </summary>
|
||||
bool Add(IBasicItem item);
|
||||
/// <summary>
|
||||
/// 通过Item本身进行移除
|
||||
/// </summary>
|
||||
bool Remove(IBasicItem item);
|
||||
bool Add(IRuntimeItem item);
|
||||
/// <summary>
|
||||
/// 通过Id移除物品(推荐)
|
||||
/// </summary>
|
||||
bool Remove(int id);
|
||||
/// <summary>
|
||||
/// 通过工厂方法移除物品
|
||||
/// </summary>
|
||||
bool Remove(Func<IBasicItem, bool> removeFactory);
|
||||
/// <summary>
|
||||
/// 通过通过Id丢下物品
|
||||
/// </summary>
|
||||
bool Drop(int Id);
|
||||
bool DropOrSpawn(IBasicItem item);
|
||||
bool Drop(int id);
|
||||
/// <summary>
|
||||
/// 注册添加物品的工厂方法,
|
||||
/// </summary>
|
||||
event Func<IBasicItem, bool> AddFactory;
|
||||
event Func<IRuntimeItem, bool> AddFactory;
|
||||
/// <summary>
|
||||
/// 注册移除物品的工厂方法
|
||||
/// </summary>
|
||||
event Func<IBasicItem, bool> RemoveFactory;
|
||||
event Func<IRuntimeItem, bool> RemoveFactory;
|
||||
/// <summary>
|
||||
/// 注册丢下物品的工厂方法
|
||||
/// </summary>
|
||||
event Func<IBasicItem, bool> DropFactory;
|
||||
event Func<IRuntimeItem, bool> DropFactory;
|
||||
/// <summary>
|
||||
/// 已添加Item的回调
|
||||
/// </summary>
|
||||
event Action<IBasicItem> OnAdd;
|
||||
|
||||
event Action<IRuntimeItem> OnAdd;
|
||||
/// <summary>
|
||||
/// 已移除Item的回调
|
||||
/// </summary>
|
||||
event Action<IBasicItem> OnRemove;
|
||||
event Action<IRuntimeItem> OnRemove;
|
||||
/// <summary>
|
||||
/// 已设置Item的回调
|
||||
/// </summary>
|
||||
event Action<IBasicItem> OnSet;
|
||||
event Action<IRuntimeItem> OnSet;
|
||||
/// <summary>
|
||||
/// 已丢下Item的回调
|
||||
/// </summary>
|
||||
event Action<IBasicItem> OnDrop;
|
||||
event Action<IRuntimeItem> OnDrop;
|
||||
/// <summary>
|
||||
/// 已重构Items的回调
|
||||
/// </summary>
|
||||
event Action<IBasicItemContainer> OnRebuild;
|
||||
|
||||
event Action<IRuntimeItemContainer> OnRebuild;
|
||||
/// <summary>
|
||||
/// 是否已完成物品交换,例如false就是开始交换物品true就是已完成交换物品,可以处理物品了
|
||||
/// </summary>
|
||||
event Action<bool> OnRelease;
|
||||
/// <summary>
|
||||
/// 添加挂起句柄
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
void AddHandle(int id);
|
||||
/// <summary>
|
||||
/// 移除挂起句柄
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
void RemoveHandle(int id);
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ using System.Numerics;
|
|||
using System.Reflection;
|
||||
using System.Text;
|
||||
using BITKit.Net.Examples;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Unity.Mathematics;
|
||||
|
||||
|
@ -21,6 +22,26 @@ namespace BITKit.Net
|
|||
{
|
||||
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 OnConnected;
|
||||
public event Action OnDisconnected;
|
||||
|
@ -29,14 +50,14 @@ namespace BITKit.Net
|
|||
public bool IsConnecting { get; private set; }
|
||||
public double RpcTimeOut { get; set; } = 5;
|
||||
public bool AutoReconnect { get; set; } = true;
|
||||
public float2 Traffic { get; set; }
|
||||
public float2 Traffic { get; private set; }
|
||||
public bool ManualTick { get; set; }
|
||||
|
||||
private readonly IntervalUpdate _reconnectInterval = new(1);
|
||||
|
||||
public int Ping { get; private set; }
|
||||
public int Id { get; private set; } = -1;
|
||||
private readonly KcpClient client;
|
||||
private readonly KcpClient _client;
|
||||
|
||||
private readonly ConcurrentQueue<byte[]> _commandQueue = new();
|
||||
|
||||
|
@ -50,7 +71,7 @@ namespace BITKit.Net
|
|||
private readonly GenericEvent _events = new();
|
||||
|
||||
private readonly ValidHandle _isConnected = new();
|
||||
|
||||
private bool _userConnected;
|
||||
private int _index = int.MinValue;
|
||||
private readonly ConcurrentDictionary<int, object> _p2p = new();
|
||||
private readonly ConcurrentDictionary<string,Func<object,UniTask<object>>> _rpc = new();
|
||||
|
@ -64,23 +85,7 @@ namespace BITKit.Net
|
|||
private ushort _connectedPort = 27014;
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -88,12 +93,12 @@ namespace BITKit.Net
|
|||
if (x)
|
||||
{
|
||||
OnConnected?.Invoke();
|
||||
BIT4Log.Log<KcpNetClient>("连接成功");
|
||||
_logger.LogInformation("连接成功");
|
||||
}
|
||||
else
|
||||
{
|
||||
OnDisconnected?.Invoke();
|
||||
BIT4Log.Log<KcpNetClient>("连接已断开");
|
||||
_logger.LogInformation("连接已断开");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,9 +109,14 @@ namespace BITKit.Net
|
|||
}
|
||||
|
||||
public async void Disconnect()
|
||||
{
|
||||
_userConnected = false;
|
||||
DisconnectInternal();
|
||||
}
|
||||
private async void DisconnectInternal()
|
||||
{
|
||||
IsConnecting = false;
|
||||
client.Disconnect();
|
||||
_client.Disconnect();
|
||||
_isConnected.RemoveElement(this);
|
||||
_timer.Stop();
|
||||
try
|
||||
|
@ -117,6 +127,7 @@ namespace BITKit.Net
|
|||
catch (OperationCanceledException){}
|
||||
}
|
||||
|
||||
private string _lastHostName;
|
||||
public async UniTask<bool> Connect(string address = "127.0.0.1", ushort port = 27014)
|
||||
{
|
||||
if (IsConnecting)
|
||||
|
@ -124,7 +135,7 @@ namespace BITKit.Net
|
|||
BIT4Log.Warning<KcpNetClient>("正在连接中");
|
||||
return false;
|
||||
}
|
||||
|
||||
_userConnected = true;
|
||||
//如果address是域名,解析为Ip
|
||||
if (address.Contains("."))
|
||||
{
|
||||
|
@ -132,7 +143,11 @@ namespace BITKit.Net
|
|||
if (ip.Length > 0)
|
||||
{
|
||||
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;
|
||||
if (client.connected) return false;
|
||||
if (_client.connected) return false;
|
||||
await BITApp.SwitchToMainThread();
|
||||
OnStartConnect?.Invoke();
|
||||
await UniTask.SwitchToThreadPool();
|
||||
|
@ -151,7 +166,7 @@ namespace BITKit.Net
|
|||
{
|
||||
_lastHeartbeat = DateTime.Now;
|
||||
|
||||
client.Connect(address, port);
|
||||
_client.Connect(address, port);
|
||||
|
||||
_timer.Start();
|
||||
_interval = TimeSpan.FromMilliseconds(_timer.Interval);
|
||||
|
@ -163,13 +178,13 @@ namespace BITKit.Net
|
|||
|
||||
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);
|
||||
client.Tick();
|
||||
_client.Tick();
|
||||
await Task.Delay(100);
|
||||
}
|
||||
|
||||
if (client.connected)
|
||||
if (_client.connected)
|
||||
{
|
||||
SendServerMessage(Environment.MachineName);
|
||||
|
||||
|
@ -177,11 +192,11 @@ namespace BITKit.Net
|
|||
|
||||
_connectedAddress = address;
|
||||
_connectedPort = port;
|
||||
return client.connected;
|
||||
return _client.connected;
|
||||
}
|
||||
|
||||
OnConnectedFailed?.Invoke();
|
||||
Disconnect();
|
||||
DisconnectInternal();
|
||||
|
||||
IsConnecting = false;
|
||||
return false;
|
||||
|
@ -220,7 +235,7 @@ namespace BITKit.Net
|
|||
switch (type)
|
||||
{
|
||||
case NetCommandType.Message:
|
||||
BIT4Log.Log<KcpNetClient>($"已收到消息:{reader.ReadString()}");
|
||||
_logger.LogInformation($"已收到消息:{reader.ReadString()}");
|
||||
break;
|
||||
case NetCommandType.AllClientCommand:
|
||||
case NetCommandType.Command:
|
||||
|
@ -424,7 +439,7 @@ namespace BITKit.Net
|
|||
|
||||
if (eventDelegate is null)
|
||||
{
|
||||
BIT4Log.Warning<KcpNetClient>($"未找到对应的事件:{rpcName}");
|
||||
//BIT4Log.Warning<KcpNetClient>($"未找到对应的事件:{rpcName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -443,9 +458,9 @@ namespace BITKit.Net
|
|||
}
|
||||
break;
|
||||
default:
|
||||
BIT4Log.Log<KcpNetClient>($"未知消息类型:{type},字节:{(byte)type}");
|
||||
_logger.LogInformation($"未知消息类型:{type},字节:{(byte)type}");
|
||||
if (bytes.Array != null)
|
||||
BIT4Log.Log<KcpNetClient>(
|
||||
_logger.LogInformation(
|
||||
$"已收到:({Id}, {BitConverter.ToString(bytes.Array, bytes.Offset, bytes.Count)} @ {channel})");
|
||||
break;
|
||||
}
|
||||
|
@ -456,17 +471,17 @@ namespace BITKit.Net
|
|||
// if (BITApp.SynchronizationContext is not null)
|
||||
// await UniTask.SwitchToSynchronizationContext(BITApp.SynchronizationContext);
|
||||
// OnConnected?.Invoke();
|
||||
// BIT4Log.Log<KcpNetClient>("已连接");
|
||||
// _logger.LogInformation("已连接");
|
||||
}
|
||||
|
||||
private async void OnDisconnectInternal()
|
||||
{
|
||||
//BIT4Log.Log<KcpNetClient>("连接被断开");
|
||||
Disconnect();
|
||||
// _logger.LogInformation("连接被断开");
|
||||
DisconnectInternal();
|
||||
}
|
||||
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)
|
||||
|
@ -500,6 +515,10 @@ namespace BITKit.Net
|
|||
|
||||
public async UniTask<T> GetFromServer<T>(string path = default,params object[] pars)
|
||||
{
|
||||
if (IsConnected is false)
|
||||
{
|
||||
throw new NetOfflineException();
|
||||
}
|
||||
//await UniTask.SwitchToThreadPool();
|
||||
var id = _index++;
|
||||
var ms = new MemoryStream();
|
||||
|
@ -528,6 +547,10 @@ namespace BITKit.Net
|
|||
|
||||
while (true)
|
||||
{
|
||||
if (IsConnected is false)
|
||||
{
|
||||
throw new NetOfflineException();
|
||||
}
|
||||
if ((_now - startTime).TotalSeconds > RpcTimeOut)
|
||||
{
|
||||
await BITApp.SwitchToMainThread();
|
||||
|
@ -573,24 +596,25 @@ namespace BITKit.Net
|
|||
foreach (var methodInfo in rpcHandle.GetType().GetMethods())
|
||||
{
|
||||
var att = methodInfo.GetCustomAttribute<NetRpcAttribute>();
|
||||
if(att is null)continue;
|
||||
if (att is null) continue;
|
||||
_rpcMethods.AddOrUpdate(methodInfo.Name, methodInfo, (s, info) => methodInfo);
|
||||
_rpcHandles.AddOrUpdate(methodInfo.Name, rpcHandle, (s, o) => rpcHandle);
|
||||
|
||||
reportBuilder.AppendLine($"Add [{methodInfo.Name}] as MethodInfo");
|
||||
}
|
||||
|
||||
foreach (var eventInfo in rpcHandle.GetType().GetEvents())
|
||||
{
|
||||
var att = eventInfo.GetCustomAttribute<NetRpcAttribute>();
|
||||
if(att is null)continue;
|
||||
|
||||
if (att is null) continue;
|
||||
|
||||
_rpcEvents.TryAdd(eventInfo.Name, eventInfo);
|
||||
_rpcHandles.TryAdd(eventInfo.Name, rpcHandle);
|
||||
|
||||
|
||||
reportBuilder.AppendLine($"Add [{eventInfo.Name}] as EventInfo");
|
||||
}
|
||||
|
||||
BIT4Log.Log<KcpNetClient>(reportBuilder);
|
||||
|
||||
_logger.LogInformation(reportBuilder.ToString());
|
||||
}
|
||||
|
||||
public void AddCommandListener<T>(Action<T> handle)
|
||||
|
@ -621,6 +645,10 @@ namespace BITKit.Net
|
|||
|
||||
public void SendRT(string rpcName, params object[] pars)
|
||||
{
|
||||
if (IsConnected is false)
|
||||
{
|
||||
throw new NetOfflineException();
|
||||
}
|
||||
using var ms = new MemoryStream();
|
||||
using var writer = new BinaryWriter(ms);
|
||||
writer.Write((byte)NetCommandType.GetFromServer);
|
||||
|
@ -650,36 +678,36 @@ namespace BITKit.Net
|
|||
.Write(message)
|
||||
.Build();
|
||||
Traffic+=new float2(bytes.Length,0);
|
||||
client.Send(bytes, KcpChannel.Reliable);
|
||||
_client.Send(bytes, KcpChannel.Reliable);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private readonly IntervalUpdate _pingInterval = new(1);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
_now = DateTime.Now;
|
||||
if(_userConnected is false)return;
|
||||
try
|
||||
{
|
||||
_now = DateTime.UtcNow;
|
||||
|
||||
|
||||
if (client.connected)
|
||||
if (_client.connected)
|
||||
{
|
||||
if (DateTime.Now - _lastHeartbeat > TimeSpan.FromSeconds(5))
|
||||
{
|
||||
BIT4Log.Warning<KcpNetClient>("心跳超时,自动断开");
|
||||
Disconnect();
|
||||
DisconnectInternal();
|
||||
_commandQueue.Clear();
|
||||
return;
|
||||
}
|
||||
while (_commandQueue.TryDequeue(out var bytes))
|
||||
{
|
||||
Traffic += new float2(bytes.Length, 0);
|
||||
client.Send(bytes, KcpChannel.Reliable);
|
||||
_client.Send(bytes, KcpChannel.Reliable);
|
||||
}
|
||||
Traffic+=new float2(1,0);
|
||||
client.Send(_heartBeat, KcpChannel.Unreliable);
|
||||
_client.Send(_heartBeat, KcpChannel.Unreliable);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -696,16 +724,7 @@ namespace BITKit.Net
|
|||
//BIT4Log.Warning<KcpNetClient>("连接已断开,清空指令队列");
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (_pingInterval.AllowUpdate)
|
||||
{
|
||||
_lastPingTime = DateTime.Now;
|
||||
client.Send(new[] { (byte)NetCommandType.Ping }, KcpChannel.Reliable);
|
||||
}
|
||||
#endif
|
||||
|
||||
client.Tick();
|
||||
_client.Tick();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -718,7 +737,7 @@ namespace BITKit.Net
|
|||
{
|
||||
// send client to server
|
||||
Traffic+=new float2(2,0);
|
||||
client.Send(new byte[]{0x01, 0x02}, KcpChannel.Reliable);
|
||||
_client.Send(new byte[]{0x01, 0x02}, KcpChannel.Reliable);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -101,7 +101,7 @@ namespace BITKit.Net
|
|||
BIT4Log.Log<KCPNetServer>($"{Name}:链接{id}超时,已断开");
|
||||
}
|
||||
|
||||
if (server.IsActive() is false || ManualTick) return;
|
||||
if (server.IsActive() is false) return;
|
||||
|
||||
server.Tick();
|
||||
|
||||
|
@ -150,9 +150,11 @@ namespace BITKit.Net
|
|||
_timer.Interval = 1000f / TickRate;
|
||||
_interval = TimeSpan.FromSeconds(1.0 / TickRate);
|
||||
}
|
||||
|
||||
OnStartServer?.Invoke();
|
||||
server.Start(port);
|
||||
_timer.Start();
|
||||
if (ManualTick is false)
|
||||
_timer.Start();
|
||||
_isStarted = true;
|
||||
BIT4Log.Log<KCPNetServer>($"已启动KCP服务器:{port}");
|
||||
}
|
||||
|
@ -551,14 +553,14 @@ namespace BITKit.Net
|
|||
{
|
||||
foreach (var methodInfo in rpcHandle.GetType().GetMethods())
|
||||
{
|
||||
var att = methodInfo.GetCustomAttribute<NetRpcAttribute>();
|
||||
var att = methodInfo.GetCustomAttribute<NetRpcAttribute>(true);
|
||||
if(att is null)continue;
|
||||
_rpcMethods.TryAdd(methodInfo.Name, methodInfo);
|
||||
_rpcHandles.TryAdd(methodInfo.Name, rpcHandle);
|
||||
}
|
||||
foreach (var eventInfo in rpcHandle.GetType().GetEvents())
|
||||
{
|
||||
var att = eventInfo.GetCustomAttribute<NetRpcAttribute>();
|
||||
var att = eventInfo.GetCustomAttribute<NetRpcAttribute>(true);
|
||||
if(att is null)continue;
|
||||
|
||||
_rpcEvents.TryAdd(eventInfo.Name, eventInfo);
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a3e65b7e409c5ce40b5ce927156ae2ad
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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>
|
||||
|
|
|
@ -126,7 +126,7 @@ namespace BITKit.Mod
|
|||
}
|
||||
}
|
||||
|
||||
public class ModService
|
||||
public partial class ModService
|
||||
{
|
||||
public static async UniTask<ModPackage[]> SearchPackages()
|
||||
{
|
||||
|
|
|
@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e0adbb35ce4784342a0ed37ac4a56200
|
||||
guid: c63d2ab8d0ff2304e980bb1e0585c903
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
|
@ -1,5 +1,7 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using Microsoft.SqlServer.Server;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
|
@ -7,84 +9,103 @@ using Newtonsoft.Json.Linq;
|
|||
namespace BITKit
|
||||
// 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",
|
||||
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
|
||||
StatusCode = 200,
|
||||
Message = "success",
|
||||
Data = data
|
||||
};
|
||||
}
|
||||
|
||||
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 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)
|
||||
public static ContextModel GetFromJson(string json)
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (Data)
|
||||
{
|
||||
case T t:
|
||||
value = t;
|
||||
return true;
|
||||
case JToken jToken:
|
||||
value = jToken.ToObject<T>();
|
||||
return true;
|
||||
default:
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
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()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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") { }
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 618a48f7c780dd148ae1fa0fba95b583
|
||||
guid: d38fad9bff7582b4b8287df7c47a47b7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
|
@ -18,7 +18,14 @@ namespace BITKit
|
|||
[AttributeUsage(AttributeTargets.Method|AttributeTargets.Event)]
|
||||
public sealed class NetRpcAttribute : Attribute
|
||||
{
|
||||
|
||||
public readonly bool AddTypeNamePrefix;
|
||||
public NetRpcAttribute()
|
||||
{
|
||||
}
|
||||
public NetRpcAttribute(bool addTypeNamePrefix)
|
||||
{
|
||||
AddTypeNamePrefix = addTypeNamePrefix;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 帮助类
|
||||
|
@ -84,7 +91,6 @@ namespace BITKit
|
|||
/// </summary>
|
||||
public interface INetProvider
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 向服务端发送指令
|
||||
/// </summary>
|
||||
|
@ -192,6 +198,10 @@ namespace BITKit
|
|||
/// </summary>
|
||||
public interface INetServer
|
||||
{
|
||||
/// <summary>
|
||||
/// 通信接口
|
||||
/// </summary>
|
||||
public INetProvider NetProvider=>this as INetProvider;
|
||||
/// <summary>
|
||||
/// 源物体,用于通过代理直接访问
|
||||
/// </summary>
|
||||
|
@ -274,6 +284,10 @@ namespace BITKit
|
|||
/// </summary>
|
||||
public interface INetClient
|
||||
{
|
||||
/// <summary>
|
||||
/// 通讯接口
|
||||
/// </summary>
|
||||
public INetProvider NetProvider=>this as INetProvider;
|
||||
/// <summary>
|
||||
/// 源物体,用于通过代理直接访问
|
||||
/// </summary>
|
||||
|
@ -318,85 +332,4 @@ namespace BITKit
|
|||
/// <param name="message">消息</param>
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bd95732fea121104b8f974d4b29a9325
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fa022cf4131f15247b756ad81daac733
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bc8df90017042c340a06728f62ed53af
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e61132c5a20b1cd429a70da257582959
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d543b75a249edac42bc53defe955e9fa
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e517c312e735e1042b911efc79deb1f6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2f1e118cfd267c74abc0e9464f5088ea
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a8711b7cfbb88574d928e3ba11162109
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3cb533c3705546949815e7583cdfc65b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0ad3cecd07fd75744b93174b03537005
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0a9ce048cccddfd42972d7502f096e77
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e55e58ac133e98749bdeb60064d42ace
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0710a77f8ae8f894781e9aa63f26d36b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bfcd8de179791a8449cfd82153de4999
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 088acda9508faea49be2d9f04eb1308e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BITKit
|
||||
{
|
||||
|
@ -26,6 +27,11 @@ namespace BITKit
|
|||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
void Remove(Action<float> action);
|
||||
/// <summary>
|
||||
/// 手动调用循环
|
||||
/// </summary>
|
||||
/// <param name="delta"></param>
|
||||
void ManualTick(float delta);
|
||||
}
|
||||
/// <summary>
|
||||
/// 主线程循环
|
||||
|
@ -35,4 +41,40 @@ namespace BITKit
|
|||
/// 线程池循环
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: eb2d5a09faa2e9a41858ff98770b423c
|
||||
guid: e07078010581a9b4481480587c1f6c64
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e42b9df1bf3976e4e87a36eeaf8e1fb7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -19,10 +19,6 @@ namespace BITKit.UX
|
|||
/// </summary>
|
||||
bool IsWindow { get; }
|
||||
/// <summary>
|
||||
/// 该面板是否有效,用于检查该面板是否已经被销毁
|
||||
/// </summary>
|
||||
//bool IsValid { get; }
|
||||
/// <summary>
|
||||
/// 该面板的索引(入口,Key)
|
||||
/// </summary>
|
||||
string Index { get; }
|
||||
|
@ -38,77 +34,15 @@ namespace BITKit.UX
|
|||
/// 事件回调,当面板被打开时触发
|
||||
/// </summary>
|
||||
event Action OnEntry;
|
||||
event Func<UniTask> OnEntryAsync;
|
||||
event Action OnEntryCompleted;
|
||||
/// <summary>
|
||||
/// 事件回调,当面板被关闭时触发
|
||||
/// </summary>
|
||||
event Action OnExit;
|
||||
|
||||
void OnUpdate(float deltaTime);
|
||||
}
|
||||
public abstract class UXPanelImplement:IUXPanel
|
||||
{
|
||||
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);
|
||||
}
|
||||
event Func<UniTask> OnExitAsync;
|
||||
event Action OnExitCompleted;
|
||||
void OnTick(float deltaTime);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,19 @@
|
|||
using System;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace BITKit.UX
|
||||
{
|
||||
/// <summary>
|
||||
/// 基本UX服务(GUI管理器),主要通过加载叠加面板实现
|
||||
/// </summary>
|
||||
public interface IUXService
|
||||
public interface IUXService:IDisposable
|
||||
{
|
||||
object Root { get; }
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
UniTask InitializeAsync();
|
||||
/// <summary>
|
||||
/// 注册面板,加入注册队列
|
||||
/// </summary>
|
||||
|
|
|
@ -8,7 +8,11 @@ namespace BITKit.UX
|
|||
{
|
||||
string Message { get; set; }
|
||||
object Container { get; }
|
||||
public void SetMessage(string message)=>Message=message;
|
||||
public IUXWaitingHandle SetMessage(string message)
|
||||
{
|
||||
Message=message;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
public interface IUXWaiting
|
||||
{
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 429ce01eb2ea7ac4b99ea7adc32c2c71
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9c82261b9903a8c42a7e217940015c26
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 87bc33909c0bd1c49987c7095fb608c2
|
||||
guid: 6a9ecef037d512f40886666749c60d48
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
|
@ -1,12 +1,10 @@
|
|||
{
|
||||
"name": "BITKit.MotionMatching",
|
||||
"name": "BITKit.WorldNode",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
|
||||
"GUID:d525ad6bd40672747bde77962f1c401e",
|
||||
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
|
||||
"GUID:e34a5702dd353724aa315fb8011f08c3",
|
||||
"GUID:296866320aab85a42a0403bf684bac59"
|
||||
"GUID:d8b63aba1907145bea998dd612889d6b"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9f93c330bf016d047a49b0c29307ef42
|
||||
guid: d750d221812bb1d48baff92e6ef73e28
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
|
@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 119f6a0b25fc0cc47b6f950cd9902aba
|
||||
guid: 051c8f58c08cb14449b4da1aca114caf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ca82e09109a03de47b638f35b49b59e5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 132a27b99db1e664692937ec23f40676
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bb1616e9308157b43b675b2830b6da23
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cd869f7557b7c4f4a889a1103ce044d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f6f2e220c849a4440877e3b2ce108d13
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Before Width: | Height: | Size: 307 B After Width: | Height: | Size: 307 B |
Before Width: | Height: | Size: 583 B After Width: | Height: | Size: 583 B |
Before Width: | Height: | Size: 187 B After Width: | Height: | Size: 187 B |
Before Width: | Height: | Size: 208 B After Width: | Height: | Size: 208 B |
Before Width: | Height: | Size: 111 B After Width: | Height: | Size: 111 B |
Before Width: | Height: | Size: 128 B After Width: | Height: | Size: 128 B |
Before Width: | Height: | Size: 197 B After Width: | Height: | Size: 197 B |
Before Width: | Height: | Size: 201 B After Width: | Height: | Size: 201 B |
Before Width: | Height: | Size: 145 B After Width: | Height: | Size: 145 B |
Before Width: | Height: | Size: 150 B After Width: | Height: | Size: 150 B |
Before Width: | Height: | Size: 151 B After Width: | Height: | Size: 151 B |