H5 SDK 开发指南
版本号:master-3.2
1 文档概述
欢迎使用海马云游戏服务,本文档主要介绍云游戏H5端SDK的接入流程及提供支持的API详情,开发者通过接入海马云游戏SDK,可以实现云游戏的播放、停止、状态回调等各类控制操作和数据交互,在海马云游戏端到端全栈云服务能力基础上,为用户带来顺畅的云游戏体验。在开始接入SDK前,请确认您已经拥有海马云游戏平台接入商ID并创建了渠道号,并已经完成了在海马云游戏平台上传游戏包等相关准备工作。
2 名词解释
- saas-sdk:SDK名称,即和海马云SAAS平台交互的SDK。
- accessKeyID:接入商唯一ID。
- appChannel:渠道号,同一接入商可以创建多个渠道号,用来区别管理游戏包,包名相同的游戏可以在两个渠道分别上架和应用。
- channelId:接入方自行定义,主要方便接入方推广需要,如一个APK,发布到不同的推广平台时用不一样channelId,根据channelId区分各个推广平台的推广效果。
- cid:海马云游戏单次游戏的唯一标识。
- HSN数量:接入商在海马云游戏平台配置的云端实例数量,即能允许用户进行云游戏的最大并发数量。
3 接入步骤
本文档为H5端SDK接入说明,在接入SDK前,需要您明确如下事项:
- 目前只支持http协议,暂不支持https协议
- 网页必须执行H5标准
- viewport必须设置为禁止缩放
- 必须设定唯一的DOM节点,SDK将在这个节点内加载游戏
其他端SDK接入,请参考对应的接入手册
3.1 导入SDK
直接导入SDK,saas-sdk将自动识别平台类型是Android还是iOS,并引入对应的播放器。
代码示例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" type="text/css" href="./saas-sdk.css">
<script src="./saas-sdk.js"></script>
</head>
<body>
<div id="example"></div>
</body>
</html>
您还可以自行引入播放器,以提高加载速度,如果自行引入,请在引入saas-sdk.js之前引入对应平台的播放器文件,具体如下:
- Android平台请引入saas-player-android.js
- iOS平台请引入saas-player-ios.js
播放器文件被提前引入后,saas-sdk依然会自动识别平台类型,并判断对应的播放器是否已经被引入,如果没有,则会引入正确的播放器文件。
代码示例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" type="text/css" href="./saas-sdk.css">
<script src="./saas-player-android.js"></script>
<script src="./saas-sdk.js"></script>
</head>
<body>
<div id="example"></div>
</body>
</html>
文件名 | 说明 |
---|---|
saas-sdk.css | 云游戏SDK的css样式 |
saas-sdk.js | 云游戏SDK的js插件 |
saas-player-android.js | 云游戏SDK的Android平台播放器 |
saas-player-ios.js | 云游戏SDK的iOS平台播放器 |
4 API接口
4.1 初始化
SDK初始化依赖accessKeyID等字段,请确认配置了正确的数据。
代码示例
Cloudplay.initSDK({
accessKeyID: '',
channelId: '',
onSceneChanged: function(sceneId, extraInfo) {
console.log('sceneId:'+sceneId,extraInfo);
if(sceneId == 'play') {
alert('游戏开始了');
}
},
MessageHandler: function(message) {
console.log('got message:',message);
}
});
参数说明
参数 | 类型 | 必选 | 说明 |
---|---|---|---|
accessKeyID | string | 是 | 接入商唯一ID |
channelId | string | 是 | 接入方自行定义,主要方便接入方推广需要,如一个APK,发布到不同的推广平台时用不同channelId |
onSceneChanged | function | 是 | 场景切换回调函数:参考本文档中的“onSceneChanged回调函数说明” |
MessageHandler | function | 否 | 支付消息的回调函数:参考本文档中的“MessageHandler回调函数说明” |
onSceneChanged回调函数说明
游戏过程中的各个场景切换,会通过onSceneChanged回调函数传递,您可以在场景回调时进行相应的处理操作。以下为参数说明:
sceneId | sceneId说明 | extraInfo类型 | extraInfo属性名 | extraInfo属性说明 |
---|---|---|---|---|
init | 初始化中 | null | ||
play | 开始游戏 | null | ||
stop | 游戏结束 | object | interval | 本次游戏时间,单位:秒 |
~ | ~ | ~ | reason | timeLimit:本次游戏时间已到 noOperation:无操作超时,默认20分钟,可联系运营人员修改配置 instanceError:云端实例出错 tokenExpire:Token过期失效 maintainServer:正在维护服务器 internalError:内部错误 instanceLimit:超过实例数的申请上限 queueLimit:排队人数过多,禁止排队 internal:主动停止游戏 tokenFailed:token鉴权失败 |
~ | ~ | ~ | message | 提示信息 |
maintain | 服务器维护 | object | progress | soon:维护进度即将开始 start:开始 |
~ | ~ | ~ | time | 时间,单位:秒 |
~ | ~ | ~ | message | 提示信息 |
timeout | 游戏时间结束,但不结束游戏 | object | interval | 本次游戏时间,单位:秒 |
~ | ~ | ~ | message | 提示信息 |
remainingTime | 剩余游戏时间 | object | time | 剩余可玩的游戏时间,单位:秒 |
~ | ~ | ~ | countDown | true:需要倒计时 false:不需要倒计时 |
wait | 排队 | object | reason | showQueueInfo:显示当前的排队信息 applyGame:排队完成,正在进入游戏 whetherToQueue:是否进入排队,如果进入排队调用Cloudplay.enterQueue(),否则调用Cloudplay.outQueue() |
~ | ~ | ~ | message | 提示信息 |
~ | ~ | ~ | waitingPeople | 等待人数 |
~ | ~ | ~ | waitingTime | 预计等待时间,单位:秒 |
totalTime | 本次可玩游戏时间 | object | time | 单位:秒 |
~ | ~ | ~ | message | 提示信息 |
reconnectingStatus | 重连状态提示 | object | message | 提示信息 |
offline | 断网提示 | object | message | 提示信息 |
warning | 出错提示 | object | message | 提示信息 |
loadPlayer | 加载播放器 | object | result | true:成功 false:失败 |
qqup | 云上调起了QQ应用 | null | ||
delay | 画面延迟 | object | delay | 当前画面的延时,单位:毫秒。 备注:只有使用WebRTC播放器时才有此参数 |
packetsLost | 丢包率 | object | packetsLost | 当前画面的丢包率,单位:%。 备注:只有使用WebRTC播放器时才有此参数 |
说明:’~’ 意思为同上
MessageHandler回调函数说明
游戏过程中的各种消息,都会通过MessageHandler回调函数传递,您可以在消息回调时进行相应的处理操作。以下为参数说明:
参数名 | 类型 | 必选 | 说明 |
---|---|---|---|
userId | string | 否 | 目标userId |
from | string | 是 | 消息发送方标识 |
to | string | 是 | 消息接收方标识 |
mid | string | 是 | 消息ID |
type | int | 是 | 消息类型:固定值为1 |
ack | int | 是 | 应答类型:固定值为0 |
payload | string | 是 | 消息内容 |
4.2 启动游戏
完成初始化设置后即可调用游戏启动接口来启动游戏,请确认游戏包名、渠道号等信息正确,并确保相应游戏已经在海马云平台成功完成游戏包的上架及相关配置信息设定。
代码示例
Cloudplay.startGame('example', {
userInfo: {
uId: '',
uToken: '',
uType: 0
},
pkgName: '',
appChannel: '1'
cToken: '',
isPortrait: false,
priority: 0,
configInfo: '',
playingtime: 1200,
streamType: '1',
isArchive: true,
cid: '',
protoData: '',
extraId: '',
});
参数说明
参数 | 类型 | 必选 | 说明 |
---|---|---|---|
uId | string | 是 | 自行定义32位以内a-z,0-9字符串 |
uToken | string | 是 | 自行定义32位以内a-z,0-9字符串 |
uType | int | 是 | 默认传0 |
pkgName | string | 是 | 游戏包名称 |
appChannel | string | 否 | 游戏渠道号 |
cToken | string | 是 | 用来校验参数的有效性:生成算法详见本文档的cToken生成算法 |
isPortrait | boolean | 是 | 游戏的横竖屏属性:true为竖屏游戏;false为横屏游戏 |
priority | int | 是 | 申请游戏服务的优先级;默认设置为0;值越大优先级越高 |
configInfo | string | 是 | 免登录功能所需信息,如不使用,传任意非空字符串 |
playingtime | int | 是 | 用户可以玩的总游戏时间,单位:毫秒,可联系营运人员配置 |
streamType | string | 否 | 指定使用哪种 Streaming, 1:WebRTC, 0:H5Streaming |
isArchive | boolean | 否 | 是否存档:默认为true;true存档;false不存档; |
cid | int | 否 | 游戏的cid |
protoData | string | 否 | 透传字段,服务器端状态同步接口透传使用 |
extraId | string | 否 | 预留字段,传空字符串 |
4.3 停止游戏
需要停止游戏时,请调用该接口。
代码示例
Cloudplay.stopGame();
4.4 音频播放
可以通过该接口控制音频播放,true表示播放,false表示静音。
代码示例
Cloudplay.enableAudio(enabled);
参数配置
参数 | 类型 | 必选 | 说明 |
---|---|---|---|
enabled | boolean | 是 | 是否播放音频:true: 播放;false: 静音 |
说明:
1、因Safari浏览器的特殊性,初始时传true不会播放音频,需要用户点击后传true方可播放音频。
2、微信内置浏览器可以通过监听”WeixinJSBridgeReady”事件执行Cloudplay.enableAudio(true)来自动播放音频,否则也和Safari浏览器一样,需要用户点击后传true方可播放音频。
5 进阶API
5.1 获取cid
通过该接口获取本次云游戏的cid。
代码示例
Cloudplay.getCid();
返回的数据说明
属性 | 类型 | 说明 |
---|---|---|
cid | string | 本次游戏的cid |
5.2 获取游戏的存档状态
查看当前用户的游戏是否正在存档中,如果不是可以进行游戏,否则须要等存档完成。
代码示例
Cloudplay.getGameArchiveStatus ({
userInfo: {
uId: '',
uToken: '',
uType: 0
},
pkgName: '',
appChannel: '1'
ifCanPlay:function(res) {
// todo
},
});
参数配置
参数 | 类型 | 必选 | 说明 |
---|---|---|---|
uId | string | 是 | 自行定义32位以内a-z,0-9字符串 |
uToken | string | 否 | 自行定义32位以内a-z,0-9字符串 |
uType | int | 否 | 默认传0 |
pkgName | string | 是 | 游戏包名称 |
appChannel | string | 否 | 游戏渠道号 |
ifCanPlay | function | 是 | 回调函数 |
回调函数返回的数据说明
属性 | 类型 | 说明 |
---|---|---|
code | int | 0:获取信息成功, 1:获取信息失败 |
errorMessage | string | 报错信息 |
ifCanPlay | boolen | true: 可以进行游戏, false:不可进行游戏 |
5.3 游戏是否存在存档
查看当前用户的这个游戏在云端是否存在存档。
代码示例
Cloudplay.gameArchived ({
userInfo: {
uId: '',
uToken: '',
uType: 0
},
pkgName: '',
appChannel: '1'
hasArchive:function(res) {
// todo
},
});
参数配置
参数 | 类型 | 必选 | 说明 |
---|---|---|---|
uId | string | 是 | 自行定义32位以内a-z,0-9字符串 |
uToken | string | 否 | 自行定义32位以内a-z,0-9字符串 |
uType | int | 否 | 默认传0 |
pkgName | string | 是 | 游戏包名称 |
appChannel | string | 否 | 游戏渠道号 |
hasArchive | function | 是 | 回调函数 |
回调函数返回的数据说明
属性 | 类型 | 说明 |
---|---|---|
code | int | 0:获取信息成功, 1:获取信息失败 |
errorMessage | string | 报错信息 |
hasArchive | boolen | true: 有存档, false:没有存档 |
5.4 检测进行中的游戏
检测当前用户在云端运行中的游戏信息,并获取未释放游戏的列表及每个游戏的详细信息,开发者可以根据游戏的详细信息对其进行启动游戏或者停止游戏操作。
代码示例
Cloudplay.checkPlayingGame ({
userInfo: {
uId: '',
uToken: '',
uType: 0
},
playingGame:function(res) {
// todo
},
});
参数配置
参数 | 类型 | 必选 | 说明 |
---|---|---|---|
uId | string | 是 | 自行定义32位以内a-z,0-9字符串 |
uToken | string | 否 | 自行定义32位以内a-z,0-9字符串 |
uType | int | 否 | 默认传0 |
playingGame | function | 是 | 回调函数 |
回调函数返回的数据说明
属性 | 类型 | 说明 |
---|---|---|
code | int | 0:获取信息成功, 1:获取信息失败 |
errorMessage | string | 报错信息 |
playingGame | array | [{appChannel: “”, cid: “”, pkgName: “”}] 未释放游戏的appChannel、cid、pkgName信息。如果数组为空,则没有未释放的游戏 |
5.5 获取云游戏状态码
收到stop场景时,可以调用该接口来获取云游戏状态码,来分析和统计游戏失败的原因。
代码示例
Cloudplay.getCloudPlayStatusCode ();
返回的数据说明
属性 | 类型 | 说明 |
---|---|---|
100999001 | int | 没流地址,cid未获取成功(初始状态) |
100999002 | int | 没流地址,cid获取成功,Socket连接失败。 |
100999003 | int | 没流地址,cid获取成功,Socket连接成功,乒乓状态异常。 |
100999004 | int | 没流地址,cid获取成功,Socket连接成功,乒乓状态正常。 |
100999005 | int | 有流地址,video成功。 |
100999006 | int | 有流地址,video失败,audio成功。 |
100999007 | int | 有流地址,video失败,audio失败,input成功。 |
100999008 | int | 有流地址,video失败,audio失败,input失败。 |
5.6 云游戏结束信息上报接口
游戏结束时,可以通过该接口上报游戏是正常结束还是发生了异常。
代码示例
Cloudplay.reportFinishInfo ({
finishCode: 0,
pkgName: '',
appChannel: '',
gameId: '',
cid: '',
sdkVersion: '',
});
参数配置
参数 | 类型 | 说明 |
---|---|---|
finishCode | string | 结束状态 0:正常结束, 1:超时结束, 2:异常结束有SDK错误码,3:异常结束无SDk错误码。 |
pkgName | string | 云游戏包名 |
appChannel | string | 云游戏渠道号 |
gameId | string | 云游戏id号 |
cid | string | 云游戏cid |
sdkVersion | string | SDK版本号 |
5.7 获取网络请求数据接口
查看游戏申请过程中请求游戏信息以及连接游戏所用的时间。
代码示例
Cloudplay.getNetInfo();
返回的参数说明
属性 | 说明 |
---|---|
url | 网络请求的url |
time | 请求耗时 |
result | 请求结果。 |
msg | 结果详细描述 |
uid | 当前用户的uid |
cid | 当前云游的cid |
action | 请求的类型(长连接:access、input连接:input、视频流:videoUrl、音频流:audioUrl、action:actionId)。 |
transId | 当前申请的唯一ID |
packageName | 请求的游戏包名。 |
startTime | 开始请求的时间 |
responseTime | 请求结束的时间。 |
5.8 发送消息到服务器
用户需要发送消息到服务器时调用这个接口
代码示例
Cloudplay.sendMessage(payload)
参数说明
参数名 | 类型 | 必选 | 说明 |
---|---|---|---|
payload | string | 是 | 发送的数据信息 |
6 场景说明
6.1 排队
实例数不足的时候,会发生排队现象,实例数即接入商在海马云平台订购的HSN数量。通过SDK提供的接口,您可以让用户选择是否进入排队队列,具体方法为,发生排队现象的时候,在启动游戏传入的回调函数onSceneChanged会收到sceneId:wait,reason:whetherToQueue的信息,询问是否进入排队。同时还会收到message:排队的原因,waitingPeople:当前排队人数;如果用户选择进入排队,请调用Cloudplay.enterQueue(),否则请调用Cloudplay.outQueue()。
代码示例
function sceneChanged(sceneId, extraInfo) {
if(sceneId == 'wait' && extraInfo.reason == 'whetherToQueue') {
var queueOrNot = confirm("是否进入排队");//可以设计漂亮UI提示代替这部分内容
if(queueOrNot == true) {
Cloudplay.enterQueue();
}else {
Cloudplay.outQueue();
}
}
}
6.2 游戏时间
启动游戏的时候,可以设定可玩游戏时间,当可玩游戏时间用完的时候,系统会停止游戏,并通过回调函数通知SDK。可玩游戏时间可以通过Cloudplay.startGame()中的playingtime来设置,单位是毫秒。例如设置60分钟,即3600*1000毫秒
代码示例
Cloudplay.startGame('example', {
pkgName: testPackageName,
userInfo: {
uId: testUserId,
uToken: testUserToken,
uType: 0
},
priority: 0,
extraId: '',
playingtime: 3600*1000,
configInfo: 'a',
cToken: generateCToken(),
isArchive: false,
isPortrait: false,
appChannel: testAppChannel
});
在游戏过程中,你可能收到sceneId为totalTime、remainingTime、timeout 等场景;收到这些场景时,你可以给用户相应的消息提示:
- totalTime: 本次游戏可玩时长
- remainingTime:游戏剩余时间
- timeout:游戏时间结束,但不结束游戏
代码示例
function sceneChanged(sceneId, extraInfo) {
if(sceneId == 'totalTime') {
alert('你本次游戏可玩时长'+extraInfo.time+'秒。');
}
if(sceneId == 'remainingTime') {
alert('你的游戏时间还剩'+extraInfo.time+'秒。');
}
if(sceneId == 'timeout') {
alert('你已经玩了'+extraInfo.interval+'秒,游戏时间已用完。');
}
}
6.3 免登
该功能需要接入方配合开发,将用户信息传给SaaS-SDK,SaaS-SDK将接收到的信息与接入方提供的校验接口校验,校验通过则读取用户的存档数据开始游戏;校验不通过则通知接入方用户登录状态失效,如果不通知也可以在实例中输入用户名密码登录,此方案可选。
6.4 检测进行中的游戏
检测当前用户在云端运行的游戏,根据cid信息可对其进行启动游戏或者停止游戏操作。
代码示例
function sceneChanged(sceneId, extraInfo) {
if(sceneId == 'stop' && extraInfo.reason == 'instanceLimit') { //用户启动实例超限制时的场景
Cloudplay.checkPlayingGame ({ //检测该用户在云端正在进行中的游戏
userInfo: {
uId: '',
uToken: '',
uType: 0
},
playingGame: function(res) {
if(res.code == 0 && res.playingGame.length > 0) {// 用户在云端有正运行的实例
var startGameData = {
userInfo: {
uId: "hmTest01",
uToken: "hmTest",
uType: 0
},
priority: 0,
extraId: '',
playingtime: 3600*1000,
configInfo: 'a',
isArchive: false,
isPortrait: false,
cid: res.playingGame[0].cid, // 处理请查询返回的数据
pkgName: res.playingGame[0].pkgName, // 处理请查询返回的数据
appChannel: res.playingGame[0].appChannel, // 处理请查询返回的数据
cToken: generateCToken() //根据查询的数据生成cToken,生成cToken方法请参看demo
}
Cloudplay.startGame('example', startGameData); //利用查询的数据启动游戏
}
},
});
}
}
6.5 将游戏切换到前台
场景描述
- 用户点击“与QQ好友玩”按钮后,QQ应用会被调起并在前台显示;
- QQ登录可能失败或用户不想继续登录,这时候系统不会把游戏切换到前台,需要接入方提供一个按钮,通过调用Cloudplay.bringUpApp()方法,把游戏切换到前台,并控制按钮的状态;
- QQ登录成功后,系统自动把游戏切换到前台,游戏在前台的时候,调用Cloudplay.bringUpApp(),不会有任何其它影响;
函数原型
Cloudplay.bringUpApp();
代码示例
var oBackBtn = document.getElementById('back_btn');
oBackBtn.onclick = function(){
Cloudplay.bringUpApp();
oBackBtn.style.display = "none";
sessionStorage.removeItem("qqupState");
}
6.6 QQ登录接入过程说明
用户在云游戏点击“与QQ好友玩”按钮后,SDK会通过接入方提供的onSceneChanged回调函数,通知接入方用户进入了 QQ 登录界面,此时接入方收到“qqup”消息后,提示用户QQ登录、以及QQ登录异地登录风控等信息。
代码示例
var oBackBtn = document.getElementById('back_btn');
onSceneChanged: function(sceneId, extraInfo) {
if(sceneId == "qqup" || sessionStorage.getItem("qqupState")){
sessionStorage.setItem("qqupState", "true");
oBackBtn.style.display = "block";
// QQ登录 do something;
}
}
7 数据结构定义
7.1 cToken生成算法
- 根据userId,userToken,pkgName,accessKeyID,channelId这几个字段合并成一个新的字符串
- 对字符串进行 AES 处理并且配以accessKey(AES mode:ECB,pad:Pkcs7)
- 对生成的字节数组进行SHA-1 处理
- 再传化为新的字符串,这个字符串就是新生成的cToken
cToken 算法示例:
accessKey:32ff95eb0880face7c7be338608fdfe4(注:海马云分配)
userId:cpd301548
userToken:tokend2d23cc6522a50760fa027d1f9
pkgName: com.hm.apk
accessKeyID:bid999889(注:海马云分配)
channelId:haimayun
合并字符串为:cpd301548tokend2d23cc6522a50760fa027d1f9com.hm.apkbid999889haimayun
cToken为:899453e46440ba04df6acb8c19261855bacf6547