任务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 消息,修改这个消息体,然后能把房间里所有的玩家的准备状态广播给客户端,这样每个玩家都能及时看到其它玩家的准备状态

 

posted @ 2023-02-16 14:35  Domefy  阅读(56)  评论(0编辑  收藏  举报