游戏服务器之数据服务器存档(多进程版)

时间:2022-10-11 16:43:28



1、数据服务的逻辑循环

数据服务器的逻辑线程的循环里处理db玩家的存档数据

void main_logic_thread::run()
{
while(!isFinal())
{
setRunning();
main_logic_thread::currentTime.now();
g_server.handle_msg();//处理中心服务器消息
g_db_session_manager.handle_msg();
g_player_mgr.traverse_every_player(user_loop_exec);//遍历所有的玩家,处理缓存的数据,写到mysql数据库

...

}

...

}

 

struct EveryplayerLoopExec : public callback<db_player>
{
bool invoke(db_player *entry)
{
entry->loop();
#ifdef LY_DEBUG
        //g_log->debug("角色在线:%u,%s",entry->id, entry->name.c_str());
#endif
return true;
}
}user_loop_exec;

 

bool db_player::loop() 
{
checkFlush();//检查缓存,有数据就写到数据库
return true;
}

void db_player::checkFlush() 
{
    if (save_tag != 0) 
    {
        save();
        save_tag = 0;
}
}

2、处理接收的消息


把发来的角色数据存档消息写到缓存里

bool db_session::msgParse(const MSG::base_msg *ptrMsg, const unsigned int msglen)

{
...  
 stWriteplayerRoleDataRecordCmd *rev = (stWriteplayerRoleDataRecordCmd*)ptrMsg;
 db_player *pplayer = g_player_mgr.get_player_by_id(rev->charid);
                           
 if(pplayer)
         {

          pplayer->update_all(rev);//把发来的角色数据存档消息写到缓存里

}

...

}


角色存档的数据:角色属性数据、角色拓展数据

bool db_player::update_all(MSG::DB::stWriteplayerRoleDataRecordCmd *recv)
{
    if(recv == NULL) return false;
    update_roledata((const void*)&recv->data.roledata);
    if(recv->data.size)
    {
        update_binary_buffer(&(recv->data.data[0]), recv->data.size);
    }
    //g_log->debug("%s,%u",__PRETTY_FUNCTION__, recv->data.size);
    return true;
}

角色属性数据
bool db_player::update_roledata(const void* data)
{
    bcopy(data, (void*)&(this->roledata), sizeof(RoleData));
    this->save_tag |= (1<<SAVE_ROLE_DATA);//设置要存档的数据的标签
    return true;
}

角色拓展数据:道具数据、任务数据、金钱数据、其他数据

bool db_player::update_binary_buffer(const char* pdata, const uint32 dataLen)
{
    const char* data = pdata;
    uint32 dataSize = 0;
    while (dataSize != dataLen) 
    {
        switch (*(uint32*)&data[dataSize])
        {
            case BinaryType_Item:
                {
                    dataSize += sizeof(uint32);
                    uint32 itemSize = *(uint32*)(&data[dataSize]);
                    if(itemSize != 0)
                    {
                        bcopy((const void*)&data[dataSize], (void*)item_buffer, itemSize + sizeof(uint32));
                        dataSize += sizeof(uint32); 
                        dataSize += itemSize;
                        save_tag |= (1<<SAVE_ITEM);
                    }
                    else
                    {
                        dataSize += sizeof(uint32); 
                    }
                    //g_log->info("BinaryType_Item size:%u",itemSize);
                }
                break;
            case BinaryType_Task:
                {
                    dataSize += sizeof(uint32);
                    uint32 taskSize = *(uint32*)(&data[dataSize]);
                    if(taskSize != 0)
                    {
                        bcopy((const void*)&data[dataSize], (void*)task_buffer, taskSize + sizeof(uint32));
                        dataSize += sizeof(uint32); 
                        dataSize += taskSize;
                        save_tag |= (1<<SAVE_TASK);
                    }
                    else
                    {
                        dataSize += sizeof(uint32); 
                    }
                    //g_log->info("BinaryType_Task size:%u",taskSize);
                }
                break;
            case BinaryType_Money:
                {
                    dataSize += sizeof(uint32);
                    uint32 moneySize = *(uint32*)(&data[dataSize]);
                    if(moneySize != 0)
                    {
                        bcopy((const void*)&data[dataSize], (void*)money_buffer, moneySize + sizeof(uint32));
                        dataSize += sizeof(uint32); 
                        dataSize += moneySize;
                        save_tag |= (1<<SAVE_MONEY);
                    }
                    else
                    {
                        dataSize += sizeof(uint32); 
                    }
                    //g_log->info("BinaryType_Money size:%u",moneySize);
                }
                break;
            case BinaryType_Other:
                {
                    dataSize += sizeof(uint32);
                    uint32 otherSize = *(uint32*)(&data[dataSize]);
                    if(otherSize != 0)
                    {
                        bcopy((const void*)&data[dataSize], (void*)other_buffer, otherSize + sizeof(uint32));
                        dataSize += sizeof(uint32); 
                        dataSize += otherSize;
                        save_tag |= (1<<SAVE_OTHER);
                    }
                    else
                    {
                        dataSize += sizeof(uint32); 
                    }
                    //g_log->info("BinaryType_Other size:%u",otherSize);
                }
                break;
            default:
                {
                    g_log->error("角色[%u]二进制数据长度有误 %u,%u", this->id, dataLen, dataSize);
                    assert(0);
                }
                break;
        }
    }
    return true;
}



3、把缓存数据写到数据库

bool db_player::save() 

bool ret = false;
mysql_handle *handle = db_server::mysqlPool->getHandle();
if (!handle) 
    {
g_log->error("%u 不能从数据库连接池获取连接句柄", this->id);
return ret;
}
mysql_record column, where;
std::ostringstream os;
os << "charid = " << this->id;
where.put("charid", os.str());


    if(save_tag & (1 << SAVE_ROLE_DATA))
    {
        column.put("pk", roledata.pk);
        column.put("fight", roledata.fight);
        column.put("mapid", roledata.mapid);
        column.put("x", roledata.x);
        column.put("y", roledata.y);
        column.put("hp", roledata.hp);
        column.put("mp", roledata.mp);
        column.put("sp", roledata.sp);
        column.put("sd",roledata.sd);
        column.put("changeLife", (int16)roledata.changeLife);
        column.put("changeJob",(int16)roledata.changeJob);
        column.put("exp", roledata.exp);
        column.put("nextexp", roledata.nextexp);
        column.put("level", roledata.level);
        column.put("leftAttrPoint", roledata.leftAttrPoint);
        column.put("strength", roledata.strength);
        column.put("agility", roledata.agility);
        column.put("power", roledata.power);
        column.put("intell", roledata.intell);
        column.put("f_strength", roledata.f_strength);
        column.put("f_agility", roledata.f_agility);
        column.put("f_power", roledata.f_power);
        column.put("f_intell", roledata.f_intell);
        column.put("gold", roledata.gold);
        column.put("silver", roledata.silver);
        column.put("money", roledata.money);
        column.put("bindmoney",roledata.bindmoney);
        column.put("pkmode",(int16)roledata.pkmode);
        column.put("psychicPower",roledata.psychicPower);
        column.put("exchangedAttrNum",roledata.exchangedAttrNum);
        column.put("levelstep",roledata.levelstep);
        column.put("chaosVal",roledata.chaosVal);
        column.put("pkstate",(int16)roledata.pkstate);
        column.put("redType",(int16)roledata.redType);
        column.put("bitmask", roledata.bitmask);
        column.put("backdata", roledata.backdata);
        column.put("onlinetime", roledata.onlinetime);
        column.put("antiAddictionTime", roledata.antiAddictionTime);
        column.put("firstLoginTime", roledata.firstLoginTime);
        column.put("firstOfflineTime", roledata.firstOfflineTime);
        column.put("lastLoginTime", roledata.lastLoginTime);
        column.put("isOnline", roledata.isOnline);
        column.put("lastOfflineTime", roledata.lastOfflineTime);
        column.put("totalPayPoint", roledata.totalPayPoint);
        column.put("activity",roledata.activity);
        column.put("multiExpBeforeTwo",roledata.multiExpBeforeTwo);
        column.put("multiExpBeforeOne",roledata.multiExpBeforeOne);
        column.put("multiExpToday",roledata.multiExpToday);
        column.put("attackMax",roledata.attackMax);
        column.put("attackMagicMax",roledata.attackMagicMax);
        column.put("attackSpeed",roledata.attackSpeed);
        column.put("defend",roledata.defend);
        column.put("autoUse",(int16)roledata.autoUse);
    }
    if(save_tag & (1<<SAVE_ITEM))
    {
        uint32 itemSize = *(uint32*)(&item_buffer);
        column.put("itembinary", (const void *)(&item_buffer[4]), itemSize);
        //g_log->debug("save itembinary size :%u", itemSize);
    }
    if(save_tag & (1<<SAVE_TASK))
    {
        uint32 taskSize = *(uint32*)(&task_buffer);
        column.put("taskbinary", (const void *)(&task_buffer[4]), taskSize);
        //g_log->debug("save taskbinary size :%u", taskSize);
    }
    if(save_tag & (1<<SAVE_MONEY))
    {
        uint32 moneySize = *(uint32*)(&money_buffer);
        column.put("moneybinary", (const void *)(&money_buffer[4]), moneySize);
        //g_log->debug("save moneybinary size :%u", moneySize);
    }
    if(save_tag & (1<<SAVE_OTHER))
    {
        uint32 otherSize = *(uint32*)(&other_buffer);
        column.put("otherbinary", (const void *)(&other_buffer[4]), otherSize);
        //g_log->debug("save otherbinary size :%u", otherSize);
    }
    uint32 affect = handle->exeUpdate("ROLE", &column, &where);
    if (1 == affect || 0 == affect)
    {
        ret = true;
    } 
    else
    {
        g_log->error("用户 [%u,%s] 存档失败,errno:%u,tag:%u", this->id,this->name.c_str(),affect,save_tag);
        ret = false;
    }
    db_server::mysqlPool->putHandle(handle);
    return ret;
}



4、执行mysql sql语句

(1)更新语句

把数据和条件组成sql语句来执行。

unsigned int mysql_handle::exeUpdate(const char *tablename, mysql_record* data, mysql_record* where)
{
mysql_table* table  = this->pool->tm[this->_hashcode]->getTableByName(tablename);


if (NULL == data)
{
error_log("执行%s时data指针为空",__FUNCTION__);
return (unsigned int)-1;
}
if (NULL == _mysql)
{
error_log("执行%s时_mysql指针为空",__FUNCTION__);
return (unsigned int)-1;
}
if (NULL == table)
{
error_log("执行%s时table指针为空,找不到%s表",__FUNCTION__,tablename);
return (unsigned int)-1;
}


_update_time.now();


std::ostringstream query_string;


query_string << "UPDATE ";
query_string << "`" << table->name << "`";
query_string << " SET ";


struct UpdateExec : public callback<mysql_field>
{
bool _first;
mysql_table* _table;
std::ostringstream &query_string;
mysql_handle *_handle;
UpdateExec(mysql_table* table, std::ostringstream &query_string, mysql_handle *handle)
: _first(true), _table(table), query_string(query_string), _handle(handle)
{
}
bool invoke(mysql_field *entry)
{
mysql_field *basefield = _table->fs.get(entry->name.c_str());
if(!basefield)
{
return true;
}
if (_first)
{
_first=false;
}
else
{
query_string <<  " ,";
}


query_string << "`" << entry->name << "`" << " = ";


_handle->storeValue(_table, query_string, basefield->type, entry->data);
return true;
}
} udcb(table, query_string, this);


data->traverseField(udcb);


getWhere(table, query_string, where);
unsigned int ret = (unsigned int)-1;
if (0 == execSql(query_string.str().c_str(), query_string.str().size()))
{
ret = (unsigned int)mysql_affected_rows(_mysql);
}


if(_update_time.elapse(realtime()) >= 3 * 1000L)
{
std::ostringstream where_string;
getWhere(table, where_string, where);
g_log->warn("超时%llu毫秒sql:%s",_select_time.elapse(realtime()),query_string.str().c_str());
}
return ret;
}



数据服务器的逻辑线程的循环里处理db玩家的存档数据

void main_logic_thread::run()
{
while(!isFinal())
{
setRunning();
main_logic_thread::currentTime.now();
g_server.handle_msg();//处理中心服务器消息
g_db_session_manager.handle_msg();
g_player_mgr.traverse_every_player(user_loop_exec);//遍历所有的玩家,处理缓存的数据,写到mysql数据库

...

}

...

}