前言

游戏开发中,玩家数据格式不是一成不变的,保证玩家数据格式变更后玩家上线后依然能够进行游戏是基本原则。 本文主要阐述数据库是Mysql时,以玩家表human为例子说明玩家数据兼容的一些方法。

CREATE TABLE `human` (
    `uid` int(10) NOT NULL COMMENT '玩家唯一ID',
    `name` varchar(50) COLLATE utf8_unicode_ci DEFAULT '' COMMENT '玩家名字',
    `head` varchar(50) COLLATE utf8_unicode_ci DEFAULT '[1,1,3]' COMMENT '当前头像',
    `exp` int(10) unsigned DEFAULT '0' COMMENT '当前经验',
    `level` int(3) unsigned DEFAULT '0' COMMENT '当前等级',
    `age` int(3) unsigned DEFAULT '0' COMMENT '玩家年龄',
    `gold` bigint(20) unsigned DEFAULT '0' COMMENT '当前元宝数',
    `diamond` bigint(20) unsigned DEFAULT '0' COMMENT '当前钻石数量',
    `VIP` int(2) unsigned DEFAULT '0' COMMENT 'VIP等级',
    `ctGetSkin` text COLLATE utf8_unicode_ci COMMENT '已获得不重复皮肤CfgId',
    PRIMARY KEY (`uid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='玩家基础信息';
human对象结构
{
    uid:0,
    name:'',
    exp:0,
    level:0,
    age:0,
    gold:0,
    diamond:0,
    VIP:0,
    ctGetSkin:'{}'
}

一、增删某个字段

eg.策划需求变更,需要删除human.age字段 1.直接删除tb human.age字段 2.删除human实例化对象中age属性

二、字段数据类型变更

eg.uid从int类型转换为varchar 1.tb human.age字段类型能强转,直接转换 1.1 human实例化对象中uid属性调整为string

2.human中uid类型不能强转,uid字段可以变更 2.1 采用增删字段的解决方式

3.human中uid类型不能强转,uid字段不可以变更 3.1 新建字段内uid_1 3.2 写策略把uid值调整为目标格式,写到uid_1内 3.3 删除uid字段,uid_1字段重命名为uid

3.*的方式不推荐,操作复杂,容易出错。

三、字段内数据内容格式变更

eg. ctGetSkin:‘{id:{num},id:{num}}’ => ctGetSkin:‘{id:{num, liftTime},id:{num, liftTime}}’ 1.tb human.ctGetSkin 字段不变更 1.1 skin对象的添加lifeTime属性 1.2 调整ctGetSkin的解析方式,ctGetSkin = OnLoadCtGetSkin(reqly.ctGetSkin);

//CtGetSkin对象
function CtGetSkinDefault() {
    return {};
}

//Skin对象
function SkinDefault() {
    return {
        num:1,
        lifeTime:-1,
    }
}

//对象兼容解析
function ObjCompatible(tarObj, srcObj) {
    if(!srcObj) {
        return tarObj;
    }
    if(typeof srcObj === 'string'){
        srcObj = JSON.parse(srcObj);
    }
    if(typeof srcObj !== 'object'){
        return tarObj;
    }
    for(let i in tarObj){
        let k = i + '';
        if(srcObj[k] === undefined || srcObj[k] === null) {
            continue;
        }
        if(typeof tarObj[k] === 'object') {
            ObjCompatible(tarObj[k], srcObj[k]);
            continue;
        }
        tarObj[k] = srcObj[k];
    }
    return tarObj;
}

//数据解析容错
function OnLoadCtGetSkin(data) {
    let ret = CtGetSkinDefault();
    if(typeof data === 'string') {
        data = JSON.parse(data);
    }
    if(typeof data !== 'object') {
        return ret;
    }
    //应该做一个属性递归判断
    for(let k in ret) {
        if(data[k] === undefined || data[k] === null) {
            continue;
        }
        ret[k] = ObjCompatible(SkinDefault(), data[k]);
    }
}

归纳总结

分析完上面三个场景解决方案,发现OnLoadCtGetSkin的这种方式可以通用。只要设计好一套的human解析方式,兼容问题代码方面基本不会出问题,所有关注点就在table调整上了。