项目应用
游戏中,将对局的数据保留下来,用于对局回顾及debug等用途,由于协议采用PB,故以二进制的pb格式写入文件,在使用该对局内容的时候,按照格式反序列化出来用于播放对局、压测数据构造等。
涉及的部分: pvp服务器,产生对局数据,然后通过路由发送到recordsvr,一个专门写文件的服务器,写完文件后,使用时对文件解析。
文件的格式: head-data-head-data….
相关的协议如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| message RecordReq { optional uint64 game_id = 1; optional string record_name = 2; optional bool finish = 3; optional RecordPkg Pkg = 4; }
message RecordPkg { optional uint32 time_offfect = 1; optional uint32 cmd = 2; optional bytes fragment_data = 3; }
message RecordHead { required fixed32 cmd = 1; required fixed32 len = 2; required fixed32 time_offect = 3; }
|
pvp对局服务器,产生数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| void CPvPGame::SendRecord(uint16_t uiCmd, const Message* msg, bool bFinish/* = false*/) { unsigned int uiNow = CSingletonPvPDateTime::instance()->GetSec(); uint32_t uiTimeOffset = uiNow - m_battleInfo.battle_base.battle_start_time;
RecordReq msgReq; RecordPkg* pPkg = msgReq.mutable_pkg(); if (NULL == pPkg) { return; } msgReq.set_game_id(GetGameId()); msgReq.set_record_name(GetRecordName()); msgReq.set_finish(bFinish); pPkg->set_time_offfect(uiTimeOffset); pPkg->set_cmd(uiCmd);
if (GAME_RECORD_START == uiCmd || GAME_RECORD_END == uiCmd) { GameDataInfo gameData; FillGameInfo(&gameData); //修正starttime gameData.mutable_game_init_info()->set_start_time(m_uiStartTime); gameData.SerializeToString(pPkg->mutable_fragment_data()); } else if (NULL != msg) { msg->SerializeToString(pPkg->mutable_fragment_data()); } else { return; }
int iRet = 0; iRet = CSingletonPvPProcessor::instance()-> SendMsgToAllServerByIdc(GetOwer(), RecordSvrCmd::RECORD_REQ, msgReq, SERVER_FAMILY_RECORDSVR);
|
录像服务器,专门写文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| std::stringstream sstrNameTmp; sstrNameTmp << szRecordDir << "/tmp_" << msgReq.game_id() << "_tmp";
RecordHead rHead; rHead.set_cmd(msgReq.pkg().cmd()); rHead.set_len(msgReq.pkg().fragment_data().size()); rHead.set_time_offect(msgReq.pkg().time_offfect());
//std::fstream output(sstrNameTmp.str().c_str(), std::ios::out | std::ios::binary | std::ios::app); std::fstream& output = OpenFile(sstrNameTmp.str().c_str());
std::string strout; rHead.SerializeToString(&strout); output << strout; if (msgReq.pkg().has_fragment_data()) { output << msgReq.pkg().fragment_data(); }
//output.close();
LOG_DEBUG(0, 0, "write record|%lu|%s", msgReq.game_id(), sstrNameTmp.str().c_str()); LOG_DEBUG(0, 0, "write record|%lu|%d|%s", msgReq.game_id(), rHead.ByteSize(), rHead.ShortDebugString().c_str());
if (msgReq.finish()) { CloseFile(sstrNameTmp.str().c_str()); rename(sstrNameTmp.str().c_str(), strLogName.c_str()); LOG_DEBUG(0, 0, "rename|%lu|%s|%s", msgReq.game_id(), sstrNameTmp.str().c_str(), strLogName.c_str()); }
|
解析对局数据,用于回放或性能测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| bool Robot::ReadData() { m_IsInitGameDataInfo = true; char* writebuff=RunTime::GetInst()->getwritebuff(); long filelength=RunTime::GetInst()->getfilelength(); DEBUG("enter ReadData"); DEBUG("writebuff len:%d,length:%ld",(int)strlen(writebuff),filelength);
/* message RecordHead { required fixed32 cmd = 1; //cmd 为0的时候,包体为GameDataInfo结构 required fixed32 len = 2; required fixed32 time_offect = 3; } */ // 先 算一个包头 RecordHead recHead; recHead.set_cmd(869); // GAME_RECORD_START recHead.set_len(1024); // fixed32 any 32 bit recHead.set_time_offect(1024); // fixed32; any 32 bit std::string strout; recHead.SerializeToString(&strout); int HeadSize = strout.size();
recHead.ParseFromArray(writebuff, HeadSize ); DEBUG("RecordHead: %s", recHead.ShortDebugString().c_str());
m_gameDataInfo.ParseFromArray(writebuff + HeadSize, recHead.len()); DEBUG("m_gameDataInfo: %s", m_gameDataInfo.ShortDebugString().c_str());
|