任务36:后端出牌请求的Handler及前端收到广播有效出牌的Handler
服务端添加前端出牌、不出、获取提示牌请求对应的Handler
注意下牌序组件,出牌缓存组件是如何在这些handler中起作用的
GamerPlayCard_Handler
\Server\Hotfix\Landlords\Handler\Map\GamerPlayCard_Handler.cs
这个比较好理解,每次有人出牌,跟上家的出牌进行比较,符合规则清除上家的出牌缓存,此出牌加入到出牌缓存中。
//如果符合将牌从手牌移到出牌缓存区 deskCardsCache.Clear(); deskCardsCache.Rule = type; HandCardsComponent handCards = gamer.GetComponent<HandCardsComponent>(); foreach (var card in request.Cards) { handCards.PopCard(card); deskCardsCache.AddCard(card); }
GamerPlayCard_Handler完整代码
using System; using System.Collections.Generic; using System.Threading.Tasks; using ETModel; namespace ETHotfix { [ActorMessageHandler(AppType.Map)] public class GamerPlayCard_Handler: AMActorRpcHandler<Gamer, Actor_GamerPlayCard_Req, Actor_GamerPlayCard_Back> { protected override async ETTask Run(Gamer gamer, Actor_GamerPlayCard_Req request, Actor_GamerPlayCard_Back response, Action reply) { try { Room room = Game.Scene.GetComponent<LandMatchComponent>().GetGamingRoom(gamer); GameControllerComponent gameController = room.GetComponent<GameControllerComponent>(); DeskCardsCacheComponent deskCardsCache = room.GetComponent<DeskCardsCacheComponent>(); OrderControllerComponent orderController = room.GetComponent<OrderControllerComponent>(); //检测是否符合出牌规则 if (CardHelper.PopEnable(To.Array(request.Cards), out CardsType type)) { //当前出牌牌型是否比牌桌上牌型的权重更大 bool isWeightGreater = CardHelper.GetWeight(To.Array(request.Cards), type) > deskCardsCache.GetTotalWeight(); //当前出牌牌型是否和牌桌上牌型的数量一样 bool isSameCardsNum = request.Cards.Count == deskCardsCache.GetAll().Length; //当前出牌玩家是否是上局最大出牌者 bool isBiggest = orderController.Biggest == orderController.CurrentAuthority; //当前牌桌牌型是否是顺子 bool isStraight = deskCardsCache.Rule == CardsType.Straight || deskCardsCache.Rule == CardsType.DoubleStraight || deskCardsCache.Rule == CardsType.TripleStraight; //当前出牌牌型是否和牌桌上牌型一样 bool isSameCardsType = type == deskCardsCache.Rule; if (isBiggest || //先手出牌玩家 type == CardsType.JokerBoom || //王炸 type == CardsType.Boom && isWeightGreater || //更大的炸弹 isSameCardsType && isStraight && isSameCardsNum && isWeightGreater || //更大的顺子 isSameCardsType && isWeightGreater) //更大的同类型牌 { if (type == CardsType.JokerBoom) { //王炸翻4倍 gameController.Multiples *= 4; room.Broadcast(new Actor_SetMultiples_Ntt() { Multiples = gameController.Multiples }); } else if (type == CardsType.Boom) { //炸弹翻2倍 gameController.Multiples *= 2; room.Broadcast(new Actor_SetMultiples_Ntt() { Multiples = gameController.Multiples }); } } else { response.Error = ErrorCode.ERR_PlayCardError; reply(); return; } } else { response.Error = ErrorCode.ERR_PlayCardError; reply(); return; } //如果符合将牌从手牌移到出牌缓存区 deskCardsCache.Clear(); deskCardsCache.Rule = type; HandCardsComponent handCards = gamer.GetComponent<HandCardsComponent>(); foreach (var card in request.Cards) { handCards.PopCard(card); deskCardsCache.AddCard(card); } reply(); //转发玩家出牌消息 room.Broadcast(new Actor_GamerPlayCard_Ntt() { UserID = gamer.UserID, Cards = request.Cards }); //游戏控制器继续游戏 gameController.Continue(gamer); await ETTask.CompletedTask; } catch (Exception e) { ReplyError(response, e, reply); } } } }
GamerDontPlay_Handler
\Server\Hotfix\Landlords\Handler\Map\ToGamer\GamerDontPlay_Landlords.cs
using System; using System.Collections.Generic; using System.Threading.Tasks; using ETModel; namespace ETHotfix { [ActorMessageHandler(AppType.Map)] public class GamerDontPlay_Handler : AMActorHandler<Gamer, Actor_GamerDontPlayCard_Ntt > { protected override async ETTask Run(Gamer gamer, Actor_GamerDontPlayCard_Ntt message) { Room room = Game.Scene.GetComponent<LandMatchComponent>().GetGamingRoom(gamer); OrderControllerComponent orderController = room.GetComponent<OrderControllerComponent>(); if (orderController.CurrentAuthority == gamer.UserID) { //转发玩家不出牌消息 Actor_GamerDontPlayCard_Ntt transpond = new Actor_GamerDontPlayCard_Ntt (); transpond.UserID = gamer.UserID; room.Broadcast(transpond); //轮到下位玩家出牌 orderController.Turn(); //判断是否先手 bool isFirst = orderController.CurrentAuthority == orderController.Biggest; if (isFirst) { room.GetComponent<DeskCardsCacheComponent>().Clear(); } room.Broadcast(new Actor_AuthorityPlayCard_Ntt() { UserID = orderController.CurrentAuthority, IsFirst = isFirst }); } await ETTask.CompletedTask; } } }
GamerPrompt_Handler
\Server\Hotfix\Landlords\Handler\Map\ToGamer\GamerPrompt_Landlords.cs
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using ETModel; namespace ETHotfix { [ActorMessageHandler(AppType.Map)] public class GamerPrompt_Handler: AMActorRpcHandler<Gamer, Actor_GamerPrompt_Req, Actor_GamerPrompt_Back> { protected override async ETTask Run(Gamer gamer, Actor_GamerPrompt_Req request, Actor_GamerPrompt_Back response, Action reply) { try { Room room = Game.Scene.GetComponent<LandMatchComponent>().GetGamingRoom(gamer); OrderControllerComponent orderController = room.GetComponent<OrderControllerComponent>(); DeskCardsCacheComponent deskCardsCache = room.GetComponent<DeskCardsCacheComponent>(); List<Card> handCards = new List<Card>(gamer.GetComponent<HandCardsComponent>().GetAll()); CardHelper.SortCards(handCards); if (gamer.UserID == orderController.Biggest) { response.Cards = To.RepeatedField(handCards.Where(card => card.CardWeight == handCards[handCards.Count - 1].CardWeight).ToArray()); } else { List<Card[]> result = await CardHelper.GetPrompt(handCards, deskCardsCache, deskCardsCache.Rule); if (result.Count > 0) { response.Cards = To.RepeatedField(result[RandomHelper.RandomNumber(0, result.Count)]); } } reply(); } catch (Exception e) { ReplyError(response, e, reply); } } } }
在 GameControllerComponentSystem 中添加 Continue方法
每次收到出牌请求都判断是继续游戏或有玩家牌出完了结束游戏。
本节还没讲到游戏结束逻辑先把相关代码注释或空着
\Server\Hotfix\Landlords\System\GameControllerComponentSystem.cs
/// <summary> /// 判断出牌后游戏继续or结束 /// </summary> public static void Continue(this GameControllerComponent self, Gamer lastGamer) { Room room = self.GetParent<Room>(); OrderControllerComponent orderController = room.GetComponent<OrderControllerComponent>(); //是否结束,当前出牌者手牌数为0时游戏结束 bool isEnd = lastGamer.GetComponent<HandCardsComponent>().CardsCount == 0; if (isEnd) { //当前最大出牌者为赢家 Identity winnerIdentity = room.GetGamerFromUserID(orderController.Biggest).GetComponent<HandCardsComponent>().AccessIdentity; //List<GamerScore> gamersScore = new List<GamerScore>(); //游戏结束所有玩家摊牌 //... //self.GameOver(gamersScore, winnerIdentity); } else { //轮到下位玩家出牌 orderController.Biggest = lastGamer.UserID; orderController.Turn(); room.Broadcast(new Actor_AuthorityPlayCard_Ntt() { UserID = orderController.CurrentAuthority, IsFirst = false }); } }
本节相关的消息定义
\Proto\OuterMessage.proto
放在这前已经添加的Actor_GamerDontPlayCard_Ntt后面
message Actor_GamerPlayCard_Ntt // IActorMessage { int32 RpcId = 90; int64 ActorId = 93; int64 UserID = 1; repeated ETModel.Card Cards = 2; }
利用前端proto工具生成消息文件,复制到服务端,编译运行服务端项目看看有没有报错。
出牌:前端收到Actor_GamerPlayCard_Ntt消息的处理Handler
房间有玩家出牌通过服务端的规则判断出牌有效果,即会广播到每个玩家客户端这次的出牌内容。
前端收到Actor_GamerPlayCard_Ntt消息后,判断:
- 如果是刚刚自己的出牌,就只需要关闭出牌按钮
- 如果是其它玩家的出牌,就更新他的出牌界面显示出来
\Assets\Model\Landlords\Handler\Actor10_PlayCardGamer_NttHandler.cs
namespace ETModel { [MessageHandler] public class Actor_GamerPlayCard_NttHandler : AMHandler<Actor_GamerPlayCard_Ntt> { protected override async ETTask Run(ETModel.Session session, Actor_GamerPlayCard_Ntt message) { UI uiRoom = Game.Scene.GetComponent<UIComponent>().Get(LandUIType.LandRoom); LandRoomComponent room = uiRoom.GetComponent<LandRoomComponent>(); Gamer gamer = room.GetGamer(message.UserID); if (gamer != null) { gamer.GetComponent<LandlordsGamerPanelComponent>().ResetPrompt(); //本地玩家清空选中牌 关闭出牌按钮 if (gamer.UserID == LandRoomComponent.LocalGamer.UserID) { LandInteractionComponent interaction = uiRoom.GetComponent<LandRoomComponent>().Interaction; interaction.Clear(); interaction.EndPlay(); } //出牌后更新玩家手牌 HandCardsComponent handCards = gamer.GetComponent<HandCardsComponent>(); Card[] Tcards = new Card[message.Cards.Count]; for (int i = 0; i < message.Cards.Count; i++) { Tcards[i] = message.Cards[i]; } handCards.PopCards(Tcards); } await ETTask.CompletedTask; } } }
不出:前端收到Actor_GamerDontPlayCard_Ntt消息的处理Handler
\Assets\Model\Landlords\Handler\Actor11_DontPlayCardGamer_NttHandler.cs
using System; using System.Collections.Generic; namespace ETModel { [MessageHandler] public class Actor_GamerDontPlayCard_NttHandler : AMHandler<Actor_GamerDontPlayCard_Ntt> { protected override async ETTask Run(ETModel.Session session, Actor_GamerDontPlayCard_Ntt message) { UI uiRoom = Game.Scene.GetComponent<UIComponent>().Get(LandUIType.LandRoom); LandRoomComponent room = uiRoom.GetComponent<LandRoomComponent>(); Gamer gamer = room.GetGamer(message.UserID); if (gamer != null) { if (gamer.UserID == LandRoomComponent.LocalGamer.UserID) { uiRoom.GetComponent<LandRoomComponent>().Interaction.EndPlay(); } gamer.GetComponent<HandCardsComponent>().ClearPlayCards(); gamer.GetComponent<LandlordsGamerPanelComponent>().SetDiscard(); } await ETTask.CompletedTask; } } }
LandlordsGamerPanelComponent组件中增加玩家不出牌的方法
\Assets\Model\Landlords\Component\LandlordsGamerPanelComponent.cs
/// <summary> /// 玩家不出 /// </summary> public void SetDiscard() { prompt.text = "不出"; }
接下来就可以进行出牌与牌型、出牌规则大小判断的测试了。