天天看點

ET篇:master消息機制介紹(Actor消息的流轉)

繼續上一次的一般消息流轉,那麼這次,我們來走一遍ET的另一核心——Actor消息機制

建議先看ETBook的Actor消息機制介紹,位址:

  • https://blog.csdn.net/qq_15020543/article/details/88539225
  • https://blog.csdn.net/qq_15020543/article/details/89219390

繼續上次,我們回到用戶端

// 建立一個ETModel層的Session,并且儲存到ETModel.SessionComponent中
ETModel.Session gateSession = ETModel.Game.Scene.GetComponent<NetOuterComponent>().Create(r2CLogin.Address);
//添加ETModel.SessionComponent元件,并對Session指派
ETModel.Game.Scene.AddComponent<ETModel.SessionComponent>().Session = gateSession;
				
// 建立一個ETHotfix層的Session, 并且儲存到ETHotfix.SessionComponent中,
// 目前為止,所有用戶端的消息的收發都将被GateSession管理
//(Model層和Hotfix層都有各自的GateSession,
// 本質上還是ETModel.Session和ETHotfix.Session)
Game.Scene.AddComponent<SessionComponent>().Session = ComponentFactory.Create<Session, ETModel.Session>(gateSession);
				
//向服務端請求登入進gate伺服器,如果登入成功,此後與服務端的通信都将通過gate伺服器這個中介者
G2C_LoginGate g2CLoginGate = (G2C_LoginGate)await SessionComponent.Instance.Session.Call(new C2G_LoginGate() { Key = r2CLogin.Key });

Log.Info("登陸gate成功!");
           

然後回到服務端

//從已經分發的KEY裡面尋找,如果沒找到,說明非法使用者,不給他連接配接gate伺服器
string account = Game.Scene.GetComponent<GateSessionKeyComponent>().Get(message.Key);
if (account == null)
{
	response.Error = ErrorCode.ERR_ConnectGateKeyError;
	response.Message = "Gate key驗證失敗!";
	reply(response);
	return;
}
//專門給這個玩家建立一個Player對象
Player player = ComponentFactory.Create<Player, string>(account);
//注冊到PlayerComponent,友善管理
Game.Scene.GetComponent<PlayerComponent>().Add(player);

//給這個session安排上Player
session.AddComponent<SessionPlayerComponent>().Player = player;
//添加郵箱元件表示該session是一個Actor,接收的消息将會隊列處理
session.AddComponent<MailBoxComponent, string>(MailboxType.GateSession);

response.PlayerId = player.Id;
				
//回複用戶端的連接配接gate伺服器請求
reply(response);

//向用戶端發送熱更層資訊
session.Send(new G2C_TestHotfixMessage() { Info = "recv hotfix message success" });
           

再回到用戶端

Log.Info("登陸gate成功!");

// 建立Player
Player player = ETModel.ComponentFactory.CreateWithId<Player>(g2CLoginGate.PlayerId);
PlayerComponent playerComponent = ETModel.Game.Scene.GetComponent<PlayerComponent>();
playerComponent.MyPlayer = player;

//分發登入完成的事件
Game.EventSystem.Run(EventIdType.LoginFinish);

// 測試消息有成員是class類型
G2C_PlayerInfo g2CPlayerInfo = (G2C_PlayerInfo) await SessionComponent.Instance.Session.Call(new C2G_PlayerInfo());
Debug.Log("測試玩家資訊為" + g2CPlayerInfo.Message);
           

這下我們不用再回到服務端了,因為一般消息的流轉我們了解的已經差不多了,我們直接來到下一個重要的階段

來到MapHelper.cs類,檢視登入Map伺服器相關代碼

// 擷取資源元件
ResourcesComponent resourcesComponent = ETModel.Game.Scene.GetComponent<ResourcesComponent>();
// 加載Unit資源
await resourcesComponent.LoadBundleAsync($"unit.unity3d");

// 加載場景資源
await ETModel.Game.Scene.GetComponent<ResourcesComponent>().LoadBundleAsync("map.unity3d");
// 切換到map場景
using (SceneChangeComponent sceneChangeComponent = ETModel.Game.Scene.AddComponent<SceneChangeComponent>())
{
     await sceneChangeComponent.ChangeSceneAsync(SceneType.Map);
}
				
//請求登入Map伺服器
G2C_EnterMap g2CEnterMap = await ETModel.SessionComponent.Instance.Session.Call(new C2G_EnterMap()) as G2C_EnterMap;
           

好了,我們又要去服務端了,注意,下面的消息是屬于伺服器内部的消息流通,是以不用網絡層的傳輸,直接進行内部資料傳輸最後被響應Handler處理

//擷取Player對象引用
Player player = session.GetComponent<SessionPlayerComponent>().Player;
// 在map伺服器上建立戰鬥Unit
IPEndPoint mapAddress = StartConfigComponent.Instance.MapConfigs[0].GetComponent<InnerConfig>().IPEndPoint;
Session mapSession = Game.Scene.GetComponent<NetInnerComponent>().Get(mapAddress);
                
//由gate伺服器向map伺服器發送建立戰鬥機關請求,這裡的session.InstanceId将由IdGenerater建立,
//用以保證不會沖突
M2G_CreateUnit createUnit =
(M2G_CreateUnit) await mapSession.Call(new G2M_CreateUnit() { PlayerId = player.Id, GateSessionId = session.InstanceId });
           

那我們直接到它的Handler這裡吧

//建立戰鬥機關(小骷髅給勁哦)
Unit unit = ComponentFactory.CreateWithId<Unit>(IdGenerater.GenerateId());
//增加移動元件
unit.AddComponent<MoveComponent>();
//增加尋路相關元件
unit.AddComponent<UnitPathComponent>();
//設定小骷髅位置
unit.Position = new Vector3(-10, 0, -10);
				
//給小骷髅添加信箱元件,隊列處理收到的消息
await unit.AddComponent<MailBoxComponent>().AddLocation();
//添加同gate伺服器通信基礎元件,主要是賦予ID
unit.AddComponent<UnitGateComponent, long>(message.GateSessionId);
//将這個小骷髅維護在Unit元件裡
Game.Scene.GetComponent<UnitComponent>().Add(unit);
//設定回複消息的ID
response.UnitId = unit.Id;
				
// 廣播建立的unit
M2C_CreateUnits createUnits = new M2C_CreateUnits();
Unit[] units = Game.Scene.GetComponent<UnitComponent>().GetAll();
foreach (Unit u in units)
{
	UnitInfo unitInfo = new UnitInfo();
	unitInfo.X = u.Position.x;
	unitInfo.Y = u.Position.y;
	unitInfo.Z = u.Position.z;
	unitInfo.UnitId = u.Id;
	createUnits.Units.Add(unitInfo);
}
//廣播所有小骷髅資訊
MessageHelper.Broadcast(createUnits); 
				
//廣播完回複用戶端,這邊搞好了
reply(response);
           

 注意,這裡面的MessageHelper.Broadcast(createUnits); 就是我們的核心Actor機制的展現了,我們隻要把消息發到gatesession,gatesession将會自動根據id轉發消息到相應用戶端,而裡面涉及到的相關ID,我們在由gate伺服器向map伺服器發送建立戰鬥機關請求的時候,已經傳好參數了

// 從Game.Scene上擷取ActorSenderComponent,然後通過InstanceId擷取ActorMessageSender
ActorSenderComponent actorSenderComponent = Game.Scene.GetComponent<ActorSenderComponent>();
ActorMessageSender actorMessageSender = actorSenderComponent.Get(unitGateComponent.GateSessionActorId);
// send
actorMessageSender.Send(message);

// rpc
var response = actorMessageSender.Call(message);
           

好了,回到用戶端

//設定UnitID
PlayerComponent.Instance.MyPlayer.UnitId = g2CEnterMap.UnitId;
				
//增加。。。emmm不知道怎麼翻譯這個元件好,他負責點選地面控制小骷髅移動
Game.Scene.AddComponent<OperaComponent>();
				
//分發進入正式遊戲成功事件
Game.EventSystem.Run(EventIdType.EnterMapFinish);
           

然後我們突然想起來,剛剛亡靈領主出生的時候向别的小骷髅廣播了一下來着,我們看會做什麼操作

foreach (UnitInfo unitInfo in message.Units)
{
	if (unitComponent.Get(unitInfo.UnitId) != null)
	{
		continue;
	}
	//根據不同ID,建立小骷髅
	Unit unit = UnitFactory.Create(unitInfo.UnitId);
	unit.Position = new Vector3(unitInfo.X, unitInfo.Y, unitInfo.Z);
}
           

好了 ,小骷髅都安排好了,該向服務端發消息了,也就是尋路,還記得我們之前添加的OperaComponent,他就是負責尋路的

//發送點選地圖消息
ETModel.SessionComponent.Instance.Session.Send(frameClickMap);
 |
 |
 |
\|/
//服務端Frame_ClickMapHandler處理位置資訊
Vector3 target = new Vector3(message.X, message.Y, message.Z);
unit.GetComponent<UnitPathComponent>().MoveTo(target).Coroutine();
 |
 |
 |
\|/
//移動到指定位置
await self.MoveAsync(self.ABPath.Result);
 |
 |
 |
\|/
// 每移動3個點發送下3個點給用戶端
if (i % 3 == 1)
{
     self.BroadcastPath(path, i, 3);
}
 |
 |
 |
\|/
// 用戶端處理服務端的同步資訊,利用尋路元件進行尋路
unitPathComponent.StartMove(message).Coroutine();
           

後記

至此,連接配接伺服器,建立小骷髅,并且自動尋路的操作就完成了,我中間很多細節沒有講,其實沒必要講(觀衆:懶還有理了???),那些東西需要大家自行了解和體會的,而到現在master的Demo解讀也該告一段落了。