[+] 接入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,141 @@
using System;
using System.Runtime.InteropServices;
using MemoryPack;
using MongoDB.Bson.Serialization.Attributes;
namespace ET
{
[MemoryPackable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public partial struct Address
{
[MemoryPackOrder(0)]
public int Process;
[MemoryPackOrder(1)]
public int Fiber;
public bool Equals(Address other)
{
return this.Process == other.Process && this.Fiber == other.Fiber;
}
public override bool Equals(object obj)
{
return obj is Address other && Equals(other);
}
public override int GetHashCode()
{
return HashCode.Combine(this.Process, this.Fiber);
}
public Address(int process, int fiber)
{
this.Process = process;
this.Fiber = fiber;
}
public static bool operator ==(Address left, Address right)
{
return left.Process == right.Process && left.Fiber == right.Fiber;
}
public static bool operator !=(Address left, Address right)
{
return !(left == right);
}
public override string ToString()
{
return $"{this.Process}:{this.Fiber}";
}
}
[MemoryPackable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public partial struct ActorId
{
public bool Equals(ActorId other)
{
return this.Address == other.Address && this.InstanceId == other.InstanceId;
}
public override bool Equals(object obj)
{
return obj is ActorId other && Equals(other);
}
public override int GetHashCode()
{
return HashCode.Combine(this.Address, this.InstanceId);
}
[MemoryPackOrder(0)]
public Address Address;
[MemoryPackOrder(1)]
public long InstanceId;
[BsonIgnore]
public int Process
{
get
{
return this.Address.Process;
}
set
{
this.Address.Process = value;
}
}
[BsonIgnore]
public int Fiber
{
get
{
return this.Address.Fiber;
}
set
{
this.Address.Fiber = value;
}
}
public ActorId(int process, int fiber)
{
this.Address = new Address(process, fiber);
this.InstanceId = 1;
}
public ActorId(int process, int fiber, long instanceId)
{
this.Address = new Address(process, fiber);
this.InstanceId = instanceId;
}
public ActorId(Address address): this(address, 1)
{
}
public ActorId(Address address, long instanceId)
{
this.Address = address;
this.InstanceId = instanceId;
}
public static bool operator ==(ActorId left, ActorId right)
{
return left.InstanceId == right.InstanceId && left.Address == right.Address;
}
public static bool operator !=(ActorId left, ActorId right)
{
return !(left == right);
}
public override string ToString()
{
return $"{this.Process}:{this.Fiber}:{this.InstanceId}";
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9e8f0bd4aa4045ddb59117f81ff62be5
timeCreated: 1687104833

View File

@@ -0,0 +1,36 @@
namespace ET
{
public interface IMessage
{
}
public interface IRequest: IMessage
{
int RpcId
{
get;
set;
}
}
public interface IResponse: IMessage
{
int Error
{
get;
set;
}
string Message
{
get;
set;
}
int RpcId
{
get;
set;
}
}
}

View File

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

View File

@@ -0,0 +1,22 @@
namespace ET
{
public interface ISingletonAwake
{
void Awake();
}
public interface ISingletonAwake<A>
{
void Awake(A a);
}
public interface ISingletonAwake<A, B>
{
void Awake(A a, B b);
}
public interface ISingletonAwake<A, B, C>
{
void Awake(A a, B b, C c);
}
}

View File

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

View File

@@ -0,0 +1,7 @@
namespace ET
{
public interface ISingletonDestroy
{
void Destroy();
}
}

View File

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

View File

@@ -0,0 +1,7 @@
namespace ET
{
public interface ISingletonLoad
{
void Load();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a6552160d3164dee967d0dc9db1611a3
timeCreated: 1687250763

View File

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

View File

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

View File

@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
namespace ET
{
public class ActorMessageDispatcherInfo
{
public SceneType SceneType { get; }
public IMActorHandler IMActorHandler { get; }
public ActorMessageDispatcherInfo(SceneType sceneType, IMActorHandler imActorHandler)
{
this.SceneType = sceneType;
this.IMActorHandler = imActorHandler;
}
}
/// <summary>
/// Actor消息分发组件
/// </summary>
public class ActorMessageDispatcherComponent: SingletonLock<ActorMessageDispatcherComponent>, ISingletonAwake
{
private readonly Dictionary<Type, List<ActorMessageDispatcherInfo>> ActorMessageHandlers = new();
public void Awake()
{
HashSet<Type> types = EventSystem.Instance.GetTypes(typeof (ActorMessageHandlerAttribute));
foreach (Type type in types)
{
this.Register(type);
}
HashSet<Type> types2 = EventSystem.Instance.GetTypes(typeof (ActorMessageLocationHandlerAttribute));
foreach (Type type in types2)
{
this.Register(type);
}
}
public override void Load()
{
World.Instance.AddSingleton<ActorMessageDispatcherComponent>(true);
}
private void Register(Type type)
{
object obj = Activator.CreateInstance(type);
IMActorHandler imHandler = obj as IMActorHandler;
if (imHandler == null)
{
throw new Exception($"message handler not inherit IMActorHandler abstract class: {obj.GetType().FullName}");
}
object[] attrs = type.GetCustomAttributes(typeof(ActorMessageHandlerAttribute), true);
foreach (object attr in attrs)
{
ActorMessageHandlerAttribute actorMessageHandlerAttribute = attr as ActorMessageHandlerAttribute;
Type messageType = imHandler.GetRequestType();
Type handleResponseType = imHandler.GetResponseType();
if (handleResponseType != null)
{
Type responseType = OpcodeType.Instance.GetResponseType(messageType);
if (handleResponseType != responseType)
{
throw new Exception($"message handler response type error: {messageType.FullName}");
}
}
ActorMessageDispatcherInfo actorMessageDispatcherInfo = new(actorMessageHandlerAttribute.SceneType, imHandler);
this.RegisterHandler(messageType, actorMessageDispatcherInfo);
}
}
private void RegisterHandler(Type type, ActorMessageDispatcherInfo handler)
{
if (!this.ActorMessageHandlers.ContainsKey(type))
{
this.ActorMessageHandlers.Add(type, new List<ActorMessageDispatcherInfo>());
}
this.ActorMessageHandlers[type].Add(handler);
}
public async ETTask Handle(Entity entity, Address fromAddress, MessageObject message)
{
List<ActorMessageDispatcherInfo> list;
if (!this.ActorMessageHandlers.TryGetValue(message.GetType(), out list))
{
throw new Exception($"not found message handler: {message} {entity.GetType().FullName}");
}
SceneType sceneType = entity.IScene.SceneType;
foreach (ActorMessageDispatcherInfo actorMessageDispatcherInfo in list)
{
if (!actorMessageDispatcherInfo.SceneType.HasSameFlag(sceneType))
{
continue;
}
await actorMessageDispatcherInfo.IMActorHandler.Handle(entity, fromAddress, message);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 294f1f138d2646e48b9bcbf0735b1e40
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 ActorMessageHandlerAttribute: BaseAttribute
{
public SceneType SceneType { get; }
public ActorMessageHandlerAttribute(SceneType sceneType)
{
this.SceneType = sceneType;
}
}
}

View File

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

View File

@@ -0,0 +1,11 @@
using System;
namespace ET
{
public class ActorMessageLocationHandlerAttribute: ActorMessageHandlerAttribute
{
public ActorMessageLocationHandlerAttribute(SceneType sceneType): base(sceneType)
{
}
}
}

View File

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

View File

@@ -0,0 +1,67 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace ET
{
public struct ActorMessageInfo
{
public ActorId ActorId;
public MessageObject MessageObject;
}
public class ActorMessageQueue: Singleton<ActorMessageQueue>, ISingletonAwake
{
private readonly ConcurrentDictionary<int, ConcurrentQueue<ActorMessageInfo>> messages = new();
public void Awake()
{
}
public void Send(ActorId actorId, MessageObject messageObject)
{
this.Send(actorId.Address, actorId, messageObject);
}
public void Reply(ActorId actorId, MessageObject messageObject)
{
this.Send(actorId.Address, actorId, messageObject);
}
public void Send(Address fromAddress, ActorId actorId, MessageObject messageObject)
{
if (!this.messages.TryGetValue(actorId.Address.Fiber, out var queue))
{
return;
}
queue.Enqueue(new ActorMessageInfo() {ActorId = new ActorId(fromAddress, actorId.InstanceId), MessageObject = messageObject});
}
public void Fetch(int fiberId, int count, List<ActorMessageInfo> list)
{
if (!this.messages.TryGetValue(fiberId, out var queue))
{
return;
}
for (int i = 0; i < count; ++i)
{
if (!queue.TryDequeue(out ActorMessageInfo message))
{
break;
}
list.Add(message);
}
}
public void AddQueue(int fiberId)
{
var queue = new ConcurrentQueue<ActorMessageInfo>();
this.messages[fiberId] = queue;
}
public void RemoveQueue(int fiberId)
{
this.messages.TryRemove(fiberId, out _);
}
}
}

View File

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

View File

@@ -0,0 +1,11 @@
using System;
namespace ET
{
public interface IMActorHandler
{
ETTask Handle(Entity entity, Address fromAddress, MessageObject actorMessage);
Type GetRequestType();
Type GetResponseType();
}
}

View File

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

View File

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

View File

@@ -0,0 +1,9 @@
using System;
namespace ET
{
[AttributeUsage(AttributeTargets.Class)]
public class ConfigAttribute: BaseAttribute
{
}
}

View File

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

View File

@@ -0,0 +1,82 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ET
{
/// <summary>
/// Config组件会扫描所有的有ConfigAttribute标签的配置,加载进来
/// </summary>
public class ConfigComponent: Singleton<ConfigComponent>, ISingletonAwake
{
public struct GetAllConfigBytes
{
}
public struct GetOneConfigBytes
{
public string ConfigName;
}
private readonly ConcurrentDictionary<Type, ISingleton> allConfig = new();
public void Awake()
{
}
public void Reload(Type configType)
{
byte[] oneConfigBytes =
EventSystem.Instance.Invoke<GetOneConfigBytes, byte[]>(new GetOneConfigBytes() { ConfigName = configType.Name });
object category = MongoHelper.Deserialize(configType, oneConfigBytes, 0, oneConfigBytes.Length);
ISingleton singleton = category as ISingleton;
this.allConfig[configType] = singleton;
singleton.Register();
}
public void Load()
{
this.allConfig.Clear();
Dictionary<Type, byte[]> configBytes = EventSystem.Instance.Invoke<GetAllConfigBytes, Dictionary<Type, byte[]>>(new GetAllConfigBytes());
foreach (Type type in configBytes.Keys)
{
byte[] oneConfigBytes = configBytes[type];
this.LoadOneInThread(type, oneConfigBytes);
}
}
public async ETTask LoadAsync()
{
this.allConfig.Clear();
Dictionary<Type, byte[]> configBytes = EventSystem.Instance.Invoke<GetAllConfigBytes, Dictionary<Type, byte[]>>(new GetAllConfigBytes());
using ListComponent<Task> listTasks = ListComponent<Task>.Create();
foreach (Type type in configBytes.Keys)
{
byte[] oneConfigBytes = configBytes[type];
Task task = Task.Run(() => LoadOneInThread(type, oneConfigBytes));
listTasks.Add(task);
}
await Task.WhenAll(listTasks.ToArray());
}
private void LoadOneInThread(Type configType, byte[] oneConfigBytes)
{
object category = MongoHelper.Deserialize(configType, oneConfigBytes, 0, oneConfigBytes.Length);
lock (this)
{
ISingleton singleton = category as ISingleton;
this.allConfig[configType] = singleton;
singleton.Register();
}
}
}
}

View File

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

View File

@@ -0,0 +1,71 @@
using System;
using System.ComponentModel;
namespace ET
{
public abstract class ConfigSingleton<T>: ISingleton, ISupportInitialize where T: ConfigSingleton<T>, new()
{
private bool isDisposed;
[StaticField]
private static T instance;
[StaticField]
private static object lockObj = new();
public static T Instance
{
get
{
lock (lockObj)
{
return instance;
}
}
private set
{
lock (lockObj)
{
instance = value;
}
}
}
public virtual void Register()
{
Instance = (T)this;
}
public bool IsDisposed()
{
return this.isDisposed;
}
protected virtual void Destroy()
{
}
void IDisposable.Dispose()
{
if (this.isDisposed)
{
return;
}
this.isDisposed = true;
Instance = null;
this.Destroy();
}
public virtual void BeginInit()
{
}
public virtual void EndInit()
{
}
}
}

View File

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

View File

@@ -0,0 +1,10 @@
namespace ET
{
/// <summary>
/// 每个Config的基类
/// </summary>
public interface IConfig
{
int Id { get; set; }
}
}

View File

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

View File

@@ -0,0 +1,7 @@
namespace ET
{
public interface IMerge
{
void Merge(object o);
}
}

View File

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

View File

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

View File

@@ -0,0 +1,9 @@
using System;
namespace ET
{
[AttributeUsage(AttributeTargets.Class)]
public class BaseAttribute: Attribute
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6d33b65898d50804188d439209df30d7
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 EventAttribute: BaseAttribute
{
public SceneType SceneType { get; }
public EventAttribute(SceneType sceneType)
{
this.SceneType = sceneType;
}
}
}

View File

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

View File

@@ -0,0 +1,250 @@
using System;
using System.Collections.Generic;
namespace ET
{
public class EventSystem: Singleton<EventSystem>, ISingletonAwake<Dictionary<string, Type>>
{
private class EventInfo
{
public IEvent IEvent { get; }
public SceneType SceneType {get; }
public EventInfo(IEvent iEvent, SceneType sceneType)
{
this.IEvent = iEvent;
this.SceneType = sceneType;
}
}
private readonly Dictionary<string, Type> allTypes = new();
private readonly UnOrderMultiMapSet<Type, Type> types = new();
private readonly Dictionary<Type, List<EventInfo>> allEvents = new();
private Dictionary<Type, Dictionary<long, object>> allInvokes = new();
public void Awake(Dictionary<string, Type> addTypes)
{
this.allTypes.Clear();
this.types.Clear();
foreach ((string fullName, Type type) in addTypes)
{
this.allTypes[fullName] = type;
if (type.IsAbstract)
{
continue;
}
// 记录所有的有BaseAttribute标记的的类型
object[] objects = type.GetCustomAttributes(typeof(BaseAttribute), true);
foreach (object o in objects)
{
this.types.Add(o.GetType(), type);
}
}
this.allEvents.Clear();
foreach (Type type in types[typeof (EventAttribute)])
{
IEvent obj = Activator.CreateInstance(type) as IEvent;
if (obj == null)
{
throw new Exception($"type not is AEvent: {type.Name}");
}
object[] attrs = type.GetCustomAttributes(typeof(EventAttribute), false);
foreach (object attr in attrs)
{
EventAttribute eventAttribute = attr as EventAttribute;
Type eventType = obj.Type;
EventInfo eventInfo = new(obj, eventAttribute.SceneType);
if (!this.allEvents.ContainsKey(eventType))
{
this.allEvents.Add(eventType, new List<EventInfo>());
}
this.allEvents[eventType].Add(eventInfo);
}
}
this.allInvokes = new Dictionary<Type, Dictionary<long, object>>();
foreach (Type type in types[typeof (InvokeAttribute)])
{
object obj = Activator.CreateInstance(type);
IInvoke iInvoke = obj as IInvoke;
if (iInvoke == null)
{
throw new Exception($"type not is callback: {type.Name}");
}
object[] attrs = type.GetCustomAttributes(typeof(InvokeAttribute), false);
foreach (object attr in attrs)
{
if (!this.allInvokes.TryGetValue(iInvoke.Type, out var dict))
{
dict = new Dictionary<long, object>();
this.allInvokes.Add(iInvoke.Type, dict);
}
InvokeAttribute invokeAttribute = attr as InvokeAttribute;
try
{
dict.Add(invokeAttribute.Type, obj);
}
catch (Exception e)
{
throw new Exception($"action type duplicate: {iInvoke.Type.Name} {invokeAttribute.Type}", e);
}
}
}
}
public HashSet<Type> GetTypes(Type systemAttributeType)
{
if (!this.types.ContainsKey(systemAttributeType))
{
return new HashSet<Type>();
}
return this.types[systemAttributeType];
}
public Dictionary<string, Type> GetTypes()
{
return allTypes;
}
public Type GetType(string typeName)
{
return this.allTypes[typeName];
}
public async ETTask PublishAsync<S, T>(S scene, T a) where S: class, IScene where T : struct
{
List<EventInfo> iEvents;
if (!this.allEvents.TryGetValue(typeof(T), out iEvents))
{
return;
}
using ListComponent<ETTask> list = ListComponent<ETTask>.Create();
foreach (EventInfo eventInfo in iEvents)
{
if (!scene.SceneType.HasSameFlag(eventInfo.SceneType))
{
continue;
}
if (!(eventInfo.IEvent is AEvent<S, T> aEvent))
{
Log.Error($"event error: {eventInfo.IEvent.GetType().FullName}");
continue;
}
list.Add(aEvent.Handle(scene, a));
}
try
{
await ETTaskHelper.WaitAll(list);
}
catch (Exception e)
{
Log.Error(e);
}
}
public void Publish<S, T>(S scene, T a) where S: class, IScene where T : struct
{
List<EventInfo> iEvents;
if (!this.allEvents.TryGetValue(typeof (T), out iEvents))
{
return;
}
SceneType sceneType = scene.SceneType;
foreach (EventInfo eventInfo in iEvents)
{
if (!sceneType.HasSameFlag(eventInfo.SceneType))
{
continue;
}
if (!(eventInfo.IEvent is AEvent<S, T> aEvent))
{
Log.Error($"event error: {eventInfo.IEvent.GetType().FullName}");
continue;
}
aEvent.Handle(scene, a).Coroutine();
}
}
// Invoke跟Publish的区别(特别注意)
// Invoke类似函数必须有被调用方否则异常调用者跟被调用者属于同一模块比如MoveComponent中的Timer计时器调用跟被调用的代码均属于移动模块
// 既然Invoke跟函数一样那么为什么不使用函数呢? 因为有时候不方便直接调用比如Config加载在客户端跟服务端加载方式不一样。比如TimerComponent需要根据Id分发
// 注意不要把Invoke当函数使用这样会造成代码可读性降低能用函数不要用Invoke
// publish是事件抛出去可以没人订阅调用者跟被调用者属于两个模块比如任务系统需要知道道具使用的信息则订阅道具使用事件
public void Invoke<A>(long type, A args) where A: struct
{
if (!this.allInvokes.TryGetValue(typeof(A), out var invokeHandlers))
{
throw new Exception($"Invoke error1: {type} {typeof(A).FullName}");
}
if (!invokeHandlers.TryGetValue(type, out var invokeHandler))
{
throw new Exception($"Invoke error2: {type} {typeof(A).FullName}");
}
var aInvokeHandler = invokeHandler as AInvokeHandler<A>;
if (aInvokeHandler == null)
{
throw new Exception($"Invoke error3, not AInvokeHandler: {type} {typeof(A).FullName}");
}
aInvokeHandler.Handle(args);
}
public T Invoke<A, T>(long type, A args) where A: struct
{
if (!this.allInvokes.TryGetValue(typeof(A), out var invokeHandlers))
{
throw new Exception($"Invoke error4: {type} {typeof(A).FullName}");
}
if (!invokeHandlers.TryGetValue(type, out var invokeHandler))
{
throw new Exception($"Invoke error5: {type} {typeof(A).FullName}");
}
var aInvokeHandler = invokeHandler as AInvokeHandler<A, T>;
if (aInvokeHandler == null)
{
throw new Exception($"Invoke error6, not AInvokeHandler: {type} {typeof(A).FullName} {typeof(T).FullName} ");
}
return aInvokeHandler.Handle(args);
}
public void Invoke<A>(A args) where A: struct
{
Invoke(0, args);
}
public T Invoke<A, T>(A args) where A: struct
{
return Invoke<A, T>(0, args);
}
}
}

View File

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

View File

@@ -0,0 +1,34 @@
using System;
namespace ET
{
public interface IEvent
{
Type Type { get; }
}
public abstract class AEvent<S, A>: IEvent where S: class, IScene where A: struct
{
public Type Type
{
get
{
return typeof (A);
}
}
protected abstract ETTask Run(S scene, A a);
public async ETTask Handle(S scene, A a)
{
try
{
await Run(scene, a);
}
catch (Exception e)
{
Log.Error(e);
}
}
}
}

View File

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

View File

@@ -0,0 +1,35 @@
using System;
namespace ET
{
public interface IInvoke
{
Type Type { get; }
}
public abstract class AInvokeHandler<A>: IInvoke where A: struct
{
public Type Type
{
get
{
return typeof (A);
}
}
public abstract void Handle(A args);
}
public abstract class AInvokeHandler<A, T>: IInvoke where A: struct
{
public Type Type
{
get
{
return typeof (A);
}
}
public abstract T Handle(A args);
}
}

View File

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

View File

@@ -0,0 +1,12 @@
namespace ET
{
public class InvokeAttribute: BaseAttribute
{
public long Type { get; }
public InvokeAttribute(long type = 0)
{
this.Type = type;
}
}
}

View File

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

View File

@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
namespace ET
{
public class TypeSystems
{
public class OneTypeSystems
{
public OneTypeSystems(int count)
{
this.QueueFlag = new bool[count];
}
public readonly UnOrderMultiMap<Type, object> Map = new();
// 这里不用hash数量比较少直接for循环速度更快
public readonly bool[] QueueFlag;
}
private readonly int count;
public TypeSystems(int count)
{
this.count = count;
}
private readonly Dictionary<Type, OneTypeSystems> typeSystemsMap = new();
public OneTypeSystems GetOrCreateOneTypeSystems(Type type)
{
OneTypeSystems systems = null;
this.typeSystemsMap.TryGetValue(type, out systems);
if (systems != null)
{
return systems;
}
systems = new OneTypeSystems(this.count);
this.typeSystemsMap.Add(type, systems);
return systems;
}
public OneTypeSystems GetOneTypeSystems(Type type)
{
OneTypeSystems systems = null;
this.typeSystemsMap.TryGetValue(type, out systems);
return systems;
}
public List<object> GetSystems(Type type, Type systemType)
{
OneTypeSystems oneTypeSystems = null;
if (!this.typeSystemsMap.TryGetValue(type, out oneTypeSystems))
{
return null;
}
if (!oneTypeSystems.Map.TryGetValue(systemType, out List<object> systems))
{
return null;
}
return systems;
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
namespace ET
{
public struct FiberInit
{
public Fiber Fiber { get; set; }
}
}

View File

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

View File

@@ -0,0 +1,126 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace ET
{
public enum SchedulerType
{
Main,
Thread,
ThreadPool,
}
public class FiberManager: Singleton<FiberManager>, ISingletonAwake
{
private readonly IScheduler[] schedulers = new IScheduler[3];
private int idGenerator = 10000000; // 10000000以下为保留的用于StartSceneConfig的fiber id, 1个区配置1000个纤程可以配置10000个区
private ConcurrentDictionary<int, Fiber> fibers = new();
private MainThreadScheduler mainThreadScheduler;
public void Awake()
{
this.mainThreadScheduler = new MainThreadScheduler(this);
this.schedulers[(int)SchedulerType.Main] = this.mainThreadScheduler;
#if ENABLE_VIEW && UNITY_EDITOR
this.schedulers[(int)SchedulerType.Thread] = this.mainThreadScheduler;
this.schedulers[(int)SchedulerType.ThreadPool] = this.mainThreadScheduler;
#else
this.schedulers[(int)SchedulerType.Thread] = new ThreadScheduler(this);
this.schedulers[(int)SchedulerType.ThreadPool] = new ThreadPoolScheduler(this);
#endif
}
public void Update()
{
this.mainThreadScheduler.Update();
}
public void LateUpdate()
{
this.mainThreadScheduler.LateUpdate();
}
protected override void Destroy()
{
foreach (IScheduler scheduler in this.schedulers)
{
scheduler.Dispose();
}
foreach (var kv in this.fibers)
{
kv.Value.Dispose();
}
this.fibers = null;
}
public async Task<int> Create(SchedulerType schedulerType, int fiberId, int zone, SceneType sceneType, string name)
{
try
{
Fiber fiber = new(fiberId, Options.Instance.Process, zone, sceneType, name);
this.fibers.TryAdd(fiberId, fiber);
this.schedulers[(int) schedulerType].Add(fiberId);
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
fiber.ThreadSynchronizationContext.Post(async () =>
{
try
{
// 根据Fiber的SceneType分发Init,必须在Fiber线程中执行
await EventSystem.Instance.Invoke<FiberInit, ETTask>((long)sceneType, new FiberInit() {Fiber = fiber});
tcs.SetResult(true);
}
catch (Exception e)
{
Log.Error($"init fiber fail: {sceneType} {e}");
}
});
await tcs.Task;
return fiberId;
}
catch (Exception e)
{
throw new Exception($"create fiber error: {fiberId} {sceneType}", e);
}
}
public async Task<int> Create(SchedulerType schedulerType, int zone, SceneType sceneType, string name)
{
int fiberId = Interlocked.Increment(ref this.idGenerator);
return await this.Create(schedulerType, fiberId, zone, sceneType, name);
}
public void Remove(int id)
{
Fiber fiber = this.Get(id);
fiber.ThreadSynchronizationContext.Post(()=>{fiber.Dispose();});
// 这里不能dispose因为有可能fiber还在运行会出现线程竞争
//fiber.Dispose();
}
internal void RemoveReal(int id)
{
this.fibers.Remove(id, out _);
// 这里不能dispose因为有可能fiber还在运行会出现线程竞争
//fiber.Dispose();
}
// 不允许外部调用,容易出现多线程问题, 只能通过消息通信不允许直接获取其它Fiber引用
internal Fiber Get(int id)
{
this.fibers.TryGetValue(id, out Fiber fiber);
return fiber;
}
}
}

View File

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

View File

@@ -0,0 +1,9 @@
using System;
namespace ET
{
public interface IScheduler: IDisposable
{
void Add(int fiberId);
}
}

View File

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

View File

@@ -0,0 +1,97 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
namespace ET
{
internal class MainThreadScheduler: IScheduler
{
private readonly ConcurrentQueue<int> idQueue = new();
private readonly ConcurrentQueue<int> addIds = new();
private readonly FiberManager fiberManager;
private readonly ThreadSynchronizationContext threadSynchronizationContext = new();
public MainThreadScheduler(FiberManager fiberManager)
{
SynchronizationContext.SetSynchronizationContext(this.threadSynchronizationContext);
this.fiberManager = fiberManager;
}
public void Dispose()
{
this.addIds.Clear();
this.idQueue.Clear();
}
public void Update()
{
SynchronizationContext.SetSynchronizationContext(this.threadSynchronizationContext);
this.threadSynchronizationContext.Update();
int count = this.idQueue.Count;
while (count-- > 0)
{
if (!this.idQueue.TryDequeue(out int id))
{
continue;
}
Fiber fiber = this.fiberManager.Get(id);
if (fiber == null)
{
continue;
}
if (fiber.IsDisposed)
{
continue;
}
SynchronizationContext.SetSynchronizationContext(fiber.ThreadSynchronizationContext);
fiber.Update();
this.idQueue.Enqueue(id);
}
}
public void LateUpdate()
{
int count = this.idQueue.Count;
while (count-- > 0)
{
if (!this.idQueue.TryDequeue(out int id))
{
continue;
}
Fiber fiber = this.fiberManager.Get(id);
if (fiber == null)
{
continue;
}
if (fiber.IsDisposed)
{
continue;
}
SynchronizationContext.SetSynchronizationContext(fiber.ThreadSynchronizationContext);
fiber.LateUpdate();
this.idQueue.Enqueue(id);
}
while (this.addIds.Count > 0)
{
this.addIds.TryDequeue(out int result);
this.idQueue.Enqueue(result);
}
}
public void Add(int fiberId = 0)
{
this.addIds.Enqueue(fiberId);
}
}
}

View File

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

View File

@@ -0,0 +1,78 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
namespace ET
{
internal class ThreadPoolScheduler: IScheduler
{
private readonly List<Thread> threads;
private readonly ConcurrentQueue<int> idQueue = new();
private readonly FiberManager fiberManager;
public ThreadPoolScheduler(FiberManager fiberManager)
{
this.fiberManager = fiberManager;
int threadCount = Environment.ProcessorCount;
this.threads = new List<Thread>(threadCount);
for (int i = 0; i < threadCount; ++i)
{
Thread thread = new(this.Loop);
this.threads.Add(thread);
thread.Start();
}
}
private void Loop()
{
while (true)
{
if (this.fiberManager.IsDisposed())
{
return;
}
if (!this.idQueue.TryDequeue(out int id))
{
Thread.Sleep(1);
continue;
}
Fiber fiber = this.fiberManager.Get(id);
if (fiber == null)
{
continue;
}
if (fiber.IsDisposed)
{
continue;
}
SynchronizationContext.SetSynchronizationContext(fiber.ThreadSynchronizationContext);
fiber.Update();
fiber.LateUpdate();
SynchronizationContext.SetSynchronizationContext(null);
this.idQueue.Enqueue(id);
Thread.Sleep(1);
}
}
public void Dispose()
{
foreach (Thread thread in this.threads)
{
thread.Join();
}
}
public void Add(int fiberId)
{
this.idQueue.Enqueue(fiberId);
}
}
}

View File

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

View File

@@ -0,0 +1,67 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
namespace ET
{
// 一个Fiber一个固定的线程
internal class ThreadScheduler: IScheduler
{
private readonly ConcurrentDictionary<int, Thread> dictionary = new();
private readonly FiberManager fiberManager;
public ThreadScheduler(FiberManager fiberManager)
{
this.fiberManager = fiberManager;
}
private void Loop(int fiberId)
{
Fiber fiber = fiberManager.Get(fiberId);
SynchronizationContext.SetSynchronizationContext(fiber.ThreadSynchronizationContext);
while (true)
{
if (this.fiberManager.IsDisposed())
{
return;
}
fiber = fiberManager.Get(fiberId);
if (fiber == null)
{
this.dictionary.Remove(fiberId, out _);
return;
}
if (fiber.IsDisposed)
{
this.dictionary.Remove(fiberId, out _);
return;
}
fiber.Update();
fiber.LateUpdate();
Thread.Sleep(1);
}
}
public void Dispose()
{
foreach (var kv in this.dictionary.ToArray())
{
kv.Value.Join();
}
}
public void Add(int fiberId)
{
Thread thread = new(() => this.Loop(fiberId));
this.dictionary.TryAdd(fiberId, thread);
thread.Start();
}
}
}

View File

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

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7e3e2aed9e0845edb35993d7496f871f
timeCreated: 1687098052

View File

@@ -0,0 +1,27 @@
namespace ET
{
public class IdValueGenerater: Singleton<IdValueGenerater>, ISingletonAwake
{
private uint value;
public uint Value
{
get
{
lock (this)
{
if (++this.value > IdGenerater.Mask20bit - 1)
{
this.value = 0;
}
return this.value;
}
}
}
public void Awake()
{
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d4510ce4f5e843f9a9efc99d1714845a
timeCreated: 1687098066

View File

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

View File

@@ -0,0 +1,17 @@
namespace ET
{
public interface ILog
{
void Trace(string message);
void Warning(string message);
void Info(string message);
void Debug(string message);
void Error(string message);
void Fatal(string message);
void Trace(string message, params object[] args);
void Warning(string message, params object[] args);
void Info(string message, params object[] args);
void Debug(string message, params object[] args);
void Error(string message, params object[] args);
}
}

View File

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

View File

@@ -0,0 +1,90 @@
using System;
using System.Diagnostics;
namespace ET
{
public static class Log
{
[Conditional("DEBUG")]
public static void Trace(string msg)
{
Logger.Instance.Trace(msg);
}
[Conditional("DEBUG")]
public static void Debug(string msg)
{
Logger.Instance.Debug(msg);
}
public static void Info(string msg)
{
Logger.Instance.Info(msg);
}
[Conditional("DEBUG")]
public static void Warning(string msg)
{
Logger.Instance.Warning(msg);
}
public static void Error(string msg)
{
Logger.Instance.Error(msg);
}
public static void Fatal(Exception msg)
{
Logger.Instance.Fatal(msg.Message);
}
public static void Fatal(string msg)
{
Logger.Instance.Fatal(msg);
}
public static void Error(Exception msg)
{
Logger.Instance.Error(msg);
}
[Conditional("DEBUG")]
public static void Console(string msg)
{
Logger.Instance.Console(msg);
}
#if DOTNET
[Conditional("DEBUG")]
public static void Trace(ref System.Runtime.CompilerServices.DefaultInterpolatedStringHandler message)
{
Logger.Instance.Trace(message.ToStringAndClear());
}
[Conditional("DEBUG")]
public static void Warning(ref System.Runtime.CompilerServices.DefaultInterpolatedStringHandler message)
{
Logger.Instance.Warning(message.ToStringAndClear());
}
public static void Info(ref System.Runtime.CompilerServices.DefaultInterpolatedStringHandler message)
{
Logger.Instance.Info(message.ToStringAndClear());
}
[Conditional("DEBUG")]
public static void Debug(ref System.Runtime.CompilerServices.DefaultInterpolatedStringHandler message)
{
Logger.Instance.Debug(message.ToStringAndClear());
}
public static void Error(ref System.Runtime.CompilerServices.DefaultInterpolatedStringHandler message)
{
Logger.Instance.Error(message.ToStringAndClear());
}
[Conditional("DEBUG")]
public static void Console(ref System.Runtime.CompilerServices.DefaultInterpolatedStringHandler message)
{
Logger.Instance.Console(message.ToStringAndClear());
}
#endif
}
}

View File

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

View File

@@ -0,0 +1,294 @@
using System.Diagnostics;
using System.Text;
namespace ET
{
public class Logger : Singleton<Logger>, ISingletonAwake
{
private ILog iLog;
public ILog ILog
{
set { this.iLog = value; }
}
public enum LogLevel
{
INFO,
SUCCESSED,
ASSERT,
WARNING,
ERROR,
EXCEPTION,
}
public void Awake()
{
}
private LogLevel _filterLevel = LogLevel.INFO;
private StringBuilder _stringBuilder = new StringBuilder();
private const int TraceLevel = 1;
private const int DebugLevel = 2;
private const int InfoLevel = 3;
private const int WarningLevel = 4;
private bool CheckLogLevel(int level)
{
if (Options.Instance == null)
{
return true;
}
return Options.Instance.LogLevel <= level;
}
public void Trace(string msg)
{
if (!CheckLogLevel(DebugLevel))
{
return;
}
StackTrace st = new StackTrace(2, true);
string log = $"{msg}\n{st}";
this.Log(LogLevel.INFO, log);
this.iLog.Trace(log);
}
public void Debug(string msg)
{
if (!CheckLogLevel(DebugLevel))
{
return;
}
this.Log(LogLevel.INFO, msg);
this.iLog.Debug(msg);
}
public void Info(string msg)
{
if (!CheckLogLevel(InfoLevel))
{
return;
}
this.Log(LogLevel.INFO, msg);
this.iLog.Info(msg);
}
public void TraceInfo(string msg)
{
if (!CheckLogLevel(InfoLevel))
{
return;
}
StackTrace st = new StackTrace(2, true);
string log = $"{msg}\n{st}";
this.Log(LogLevel.INFO, msg);
this.iLog.Trace(log);
}
public void Warning(string msg)
{
if (!CheckLogLevel(WarningLevel))
{
return;
}
this.Log(LogLevel.WARNING, msg);
this.iLog.Warning(msg);
}
public void Error(string msg)
{
StackTrace st = new StackTrace(2, true);
string log = $"{msg}\n{st}";
this.Log(LogLevel.ERROR, log);
this.iLog.Error(log);
}
public void Error(Exception e)
{
if (e.Data.Contains("StackTrace"))
{
this.iLog.Error($"{e.Data["StackTrace"]}\n{e}");
return;
}
string str = e.ToString();
this.Log(LogLevel.ERROR, str);
this.iLog.Error(str);
}
public void Fatal(string msg)
{
StackTrace st = new StackTrace(2, true);
this.iLog.Fatal($"{msg}\n{st}");
}
public void Trace(string message, params object[] args)
{
if (!CheckLogLevel(TraceLevel))
{
return;
}
StackTrace st = new StackTrace(2, true);
this.iLog.Trace($"{string.Format(message, args)}\n{st}");
}
public void Warning(string message, params object[] args)
{
if (!CheckLogLevel(WarningLevel))
{
return;
}
this.iLog.Warning(string.Format(message, args));
}
public void Info(string message, params object[] args)
{
if (!CheckLogLevel(InfoLevel))
{
return;
}
this.iLog.Info(string.Format(message, args));
}
public void Debug(string message, params object[] args)
{
if (!CheckLogLevel(DebugLevel))
{
return;
}
this.iLog.Debug(string.Format(message, args));
}
public void Error(string message, params object[] args)
{
StackTrace st = new StackTrace(2, true);
string s = string.Format(message, args) + '\n' + st;
this.iLog.Error(s);
}
public void Console(string message)
{
if (Options.Instance.Console == 1)
{
this.Log(LogLevel.SUCCESSED, message);
}
this.iLog.Debug(message);
}
public void Console(string message, params object[] args)
{
string s = string.Format(message, args);
if (Options.Instance.Console == 1)
{
System.Console.WriteLine(s);
}
this.iLog.Debug(s);
}
private StringBuilder GetFormatString(LogLevel logLevel, string logString)
{
_stringBuilder.Clear();
switch (logLevel)
{
case LogLevel.SUCCESSED:
_stringBuilder.AppendFormat(
"[TEngineServer][SUCCESSED][{0}] - {1}",
System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString);
break;
case LogLevel.INFO:
_stringBuilder.AppendFormat(
"[TEngineServer][INFO][{0}] - {1}",
System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString);
break;
case LogLevel.ASSERT:
_stringBuilder.AppendFormat(
"[TEngineServer][ASSERT][{0}] - {1}",
System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString);
break;
case LogLevel.WARNING:
_stringBuilder.AppendFormat(
"[TEngineServer][WARNING][{0}] - {1}", System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"),
logString);
break;
case LogLevel.ERROR:
_stringBuilder.AppendFormat(
"[TEngineServer][ERROR][{0}] - {1}",
System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString);
break;
case LogLevel.EXCEPTION:
_stringBuilder.AppendFormat(
"[TEngineServer][EXCEPTION][{0}] - {1}",
System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString);
break;
}
return _stringBuilder;
}
private void Log(LogLevel type, string logString)
{
if (type < _filterLevel)
{
return;
}
StringBuilder infoBuilder = GetFormatString(type, logString);
if (type == LogLevel.ERROR || type == LogLevel.WARNING || type == LogLevel.EXCEPTION)
{
StackFrame[] sf = new StackTrace().GetFrames();
for (int i = 0; i < sf.Length; i++)
{
StackFrame frame = sf[i];
string declaringTypeName = frame.GetMethod()?.DeclaringType.FullName;
string methodName = frame.GetMethod()?.Name;
infoBuilder.AppendFormat("[{0}::{1}\n", declaringTypeName, methodName);
}
}
string logStr = infoBuilder.ToString();
if (type == LogLevel.INFO)
{
System.Console.ForegroundColor = ConsoleColor.White;
System.Console.WriteLine(logStr);
}
else if (type == LogLevel.SUCCESSED)
{
System.Console.ForegroundColor = ConsoleColor.Green;
System.Console.WriteLine(logStr);
}
else if (type == LogLevel.WARNING)
{
System.Console.ForegroundColor = ConsoleColor.DarkYellow;
System.Console.WriteLine(logStr);
}
else if (type == LogLevel.ASSERT)
{
System.Console.ForegroundColor = ConsoleColor.Red;
System.Console.WriteLine(logStr);
}
else if (type == LogLevel.ERROR)
{
System.Console.ForegroundColor = ConsoleColor.Red;
System.Console.WriteLine(logStr);
}
else if (type == LogLevel.EXCEPTION)
{
System.Console.ForegroundColor = ConsoleColor.Magenta;
System.Console.WriteLine(logStr);
}
}
}
}

View File

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

View File

@@ -0,0 +1,78 @@
using System;
using NLog;
namespace ET
{
public class NLogger: ILog
{
private readonly NLog.Logger logger;
public NLogger(string name, int process, string configPath)
{
LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(configPath);
LogManager.Configuration.Variables["appIdFormat"] = $"{process:000000}";
LogManager.Configuration.Variables["currentDir"] = Environment.CurrentDirectory;
this.logger = LogManager.GetLogger(name);
}
public void Trace(string message)
{
this.logger.Trace(message);
}
public void Warning(string message)
{
this.logger.Warn(message);
}
public void Info(string message)
{
this.logger.Info(message);
}
public void Debug(string message)
{
this.logger.Debug(message);
}
public void Error(string message)
{
this.logger.Error(message);
}
public void Fatal(string message)
{
this.logger.Fatal(message);
}
public void Trace(string message, params object[] args)
{
this.logger.Trace(message, args);
}
public void Warning(string message, params object[] args)
{
this.logger.Warn(message, args);
}
public void Info(string message, params object[] args)
{
this.logger.Info(message, args);
}
public void Debug(string message, params object[] args)
{
this.logger.Debug(message, args);
}
public void Error(string message, params object[] args)
{
this.logger.Error(message, args);
}
public void Fatal(string message, params object[] args)
{
this.logger.Fatal(message, args);
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
namespace ET
{
public class ObjectPool: Singleton<ObjectPool>, ISingletonAwake
{
private Dictionary<Type, Queue<object>> pool;
public void Awake()
{
lock (this)
{
this.pool = new Dictionary<Type, Queue<object>>();
}
}
public T Fetch<T>() where T: class
{
return this.Fetch(typeof (T)) as T;
}
public object Fetch(Type type)
{
lock (this)
{
Queue<object> queue = null;
object o;
if (!pool.TryGetValue(type, out queue))
{
o = Activator.CreateInstance(type);
}
else if (queue.Count == 0)
{
o = Activator.CreateInstance(type);
}
else
{
o = queue.Dequeue();
}
if (o is IPool iPool)
{
iPool.IsFromPool = true;
}
return o;
}
}
public void Recycle(object obj)
{
if (obj is IPool p)
{
if (!p.IsFromPool)
{
return;
}
// 防止多次入池
p.IsFromPool = false;
}
Type type = obj.GetType();
RecycleInner(type, obj);
}
private void RecycleInner(Type type, object obj)
{
lock (this)
{
Queue<object> queue = null;
if (!pool.TryGetValue(type, out queue))
{
queue = new Queue<object>();
pool.Add(type, queue);
}
// 一种对象最大为1000个
if (queue.Count > 1000)
{
return;
}
queue.Enqueue(obj);
}
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,41 @@
using CommandLine;
using System;
using System.Collections.Generic;
namespace ET
{
public enum AppType
{
Server,
Watcher, // 每台物理机一个守护进程,用来启动该物理机上的所有进程
GameTool,
ExcelExporter,
Proto2CS,
BenchmarkClient,
BenchmarkServer,
Demo,
LockStep,
}
public class Options: Singleton<Options>
{
[Option("AppType", Required = false, Default = AppType.Server, HelpText = "AppType enum")]
public AppType AppType { get; set; }
[Option("StartConfig", Required = false, Default = "StartConfig/Localhost")]
public string StartConfig { get; set; }
[Option("Process", Required = false, Default = 1)]
public int Process { get; set; }
[Option("Develop", Required = false, Default = 0, HelpText = "develop mode, 0正式 1开发 2压测")]
public int Develop { get; set; }
[Option("LogLevel", Required = false, Default = 2)]
public int LogLevel { get; set; }
[Option("Console", Required = false, Default = 0)]
public int Console { get; set; }
}
}

View File

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

View File

@@ -0,0 +1,118 @@
using System;
using System.Threading;
namespace ET
{
public interface ISingleton: IDisposable
{
void Register();
}
public abstract class Singleton<T>: ISingleton where T: Singleton<T>, new()
{
protected bool isDisposed;
[StaticField]
private static T instance;
public static T Instance
{
get
{
return instance;
}
private set
{
instance = value;
}
}
public virtual void Register()
{
Instance = (T)this;
}
public bool IsDisposed()
{
return this.isDisposed;
}
protected virtual void Destroy()
{
}
void IDisposable.Dispose()
{
if (this.isDisposed)
{
return;
}
this.isDisposed = true;
this.Destroy();
}
}
public abstract class SingletonLock<T>: ISingleton, ISingletonLoad where T: SingletonLock<T>, new()
{
private bool isDisposed;
[StaticField]
private static T instance;
[StaticField]
private static object lockObj = new();
public static T Instance
{
get
{
lock (lockObj)
{
return instance;
}
}
private set
{
lock (lockObj)
{
instance = value;
}
}
}
public virtual void Register()
{
Instance = (T)this;
}
public bool IsDisposed()
{
return this.isDisposed;
}
protected virtual void Destroy()
{
}
void IDisposable.Dispose()
{
if (this.isDisposed)
{
return;
}
this.isDisposed = true;
Instance = null;
this.Destroy();
}
public abstract void Load();
}
}

View File

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

View File

@@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
namespace ET
{
public class World: IDisposable
{
[StaticField]
private static World instance;
public static World Instance
{
get
{
return instance ??= new World();
}
}
private readonly Stack<Type> stack = new();
private readonly Dictionary<Type, ISingleton> singletons = new();
private World()
{
}
public void Dispose()
{
instance = null;
lock (this)
{
while (this.stack.Count > 0)
{
Type type = this.stack.Pop();
this.singletons[type].Dispose();
}
}
}
public T AddSingleton<T>(bool replace = false) where T : class, ISingleton, ISingletonAwake, new()
{
T singleton = new();
singleton.Awake();
AddSingleton(singleton, replace);
return singleton;
}
public T AddSingleton<T, A>(A a, bool replace = false) where T : class, ISingleton, ISingletonAwake<A>, new()
{
T singleton = new();
singleton.Awake(a);
AddSingleton(singleton, replace);
return singleton;
}
public T AddSingleton<T, A, B>(A a, B b, bool replace = false) where T : class, ISingleton, ISingletonAwake<A, B>, new()
{
T singleton = new();
singleton.Awake(a, b);
AddSingleton(singleton, replace);
return singleton;
}
public T AddSingleton<T, A, B, C>(A a, B b, C c, bool replace = false) where T : class, ISingleton, ISingletonAwake<A, B, C>, new()
{
T singleton = new();
singleton.Awake(a, b, c);
AddSingleton(singleton, replace);
return singleton;
}
public void AddSingleton(ISingleton singleton, bool replace = false)
{
lock (this)
{
Type type = singleton.GetType();
if (!replace)
{
this.stack.Push(type);
}
singletons[type] = singleton;
}
singleton.Register();
}
public void Load()
{
lock (this)
{
foreach (Type type in this.stack)
{
ISingleton singleton = this.singletons[type];
if (singleton is not ISingletonLoad iSingletonLoad)
{
continue;
}
iSingletonLoad.Load();
}
}
}
}
}

View File

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