任务29:【重要】房间内玩家都确认准备游戏和另一种方式使用Actor消息
在开始本节前,先抛出两个问题:
问题一、之前我们使用过发送ActorMessage,是构建ActorMessageSender来发送的,那能不能用session.Send来发送ActorMessage?
可以,本节就要使用客户端与网关的连接session向网关发ActorMessage。
但这种方式发送的ActorMessage,消息到网关后还是会在OuterMessageDispatcher中用网关上的user.ActorID构建ActorMessageSender向Map上的gamer发送消息的,这才真正的消息目标。user.ActorID是什么?退出房间功能与网关user的ActorID
查看\Server\Hotfix\Module\Message\OuterMessageDispatcher.cs 不难理解这个过程。
问题二、如果用session发ActorMessage消息,是不是只能向网关发送?
是的,使用ActorMessage的原因是可以不用考虑玩家的gamer在哪个地图向gamer发送消息。而网关上有这个玩家当前地图中的gamer的InstanceId,可以去回顾网关上的user.ActorID是什么?退出房间功能与网关user的ActorID
所以现在我们已经学会了两种方法使用Actor:
- 1、在服务器之间,实体挂MailBox,用实体InstanceId构建ActorMessageSender,在网关与其它服务之间通信。
- 2、在客户端通过与网关的连接session,发送ActorMessage,用网关上保存的实体InstanceId构建ActorMessageSender,向目标服务器实体实例通信(比如Map上的gamer)。
前端房间内玩家确认准备游戏
LandRoomComponent \Assets\Model\Landlords\LandUI\LandRoom\LandRoomComponent.cs
GameObject readyButton = rc.Get<GameObject>("Ready");
private void OnReady() { //发送准备游戏的Actor_GamerReady_Landlords消息 //由客户端与网关的连接session发送,再转到Map服务 SessionComponent.Instance.Session.Send(new Actor_GamerReady_Landlords()); }
Actor_GamerReady_Landlords消息体是前后端请求时共用的,现在是客户端向Map发送Actor_GamerReady_Landlords消息。
Map服务端GamerReady_Landlords_Handler
服务端添加GamerReady_Landlords_Handler
这里是Map向客户端广播发送Actor_GamerReady_Landlords消息。
\Server\Hotfix\Landlords\Handler\Map\GamerReady_Landlords_Handler.cs
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using ETModel; namespace ETHotfix { [ActorMessageHandler(AppType.Map)] public class GamerReady_Landlords_Handler : AMActorHandler<Gamer, Actor_GamerReady_Landlords> { protected override async ETTask Run(Gamer gamer, Actor_GamerReady_Landlords message) { LandMatchComponent landordsMatchComponent = Game.Scene.GetComponent<LandMatchComponent>(); //请求的gamer目前所在等待中的房间 Room room = landordsMatchComponent.GetWaitingRoom(gamer); if(room != null) { //找到玩家的座位顺序 int seatIndex = room.GetGamerSeat(gamer.UserID); if (seatIndex >= 0) { //由等待状态设置为准备状态 room.isReadys[seatIndex] = true; //广播通知全房间玩家此gamer已经准备好 room.Broadcast(new Actor_GamerReady_Landlords() { UserID = gamer.UserID }); //检测开始游戏,判断如果房间内三个人都是准备状态就开始游戏和发牌 room.CheckGameStart(); } else { Log.Error("玩家不在正确的座位上"); } } await ETTask.CompletedTask; } } }
RoomSystem中添加CheckGameStart,GameStart方法
\Server\Hotfix\Landlords\System\RoomSystem.cs
/// <summary> /// 检查游戏是否可以开始 /// </summary> public static void CheckGameStart(this Room self) { Log.Debug("检查游戏是否可以开始"); bool isOK = true; for (int i = 0;i <self.isReadys.Length;i ++) { //遍历所有准备状态 任何一个状态为false结果都是false if(self.isReadys[i] == false) { isOK = false; } } if(isOK) { Log.Debug("满足游戏开始条件"); self.GameStart(); } } /// <summary> /// 斗地主游戏开始 /// </summary> public static void GameStart(this Room self) { //更改房间状态 从空闲房间移除 添加到游戏中房间列表 LandMatchComponent Match = Game.Scene.GetComponent<LandMatchComponent>(); Match.FreeLandlordsRooms.Remove(self.Id); Match.GamingLandlordsRooms.Add(self.Id, self); //更该玩家状态 for(int i=0;i<self.gamers.Length;i++) { Gamer gamer = self.gamers[i]; Match.Waiting.Remove(gamer.UserID); Match.Playing.Add(gamer.UserID, self); } //添加开始斗地主游戏需要的组件 self.AddComponent<GameControllerComponent, RoomConfig>(GateHelper.GetLandlordsConfig(RoomLevel.Lv100)); //... //开始游戏 //self.GetComponent<GameControllerComponent>().StartGame(); }
服务端添加GameControllerComponent
\Server\ET.Core\Landlords\Component\Map\GameControllerComponent.cs
namespace ETModel { public class GameControllerComponent : Component { //房间配置 public RoomConfig Config { get; set; } //底分 public long BasePointPerMatch { get; set; } //全场倍率 public int Multiples { get; set; } //最低入场门槛 public long MinThreshold { get; set; } public override void Dispose() { if(this.IsDisposed) { return; } base.Dispose(); this.BasePointPerMatch = 0; this.Multiples = 0; this.MinThreshold = 0; } } }
到目前为止,已经实现了后端判断房间内玩家是否都做好准备,并且发送广播消息通知所有玩家客户端,显示他们的准备状态。
然后给GameControllerComponent组件添加扩展对象与扩展方法,就可以实现发牌到每一个玩家,下节课的内容。
前端GamerReady_Landlords_NttHandler
前端添加Actor02_GamerReady_NttHandler
\Assets\Model\Landlords\Handler\Actor02_GamerReady_NttHandler.cs
using System; using System.Collections.Generic; using ETModel; using UnityEngine; namespace ETModel { [MessageHandler] public class Actor_GamerReady_NttHandler : AMHandler<Actor_GamerReady_Landlords> { protected override async ETTask Run(ETModel.Session session, Actor_GamerReady_Landlords message) { UI uiRoom = Game.Scene.GetComponent<UIComponent>().Get(LandUIType.LandRoom); LandRoomComponent room = uiRoom.GetComponent<LandRoomComponent>(); Gamer gamer = room.GetGamer(message.UserID); gamer.GetComponent<LandlordsGamerPanelComponent>().SetReady(); //本地玩家准备,隐藏准备按钮 if (gamer.UserID == LandRoomComponent.LocalGamer.UserID) { uiRoom.GameObject.Get<GameObject>("Ready").SetActive(false); } await ETTask.CompletedTask; } } }
本节需要增加的消息定义
\Proto\HotfixMessage.proto
//准备游戏消息 message Actor_GamerReady_Landlords // IActorMessage { int32 RpcId = 90; int64 ActorId = 93; int64 UserID = 1; }
本节的效果:
课外扩展:
想想,能不能实现通过服务端广播给客户端的Actor_GamerReady_Landlords 消息,修改这个消息体,然后能把房间里所有的玩家的准备状态广播给客户端,这样每个玩家都能及时看到其它玩家的准备状态