日志接入流程大致分为三个步骤:
1.客户端日志接入开发负责人根据日志接入流程文档,调用日志接口,完成几个基础日志的记录;
2.掌趣科技数据中心对客户端接入的日志进行格式和字段内容检查;
3.客户端日志接入开发负责人与掌趣技术人员通过日志接入测试用例后,日志接入工作正式完成。
JSSDK日志接口,用于采集客户端的诸如玩家注册,玩家登录,玩家充值,玩家虚拟物品变更,玩家属性变更等用户行为,为产品运营,推广营销等工作提供数据支持。
在游戏主HTML页面必须引入掌趣 ourpalm-sdk-agent.js,后续接口都有该js提供。
<script type="text/javascript" src="https://gscservice.gamebean.net/h5sdk/v1/js/ourpalm-sdk-agent.js"></script>
建议放置到页面顶部,优先加载 ourpalm-sdk-agent.js
上线后必须使用https;
在调用日志接口时,客户端需要为日志接口提供日志ID,日志关键字,日志内容三个参数,其中,日志内容(content)是一个json格式的字符串,里面详细描述了该次用户行为的具体信息。例如:一个玩家虚拟物品变更日志,他的几个参数大概是这样的:
{
"content": {
"roleLevel":"10",
"roleVipLevel":"2",
"updateType":"1",
"itemId":"diamond",
"itemName":"钻石",
"itemCount":"100",
"custom":"商城购买"
},
"id": "9",
"key": "role-item-update"
}
从示例中可见,
日志的id为9,日志的key为role-item-update 意味着这是一条物品变更日志
当时用户的角色等级为10级
当时用户的vip等级为2级
updateType字段,即消费类型记录的是1,意味着用户获得了道具
itemId字段记录的是diamond,itemName字段记录的是钻石,意味着用户获得的道具叫钻石
itemCount字段记录了100,意味着用户获得的钻石数量是100个
custom字段记录的是商城购买,意味着用户是通过购买操作获得这100个钻石
从这样一条日志记录中,我们就可以分析出某个用户于某个时间(sdk会在发送日志时自动把时间,玩家所在逻辑服,角色名,角色id等信息带上),通过商城购买了100钻石,在系统给他发放这100钻石时,他的等级为10,vip等级为2。
需要注意的地方:
当传入中文字符串时,务必确保该中文字符串使用的是utf8编码。例如,商品名称。
由的产品人员和开发人员自行设定的字段信息,也必须由相应的负责人确保内容的一致性。 例如同一个逻辑服下,如果在用户登录注册时调用日志接口传递的逻辑服标识是1001,那么在调用计费中心的充值计费接口时逻辑服标识也应该是同样的1001,而不应该是其他的值。同样的,如果A用户在购买一个极品武器时记录的物品id是001weapon,那么另外一个用户在拍卖同样一个极品武器时物品id也应该是001weapon。
功能说明
收集游戏玩家行为信息,用于统计分析玩家行为。
接口定义
window.ourpalmSdk.log(logInfo);
参数说明
日志信息 logInfo
参数名称 | 重要性 | 类型 | 说明 |
---|---|---|---|
id | 必须 | 字符串 | 日志ID |
key | 必须 | 字符串 | 日志KEY |
content | 必须 | 字符串 | 日志内容,json格式 |
接口示例
let logInfo = {};
logInfo.id = '9';
logInfo.key = 'role-item-update';
logInfo.content = {};
logInfo.content.roleLevel = '10';
logInfo.content.roleVipLevel = '2';
logInfo.content.updateType = '1';
logInfo.content.itemId = 'diamond';
logInfo.content.itemName = '钻石';
logInfo.content.itemCount = '100';
logInfo.content.custom = '商城购买';
window.ourpalmSdk.log(logInfo);
LogId和LogKey对应表
id | key | 描述 | GSC平台显示名称 |
---|---|---|---|
91 | role-income-update | 玩家虚拟货币变更 | 货币变更 |
9 | role-item-update | 玩家虚拟物品变更 | 物品变更 |
10 | role-prop-update | 玩家属性变更 | 玩家属性变更 |
1001 | role-task | 任务 | 游戏任务 |
1002 | role-stage | 副本,场景 | 游戏关卡 |
1003 | role-act | 玩家其他事件 | 其他事件 |
2001 | role-interact | 自定义交互事件 | 交互日志 |
当用户在游戏内,因为种种原因产生虚拟货币的增加和消耗时,需要记录一条对应的虚拟货币变更日志。
变更类型:用于标识本次变更是新增了虚拟货币还是消耗了虚拟货币,新增记录1,消耗记录0。
自定义变更描述:用于标识用户因为什么原因变更虚拟货币,如任务获得,怪物掉落,系统赠送,副本奖励,世界喊话,家族奉献,婚姻喜宴等等。
货币ID:用于记录本次引发虚拟货币变更的货币ID
货币名称:用于记录本次引发虚拟货币变更的货币名称
是否为珍贵物品:用于区分珍贵物品,一般情况下直接充值获得的即为珍贵物品,例如 钻石和铜币, 钻石可视为珍贵物品,铜币可视为非珍贵物品。
货币个数:用于记录用户本次消费行为涉及多少个货币,如果用户本次消费行为与具体货币无关,则该字段置空即可。
变化后货币数量:用于记录获得或者消耗后剩余的货币数量
变更描述:用于记录货币变化的原因或途径
需要注意的是,如果玩家的一次用户行为导致多种虚拟货币变更,比如完成一次购买同时获得1000钻石20000铜币,则需要记录相应的两条虚拟货币变更日志。
id | 日志名称 | key |
---|---|---|
91 | 玩家虚拟货币变更日志 | role-income-update |
字段名 | 字段类型 | 字段说明 | 备注 |
---|---|---|---|
roleLevel | Integer | 角色等级 | |
roleVipLevel | Integer | 角色vip等级 | |
updateType | String | 变更类型 | 1:新增 0:消耗 |
itemId | String | 货币ID | |
itemName | String | 货币名称 | 便于运营人员识别商品,一般传入中文 |
isPrecious | String | 是否为珍贵物品 | 1:是 0:否 |
itemCount | Integer | 货币个数 | 必须是一个大于0的整数 |
remains | Integer | 变化后货币数量 | 必须是一个大于等于0的整数 |
custom | String | 变更描述 | 描述变更的原因或途径 |
日志格式示例:
let logInfo = {};
logInfo.id = '91';
logInfo.key = 'role-income-update';
logInfo.content = {};
logInfo.content.roleLevel = '1';
logInfo.content.roleVipLevel = '0';
logInfo.content.updateType = '0';
logInfo.content.itemId = 'diamond';
logInfo.content.itemName = '钻石';
logInfo.content.isPrecious = '1';
logInfo.content.itemCount = '100';
logInfo.content.remains = '2000';
logInfo.content.custom = '购买道具';
window.ourpalmSdk.log(logInfo);
上面日志表示当前登录的角色为了购买道具花费了100钻石、剩余2000 钻石。
当用户在游戏内,因为种种原因产生虚拟物品的增加和消耗时,需要记录一条对应的虚拟物品变更日志。
变更类型:用于标识本次变更是新增了虚拟物品还是消耗了虚拟物品,新增记录1,消耗记录0。
自定义变更描述:用于标识用户因为什么原因变更虚拟物品,如任务获得,怪物掉落,系统赠送,副本奖励,世界喊话,家族奉献,婚姻喜宴等等。
商品ID:用于记录用户本次是因为哪一个游戏内的商品引发的虚拟物品变更,例如购买游戏内的装备、经验、头衔、坐骑等。商品名称由游戏策划人员自行定义,比如 0001对应装备图纸,0002对应黄金翅膀,0003对应经验丹,0004对应体力等等。传入0001,0002,0003……这些ID即可
商品名称:用于记录用户本次是因为哪一个游戏内的商品引发的虚拟物品变更,例如购买游戏内的装备、经验、头衔、坐骑等。商品名称由游戏策划人员自行定义,比如 0001装备图纸,0002黄金翅膀,0003经验丹,0004体力等等。统计结果也会直接将日志中的商品名称展示出来,如果用户本次变更行为与具体商品道具无关,则该字段置空即可。
商品个数:用于记录用户本次消费行为涉及多少个商品,如果用户本次消费行为与具体商品无关,则该字段置空即可。
商品剩余个数:用于记录本次商品获得或者消耗后,当前剩余的个数。
变更描述:用于记录本次商品获得或者消耗的原因或途径。
需要注意的是,如果玩家的一次用户行为导致多种虚拟物品变更,比如完成一次史诗副本同时获得1000珍珠20000石币,则需要记录相应的两条虚拟物品变更日志。
id | 日志名称 | key |
---|---|---|
9 | 玩家虚拟物品变更日志 | role-item-update |
字段名 | 字段类型 | 字段说明 | 备注 |
---|---|---|---|
roleLevel | Integer | 角色等级 | |
roleVipLevel | Integer | 角色vip等级 | |
updateType | String | 变更类型 | 1:新增 0:消耗 |
itemId | String | 商品ID | |
itemName | String | 商品名称 | 便于运营人员识别商品,一般传入中文 |
itemCount | Integer | 商品个数 | 必须是一个大于0的整数 |
remains | Integer | 商品剩余个数 | 必须是一个大于等于0的整数 |
custom | String | 变更描述 | 描述变更的原因或途径 |
日志格式示例:
let logInfo = {};
logInfo.id = '9';
logInfo.key = 'role-item-update';
logInfo.content = {};
logInfo.content.roleLevel = '1';
logInfo.content.roleVipLevel = '0';
logInfo.content.updateType = '0';
logInfo.content.itemId = 'diamond';
logInfo.content.itemName = '钻石';
logInfo.content.itemCount = '100';
logInfo.content.remains = '255';
logInfo.content.custom = '购买';
window.ourpalmSdk.log(logInfo);
let logInfo = {};
logInfo.id = '9';
logInfo.key = 'role-item-update';
logInfo.content = {};
logInfo.content.roleLevel = '1';
logInfo.content.roleVipLevel = '0';
logInfo.content.updateType = '1';
logInfo.content.itemId = 'MGH_Weapon_001';
logInfo.content.itemName = '玛格汉铁锤';
logInfo.content.itemCount = '1';
logInfo.content.remains = '2';
logInfo.content.custom = '购买';
window.ourpalmSdk.log(logInfo);
上面两条日志表示当前登录的角色马克.吐温为了购买id为玛格汉铁锤 的武器花了100钻石,剩余255钻石,拥有了2把玛格汉铁锤。
当用户的角色,发生一些属性上的变化时,记录相应的角色属性变更日志。例如等级提升,军衔提升,VIP等级提升,属性点重配,种族天赋变更等等。
属性标识:角色变更了哪种属性,其中等级标识为level,VIP等级标识为viplevel,其余属性标识由游戏产品和开发人员自行设定。
新属性值:角色完成属性变更之后,该属性的值变成了什么。
变更值:如果该属性值是一个数字,比如等级level,那么本次用户的等级变更了多少,例如购买大礼包level连升三级,则变更值为3。
变更描述:用于记录属性变化的原因或途径
id | 日志名称 | key |
---|---|---|
10 | 玩家属性变更日志 | role-prop-update |
字段名 | 字段类型 | 字段说明 | 备注 |
---|---|---|---|
roleLevel | Integer | 角色等级 | |
roleVipLevel | Integer | 角色vip等级 | |
propKey | String | 属性标识 | |
propValue | String | 新属性值 | |
rangeability | String | 变更值 | |
custom | String | 变更描述 | 描述变更的原因或途径 |
日志格式示例:
let logInfo = {};
logInfo.id = '10';
logInfo.key = 'role-prop-update';
logInfo.content = {};
logInfo.content.roleLevel = '3';
logInfo.content.roleVipLevel = '0';
logInfo.content.propKey = 'level';
logInfo.content.propValue = '3';
logInfo.content.rangeability = '2';
logInfo.content.custom = '';
window.ourpalmSdk.log(logInfo);
let logInfo = {};
logInfo.id = '10';
logInfo.key = 'role-prop-update';
logInfo.content = {};
logInfo.content.roleLevel = '2';
logInfo.content.roleVipLevel = '1';
logInfo.content.propKey = 'vipLevel';
logInfo.content.propValue = '1';
logInfo.content.rangeability = '1';
logInfo.content.custom = '首次充值';
window.ourpalmSdk.log(logInfo);
该记录表示当前角色马克.吐温连升了两级,目前等级为3。他的vip等级升了一级,目前vip等级为1
某些游戏的游戏内商品也有各种属性,比如射击类有戏的枪械会因为改装增加各种新功能,赛车游戏的赛车可以更改引擎、喷漆、轮胎,rpg游戏的玩家会饲养和升级宠物,升级随从的等级和装备等等,因此我们提供了一个游戏内商品属性变更日志,用于记录游戏内各种物品,道具,宠物,随从,武器的等级、技能等属性变更情况。
玩家商品属性变更日志的格式与玩家属性变更日志的区别在于,商品属性变更日志中多了两个用于描述商品的itemId和itemName字段。
id | 日志名称 | key |
---|---|---|
13 | 玩家商品属性变更日志 | role-item-prop-update |
字段名 | 字段类型 | 字段说明 | 备注 |
---|---|---|---|
roleLevel | Integer | 角色等级 | 必需 |
roleVipLevel | Integer | 角色vip等级 | 必需 |
itemId | String | 商品ID | 必需 |
itemName | String | 商品名称 | 便于运营人员识别商品,一般传入中文,必需 |
propKey | String | 属性标识 | 必需 |
propValue | String | 新属性值 | 必需 |
rangeability | String | 变更值 | 必需 |
custom | String | 变更描述 | 描述变更的原因或途径 |
日志格式示例:
let logInfo = {};
logInfo.id = '13';
logInfo.key = 'role-item-prop-update';
logInfo.content = {};
logInfo.content.roleLevel = '1';
logInfo.content.roleVipLevel = '0';
logInfo.content.itemId = 'tank_m4';
logInfo.content.itemName = 'M4谢尔曼中型坦克';
logInfo.content.propKey = 'level';
logInfo.content.propValue = '3';
logInfo.content.rangeability = '1';
logInfo.content.custom = '强化装备';
window.ourpalmSdk.log(logInfo);
该条日志记录,玩家的M4谢尔曼中型坦克等级提升1级,现在的等级为3级
需要注意的是,只有游戏内对玩家游戏体验非常重要的游戏内商品,比如能够辅助战斗的宠物、随从,赛车游戏的赛车和射击游戏的枪械等等才需要记录该日志; 另外有一个需要注意的地方是role-prop-update用于记录玩家游戏角色的等级和其他属性变更信息,而role-item-prop-update用于记录玩家角色持有的游戏内商品属性变更,两个日志的用途不同,描述的对象也不一样,请在接入时注意区分。
对于卡牌类游戏而言,卡牌的收集和升级一直是游戏重点,比如卡牌的稀有程度,等级变化,技能等级等等,因此我们提供了一个游戏内卡牌属性变更日志,用于记录游戏内对应英雄、侠客、随扈等等卡牌的属性变更情况。
玩家卡牌属性变更日志的格式与玩家商品属性变更日志的区别在于,商品属性变更日志中记录的是游戏内商品道具的属性变更,比如武器装备升级之类,而卡牌属性变更则适用于卡牌类游戏。
id | 日志名称 | key |
---|---|---|
14 | 玩家卡牌属性变更日志 | role-card-prop-update |
字段名 | 字段类型 | 字段说明 | 备注 |
---|---|---|---|
roleLevel | Integer | 角色等级 | 必需 |
roleVipLevel | Integer | 角色vip等级 | 必需 |
cardId | String | 卡牌ID | 必需 |
cardName | String | 卡牌名称 | 便于运营人员识别商品,一般传入中文,必需 |
propKey | String | 属性标识 | 必需 |
newValue | String | 新属性值 | 必需 |
oldValue | String | 旧属性值 | 必需 |
custom | String | 变更描述 | 描述变更的原因或途径 |
日志格式示例:
let logInfo = {};
logInfo.id = '14';
logInfo.key = 'role-card-prop-update';
logInfo.content = {};
logInfo.content.roleLevel = '1';
logInfo.content.roleVipLevel = '0';
logInfo.content.cardId = 'blr_man_001';
logInfo.content.cardName = '李星云';
logInfo.content.propKey = 'level';
logInfo.content.newValue = '蓝+1';
logInfo.content.oldValue = '绿';
logInfo.content.custom = '强化装备';
window.ourpalmSdk.log(logInfo);
该条日志记录,玩家的侠客——李星云,进阶从绿变成了蓝+1。
当运营人员需要对游戏内的任务完成情况,关卡完成情况进行记录时,即可记录一条游戏自定义事件。在记录日志之前,游戏运营人员有必要向掌趣科技统计后台提供一份任务,关卡,以及其他自定义事件的id和名称对照表,并在掌趣科技统计后台描述任务,关卡,道具之间的关系。
任务,副本关卡日志的格式均为stageId,stageName,detail三个字段,三个字段的值完全由游戏研发人员和运营人员自行指定,例如新手引导任务可以用 newbieguide标识stageId,也可以用task001或其他字符串标识来表示stageId,任务完成,既可以用 SUCC表示,也可以用complete表示,还可以根据任务的中断,放弃,失败等多种状况,为同一个任务记录多条日志。对于同一种事件结果,建议使用同样的detail内容来描述。尽量做到同一个detail的含义一致,同一个事件结果的detail一致。
需要注意的地方:
无论是任务,关卡还是其他自定义事件日志,taskId/stageId/actId字段都必须是相对于该日志的logId唯一,不能够与其他任务,关卡或道具的id字段重复。
尽管任务,副本/场景,以及游戏自定义事件三个日志的字段数量和格式相同,但是我们希望研发人员能够按照预设的不同logid和logkey分别记录这三类日志,这样一来,便于将相对重要的日志同其它日志区分出来,也便于我们对任务和场景副本日志采用预设的特定算法来进行统计运算。
我们要求用以下的默认字段来记录事件日志的detail,以便统计后台可以用默认的配置来对游戏自定义事件进行计算成功率,通过率等进一步指标。
如果列表中的detail无法满足游戏的日志记录要求,也允许各个游戏使用自己定义的detail来记录日志。
id | 日志名称 | key |
---|---|---|
1001 | 任务 | role-task |
字段名 | 字段类型 | 字段说明 |
---|---|---|
roleLevel | Integer | 角色等级 |
roleVipLevel | Integer | 角色vip等级 |
taskId | String | 任务编码,英文或数字 |
taskName | String | 人物名称,最好传中文 |
detail | String | 相关信息,全为英文 |
其中detail字段建议取值如下所示:
detail | 含义 |
---|---|
accept | 接任务 |
fail | 任务失败 |
cancel | 放弃任务 |
succ | 达成任务条件 |
complete | 任务完成 |
日志格式示例:
let logInfo = {};
logInfo.id = '1001';
logInfo.key = 'role-task';
logInfo.content = {};
logInfo.content.roleLevel = '10';
logInfo.content.roleVipLevel = '2';
logInfo.content.taskId = 'newbieguide1';
logInfo.content.taskName = '新手引导流程1';
logInfo.content.detail = 'accept';
window.ourpalmSdk.log(logInfo);
let logInfo = {};
logInfo.id = '1001';
logInfo.key = 'role-task';
logInfo.content = {};
logInfo.content.roleLevel = '10';
logInfo.content.roleVipLevel = '2';
logInfo.content.taskId = 'newbieguide1';
logInfo.content.taskName = '新手引导流程1';
logInfo.content.detail = 'complete';
window.ourpalmSdk.log(logInfo);
这两条日志表示,当前角色马克.吐温在17:11:30接受了新手流程任务1,在17:12:30完成了新手流程任务1.
id | 日志名称 | key |
---|---|---|
1002 | 副本场景日志 | role-stage |
字段名 | 字段类型 | 字段说明 |
---|---|---|
roleLevel | Integer | 角色等级 |
roleVipLevel | Integer | 角色vip等级 |
stageId | String | 副本场景标识,英文或数字 |
stageName | String | 副本场景名称,最好传中文 |
detail | String | 相关信息,全为英文 |
其中detail字段建议取值如下所示:
detail | 含义 |
---|---|
begin | 场景开始 |
fail | 场景失败 |
cancel | 场景取消 |
end | 场景结束 |
日志格式示例:
let logInfo = {};
logInfo.id = '1002';
logInfo.key = 'role-stage';
logInfo.content = {};
logInfo.content.roleLevel = '10';
logInfo.content.roleVipLevel = '2';
logInfo.content.stageId = 'DeadminesIstanc';
logInfo.content.stageName = '死亡矿井';
logInfo.content.detail = 'begin';
window.ourpalmSdk.log(logInfo);
以上日志表示,当前角色进入死亡矿井。
id | 日志名称 | key |
---|---|---|
1003 | 玩家其他事件 | role-act |
字段名 | 字段类型 | 字段说明 |
---|---|---|
roleLevel | Integer | 角色等级 |
roleVipLevel | Integer | 角色vip等级 |
actId | String | 事件标识,英文或数字 |
actName | String | 事件名称,最好传中文 |
detail | String | 相关信息,全为英文 |
日志格式示例:
let logInfo = {};
logInfo.id = '1003';
logInfo.key = 'role-act';
logInfo.content = {};
logInfo.content.roleLevel = '10';
logInfo.content.roleVipLevel = '2';
logInfo.content.actId = 'fight.mineer';
logInfo.content.actName = '战斗.狗头人矿工';
logInfo.content.detail = 'begin';
window.ourpalmSdk.log(logInfo);
以上日志表示,当前角色和狗头人旷工进行战斗。
游戏中除了角色与系统间发生的事件之外,还会有玩家之间的交互事件,这类事件使用交互事件日志来记录。
与游戏自定义事件相比,交互事件多了一个交互对象的概念,targetType字段用于描述本次交互的对象是哪一类对象,0、系统交互,1、玩家之间互动,2、游戏自定义交互。 系统交互事件应该尽量使用游戏自定义事件来记录,除非交互对象较为重要需要额外记录。
targetCode用于描述当前玩家与具体哪一个对象进行了交互,当targetType为2时,targetCode必须为对方角色id,targetInfo必须为对方逻辑服标识。
id | 日志名称 | key |
---|---|---|
2001 | 交互行为 | role-interact |
字段名 | 字段类型 | 字段说明 |
---|---|---|
roleLevel | Integer | 角色等级 |
roleVipLevel | Integer | 角色vip等级 |
interactId | String | 自定义事件编码,数字或字母 |
interactName | String | 自定义事件名称,建议写中文 |
targetType | String | 目标类型,0:系统,1:玩家,2:自定义类型 |
targetCode | String | 目标,若目标类型为玩家则为对方角色ID |
targetInfo | String | 目标详细信息,若目标类型为玩家,则为对方逻辑服ID |
detail | String | 相关信息,全为英文 |
日志格式示例:
let logInfo = {};
logInfo.id = '2001';
logInfo.key = 'role-interact';
logInfo.content = {};
logInfo.content.roleLevel = '10';
logInfo.content.roleVipLevel = '2';
logInfo.content.interactId = 'addfriend';
logInfo.content.interactName = '添加好友';
logInfo.content.targetType = '1';
logInfo.content.targetCode = '0010000067543';
logInfo.content.targetInfo = '珍妮佛';
logInfo.content.detail = 'ENTER';
window.ourpalmSdk.log(logInfo);
这条日志表示当前玩家马克吐温添加了玩家珍妮佛为好友
以下是我们对日志接入功能的一些开发建议,由于网游服务端和客户端技术复杂多变,我们无法面面俱到的为所有游戏提供一套统一的接入规范,因此分享一些接入过程中我们认为对日志发送功能较为有利的开发经验和建议。
如果观察我们的日志发送接口,就会发现它仅仅由三个参数组成,分别是id,key和content,其中content是json格式对象。因此我们希望游戏接入日志时,能够预先在客户端和服务器端之间架设这样一个协议:
消息头——日志发送协议ID
消息体——由三个参数组成,分别为id,key和content(一个json格式的对象)
只要服务器端通过该协议向客户端推送了这样一个消息,那么客户端就把消息体的三个参数转换为样例代码中的id,key和content对象,然后调用掌趣jssdk提供的日志发送接口发送即可。
这样一来,将来如果有日志格式的改变或者有新的日志需要发送,我们都不需要更新已经上线的客户端,只要在服务端增加相应的代码即可。可以避免由于统计数据需求导致强制更新客户端。
角色ID字段是玩家的身份标识,很多游戏都会开通多个服务器并且在多个服务器之间出现重复的角色ID,我们建议最好用某种方式确保所有服务器的角色id都是不重复的。
原因是手游运营到后期往往会进行合服或者分服操作,分服问题不大,一旦发生合服,则往往角色ID相同的玩家容易因为数据冲突发生各种各样的问题,这时候开发人员对角色ID的任何更改,都将导致统计数据的不准确,因此我们建议在游戏设计阶段就考虑到合服问题,将角色标识做成全服唯一的,以绝后患。