ET服务器框架——部署与运维笔记(一)_et框架服务端部署-CSDN博客
环境

消息对象池

链接
Bug - ET社区 (et-framework.cn)
小弟的ET笔记 (yuque.com)
e-mano 架构 et架构_mob6454cc64c0a4的技术博客_51CTO博客https://blog.51cto.com/u_16099188/6448617
流程记录
初始化服务器
ET的核心方法:CodeLoader
服务器的App会生成在Bin目录下
首先,找到当前程序集的Model程序集加载。
然后,去Bin目录下生成的HotFix.dll文件加载程序集。
然后通过反射调用静态方法:Entry.StartAsync().Coroutine()
[MongoHelper].Register,注册可序列化的结构体
(LSInput:MemoryPackable;Unity.Mathematics.float2;TrueSync.FP:Serializable)
通过CodeTypes中缓存的对象类型,[BsonClassMap].LookupClassMap处理每一个类型。
添加单例,Awake单例:Id,网络协议,对象池,消息队列,网络服务,Nav,Log
Fiber.Init-> 服务端客户端跑了同一份逻辑
FiberInit_Main
Fiber

老版本:通讯序列化IO消耗等

新版本:共享进程的内存,不需要传递消息,并且Fiber带来了线程不会竞争内存的线程安全






线程池性能更好;固定线程调试更方便
Scene

玩法结构设计

事件代码写法
1.在AllModel目录中定义事件。
服务器用的放在Server中,客户端用的放在Client中,共享的放在Share中。目前写客户端先都放Client。
增加脚本:xxxEventType。然后在里面定义结构体。
2.在AllHotfix目录中编写事件。
目录同上,写法很正常。PulishAsync发送,这里面就能接收到了。
登录7.0

Actor模型7.0
客户端结构

机器人客户端(伪客户端)

登录流程

ET EntitySystem
EntitySystemSingleton的Awake中通过反射注册所有的System类型。
首先找到,带[EntitySystem]标签的Class,其中大部分是生成的代码。
如果它继承自 IAwake, IDestroy, ILoad, ...等或自定义的ISystemType,
就会使用TypeSystems去创建或获取:OneTypeSystems,对应Max的QueueFlag,对应InstanceQueueIndex:None-1,Update0,LateUpdate1,Load2,...
然后把Entity类型和创建的System对象完成一对多的映射。然后如果这个System对象的QueueFlag不是带更新的,就是默认是None,那么就不设置QueueFlag。
Entity在被创建后,IsRegisted或者设置IScene,会调用到RegisterSystem。
如果Entity没有任何System,会返回。否则会记录。
如,第一个TimerCom,有IAwake和IUpdate。
在EntitySystem的RegisterSystem中,会完成,带QueueFlag的Update类型的增添。
在后面的Update中,它通过双向队列,完成对Disposed的Entity的移除。其他的都会保留在队列里被不断Update。
YIUI
插件:UIEffect
Loop列表制作
1.制作一个单独的页面,是Loop。

Publish
Win
1.安装.net环境
cmd: dotnet --info 弹出来说明装好了
2.打开ET工程,编译后发布


2.1ps发布:
双击PowerShell文件,生成一系列的Publish内容。包含.exe。全部拷贝到服务器电脑运行exe即可。
3.客户端连接配置:ConstValue修改地址为服务器外网地址
表格修改:
StartMachineConfig修改。有几个服务器,就部署几个,如果就只有一个,那就只填写一个。


Release
Win
说明1:编辑器下模拟打包环境:Define.IsEditor设置为false。
说明2:YIUI的资源名称是唯一的,因此要改一下熊猫的资源加载路径。
打包全流程,及作用。
-
补充元数据:Odin补充元数据,塞到HybridCLR的配置元数据列表里。这样会把dll自动拷贝到Bundle的aotdll目录。
-
重点!!添加宏定义:ENABLE_IL2CPP,它会在运行时加载到元数据补充。
-
右键编译AllHotfix程序集,这是为了能在Debug目录下生成需要打进资源包的Hotfix.dll和Model.dll。
路径:.\MTDET\Unity\Temp\Bin\Debug\Unity.AllHotfix\Unity.AllHotfix.dll
同时会在生成后把文件拷贝到Bundles/Code路径,是在AllHotfix.csproj中设置的:
HybridCLR点击。编译,生成所有,拷贝AOT。这个工具主要是为了在IL2CPP模式下的代码热更新。IL2CPP模式下应该可以适应苹果的代码热更新。点击编译是为了指定平台,点击生成所有是为了生成对应版本的所有代码变成代码资源,点击拷贝AOT是把元数据补充一下否则在上一个步骤可能会因为代码裁剪丢失一部分代码库的引用。
YooAssetBuilder点击构建:会把Bundle目录下面的东西搞到StreamingAssets目录下?(这是一个只读、不可写的目录;该文件夹下的资源会保持原始格式,dll或脚本放在里面也不会参与编译)
点击BuildPackage:就和点击Unity的打包其实是一样的。
ET8消息
使用Actor模型进行消息发送和接收。原理看字母哥的ET8网络教程。
使用说明:
1.Actor模型必须有一个发送者的ActorID和接收者的ActorID。
客户端的发送者ActorID只有一个,就是netClientActorId,它在登录的时候创建,后面就用这个Id发消息。
netClientActorId=new ActorId(self.Fiber().Process, self.fiberId);//主线程Process:1 netClient.FiberID:100001 instanceID:1
2.发送和接收的方式
1.给指定Scene发消息。发送端需要拿到:StartSceneConfig(Scene).ActorID,用MessageSender.Call发消息。
接收端使用MessageHandler<Scene,req,res>接收。
2.用Session发消息。发送端首先需要通过CreateRouterSession创建好连接,用session.Call发消息。
接收端使用MessageSessionHandler<req,res>接收。
3.说明
熊猫做了好多封装。见NetComponentOnReadInvoker_Gate
Message在Proto定义的时候,使用了不同的接口,不同的接口处理有不同的方式。
3.数据结构自定义:从现在开始用到一个加一个并进行记录分析。
MUnit数据保存组件,需要消息:Other2UnitCache_AddOrUpdateUnit
1.先在 IdGenerater 中创建 UnitIdStruct。
2.在StartSceneConfig扩展UnitID相关内容。
3.创建UnitCacheFiberInit相关。
4.熊猫的改动

5.发内部消息(InnerMessage)是服务器给服务器发。消息:IRequest&IResponse
发送方:root.GetComponent<MessageSender>().Call(ActorID, request);
它是用自己Root的Scene,给另一个Actor发消息。另一个Actor很可能是另一个Scene。
例:传送:传送的Unit的Root,是Gate。从Gate传送到Map,先在Gate上创建了一个Unit(可以从数据库加载数据但是创建还是设置Root是Gate)。传送本质:找到对应的Map(Scene)的ActorID,
创建发送的消息,把Unit变成消息的字节,销毁老Unit。
发消息之前先上锁,锁住Unit,传参Unit.Id和Unit.ActorId。
然后:root.GetComponent<MessageSender>().Call(目标ActorId,message)。
接收方:使用MessageHandler<Scene,Req,Res>接受。
总结:Scene给Scene发送,用MessageSender.Call就可以了。
6.Map给Client发消息:
unit.Root().GetComponent<MessageLocationSenderComponent>().Get(LocationType.GateSession).Send(unit.Id,msg)
msg就是IMessage,不是IRequest&IResponse,它是单方向发送。
接受端也是用Scene接收。MessageHandler<Scene,msg>
7.定位服务器消息

定位服务器,这种写法,是不是这个意思:我有一个ID,我知道这个ID注册过在哪个Location里,我用这个ID在Location中就能找到对应的玩家连接的Gate,然后告诉这个Gate把我的消息发送给客户端?
8.ET8消息记录
一共三种消息。
第一种:ISessionMessage / ISessionRequest&ISessionResponse
这种是Session消息。Session消息是客户端连接到对应的服务器IP后有了一个Session。
客户端发送:
(R2C_Login)await session.Call(new C2R_Login(){})
(G2C_LoginGate)await gateSession.Call(new C2G_LoginGate(){}))
服务端接收:
[MessageSessionHandler(SceneType.Realm)]
public class C2R_LoginHandler : MessageSessionHandler<C2R_Login, R2C_Login>
[MessageSessionHandler(SceneType.Gate)]
public class C2G_LoginGateHandler : MessageSessionHandler<C2G_LoginGate, G2C_LoginGate>
第二种:IMessage / IRequest&IResponse
这种是最简单消息。
发送方可以是任何发送方。
接收方使用MessageHandler<Scene接收。
例:M2C_CreateUnits : IMessage,是一条最简单的消息。
Map给客户端发送消息,使用root.<MessageLocationSenderComponent>().Get(LocationType.GateSession)发送。
第三种:ILocationMessage。
9.自己重写消息类型分发处理。参考NetComponentOnReadInvoker_Gate和NetComponentOnReadInvoker_Realm,Gate上的Session消息有一种处理,Realm上的可以有另一种处理。
消息类型1:Session消息流程。
- NetComponent连接指定IP,创建Session。远端就是那个IP所在Scene。
- 使用Session.Call/Send发送消息,远端Scene会接收到。
接收流程:NetComponentSystem.OnRead->NetComponentOnReadInvoker_Realm
这里面根据不同消息的接口类型,进行消息分发。
10.服务器内部发消息:统一使用<MessageSender>.Call/Send 发送。如果是<MessageLocationSender>,最终也会回到这里。
消息会转移到<ProcessInnerSender>.Call,不管是内部发送还是外部发送。
最终会有两条路:FiberId和目标相同时:<ProcessInnerSender>.HandleMessage -> <ProcessInnerSender>直接从当前纤程里找邮箱,让邮箱做处理
FiberId和目标不同时:加入到消息队列,等待Update分发处理,最终也是找到邮箱邮箱处理。
邮箱种类:有序,无序,GateSession。
无序邮箱:MessageDispatcher.Instance.Handle -> 直接到对应的反射方法。
GateSession:邮箱的父亲必须是playerSession,用这个session.Send,一定发给客户端。
服务器<->客户端发消息:使用Session可以来回发。Location消息最终也会变成Session来发消息。


