房卡麻将分析系列 "牌局回放" 之 数据设计详解及实例
发布时间 - 2026-01-11 00:03:22 点击率:次房卡麻将分析系列 "牌局回放" 之 数据设计

最近几个月,”房卡“*游戏成为了资本追逐的热点,基于微信的广大用户和社交属性,”房卡”*发展迅速。红孩儿团队因为之前几年有过相关项目的经验积累,鉴于未来广阔的地方*市场和”开发间“机制的发展前景,也开始转向基于”开房间“*游戏的项目开发中。为了更好的与开发者进行交流学习,特开设”房卡麻将游戏分析系列“。
红孩儿团队研发的"大赢家"红中麻将
本套麻将分析基于网络上流传的“网狐”房卡麻将源码做为基础,按照功能模块分为"架设指南",”服务器框架","后台系统","胡牌算法","客户端界面",“防作弊功能”等等细节做一些分析和指导,帮助广大的*游戏开发者迅速掌握“房卡”麻将的研发原理和技巧设计。也希望有兴趣的朋友多多关注。
第一次开公众号,挑个简单的下手,先来讲一讲房卡麻将中一个重要功能:“牌局回放”,我们都知道,*类游戏注重公平真实不作弊,如果玩家感觉到游戏的过程有作弊,我相信他一定会对这款游戏失去兴趣。但作弊与否,玩家并不容易进行判断。这时候提供一个“牌局回放”功能给玩家进行分析就尤为重要。
“网狐”等一些长期耕耘在*领域的企业,在这方面都有完整的经验和框架,通过参考,我发现它是通过下面一套流程来完成”牌局回放“功能的。
首先,在游戏服务器的房间类CTableFrameSink里需要有一个GameRecord结构,这个结构对 玩家信息,手牌以及每一步的动作都可以进行相应的记录:
struct GameRecordPlayer
{
DWORD dwUserID;
std::string kHead;
std::string kNickName;
std::vector<BYTE> cbCardData;
void StreamValue(datastream& kData, bool bSend)
{
Stream_VALUE(dwUserID);
Stream_VALUE(kHead);
Stream_VALUE(kNickName);
Stream_VECTOR(cbCardData);
}
};
struct GameRecordOperateResult
{
enum Type
{
TYPE_NULL,
TYPE_OperateResult,
TYPE_SendCard,
TYPE_OutCard,
TYPE_ChiHu,
};
GameRecordOperateResult()
{
cbActionType = 0;
wOperateUser = 0;
wProvideUser = 0;
cbOperateCode = 0;
cbOperateCard = 0;
}
BYTE cbActionType;
WORD wOperateUser; //操作用户
WORD wProvideUser; //供应用户
BYTE cbOperateCode; //操作代码
BYTE cbOperateCard; //操作*
void StreamValue(datastream& kData, bool bSend)
{
Stream_VALUE(cbActionType);
Stream_VALUE(wOperateUser);
Stream_VALUE(wProvideUser);
Stream_VALUE(cbOperateCode);
Stream_VALUE(cbOperateCard);
}
};
struct GameRecord
{
std::vector<GameRecordPlayer> kPlayers;
std::vector<GameRecordOperateResult> kAction;
void StreamValue(datastream& kData, bool bSend)
{
StructVecotrMember(GameRecordPlayer, kPlayers);
StructVecotrMember(GameRecordOperateResult, kAction);
}
void CleanUp()
{
kPlayers.clear();
kAction.clear();
}
};
在datastream.h中,有一套set,get数据流的宏,能够将数据放入到数据流中或从中拿出。
#define Stream_VALUE(Name) \
if(bSend) \
{ \
kData.pushValue(Name);\
}\
else\
{\
kData.popValue(Name);\
}\
好了,有了这样一个结构,在游戏开始的时候,我们就可以开始记录本局了。
//游戏开始
void CTableFrameSink::GameStart()
{
...
//填充四个玩家的基础信息
for (int i = 0; i < 4; i++)
{
GameRecordPlayer tNewRecordPlayer;
tagUserInfo * tpUserInfo = m_pITableFrame->GetTableUserItem(i)->GetUserInfo();
tNewRecordPlayer.dwUserID = tpUserInfo->dwUserID;
tNewRecordPlayer.kNickName = tpUserInfo->szNickName;
//取得手牌信息
BYTE cbCardData[MAX_COUNT];
m_GameLogic.SwitchAllToCardData(m_cbCardIndex[i], cbCardData);
for (int j = 0; j < MAX_COUNT ; j++)
{
tNewRecordPlayer.cbCardData.push_back(cbCardData[j]);
}
//存储到当前记录结构中的玩家信息容器。
m_sGameRecord.kPlayers.push_back(tNewRecordPlayer);
}
}
然后我们开始记录操作,分别在玩家出牌,以及玩家应答吃,碰,杠,胡等操作时加入记录。
//用户出牌
bool CTableFrameSink::OnUserOutCard(WORD wChairID, BYTE cbCardData)
{
...
//记录动作数据
GameRecordOperateResult tNewRecordOperateResult;
tNewRecordOperateResult.cbActionType = GameRecordOperateResult::TYPE_OutCard;
tNewRecordOperateResult.cbOperateCard = cbCardData;
tNewRecordOperateResult.cbOperateCode = WIK_NULL;
tNewRecordOperateResult.wOperateUser = wChairID;
tNewRecordOperateResult.wProvideUser = wChairID;
m_sGameRecord.kAction.push_back(tNewRecordOperateResult);
...
}
//用户操作
bool CTableFrameSink::OnUserOperateCard(WORD wChairID, BYTE cbOperateCode, BYTE cbOperateCard)
{
...
//记录动作数据
GameRecordOperateResult tNewRecordOperateResult;
tNewRecordOperateResult.cbActionType = XZDDGameRecordOperateResult::TYPE_OperateResult;
tNewRecordOperateResult.cbOperateCard = cbOperateCard;
tNewRecordOperateResult.cbOperateCode = cbOperateCode;
tNewRecordOperateResult.wOperateUser = wChairID;
tNewRecordOperateResult.wProvideUser = m_wProvideUser;
m_sGameRecord.kAction.push_back(tNewRecordOperateResult);
...
}
就这样,基本的操作记录也完成了。最后当牌局结束时,我们需要将记录提交到数据库中。
//游戏结束
bool CTableFrameSink::OnEventGameConclude(WORD wChairID, IServerUserItem * pIServerUserItem, BYTE cbReason)
{
switch (cbReason)
{
case GER_NORMAL: //常规结束
{
...
//将记录转化为数据流。
datastream kDataStream;
m_sGameRecord.StreamValue(kDataStream, true);
//除去写分等处理,这里最后一个参数即是数据流。
m_pITableFrame->WriteTableScore(ScoreInfoArray, CountArray(ScoreInfoArray), kDataStream);
...
}
}
}
在私人场服务器中,会通过WriteTableScore这个函数调用PrivateTableInfo的writeSocre,它将将数的流记录下来。
并最终在牌局结束时DismissRoom(pTableInfo);发给了数据库。
数据库最终会通过一个存储过程的执行完成将数据流入库的工作。具体的代码就不再展示了,大家可以参考
CDataBaseEngineSink::OnRequestPrivateGameRecord()。
这样一套完整的回放数据流程就结束了。
好,今天的分析就到这里,红孩儿欢迎大家下次继续听课哦~
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
# 牌局回放
# 数据设计
# 数据设计详解及实例
# 详解房卡麻将分析系列 "牌局回放" 之 播放处理
# 麻将游戏算法深入解析及实现代码
# 红中
# 结束时
# 出牌
# 都有
# 好了
# 在这
# 感觉到
# 这款
# 几年
# 它是
# 有过
# 给了
# 希望能
# 有兴趣
# 这样一个
# 欢迎大家
# 几个月
# 一个重要
# 即是
# 谢谢大家
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
如何在 Telegram Web View(iOS)中防止键盘遮挡底部输入框
nginx修改上传文件大小限制的方法
东莞市网站制作公司有哪些,东莞找工作用什么网站好?
Laravel中DTO是什么概念_在Laravel项目中使用数据传输对象(DTO)
Linux系统命令中screen命令详解
Laravel怎么实现软删除SoftDeletes_Laravel模型回收站功能与数据恢复【步骤】
敲碗10年!Mac系列传将迎来「触控与联网」双革新
Laravel 419 page expired怎么解决_Laravel CSRF令牌过期处理
如何在阿里云域名上完成建站全流程?
悟空识字如何进行跟读录音_悟空识字开启麦克风权限与录音
网站优化排名时,需要考虑哪些问题呢?
Win11怎么设置默认图片查看器_Windows11照片应用关联设置
,网页ppt怎么弄成自己的ppt?
如何快速查询域名建站关键信息?
Laravel怎么生成二维码图片_Laravel集成Simple-QrCode扩展包与参数设置【实战】
浅析上传头像示例及其注意事项
如何用5美元大硬盘VPS安全高效搭建个人网站?
php静态变量怎么调试_php静态变量作用域调试技巧【解答】
JavaScript模板引擎Template.js使用详解
Android自定义listview布局实现上拉加载下拉刷新功能
google浏览器怎么清理缓存_谷歌浏览器清除缓存加速详细步骤
如何快速辨别茅台真假?关键步骤解析
Win11怎么关闭资讯和兴趣_Windows11任务栏设置隐藏小组件
香港服务器如何优化才能显著提升网站加载速度?
重庆市网站制作公司,重庆招聘网站哪个好?
Laravel如何实现事件和监听器?(Event & Listener实战)
Python文件流缓冲机制_IO性能解析【教程】
Laravel怎么多语言本地化设置_Laravel语言包翻译与Locale动态切换【手册】
,交易猫的商品怎么发布到网站上去?
网站制作软件有哪些,制图软件有哪些?
CSS3怎么给轮播图加过渡动画_transition加transform实现【技巧】
如何快速生成专业多端适配建站电话?
微博html5版本怎么弄发语音微博_语音录制入口及时长限制操作【教程】
Laravel如何使用Blade模板引擎?(完整语法和示例)
微信公众帐号开发教程之图文消息全攻略
Laravel怎么配置.env环境变量_Laravel生产环境敏感数据保护与读取【方法】
如何在Windows环境下新建FTP站点并设置权限?
如何在新浪SAE免费搭建个人博客?
Laravel Blade模板引擎语法_Laravel Blade布局继承用法
Laravel全局作用域是什么_Laravel Eloquent Global Scopes应用指南
Laravel如何配置中间件Middleware_Laravel自定义中间件拦截请求与权限校验【步骤】
公司网站制作价格怎么算,公司办个官网需要多少钱?
如何在阿里云完成域名注册与建站?
Laravel如何配置任务调度?(Cron Job示例)
Win11应用商店下载慢怎么办 Win11更改DNS提速下载【修复】
JavaScript如何实现路由_前端路由原理是什么
laravel怎么用DB facade执行原生SQL查询_laravel DB facade原生SQL执行方法
Laravel策略(Policy)如何控制权限_Laravel Gates与Policies实现用户授权
php在windows下怎么调试_phpwindows环境调试操作说明【操作】
如何在 Python 中将列表项按字母顺序编号(a.、b.、c. …)
下一篇:酷狗音乐多音轨开启方法
下一篇:酷狗音乐多音轨开启方法

