ET框架实现匹配功能(服务器端)

打印 上一主题 下一主题

主题 1007|帖子 1007|积分 3025

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
目次
一、界说房间中的玩家实体
二、界说房间的实体
三、实现房间管理组件
3.1房间管理组件界说 
 3.2房间管理组件行为
四、实现匹配功能
4.1匹配接口
4.2 房间实体的行为
   注:本篇文章记载用ET框架模拟实现游戏中的匹配功能,代码和思绪仅供学习使用。
  追念一下我们打王者的时候:


  • 先在大厅界面选择游戏模式(例如:匹配、排位等)
  • 然后进入到一个假造的房间
  • 点击开始匹配按钮
  • 匹配过程中的等待时间
  • 匹配成功点击进入游戏正式游玩

拿排位来举例,正式开始游戏必要十个人,所以我称匹配之前的那个所谓的房间为假造房间,成功匹配十个人后组成的房间为正式房间。我们按照上面罗列出的次序用ET来实现。
一、界说房间中的玩家实体

  我们把玩家在房间中必要的信息界说成一个新的实体带入房间供我们使用,比如头像、昵称、英雄列表、皮肤列表、段位信息、玩家经验、品级、是否是呆板人等游戏过程中所必要的字段。
  1. namespace ET.Server
  2. {
  3.     public class RoomPlayerInfo : Entity, IAwake,IDestroy
  4.     {
  5.         public long UnitId = 1;
  6.         
  7.         public long GateSessionActorId = 1;
  8.         //是否掉线
  9.         public int OfflineFlag = 0;
  10.         public string Name = "";      
  11.         public int Exp = 1000;
  12.         
  13.         public int RobotFlag = 0;
  14.         public long AddMatchingTime = 0;
  15.    
  16.         //玩家拥有的英雄列表
  17.         public List<Hero> CardList = new List<Hero>();
  18.         //玩家拥有的皮肤列表
  19.         public List<Skin> CardList = new List<Skin>();
  20.         //段位
  21.         public int StartCount = 0;
  22.         
  23.         public int HeadImgId = 1;
  24.         
  25.         public int FrameId = 1;
  26.         //单排、三排、五排
  27.         public int FriendRoomPlayFlag = 1;
  28.         
  29.         public int Level = 0;
  30.     }
  31. }
复制代码
二、界说房间的实体

  玩家匹配成功之后进入到房间中进行游戏,我们来界说房间实体的基本信息;比如房间状态(等待、匹配中、匹配成功、全军出击、龙王革新、风暴龙王到临、结算...)、房间范例(匹配玩法、娱乐模式玩法、排位玩法)、房间ID、房间中玩家信息、玩家数量等......
  1. namespace ET.Server
  2. {
  3.    
  4.     public enum RoomType: ushort
  5.     {
  6.         Wait = 0,// 等待     
  7.         Matchmaking = 1,//匹配中
  8.         MatchSuccessfully = 2,//匹配成功
  9.         GameStart = 3,//游戏开始,全军出击
  10.         DragonKingRefresh = 4,//龙王刷新
  11.         StormDragonLord = 5,//风暴龙王刷新
  12.         Settle = 6//结算
  13.     }
  14.    
  15.    
  16.     [ChildOf(typeof (RoomManagerComponent))]
  17.     public class RoomInfo: Entity, IAwake, IDestroy
  18.     {
  19.         public int RoomId = 1;
  20.         /// <summary>
  21.         /// 房间类型
  22.         /// </summary>
  23.         public int Type = 1001;
  24.         
  25.         public int PlayerCount = 3;
  26.         
  27.         public int PlayCountFlag = 1;
  28.         /// <summary>
  29.         /// 房间状态
  30.         /// </summary>
  31.         public RoomType Status = RoomType.Wait;
  32.         /// <summary>
  33.         /// 玩家信息
  34.         /// </summary>
  35.         public Dictionary<int, RoomPlayerInfo> Players = new Dictionary<int, RoomPlayerInfo>();
  36.         
  37.         public int RoomProcessNum = 1;
  38.         public int PlayCount = 0;
  39.         
  40.         //创建时间
  41.         public long CreateTime = 0;
  42.       
  43.         //房主
  44.         public long OwnerId = 0;
  45.     }
  46. }
复制代码
三、实现房间管理组件

  玩家的实体信息通过玩家本身进行创建,创建完成后匹配放入到房间中,那房间实体信息怎么创建呢?
   [ChildOf(typeof (RoomManagerComponent))]
  在上段房间实体的代码中我们用到了ChildOf,阐明房间是通过 RoomManagerComponent 也就是房间管理组件来进行管理的。

房间管理组件管理着所有房间的生成创建和烧毁。比如有人开始匹配了,没有空余房间,房间管理组件就创建一个房间把玩家放进去;游戏结束房间管理组件就把房间烧毁。房间管理组件中设置定时任务不停地实行监控各个房间、玩家的各种状态。

3.1房间管理组件界说 

  1. namespace ET.Server
  2. {
  3.     [ComponentOf(typeof(Scene))]
  4.     public class RoomManagerDDZComponent:Entity,IAwake,IDestroy
  5.     {
  6.         public long Timer;
  7.         //房间字典
  8.         public Dictionary<int, RoomInfo> RoomInfosDic = new Dictionary<int, RoomInfo>();
  9.         
  10.         //匹配队列字典
  11.         public Dictionary<int, List<RoomPlayerInfo>> MatchList = new Dictionary<int, List<RoomPlayerInfo>>();
  12.         
  13.         public int NowId = 100001;
  14.         public int MatchTime = 30000;
  15.     }
  16. }
复制代码
 3.2房间管理组件行为

RoomManagerComponentSystem
  1. namespace ET.Server
  2. {
  3.    
  4.     public class RoomManagerComponentDestroy : DestroySystem<RoomManagerComponent>
  5.     {
  6.         protected override void Destroy(RoomManagerComponent self)
  7.         {
  8.             TimerComponent.Instance.Remove(ref self.Timer);
  9.         }
  10.     }
  11.    
  12.     public class  RoomManagerComponent: AwakeSystem<RoomManagerComponent>
  13.     {
  14.         protected override void Awake(RoomManagerComponent self)
  15.         {
  16.             self.Timer = TimerComponent.Instance.NewRepeatedTimer(1000, TimerInvokeType.RoomUpdate, self);
  17.         }
  18.     }
  19.    
  20.    
  21.     [Invoke(TimerInvokeType.RoomUpdate)]
  22.     public class  RoomManagerComponentTimer : ATimer<RoomManagerComponent>
  23.     {
  24.         protected override void Run(RoomManagerComponent self)
  25.         {
  26.             try
  27.             {
  28.                 if ( self.IsDisposed || self.Parent == null )
  29.                 {
  30.                     return;
  31.                 }
  32.                 self?.Update();
  33.             }
  34.             catch (Exception e)
  35.             {
  36.                 Log.Error(e.ToString());
  37.             }
  38.         }
  39.     }
  40.    
  41.     [FriendOf(typeof(RoomInfo))]
  42.     [FriendOf(typeof(RoomManagerComponent))]
  43.     [FriendOf(typeof(RoomPlayerInfo))]
  44.     [FriendOf(typeof(ItemComponent))]
  45.     [FriendOf(typeof(BaseInfoComponent))]
  46.     [FriendOf(typeof(UnitComponent))]
  47.     [FriendOf(typeof(Unit))]
  48.     [FriendOf(typeof(Account))]
  49.     public static class RoomManagerComponentSystem
  50.     {
  51.         //定时任务,每秒执行
  52.         public static void Update(this RoomManagerComponent self)
  53.         {
  54.             //匹配方法
  55.             self.Match();
  56.             UnitComponent uc = self.DomainScene().GetComponent<UnitComponent>();
  57.             foreach (var room in self.RoomInfosDic)
  58.             {
  59.                 //同时也执行现有房间的Update方法
  60.                     room.Value.Update().Coroutine();
  61.             }
  62.         }
  63.         
  64.         //执行匹配操作
  65.         public static void Match(this RoomManagerComponent self)
  66.         {
  67.             //遍历玩家匹配列表进行匹配操作
  68.             foreach (var playerList in self.MatchList)
  69.             {
  70.                 long nowT = TimeHelper.ClientNow();
  71.                 int playerCount = 10;
  72.                 if (playerList.Value.Count >= playerCount || (playerList.Value.Count >= 1 && nowT > playerList.Value[0].AddMatchingTime + self.MatchTime) ||  (playerList.Value.Count >= 1 &&playerList.Value[0].PlayWithRobotFlag == 1))
  73.                 {
  74.                     self.GetRoomIdAndLocate(playerList.Key,0,1,0,out int locate, out int roomId);
  75.                     RoomInfo room = self.GetRoom(roomId);
  76.                     for (int i = 0; i< playerCount; i++)
  77.                     {
  78.                         if (playerList.Value.Count > i)
  79.                         {
  80.                             //i+1key自增
  81.                             room.Players.Add(i+1,playerList.Value[i]);
  82.       self.PlayerAddMatch(playerList.Value[i].UnitId,playerList.Key,0,roomId).Coroutine();
  83.                             self.ChangePlayerCount(playerList.Key, 0, 1);
  84.                             //安排机器人
  85.                             if (playerList.Value[i].PlayWithRobotFlag == 1)
  86.                             {
  87.                                     for (int j = 2; j <= room.PlayerCount; j++)
  88.                                     {
  89.                                             RoomPlayerInfo robot = RoomHelper.GetRobot(roomConfig.RoleType,roomConfig.WinningOrLosingCap);
  90.                                             room.Players.Add(j,robot);
  91.                                     }
  92.                                     break;
  93.                             }
  94.                         }else
  95.                         {
  96.                             RoomPlayerInfo robot = RoomHelper.GetRobot(roomConfig.RoleType,roomConfig.WinningOrLosingCap);
  97.                             room.Players.Add(i+1,robot);
  98.                         }
  99.                     }
  100.                     
  101.                     if (playerList.Value.Count > playerCount)
  102.                     {
  103.                         playerList.Value.RemoveRange(0,playerCount);
  104.                     }
  105.                     else
  106.                     {
  107.                         playerList.Value.Clear();
  108.                     }
  109.                     
  110.                     //匹配成功,通知给房间中的各个玩家
  111.                     room.MatchSuccess();
  112.                     return;
  113.                 }
  114.             }
  115.         }
  116.         public static async ETTask PlayerAddMatch(this RoomManagerComponent self,long unitId,int type,int rankFlag,Dictionary<int,long> macthUse,int gameGroupId,int roomId,int controlFlag)
  117.         {
  118.             UnitComponent unitComponent = self.DomainScene().GetComponent<UnitComponent>();
  119.             Unit unit = unitComponent.Get(unitId);
  120.             if (unit == null)
  121.             {
  122.                 unit = await UnitCacheHelper.GetUnitCache(self.GetParent<Scene>(),unitId);
  123.             }
  124.             //告诉玩家基本信息组件BaseInfoComponent,玩家已经开始了游戏
  125.             unit.GetComponent<BaseInfoComponent>().PlayGame(type, rankFlag, gameGroupId, 0, roomId, controlFlag);
  126.         }
  127.         public static void AddMatch(this RoomManagerComponent self,int type,RoomPlayerInfo player)
  128.         {
  129.             //如果玩家已经在匹配队列则return
  130.                 if (self.CheckInMatch(player.UnitId))
  131.                 {
  132.                         return;
  133.                 }
  134.             //如果匹配队列没有此类型的玩法比如排位,则匹配队列会增加
  135.             if (!self.MatchList.ContainsKey(type))
  136.             {
  137.                 self.MatchList.Add(type, new List<RoomPlayerInfo>());
  138.             }
  139.             //将玩家加入到匹配队列中
  140.             self.MatchList[type].Add(player);
  141.         }
  142.         //检查玩家是否在匹配队列中        
  143.         public static bool CheckInMatch(this RoomManagerComponent self,long unitId)
  144.         {
  145.                 foreach (var info in self.MatchList)
  146.                 {
  147.                         foreach (var p in info.Value)
  148.                         {
  149.                                 if (p.UnitId == unitId)
  150.                                 {
  151.                                         return true;
  152.                                 }
  153.                         }
  154.                 }
  155.                 return false;
  156.         }
  157.         //根据房间ID返回房间信息
  158.         public static RoomInfo GetRoom(this RoomManagerComponent self,int roomId)
  159.         {
  160.             return self.RoomInfosDic[roomId];
  161.         }
  162.         //返回房间ID和玩家位置
  163.         public static void GetRoomIdAndLocate(this RoomManagerComponent self,int type,int rId,int playCount,int rankFlag,out int locate, out int roomId)
  164.         {
  165.             locate = 0;
  166.             roomId = 0;
  167.             
  168.             foreach (var room in self.RoomInfosDic)
  169.             {
  170.                 if (room.Value.Type == type)
  171.                 {
  172.                     if (room.Value.Players.Count == 0)
  173.                     {
  174.                         roomId = room.Key;
  175.                         locate = 1;
  176.                         break;
  177.                     }
  178.                 }
  179.             }
  180.         
  181.         //检查玩家是否在房间中如果在返回房间ID
  182.         public static void CheckInRoom(this RoomManagerComponent self,long unitId, out int locate , out int roomId)
  183.         {
  184.             locate = 0;
  185.             roomId = 0;
  186.             foreach (var room in self.RoomInfosDic)
  187.             {
  188.                 foreach (var playerInfo in room.Value.Players)
  189.                 {
  190.                     if (playerInfo.Value.UnitId == unitId)
  191.                     {
  192.                         
  193.                         roomId = room.Key;
  194.                         locate = playerInfo.Key;
  195.                         break;
  196.                     }
  197.                 }
  198.             }
  199.         }
  200.         //创建并返回RoomPlayerInfo对象
  201.         public static RoomPlayerInfo SelfToRoomPlayerInfo(this RoomManagerComponent self,long unitId,string name,long sessionId,....//生成玩家实体所需参数)
  202.         {
  203.             return new RoomPlayerInfo()
  204.             {
  205.                 UnitId = unitId,
  206.                 GateSessionActorId = sessionId,
  207.                 AddMatchingTime = TimeHelper.ClientNow(),
  208.                 Name = name,
  209.                 ....//生成玩家实体所需参数
  210.             };
  211.         }
  212.     }
  213. }
复制代码
固然我们想要在匹配中加点其他逻辑,比如根据隐藏分来匹配也是可以的。 
四、实现匹配功能

有了前面的实体组件做铺垫,我们来实现匹配功能。
   假造房间中房主点击匹配按钮 ——> 客户端请求服务端进行匹配 ——> 玩家进入匹配队列 ——> 匹配到十个人组成房间 ——> 服务端关照房间中的所有玩家匹配成功。
  4.1匹配接口

界说接口编写Proto文件
  1. //ResponseType M2C_Matching_DDZ
  2. message C2M_MatchingGame // IActorLocationRequest
  3. {
  4.         int32 RpcId = 1;
  5.         int32 RoomType = 2;
  6. }
  7. message M2C_MatchingGame // IActorLocationResponse
  8. {
  9.         int32 RpcId    = 1;
  10.         int32 Error    = 2;
  11.         string Message = 3;
  12. }
复制代码
编写接口
  1. namespace ET.Server
  2. {
  3.     [ActorMessageHandler(SceneType.Map)]
  4.     [FriendOf(typeof(UnitGateComponent))]
  5.     [FriendOf(typeof(BaseInfoComponent))]
  6.     [FriendOf(typeof(RoomInfo))]
  7.     public class C2M_MatchingGameHandler : AMActorLocationRpcHandler<Unit,C2M_MatchingGame,M2C_MatchingGame>
  8.     {
  9.         protected override async ETTask Run(Unit unit, C2M_MatchingGame request, M2C_MatchingGame response)
  10.         {
  11.             //获取房间管理组件
  12.             RoomManagerComponent rm = unit.DomainScene().GetComponent<RoomManagerComponent>();
  13.             //获取玩家基本信息组件
  14.             BaseInfoComponent baseInfoComponent = unit.GetComponent<BaseInfoComponent>();
  15.             
  16.             if (baseInfoComponent.NowPlayGame > 0)
  17.             {
  18.                 response.Error = ErrorCode.ERR_All;
  19.                 response.Message = "已在游戏中";
  20.                 return;
  21.             }
  22.             using (await CoroutineLockComponent.Instance.Wait(CoroutineLockType.Matching, unit.Id.GetHashCode()))
  23.             {
  24.                 //生成房间中的玩家实体信息RoomPlayerInfo
  25.                 //这个方法做的就是把需要的参数传进去返回RoomPlayerInfo
  26.                     RoomPlayerInfo player = rm.SelfToRoomPlayerInfo(unit.Id, baseInfoComponent.Name, unit.GetComponent<UnitGateComponent>().GateSessionActorId,
  27.                             score,baseInfoComponent.NowUseCharacter,
  28.                             baseInfoComponent.NowUseAvatar, baseInfoComponent.NowUseAvatarFrame, baseInfoComponent.ip, baseInfoComponent.PhoneNumber,
  29.                             Level);
  30.             
  31.                 //把生成好的玩家信息加入到房间管理组件的玩家匹配队列中进行匹配
  32.                     rm.AddMatch(request.RoomType, player);
  33.                    
  34.                     await ETTask.CompletedTask;
  35.             }
  36.         }
  37.     }
  38. }
复制代码
4.2 房间实体的行为

根据房间管理组件中定时任务实行的Update方法,玩家匹配成功会主动关照房间内玩家匹配成功,房间状态发生变化正式进入游戏。


 RoomInfoSystem
  1. namespace ET.Server
  2. {
  3.     public class RoomInfoDestroy : DestroySystem<RoomInfo>
  4.     {
  5.         protected override void Destroy(RoomInfo self)
  6.         {
  7.             
  8.         }
  9.     }
  10.    
  11.    
  12.     public class  RoomInfoAwake : AwakeSystem<RoomInfo>
  13.     {
  14.         protected override void Awake(RoomInfo self)
  15.         {
  16.             
  17.         }
  18.     }
  19.    
  20.     [FriendOf(typeof(RoomInfo))]
  21.     [FriendOf(typeof(RoomPlayerInfo))]
  22.     [FriendOf(typeof(ItemComponent))]
  23.     [FriendOf(typeof(RoomManagerComponent))]
  24.     public static class RoomInfoSystem
  25.     {
  26.        //房间管理组件延伸过来的定时任务
  27.        public static async ETTask Update(this RoomInfo self)
  28.        {
  29.        }
  30.        public static void MatchSuccess(this RoomInfo self)
  31.        {
  32.             //获取房间内所有的玩家列表            
  33.             List<PlayerInRoomProto> info = self.GetPlayers();
  34.             //匹配成功后更新房间状态逻辑
  35.             self.UpdateRoomStatus(TimeHelper.ServerNow(),1,RoomType.Wait);
  36.       
  37.             foreach (var playerInfo in self.Players)
  38.             {
  39.                 M2C_MatchSuccessNotice g = new M2C_MatchSuccessNotice()
  40.                 {
  41.                     Players = info, RoomStatus = self.GetRoomStatus()//获取房间状态
  42.                 };
  43.               
  44. //正式通知房间内玩家匹配成功            
  45. GameNoticeHelper.NoticeHelper(g,playerInfo.Value.GateSessionActorId,playerInfo.Value.OfflineFlag);
  46.             }
  47.         }
  48.     }
  49. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

刘俊凯

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表