详解房卡麻将分析系列 "牌局回放" 之 播放处理

详解房卡麻将分析系列 "牌局回放" 之 播放处理

昨天红孩儿给大伙讲了讲”牌局回放“的数据记录处理,有了数据的存储,下面就是数据的显示了。

实话讲,好久没用过 SQL Server 来做数据库了, 网狐的服务器是基于WIN,IOCP,  SQL Server 这套路子。配置好后,可以在QPTreasureDB数据库中看到三个牌局相关的表。

其中dbo.PrivateGameRecord是存储当前游戏的房间及玩家,最终胜负信息的。

dbo.PrivateGameRecordChild是存储当前游戏的每一局的牌局回放,也就是咱们上篇文中所讲述的每一场牌局详情和操作数据。

dbo.PrivateGameRecordUserRecordID是记录ID与玩家ID的对应关系。

我们打开dbo.PrivateGameRecord,可以看到有一个属性字段UserData存储着一堆二进制数据。也就是我们上节中通过Stream_VALUE来将结构数据填充为字节流后存进来的。

当客户端在进入战绩界面时,会向登录服务器发送SUB_GP_GAME_RECORD_LIST消息,请求当前玩家的所有参与过的房间据,也就是dbo.PrivateGameRecord中与玩家相关的数据列表。这个可以在CGPGameRecord.cpp的CB_GetGameRecordList函数中看到。

在登录服务器的AttemperEngineSink.cpp中,我们可以看到登录服务器会在收到消息后转发数据库请求,数据库再通过存储过程拉数据出来。之后返回给客户端。

客户端收到后通过StreamValue将数据流解析到结构tagPrivateRandTotalRecord中显示出来。

当玩家看到这条信息后,如果想查看每一局的战局,会再点击"详情"按钮,这时客户端会向登录服务器再次发送SUB_GP_GAME_RECORD_CHILD消息,同上面的流程大体一致,经过这样一个来回,客户端会得到房间中每局的详细数据,收到后通过StreamValue将数据流解析到结构tagPrivateRandRecordChild中显示出来。

玩家现在能看到每一局的详情了,他如果想看牌局回放,会再调用GameScene的StartRecord(datastream kDataStream)来将tagPrivateRandRecordChild中的数据流转化为当前玩家牌局信息和操作信息。之后显示游戏场景和回放操作按钮菜单。有了具体的数据,通过按钮菜单来控制播放的速度,上一步,下一步并不复杂。在GameScene的NextRecordAction函数中,我们可以看到如何根据当前的操作类型来进行相应的操作复现玩家的出牌和操作。

void GameScence::NextRecordAction()
{
   ...
  GameRecordOperateResult& kAction = m_pGameRecord->kAction[m_iActRecordIdex];
  int iChairID = (m_wRecordSelfChairID-kAction.wOperateUser+MAX_PLAYER)%MAX_PLAYER;
  int iProvideUser = (m_wRecordSelfChairID-kAction.wProvideUser+MAX_PLAYER)%MAX_PLAYER;
  if (kAction.cbActionType == GameRecordOperateResult::TYPE_OperateResult)
  {
    Player* pPlayer = m_pPlayer[iChairID];
    CMD_S_OperateResult kTempCMD;
    kTempCMD.cbOperateCard = kAction.cbOperateCard;
    kTempCMD.cbOperateCode = kAction.cbOperateCode;
    kTempCMD.wOperateUser = kAction.wOperateUser;
    kTempCMD.wProvideUser = kAction.wProvideUser;
    Player* pProvidePlayer = m_pPlayer[iProvideUser];
    if (pProvidePlayer &&(kAction.cbOperateCode == WIK_PENG
      || kAction.cbOperateCode == WIK_LEFT
      || kAction.cbOperateCode == WIK_CENTER
      || kAction.cbOperateCode == WIK_RIGHT
      || (kAction.cbOperateCode == WIK_GANG && kAction.wOperateUser != kAction.wProvideUser )))
    {
      pProvidePlayer->removeHandOutCard(kAction.cbOperateCard);
      pProvidePlayer->setActOutCard(-1); 

      //设置当前玩家
      for (int i = 0; i<MAX_PLAYER; i++)
      {
        m_pPlayer[i]->stopAniCurrPlayer();
      }
      pProvidePlayer->runAniCurrPlayer();
    } 

    pPlayer->setOperateResoult(&kTempCMD);
    pPlayer->showCard();
  }
  if (kAction.cbActionType == GameRecordOperateResult::TYPE_SendCard)
  {
    XPlayer* pPlayer = m_pPlayer[iChairID];
    if (kAction.cbOperateCard != 0)
    {
      pPlayer->addNewInCard(kAction.cbOperateCard);
    }
    pPlayer->showCard(); 

    //设置当前玩家
    for (int i = 0; i<MAX_PLAYER; i++)
    {
      m_pPlayer[i]->stopAniCurrPlayer();
    }
    pPlayer->runAniCurrPlayer();
  }
  if (kAction.cbActionType == GameRecordOperateResult::TYPE_OutCard)
  {
    Player* pPlayer = m_pPlayer[iChairID];
    for (int i = 0;i<MAX_PLAYER;i++)
    {
      m_pPlayer[i]->setActOutCard(-1);
    }
    pPlayer->sendOutCard(kAction.cbOperateCard);
    pPlayer->showCard();
  }
  if (kAction.cbActionType == GameRecordOperateResult::TYPE_ChiHu)
  {
    Player* pPlayer = m_pPlayer[iChairID];
    for (int i = 0;i<MAX_PLAYER;i++)
    {
      m_pPlayer[i]->setActOutCard(-1);
    } 

    pPlayer->setChiHuCard(kAction.cbOperateCard);
    pPlayer->showEffect("Hu");
    if (kAction.wOperateUser != kAction.wProvideUser)
    {
      XZDDPlayer* pDestPlayer = m_pPlayer[iChairID];
      pPlayer->showHandCard();
      pPlayer->showStatusImagic("Hu");
      pDestPlayer->runAniHu();
    }
    else
    {
      pPlayer->showStatusImagic("ZiMo");
      pPlayer->runAniZiMo();
    }
    pPlayer->showCard();
  } 

  m_iActRecordIdex++;
  ...
} 

于是,一场精彩的牌局就被完完整整的回放了。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

时间: 2017-03-05

麻将游戏算法深入解析及实现代码

麻将游戏算法深入解析及实现代码 这两天为了工具箱的完善,整理了这些年引擎开发的一些资料,无意中发现06年写的一个麻将算法,编译运行了一下,还是有点意思的,拿出来整理一下分享给大家. 麻将是一种大家最喜爱的娱乐活动之一,相信所有人都有接触过.我写的这版算法,是可以吃,碰,杠,还有把牌摸完没有人胡时的皇庄和包听.是用控制台方式来表现的,什么?控制台? 对,因为是算法的设计,所以用控制台来表现当然最简单了. 当然,在交互时要用文字输入会有少许不便,不过这种形式的游戏可是图形游戏的鼻祖哦~ 好,废话不多

房卡麻将分析系列 "牌局回放" 之 数据设计详解及实例

房卡麻将分析系列 "牌局回放" 之 数据设计       最近几个月,"房卡"棋牌游戏成为了资本追逐的热点,基于微信的广大用户和社交属性,"房卡"棋牌发展迅速.红孩儿团队因为之前几年有过相关项目的经验积累,鉴于未来广阔的地方棋牌市场和"开发间"机制的发展前景,也开始转向基于"开房间"棋牌游戏的项目开发中.为了更好的与开发者进行交流学习,特开设"房卡麻将游戏分析系列". 红孩儿团队研发的&

ABP(现代ASP.NET样板开发框架)系列之二、ABP入门教程详解

ABP是"ASP.NET Boilerplate Project (ASP.NET样板项目)"的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应用程序的新起点,它旨在成为一个通用的WEB应用程序框架和项目模板. ABP的官方网站:http://www.aspnetboilerplate.com ABP在Github上的开源项目:https://github.com/aspnetboilerplate ABP 的由来 "DRY--避免重复

mysql分区功能详解,以及实例分析

一,什么是数据库分区 前段时间写过一篇关于mysql分表的 的文章,下面来说一下什么是数据库分区,以mysql为例.mysql数据库中的数据是以文件的形势存在磁盘上的,默认放在/mysql/data下面 (可以通过my.cnf中的datadir来查看),一张表主要对应着三个文件,一个是frm存放表结构的,一个是myd存放表数据的,一个是myi存表 索引的.如果一张表的数据量太大的话,那么myd,myi就会变的很大,查找数据就会变的很慢,这个时候我们可以利用mysql的分区功能,在物理上将这 一张

Java List 用法详解及实例分析

Java List 用法详解及实例分析 Java中可变数组的原理就是不断的创建新的数组,将原数组加到新的数组中,下文对Java List用法做了详解. List:元素是有序的(怎么存的就怎么取出来,顺序不会乱),元素可以重复(角标1上有个3,角标2上也可以有个3)因为该集合体系有索引 ArrayList:底层的数据结构使用的是数组结构(数组长度是可变的百分之五十延长)(特点是查询很快,但增删较慢)线程不同步 LinkedList:底层的数据结构是链表结构(特点是查询较慢,增删较快) Vector

微信 小程序前端源码详解及实例分析

微信小程序前端源码逻辑和工作流 看完微信小程序的前端代码真的让我热血沸腾啊,代码逻辑和设计一目了然,没有多余的东西,真的是大道至简. 废话不多说,直接分析前端代码.个人观点,难免有疏漏,仅供参考. 文件基本结构: 先看入口app.js,app(obj)注册一个小程序.接受一个 object 参数,其指定小程序的生命周期函数等.其他文件可以通过全局方法getApp()获取app实例,进而直接调用它的属性或方法,例如(getApp().globalData) //app.js App({ onLau

解读ASP.NET 5 & MVC6系列教程(6):Middleware详解

在第1章项目结构分析中,我们提到Startup.cs作为整个程序的入口点,等同于传统的Global.asax文件,即:用于初始化系统级的信息(例如,MVC中的路由配置).本章我们就来一一分析,在这里如何初始化这些系统级的信息. 新旧版本之间的Pipeline区别 ASP.NET 5和之前版本的最大区别是对HTTP Pipeline的全新重写,在之前的版本中,请求过滤器的通常是以HttpModule为模块组件,这些组件针对HttpApplication里定义的各个周期内的事件进行响应,从而用于实现

D3.js进阶系列之CSV表格文件的读取详解

前言 之前在入门系列的教程中,我们常用 d3.json() 函数来读取 json 格式的文件.json 格式很强大,但对于普通用户可能不太适合,普通用户更喜欢的是用 Microsoft Excel 或 OpenOffice Calc 等生成的表格文件,因为简单易懂,容易编辑. Microsoft Excel 通常会保存为 xls 格式, OpenOffice Calc 通常会保存为 ods 格式.这些格式作为表格文件来说都很强大,但要读取它们是有些麻烦的,D3 中也没有提供这样的方法.但是表格软

Android 判断SIM卡属于哪个移动运营商详解及实例

Android 判断SIM卡属于哪个移动运营商 第一种方法: 获取手机的IMSI码,并判断是中国移动\中国联通\中国电信 TelephonyManager telManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); /** 获取SIM卡的IMSI码 * SIM卡唯一标识:IMSI 国际移动用户识别码(IMSI:International Mobile Subscriber Identification

在Linux中使用tcpdump命令捕获与分析数据包详解

前言 tcpdump 是一个有名的命令行数据包分析工具.我们可以使用 tcpdump 命令捕获实时 TCP/IP 数据包,这些数据包也可以保存到文件中.之后这些捕获的数据包可以通过 tcpdump 命令进行分析.tcpdump 命令在网络层面进行故障排除时变得非常方便. tcpdump 在大多数 Linux 发行版中都能用,对于基于 Debian 的Linux,可以使用 apt 命令安装它. # apt install tcpdump -y 在基于 RPM 的 Linux 操作系统上,可以使用下