之前Server的主逻辑循环中
除了异步处理,还有一个EventSystem.Update()
因为异步处理搜索引用后,只发现网络逻辑中有使用,因此,可以猜测Game.EventSystem
就囊括了所有游戏的逻辑了
while (true)
{
try
{
Thread.Sleep(1);
OneThreadSynchronizationContext.Instance.Update(); // 处理异步回调
Game.EventSystem.Update();
}
catch (Exception e)
{
Log.Error(e);
}
}
Main方法中try{}
中一开始,就发现这两句:
Game.EventSystem.Add(DLLType.Model, typeof(Game).Assembly);
Game.EventSystem.Add(DLLType.Hotfix, DllHelper.GetHotfixAssembly());
出现Assembly,表示这个EventSystem很可能通过反射来调用接口。
看看Add 方法做了什么?
/// <summary>
/// 添加一个程序集
/// 每调用一次,所有表都会重置一次,并且load系统会执行一次
/// </summary>
/// <param name="dllType"></param>
/// <param name="assembly"></param>
public void Add(DLLType dllType, Assembly assembly)
{
// 这个表在运行过程中只覆盖,不重置。其他所有表或队列将被重置掉
this.assemblies[dllType] = assembly;
// types实际上是个Dict<AttributeType, List<ClassType>>的结构
// AttributeType应用了特性(Attribute),向程序类添加声明性信息
// 在类、方法等的声明上一行用中括号[]引用
// 继承 Attribute 的子类名字如果以 “Attribute” 结尾,则声明时是可以简化这个结尾的
// 这里将 “继承自 BaseAttribute 的 Attribute” 来声明的类都找到。
// 按照 AttributeType 来分类保存在types中
this.types.Clear();
foreach (Assembly value in this.assemblies.Values)
{
foreach (Type type in value.GetTypes())
{
if (type.IsAbstract)
{
continue;
}
object[] objects = type.GetCustomAttributes(typeof(BaseAttribute), true);
if (objects.Length == 0)
{
continue;
}
BaseAttribute baseAttribute = (BaseAttribute) objects[0];
this.types.Add(baseAttribute.AttributeType, type);
}
}
// 一堆System,主力逻辑,最重要的system是update
// 这里的system表结构是Dict<ComponentType, List<ISystem>>
this.awakeSystems.Clear();
this.lateUpdateSystems.Clear();
this.updateSystems.Clear();
this.startSystems.Clear();
this.loadSystems.Clear();
this.changeSystems.Clear();
this.destroySystems.Clear();
this.deserializeSystems.Clear();
// 把所有特性是 ObjectSystem 的类实例化,并将实例对象放入对应的表中
// 不同的接口放到不同的系统表中,系统表中按照处理的组件类型来分组
foreach (Type type in this.GetTypes(typeof(ObjectSystemAttribute)))
{
object obj = Activator.CreateInstance(type); // 实例化
switch (obj)
{
case IAwakeSystem objectSystem:
this.awakeSystems.Add(objectSystem.Type(), objectSystem);
break;
case IUpdateSystem updateSystem:
this.updateSystems.Add(updateSystem.Type(), updateSystem);
break;
case ILateUpdateSystem lateUpdateSystem:
this.lateUpdateSystems.Add(lateUpdateSystem.Type(), lateUpdateSystem);
break;
case IStartSystem startSystem:
this.startSystems.Add(startSystem.Type(), startSystem);
break;
case IDestroySystem destroySystem:
this.destroySystems.Add(destroySystem.Type(), destroySystem);
break;
case ILoadSystem loadSystem:
this.loadSystems.Add(loadSystem.Type(), loadSystem);
break;
case IChangeSystem changeSystem:
this.changeSystems.Add(changeSystem.Type(), changeSystem);
break;
case IDeserializeSystem deserializeSystem:
this.deserializeSystems.Add(deserializeSystem.Type(), deserializeSystem);
break;
}
}
// 事件分发器表,所有声明了EventAttribute的类都是监听器
// 监听的事件类型在声明EventAttribute时候定义,例如:
// ...../ET/Unity/Assets/Model/Module/Numeric/NumericChangeEvent_NotifyWatcher.cs (4, 3): [Event(EventIdType.NumbericChange)]
// EventIdType文件中定义了各种事件,注意,server 和 client 有各自的EventIdType文件
// EventSystem 的 Run 方法就是用来通知Watcher有事件触发
this.allEvents.Clear();
if (this.types.ContainsKey(typeof (EventAttribute)))
{
foreach (Type type in types[typeof(EventAttribute)])
{
object[] attrs = type.GetCustomAttributes(typeof(EventAttribute), false);
foreach (object attr in attrs)
{
EventAttribute aEventAttribute = (EventAttribute)attr;
object obj = Activator.CreateInstance(type);
IEvent iEvent = obj as IEvent;
if (iEvent == null)
{
Log.Error($"{obj.GetType().Name} 没有继承IEvent");
}
// 注册事件监听器,按事件类型分
this.RegisterEvent(aEventAttribute.Type, iEvent);
}
}
}
// 所有系统实例化完成,现在跑一次loadSystem逻辑
this.Load();
}
再看看Update逻辑,
Update的逻辑中用了双队列交替遍历,可以在一次循环中遍历所有新增的update组件。
/// <summary>
/// EventSystem 的 Update 逻辑
/// 注意写法有可能导致的问题
/// </summary>
public void Update()
{
// update前先start
// 但start的执行并不一定在update前
this.Start();
// 这里执行到所有update运行过为止
// 死循环可能:假设update中不断增加一个新的update组件
// 执行逻辑错误可能: 假设某个update中注册一个新的Entity,其带有一个update组件和一个start组件
// 那么这个Entity的 update 会比 start 先执行
// 最简单的解决方法是:
//int count = this.updates.Count;
while (this.updates.Count > 0)
{
// 从队列中获取一个实体ID
// 如果这个实体已经被删除,就不会再添加回去队列中
// 从而清理掉已经删除的实体
long instanceId = this.updates.Dequeue();
// 死循环和执行逻辑错误的解决方法:
// 如果计数器到了,直接把instanceId添加回队列中
//if (count <= 0)
//{
// this.updates2.Enqueue(instanceId);
// continue;
//}
//count--;
Entity component;
if (!this.allComponents.TryGetValue(instanceId, out component))
{
continue;
}
if (component.IsDisposed)
{
continue;
}
List<IUpdateSystem> iUpdateSystems = this.updateSystems[component.GetType()];
if (iUpdateSystems == null)
{
continue;
}
this.updates2.Enqueue(instanceId);
foreach (IUpdateSystem iUpdateSystem in iUpdateSystems)
{
try
{
// 执行update
iUpdateSystem.Run(component);
}
catch (Exception e)
{
Log.Error(e);
}
}
}
ObjectHelper.Swap(ref this.updates, ref this.updates2);
}
其他系统遍历都有类似问题