ET服务器框架学习笔记(二)

ET-EventSystem

上篇记录了对基础类的理解,这片回到program.cs文件



前言

program主要用于初始化game.scene里面的内容,首先最重要的就是事件系统。


一、EventSystem?

EventSystem用于事件通知,事件回调,走的消息监听,消息派发,消息回调的方式,是一种有效解耦各种业务逻辑代码的编程方式。

二、EventSystem详解

1.UnOrderMultiMap

要想完全理解EventSystem,先要弄清楚ET里面的UnOrderMultiMap,这是个数据结构辅助类。专门用于管理某个类型对应的List。当然这个类,还带有重用功能,用于提升性能。

  • Dictionary<T, List> dictionary,用于存放一个类型,对应另外一个类型的List字典
  • Add(T t, K k):往T这个类型对应的实例t,所代表的List中增加一个实例k。
  • First():返回字典里面第一个KeyValuePair类型的实例
  • FetchList():queue里面放着回收的list,这个方法可以获得一个List,可能是回收的,或者新实例化的。
  • RecycleList(List list):把一个list回收到quene中,当然这里做了优化处理,当超过100条当时候,就不回收了,以免白白浪费内存
  • Remove(T t, K k):与ADD对应,从t实例对应的List中移除某个项,这里有判定如果对应的List空了,就进行回收,并从字典里面移除
  • Remove(T t):将整个t对应的list给回收掉
  • K[] GetAll(T t):获取t对应的整个List,需要注意这里使用List.ToArray()来返回一个新的List在这里插入图片描述
  • List this[T t]:通过这个属性可以获得内部真实的一个List
  • K GetOne(T t):获取t对应的List里面第一个元素
  • Contains(T t, K k):对应List是否包含某个实例
  • ContainsKey(T t):查询字典里面是不是包含有这个t作为key
  • Clear():清理所有字典里面的List,同时将List回收

2.interface IEvent

事件相关接口,用于定义事件处理,一个事件监听一个IEvent,AEvent,class AEvent。具体需要具体实现

public interface IEvent
	{
		void Handle();
		void Handle(object a);
		void Handle(object a, object b);
		void Handle(object a, object b, object c);
	}

3.回到EventSystem

了解类上面的基础数据结构之后回到EventSystem.

定义几个DLLType枚举类型。

  • assemblies:字典类,定义上面的枚举对应的真实的程序集信息
  • Dictionary<long, Component> allComponents:以组件的id为key,存放所有事件系统的组件
  • UnOrderMultiMap<Type, Type> types::类型列表,存放各种类型对应的类型列表
  • Dictionary<string, List> allEvents:所有事件以及事件对应的IEVENT列表。即每个事件可以有N个监听,对应N个处理。
  • UnOrderMultiMap<Type, IAwakeSystem> awakeSystems:用于将所有带有awakeSystem类的awakeSystem放进这里进行统一管理,每个类型对应一个List<**System>
  • startSystems,destroySystems,loadSystems,updateSystems,lateUpdateSystems,changeSystems,deserializeSystems如上一样,只是触发的事件时机不同

4.EventSystem重要方法

  • Add(DLLType dllType, Assembly assembly)
    首先添加枚举以及对应的程序集到assemblies中。
  1. 遍历assemblies中所有程序集,再从每个程序集中遍历每个类型。
  2. 获取每个类型的BaseAttribute特性,包括由他派生出来的特性。这样筛选出对应类型信息出来
  3. 由于一个类可能标记类几个类型信息,然后ET使用的都是标记在类名前的第一个类型信息,当做筛选条件
  4. 获取到第一个特性后,根据第一个特性的特性类,将类型信息存放到types里面
  5. 由于types是上面说过的UnOrderMultiMap<Type, Type>类型, 这样每个特性类型,都可以通过内置的各种方法找到对应的该特性类型信息的,所有类型List集合。、
  6. 举例:类型A,B,C都标记了一个awakeBaseAttribute,那么ABC三个类都类型信息就都存放在 types[awakeBaseAttribute]对应的一个List里面了
  7. 清空各个事件系统对应的map,方便后面根据types重新反射获取
			this.awakeSystems.Clear();
			this.lateUpdateSystems.Clear();
			this.updateSystems.Clear();
			this.startSystems.Clear();
			this.loadSystems.Clear();
			this.changeSystems.Clear();
			this.destroySystems.Clear();
			this.deserializeSystems.Clear();
  1. 从types里面特性,从中取出ObjectSystemAttribute特性对应的类的List信息(这里用到了上面的this属性方法),然后遍历获取类型。
  2. 针对每个类型,获取特性,通过反射实例化,然后再根据实例的具体类型,将他们加到不同的eventSystem中。
  3. 举例:类A标记了一个ObjectSystem,然后类A是继承于AwakeSystem,那么类A被实例化出来,然后被放到awakeSystems[component的type信息]对应的List中
    这里重中之重要理解的是,所有system类已经实例化完毕,并且以他关联的组件类型为key,放入到一个列表中,然后在增加组件时,会根据组件的类型,找到对应的各个system来调用相对应的方法。
foreach (Type type in types[typeof(ObjectSystemAttribute)])
			{
				object[] attrs = type.GetCustomAttributes(typeof(ObjectSystemAttribute), false);

				if (attrs.Length == 0)
				{
					continue;
				}

				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;
				}
			}

11.然后同样的,筛选出所有EventAttribute特性标记的类,实例化出来,这些类都继承或者实现类IEvent等,然后将特性打进去的事件字符串当做key,添加到对应到事件列表中

// 分发数值监听
	[Event(EventIdType.NumbericChange)]
	public class NumericChangeEvent_NotifyWatcher: AEvent<long, NumericType, int>
	{
		public override void Run(long id, NumericType numericType, int value)
		{
			Game.Scene.GetComponent<NumericWatcherComponent>().Run(numericType, id, value);
		}
	}

上面的事件实例添加到了allEvents[EventIdType.NumbericChange]对应的List中,这样一旦派发EventIdType.NumbericChange事件,就会调用对应类型实例的Run方法里面去


5.EventSystem的Add

Game.EventSystem.Add(DLLType.Model, typeof(Game).Assembly);

通过将当前Game所在的程序集(ETModel),添加到事件系统中

Game.EventSystem.Add(DLLType.Hotfix, DllHelper.GetHotfixAssembly());

GetHotfixAssembly()方法内部通过Assembly.Load(dllBytes, pdbBytes);方式加载热更程序集,获取到程序集,并添加到事件系统中
通过上面两句调用,将下面两个程序集内的所有相关事件system进行实例化,并放入到各个事件UnOrderMultiMap中

在这里插入图片描述
监听当component通过public void Add(Component component)方法加入到事件系统中时,添加到各个队列中,然后在合适时机(比如update,在每个update中调用),调用与componnet对应的system中的对应方法

public void Add(Component component)
		{
			this.allComponents.Add(component.InstanceId, component);

			Type type = component.GetType();

			if (this.loadSystems.ContainsKey(type))
			{
				this.loaders.Enqueue(component.InstanceId);
			}

			if (this.updateSystems.ContainsKey(type))
			{
				this.updates.Enqueue(component.InstanceId);
			}

			if (this.startSystems.ContainsKey(type))
			{
				this.starts.Enqueue(component.InstanceId);
			}

			if (this.lateUpdateSystems.ContainsKey(type))
			{
				this.lateUpdates.Enqueue(component.InstanceId);
			}
		}

总结

这里先记录到这,下篇继续。回顾一下,通过遍历程序集类,获取特性标记与类型的类型信息,存到types里面,然后再遍历这个typs通过特性,以及类型信息,反射实例化,放到不同的map或者字典里面处理。

总体梳理一下现在碰到的几个特性对应的意义:

  • ObjectSystemAttribute:组件的System,比如Update,Load,Awake等,如下:
[ObjectSystem]
	public class ActorMessageDispatcherComponentStartSystem: AwakeSystem<ActorMessageDispatcherComponent>
	{
		public override void Awake(ActorMessageDispatcherComponent self)
		{
			self.Awake();
		}
	}

这个system就是继承AwakeSystem,并且关联ActorMessageDispatcherComponent组件,现在注册好这个system后,当ActorMessageDispatcherComponent添加到EventSystem后,会调用到public override void Awake(ActorMessageDispatcherComponent self)这个函数,进一步调用到ActorMessageDispatcherComponent本身的Awake函数,其他system类似如此。

  • EventAttribute:事件处理监听,当有对应事件派发时,会走到对应这个类的处理上
// 分发数值监听
	[Event(EventIdType.NumbericChange)]
	public class NumericChangeEvent_NotifyWatcher: AEvent<long, NumericType, int>
	{
		public override void Run(long id, NumericType numericType, int value)
		{
			Game.Scene.GetComponent<NumericWatcherComponent>().Run(numericType, id, value);
		}
	}
}

当有调用时,会调用到这个类的处理上

Game.EventSystem.Run(EventIdType.NumbericChange, this.Entity.Id, (NumericType) final, result);
public void Run<A, B, C>(string type, A a, B b, C c)
		{
			List<IEvent> iEvents;
			if (!this.allEvents.TryGetValue(type, out iEvents))
			{
				return;
			}
			foreach (IEvent iEvent in iEvents)
			{
				try
				{
					iEvent?.Handle(a, b, c);
				}
				catch (Exception e)
				{
					Log.Error(e);
				}
			}
		}

在这里插入图片描述
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值