[+] 接入ET8服务端

[+] 接入ET8服务端
This commit is contained in:
ALEXTANG
2023-07-13 12:23:48 +08:00
parent e0be062006
commit 336d4b2eb9
1316 changed files with 130657 additions and 626 deletions

View File

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

View File

@@ -0,0 +1,18 @@
using System;
namespace ET
{
public class AIHandlerAttribute: BaseAttribute
{
}
[AIHandler]
public abstract class AAIHandler
{
// 检查是否满足条件
public abstract int Check(AIComponent aiComponent, AIConfig aiConfig);
// 协程编写必须可以取消
public abstract ETTask Execute(AIComponent aiComponent, AIConfig aiConfig, ETCancellationToken cancellationToken);
}
}

View File

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

View File

@@ -0,0 +1,15 @@
namespace ET
{
// 客户端挂在ClientScene上服务端挂在Unit上
[ComponentOf(typeof(Scene))]
public class AIComponent: Entity, IAwake<int>, IDestroy
{
public int AIConfigId;
public ETCancellationToken CancellationToken;
public long Timer;
public int Current;
}
}

View File

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

View File

@@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.ComponentModel;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace ET
{
public partial class AIConfigCategory
{
[BsonIgnore]
public Dictionary<int, SortedDictionary<int, AIConfig>> AIConfigs = new Dictionary<int, SortedDictionary<int, AIConfig>>();
public SortedDictionary<int, AIConfig> GetAI(int aiConfigId)
{
return this.AIConfigs[aiConfigId];
}
public override void EndInit()
{
foreach (var kv in this.GetAll())
{
SortedDictionary<int, AIConfig> aiNodeConfig;
if (!this.AIConfigs.TryGetValue(kv.Value.AIConfigId, out aiNodeConfig))
{
aiNodeConfig = new SortedDictionary<int, AIConfig>();
this.AIConfigs.Add(kv.Value.AIConfigId, aiNodeConfig);
}
aiNodeConfig.Add(kv.Key, kv.Value);
}
}
}
}

View File

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

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
namespace ET
{
public class AIDispatcherComponent: SingletonLock<AIDispatcherComponent>, ISingletonAwake
{
private readonly Dictionary<string, AAIHandler> aiHandlers = new();
public void Awake()
{
var types = EventSystem.Instance.GetTypes(typeof (AIHandlerAttribute));
foreach (Type type in types)
{
AAIHandler aaiHandler = Activator.CreateInstance(type) as AAIHandler;
if (aaiHandler == null)
{
Log.Error($"robot ai is not AAIHandler: {type.Name}");
continue;
}
this.aiHandlers.Add(type.Name, aaiHandler);
}
}
public AAIHandler Get(string key)
{
this.aiHandlers.TryGetValue(key, out var aaiHandler);
return aaiHandler;
}
public override void Load()
{
World.Instance.AddSingleton<AIDispatcherComponent>(true);
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,23 @@
using System.Collections.Generic;
using System.Threading;
namespace ET
{
public static class ConsoleMode
{
public const string ReloadDll = "R";
public const string ReloadConfig = "C";
public const string ShowMemory = "M";
public const string Repl = "Repl";
public const string Debugger = "Debugger";
public const string CreateRobot = "CreateRobot";
public const string Robot = "Robot";
}
[ComponentOf(typeof(Scene))]
public class ConsoleComponent: Entity, IAwake
{
public CancellationTokenSource CancellationTokenSource;
}
}

View File

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

View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
namespace ET
{
public class ConsoleDispatcher: SingletonLock<ConsoleDispatcher>, ISingletonAwake
{
private readonly Dictionary<string, IConsoleHandler> handlers = new();
public override void Load()
{
World.Instance.AddSingleton<ConsoleDispatcher>(true);
}
public void Awake()
{
HashSet<Type> types = EventSystem.Instance.GetTypes(typeof (ConsoleHandlerAttribute));
foreach (Type type in types)
{
object[] attrs = type.GetCustomAttributes(typeof(ConsoleHandlerAttribute), false);
if (attrs.Length == 0)
{
continue;
}
ConsoleHandlerAttribute consoleHandlerAttribute = (ConsoleHandlerAttribute)attrs[0];
object obj = Activator.CreateInstance(type);
IConsoleHandler iConsoleHandler = obj as IConsoleHandler;
if (iConsoleHandler == null)
{
throw new Exception($"ConsoleHandler handler not inherit IConsoleHandler class: {obj.GetType().FullName}");
}
this.handlers.Add(consoleHandlerAttribute.Mode, iConsoleHandler);
}
}
public IConsoleHandler Get(string key)
{
return this.handlers[key];
}
}
}

View File

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

View File

@@ -0,0 +1,12 @@
namespace ET
{
public class ConsoleHandlerAttribute: BaseAttribute
{
public string Mode { get; }
public ConsoleHandlerAttribute(string mode)
{
this.Mode = mode;
}
}
}

View File

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

View File

@@ -0,0 +1,7 @@
namespace ET
{
public interface IConsoleHandler
{
ETTask Run(Fiber fiber, ModeContex contex, string content);
}
}

View File

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

View File

@@ -0,0 +1,27 @@
namespace ET
{
[EntitySystemOf(typeof(ModeContex))]
[FriendOf(typeof(ModeContex))]
public static partial class ModeContexSystem
{
[EntitySystem]
private static void Awake(this ModeContex self)
{
self.Mode = "";
}
[EntitySystem]
private static void Destroy(this ModeContex self)
{
self.Mode = "";
}
}
[ComponentOf(typeof(ConsoleComponent))]
public class ModeContex: Entity, IAwake, IDestroy
{
public string Mode = "";
}
}

View File

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

View File

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

View File

@@ -0,0 +1,17 @@
namespace ET
{
public static partial class ErrorCode
{
public const int ERR_Success = 0;
// 1-11004 是SocketError请看SocketError定义
//-----------------------------------
// 100000-109999是Core层的错误
// 110000以下的错误请看ErrorCore.cs
// 这里配置逻辑层的错误码
// 110000 - 200000是抛异常的错误
// 200001以上不抛异常
}
}

View File

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

View File

@@ -0,0 +1,12 @@
using System;
namespace ET
{
public interface IMHandler
{
void Handle(Session session, object message);
Type GetMessageType();
Type GetResponseType();
}
}

View File

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

View File

@@ -0,0 +1,100 @@
using System.Collections.Generic;
using System;
namespace ET
{
public class MessageDispatcherInfo
{
public SceneType SceneType { get; }
public IMHandler IMHandler { get; }
public MessageDispatcherInfo(SceneType sceneType, IMHandler imHandler)
{
this.SceneType = sceneType;
this.IMHandler = imHandler;
}
}
public class MessageDispatcherComponent: SingletonLock<MessageDispatcherComponent>, ISingletonAwake
{
private readonly Dictionary<ushort, List<MessageDispatcherInfo>> handlers = new();
public void Awake()
{
HashSet<Type> types = EventSystem.Instance.GetTypes(typeof (MessageHandlerAttribute));
foreach (Type type in types)
{
IMHandler iMHandler = Activator.CreateInstance(type) as IMHandler;
if (iMHandler == null)
{
Log.Error($"message handle {type.Name} 需要继承 IMHandler");
continue;
}
object[] attrs = type.GetCustomAttributes(typeof(MessageHandlerAttribute), true);
foreach (object attr in attrs)
{
MessageHandlerAttribute messageHandlerAttribute = attr as MessageHandlerAttribute;
Type messageType = iMHandler.GetMessageType();
ushort opcode = OpcodeType.Instance.GetOpcode(messageType);
if (opcode == 0)
{
Log.Error($"消息opcode为0: {messageType.Name}");
continue;
}
MessageDispatcherInfo messageDispatcherInfo = new (messageHandlerAttribute.SceneType, iMHandler);
this.RegisterHandler(opcode, messageDispatcherInfo);
}
}
}
public override void Load()
{
World.Instance.AddSingleton<MessageDispatcherComponent>(true);
}
private void RegisterHandler(ushort opcode, MessageDispatcherInfo handler)
{
if (!this.handlers.ContainsKey(opcode))
{
this.handlers.Add(opcode, new List<MessageDispatcherInfo>());
}
this.handlers[opcode].Add(handler);
}
public void Handle(Session session, object message)
{
List<MessageDispatcherInfo> actions;
ushort opcode = OpcodeType.Instance.GetOpcode(message.GetType());
if (!this.handlers.TryGetValue(opcode, out actions))
{
Log.Error($"消息没有处理: {opcode} {message}");
return;
}
SceneType sceneType = session.IScene.SceneType;
foreach (MessageDispatcherInfo ev in actions)
{
if (!ev.SceneType.HasSameFlag(sceneType))
{
continue;
}
try
{
ev.IMHandler.Handle(session, message);
}
catch (Exception e)
{
Log.Error(e);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,37 @@
using System;
namespace ET
{
public abstract class MessageHandler<Message>: IMHandler where Message : class
{
protected abstract ETTask Run(Session session, Message message);
public void Handle(Session session, object msg)
{
Message message = msg as Message;
if (message == null)
{
Log.Error($"消息类型转换错误: {msg.GetType().FullName} to {typeof (Message).Name}");
return;
}
if (session.IsDisposed)
{
Log.Error($"session disconnect {msg}");
return;
}
this.Run(session, message).Coroutine();
}
public Type GetMessageType()
{
return typeof (Message);
}
public Type GetResponseType()
{
return null;
}
}
}

View File

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

View File

@@ -0,0 +1,14 @@
using System;
namespace ET
{
public class MessageHandlerAttribute: BaseAttribute
{
public SceneType SceneType { get; }
public MessageHandlerAttribute(SceneType sceneType)
{
this.SceneType = sceneType;
}
}
}

View File

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

View File

@@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
namespace ET
{
public readonly struct RpcInfo
{
public readonly IRequest Request;
public readonly ETTask<IResponse> Tcs;
public RpcInfo(IRequest request)
{
this.Request = request;
this.Tcs = ETTask<IResponse>.Create(true);
}
}
[EntitySystemOf(typeof(Session))]
[FriendOf(typeof(Session))]
public static partial class SessionSystem
{
[EntitySystem]
private static void Awake(this Session self, AService aService)
{
self.AService = aService;
long timeNow = self.Fiber().TimeInfo.ClientNow();
self.LastRecvTime = timeNow;
self.LastSendTime = timeNow;
self.requestCallbacks.Clear();
Log.Info($"session create: zone: {self.Zone()} id: {self.Id} {timeNow} ");
}
[EntitySystem]
private static void Destroy(this Session self)
{
self.AService.Remove(self.Id, self.Error);
foreach (RpcInfo responseCallback in self.requestCallbacks.Values.ToArray())
{
responseCallback.Tcs.SetException(new RpcException(self.Error, $"session dispose: {self.Id} {self.RemoteAddress}"));
}
Log.Info($"session dispose: {self.RemoteAddress} id: {self.Id} ErrorCode: {self.Error}, please see ErrorCode.cs! {self.Fiber().TimeInfo.ClientNow()}");
self.requestCallbacks.Clear();
}
public static void OnResponse(this Session self, IResponse response)
{
if (!self.requestCallbacks.Remove(response.RpcId, out var action))
{
return;
}
if (ErrorCore.IsRpcNeedThrowException(response.Error))
{
action.Tcs.SetException(new Exception($"Rpc error, request: {action.Request} response: {response}"));
return;
}
action.Tcs.SetResult(response);
}
public static async ETTask<IResponse> Call(this Session self, IRequest request, ETCancellationToken cancellationToken)
{
int rpcId = ++Session.RpcId;
RpcInfo rpcInfo = new RpcInfo(request);
self.requestCallbacks[rpcId] = rpcInfo;
request.RpcId = rpcId;
self.Send(request);
void CancelAction()
{
if (!self.requestCallbacks.TryGetValue(rpcId, out RpcInfo action))
{
return;
}
self.requestCallbacks.Remove(rpcId);
Type responseType = OpcodeType.Instance.GetResponseType(action.Request.GetType());
IResponse response = (IResponse) Activator.CreateInstance(responseType);
response.Error = ErrorCore.ERR_Cancel;
action.Tcs.SetResult(response);
}
IResponse ret;
try
{
cancellationToken?.Add(CancelAction);
ret = await rpcInfo.Tcs;
}
finally
{
cancellationToken?.Remove(CancelAction);
}
return ret;
}
public static async ETTask<IResponse> Call(this Session self, IRequest request, int time = 0)
{
int rpcId = ++Session.RpcId;
RpcInfo rpcInfo = new(request);
self.requestCallbacks[rpcId] = rpcInfo;
request.RpcId = rpcId;
self.Send(request);
if (time > 0)
{
async ETTask Timeout()
{
await self.Fiber().TimerComponent.WaitAsync(time);
if (!self.requestCallbacks.TryGetValue(rpcId, out RpcInfo action))
{
return;
}
if (!self.requestCallbacks.Remove(rpcId))
{
return;
}
action.Tcs.SetException(new Exception($"session call timeout: {request} {time}"));
}
Timeout().Coroutine();
}
return await rpcInfo.Tcs;
}
public static void Send(this Session self, IMessage message)
{
self.Send(default, message);
}
public static void Send(this Session self, ActorId actorId, IMessage message)
{
self.LastSendTime = self.Fiber().TimeInfo.ClientNow();
Log.Debug(message.ToString());
self.AService.Send(self.Id, actorId, message as MessageObject);
}
}
[ChildOf]
public sealed class Session: Entity, IAwake<AService>, IDestroy
{
public AService AService { get; set; }
public static int RpcId
{
get;
set;
}
public readonly Dictionary<int, RpcInfo> requestCallbacks = new();
public long LastRecvTime
{
get;
set;
}
public long LastSendTime
{
get;
set;
}
public int Error
{
get;
set;
}
public IPEndPoint RemoteAddress
{
get;
set;
}
}
}

View File

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

View File

@@ -0,0 +1,9 @@
namespace ET
{
// 刚accept的session只持续5秒必须通过验证否则断开
[ComponentOf(typeof(Session))]
public class SessionAcceptTimeoutComponent: Entity, IAwake, IDestroy
{
public long Timer;
}
}

View File

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

View File

@@ -0,0 +1,8 @@
namespace ET
{
[ComponentOf(typeof(Session))]
public class SessionIdleCheckerComponent: Entity, IAwake, IDestroy
{
public long RepeatedTimer;
}
}

View File

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

View File

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

View File

@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using Unity.Mathematics;
namespace ET
{
[ComponentOf(typeof(Unit))]
public class MoveComponent: Entity, IAwake, IDestroy
{
public float3 PreTarget
{
get
{
return this.Targets[this.N - 1];
}
}
public float3 NextTarget
{
get
{
return this.Targets[this.N];
}
}
// 开启移动协程的时间
public long BeginTime;
// 每个点的开始时间
public long StartTime { get; set; }
// 开启移动协程的Unit的位置
public float3 StartPos;
public float3 RealPos
{
get
{
return this.Targets[0];
}
}
private long needTime;
public long NeedTime
{
get
{
return this.needTime;
}
set
{
this.needTime = value;
}
}
public long MoveTimer;
public float Speed; // m/s
public ETTask<bool> tcs;
public List<float3> Targets = new List<float3>();
public float3 FinalTarget
{
get
{
return this.Targets[this.Targets.Count - 1];
}
}
public int N;
public int TurnTime;
public bool IsTurnHorizontal;
public quaternion From;
public quaternion To;
}
}

View File

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

View File

@@ -0,0 +1,15 @@
namespace ET
{
namespace EventType
{
public struct MoveStart
{
public Unit Unit;
}
public struct MoveStop
{
public Unit Unit;
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,7 @@
namespace ET
{
public interface INumericWatcher
{
void Run(Unit unit, EventType.NumbericChange args);
}
}

View File

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

View File

@@ -0,0 +1,121 @@
using System.Collections.Generic;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson.Serialization.Options;
namespace ET
{
[FriendOf(typeof (NumericComponent))]
public static class NumericComponentSystem
{
public static float GetAsFloat(this NumericComponent self, int numericType)
{
return (float)self.GetByKey(numericType) / 10000;
}
public static int GetAsInt(this NumericComponent self, int numericType)
{
return (int)self.GetByKey(numericType);
}
public static long GetAsLong(this NumericComponent self, int numericType)
{
return self.GetByKey(numericType);
}
public static void Set(this NumericComponent self, int nt, float value)
{
self[nt] = (long)(value * 10000);
}
public static void Set(this NumericComponent self, int nt, int value)
{
self[nt] = value;
}
public static void Set(this NumericComponent self, int nt, long value)
{
self[nt] = value;
}
public static void SetNoEvent(this NumericComponent self, int numericType, long value)
{
self.Insert(numericType, value, false);
}
public static void Insert(this NumericComponent self, int numericType, long value, bool isPublicEvent = true)
{
long oldValue = self.GetByKey(numericType);
if (oldValue == value)
{
return;
}
self.NumericDic[numericType] = value;
if (numericType >= NumericType.Max)
{
self.Update(numericType, isPublicEvent);
return;
}
if (isPublicEvent)
{
EventSystem.Instance.Publish(self.Scene(),
new EventType.NumbericChange() { Unit = self.GetParent<Unit>(), New = value, Old = oldValue, NumericType = numericType });
}
}
public static long GetByKey(this NumericComponent self, int key)
{
long value = 0;
self.NumericDic.TryGetValue(key, out value);
return value;
}
public static void Update(this NumericComponent self, int numericType, bool isPublicEvent)
{
int final = (int)numericType / 10;
int bas = final * 10 + 1;
int add = final * 10 + 2;
int pct = final * 10 + 3;
int finalAdd = final * 10 + 4;
int finalPct = final * 10 + 5;
// 一个数值可能会多种情况影响,比如速度,加个buff可能增加速度绝对值100也有些buff增加10%速度所以一个值可以由5个值进行控制其最终结果
// final = (((base + add) * (100 + pct) / 100) + finalAdd) * (100 + finalPct) / 100;
long result = (long)(((self.GetByKey(bas) + self.GetByKey(add)) * (100 + self.GetAsFloat(pct)) / 100f + self.GetByKey(finalAdd)) *
(100 + self.GetAsFloat(finalPct)) / 100f);
self.Insert(final, result, isPublicEvent);
}
}
namespace EventType
{
public struct NumbericChange
{
public Unit Unit;
public int NumericType;
public long Old;
public long New;
}
}
[ComponentOf(typeof (Unit))]
public class NumericComponent: Entity, IAwake, ITransfer
{
[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfArrays)]
public Dictionary<int, long> NumericDic = new Dictionary<int, long>();
public long this[int numericType]
{
get
{
return this.GetByKey(numericType);
}
set
{
this.Insert(numericType, value);
}
}
}
}

View File

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

View File

@@ -0,0 +1,32 @@
namespace ET
{
// 这个可弄个配置表生成
public static class NumericType
{
public const int Max = 10000;
public const int Speed = 1000;
public const int SpeedBase = Speed * 10 + 1;
public const int SpeedAdd = Speed * 10 + 2;
public const int SpeedPct = Speed * 10 + 3;
public const int SpeedFinalAdd = Speed * 10 + 4;
public const int SpeedFinalPct = Speed * 10 + 5;
public const int Hp = 1001;
public const int HpBase = Hp * 10 + 1;
public const int MaxHp = 1002;
public const int MaxHpBase = MaxHp * 10 + 1;
public const int MaxHpAdd = MaxHp * 10 + 2;
public const int MaxHpPct = MaxHp * 10 + 3;
public const int MaxHpFinalAdd = MaxHp * 10 + 4;
public const int MaxHpFinalPct = MaxHp * 10 + 5;
public const int AOI = 1003;
public const int AOIBase = AOI * 10 + 1;
public const int AOIAdd = AOI * 10 + 2;
public const int AOIPct = AOI * 10 + 3;
public const int AOIFinalAdd = AOI * 10 + 4;
public const int AOIFinalPct = AOI * 10 + 5;
}
}

View File

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

View File

@@ -0,0 +1,18 @@
using System;
namespace ET
{
[AttributeUsage(AttributeTargets.Class)]
public class NumericWatcherAttribute : BaseAttribute
{
public SceneType SceneType { get; }
public int NumericType { get; }
public NumericWatcherAttribute(SceneType sceneType, int type)
{
this.SceneType = sceneType;
this.NumericType = type;
}
}
}

View File

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

View File

@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
namespace ET
{
public class NumericWatcherInfo
{
public SceneType SceneType { get; }
public INumericWatcher INumericWatcher { get; }
public NumericWatcherInfo(SceneType sceneType, INumericWatcher numericWatcher)
{
this.SceneType = sceneType;
this.INumericWatcher = numericWatcher;
}
}
/// <summary>
/// 监视数值变化组件,分发监听
/// </summary>
public class NumericWatcherComponent : SingletonLock<NumericWatcherComponent>, ISingletonAwake
{
private readonly Dictionary<int, List<NumericWatcherInfo>> allWatchers = new();
public void Awake()
{
HashSet<Type> types = EventSystem.Instance.GetTypes(typeof(NumericWatcherAttribute));
foreach (Type type in types)
{
object[] attrs = type.GetCustomAttributes(typeof(NumericWatcherAttribute), false);
foreach (object attr in attrs)
{
NumericWatcherAttribute numericWatcherAttribute = (NumericWatcherAttribute)attr;
INumericWatcher obj = (INumericWatcher)Activator.CreateInstance(type);
NumericWatcherInfo numericWatcherInfo = new(numericWatcherAttribute.SceneType, obj);
if (!this.allWatchers.ContainsKey(numericWatcherAttribute.NumericType))
{
this.allWatchers.Add(numericWatcherAttribute.NumericType, new List<NumericWatcherInfo>());
}
this.allWatchers[numericWatcherAttribute.NumericType].Add(numericWatcherInfo);
}
}
}
public override void Load()
{
World.Instance.AddSingleton<NumericWatcherComponent>(true);
}
public void Run(Unit unit, EventType.NumbericChange args)
{
List<NumericWatcherInfo> list;
if (!this.allWatchers.TryGetValue(args.NumericType, out list))
{
return;
}
SceneType unitDomainSceneType = unit.IScene.SceneType;
foreach (NumericWatcherInfo numericWatcher in list)
{
if (!numericWatcher.SceneType.HasSameFlag(unitDomainSceneType))
{
continue;
}
numericWatcher.INumericWatcher.Run(unit, args);
}
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,163 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace ET
{
public static class WaitTypeError
{
public const int Success = 0;
public const int Destroy = 1;
public const int Cancel = 2;
public const int Timeout = 3;
}
public interface IWaitType
{
int Error
{
get;
set;
}
}
[EntitySystemOf(typeof(ObjectWait))]
[FriendOf(typeof(ObjectWait))]
public static partial class ObjectWaitSystem
{
[EntitySystem]
private static void Awake(this ObjectWait self)
{
self.tcss.Clear();
}
[EntitySystem]
private static void Destroy(this ObjectWait self)
{
foreach (object v in self.tcss.Values.ToArray())
{
((IDestroyRun) v).SetResult();
}
}
private interface IDestroyRun
{
void SetResult();
}
private class ResultCallback<K>: IDestroyRun where K : struct, IWaitType
{
private ETTask<K> tcs;
public ResultCallback()
{
this.tcs = ETTask<K>.Create(true);
}
public bool IsDisposed
{
get
{
return this.tcs == null;
}
}
public ETTask<K> Task => this.tcs;
public void SetResult(K k)
{
var t = tcs;
this.tcs = null;
t.SetResult(k);
}
public void SetResult()
{
var t = tcs;
this.tcs = null;
t.SetResult(new K() { Error = WaitTypeError.Destroy });
}
}
public static async ETTask<T> Wait<T>(this ObjectWait self, ETCancellationToken cancellationToken = null) where T : struct, IWaitType
{
ResultCallback<T> tcs = new ResultCallback<T>();
Type type = typeof (T);
self.tcss.Add(type, tcs);
void CancelAction()
{
self.Notify(new T() { Error = WaitTypeError.Cancel });
}
T ret;
try
{
cancellationToken?.Add(CancelAction);
ret = await tcs.Task;
}
finally
{
cancellationToken?.Remove(CancelAction);
}
return ret;
}
public static async ETTask<T> Wait<T>(this ObjectWait self, int timeout, ETCancellationToken cancellationToken = null) where T : struct, IWaitType
{
ResultCallback<T> tcs = new ResultCallback<T>();
async ETTask WaitTimeout()
{
await self.Fiber().TimerComponent.WaitAsync(timeout, cancellationToken);
if (cancellationToken.IsCancel())
{
return;
}
if (tcs.IsDisposed)
{
return;
}
self.Notify(new T() { Error = WaitTypeError.Timeout });
}
WaitTimeout().Coroutine();
self.tcss.Add(typeof (T), tcs);
void CancelAction()
{
self.Notify(new T() { Error = WaitTypeError.Cancel });
}
T ret;
try
{
cancellationToken?.Add(CancelAction);
ret = await tcs.Task;
}
finally
{
cancellationToken?.Remove(CancelAction);
}
return ret;
}
public static void Notify<T>(this ObjectWait self, T obj) where T : struct, IWaitType
{
Type type = typeof (T);
if (!self.tcss.TryGetValue(type, out object tcs))
{
return;
}
self.tcss.Remove(type);
((ResultCallback<T>) tcs).SetResult(obj);
}
}
[ComponentOf]
public class ObjectWait: Entity, IAwake, IDestroy
{
public Dictionary<Type, object> tcss = new Dictionary<Type, object>();
}
}

View File

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

View File

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

View File

@@ -0,0 +1,25 @@
using System;
namespace ET
{
/// <summary>
/// 同一块地图可能有多种寻路数据玩家可以随时切换怪物也可能跟玩家的寻路不一样寻路组件应该挂在Unit上
/// </summary>
[ComponentOf(typeof(Unit))]
public class PathfindingComponent: Entity, IAwake<string>, IDestroy
{
public const int FindRandomNavPosMaxRadius = 15000; // 随机找寻路点的最大半径
public float[] extents = {15, 10, 15};
public string Name;
public IntPtr navMesh;
public float[] StartPos = new float[3];
public float[] EndPos = new float[3];
public float[] Result = new float[Recast.MAX_POLYS * 3];
}
}

View File

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

View File

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

View File

@@ -0,0 +1,21 @@
namespace ET
{
// 可以用来管理多个客户端场景,比如大世界会加载多块场景
[ComponentOf(typeof(Scene))]
public class CurrentScenesComponent: Entity, IAwake
{
private EntityRef<Scene> scene;
public Scene Scene
{
get
{
return this.scene;
}
set
{
this.scene = value;
}
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,62 @@
using System.Diagnostics;
using MongoDB.Bson.Serialization.Attributes;
using Unity.Mathematics;
namespace ET
{
[ChildOf(typeof(UnitComponent))]
[DebuggerDisplay("ViewName,nq")]
public class Unit: Entity, IAwake<int>
{
public int ConfigId { get; set; } //配置表id
[BsonIgnore]
public UnitConfig Config => UnitConfigCategory.Instance.Get(this.ConfigId);
public UnitType Type => (UnitType)UnitConfigCategory.Instance.Get(this.ConfigId).Type;
[BsonElement]
private float3 position; //坐标
[BsonIgnore]
public float3 Position
{
get => this.position;
set
{
float3 oldPos = this.position;
this.position = value;
EventSystem.Instance.Publish(this.Scene(), new EventType.ChangePosition() { Unit = this, OldPos = oldPos });
}
}
[BsonIgnore]
public float3 Forward
{
get => math.mul(this.Rotation, math.forward());
set => this.Rotation = quaternion.LookRotation(value, math.up());
}
[BsonElement]
private quaternion rotation;
[BsonIgnore]
public quaternion Rotation
{
get => this.rotation;
set
{
this.rotation = value;
EventSystem.Instance.Publish(this.Scene(), new EventType.ChangeRotation() { Unit = this });
}
}
protected override string ViewName
{
get
{
return $"{this.GetType().FullName} ({this.Id})";
}
}
}
}

View File

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

View File

@@ -0,0 +1,8 @@
namespace ET
{
[ComponentOf(typeof(Scene))]
public class UnitComponent: Entity, IAwake, IDestroy
{
}
}

View File

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

View File

@@ -0,0 +1,18 @@
using Unity.Mathematics;
namespace ET
{
namespace EventType
{
public struct ChangePosition
{
public Unit Unit;
public float3 OldPos;
}
public struct ChangeRotation
{
public Unit Unit;
}
}
}

View File

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

View File

@@ -0,0 +1,9 @@
namespace ET
{
public enum UnitType: byte
{
Player = 1,
Monster = 2,
NPC = 3,
}
}

View File

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