# 运营 SDK cocos 封装接口

## 接口：初始化（必须）

**功能简介：** 此接口初始化 SDK 内部状态，并完成“更新、激活、防沉迷”等流程。
<span style="color:red">**SDK 初始化后才能正确调用其他接口**</span>   
**调用时机：** 建议在游戏启动后调用


```typescript
public initSdk(): void {
    const operateConfigBuilder = new OperateConfig.Builder();
    operateConfigBuilder
	    // 设置调试模式，可选，默认false, 发布前应设置为false或删除调用
        .setDebugEnabled(false)
        // 设置游戏运营 key，必须
        .setGameKey(GAME_KEY)
        // 设置 SDK 页面方向，必须，应与游戏方向一致，可取值见下文说明
        .setOrientation(GAME_ORIENTATION)
        // 设置悬浮窗风格，可选，悬浮窗更多信息参考下文
        .setPopLogoStyle(OperateConfig.PopLogoStyle.POPLOGOSTYLE_ONE)
        // 设置悬浮窗初始位置，可选，有四种，分别在屏幕上下左右
        .setPopWinPosition(OperateConfig.PopWinPosition.POS_TOP)
        // 设置游戏充值是否支持超出金额，可选，true支持，默认false
        .setSupportExcess(GAME_SUPPORT_EXCESS)
        // 设置是否在 Android 9.+系统上兼容全面屏，true兼容，默认false
        .compatNotch(GAME_COMPACT_NOTCH);
    const operateConfig = operateConfigBuilder.build();
    const operateCenter = OperateCenter.getInstance();
    operateCenter.setConfig(operateConfig);
    // 注意：初始化完成后，其他接口才可用
    operateCenter.init({
        /*
         * 初始化回调
         * 只有在init之后， isLogin()返回的状态才可靠
         */
        onInitFinished() {
            // 初始化后处理
        },

        /*
         * 登出帐号的回调
         * @param fromUserCenter 标识区分是否从个人中心中登出的，若是则为true，不是为false
         */
        onUserAccountLogout(fromUserCenter: boolean): void {
            // 用户登出，游戏应回到自身登录页面
        },

        /*
         * 切换账号回调
         */
        onSwitchUserAccountFinished(fromUserCenter, user) {
            // 用户切换，游戏应回到选服页面
        }
    });
    operateConfig.rm();
    operateConfigBuilder.rm();
}
```

初始化的几点说明

### 页面方向

- *游戏页面* 应如下配置`android:configChanges`等，否则系统强制方向变化或系统设置变化时，可能发生异常
  （一般发生是打开外部应用，如支付宝、微信）

```xml
<!-- 游戏 Activity 配置：

     应该使用 android:configChanges="orientation|screenSize|keyboardHidden|screenLayout|navigation|smallestScreenSize|fontScale"
     若游戏覆盖了 `android:resizeableActivity="true"` ，则必须按上面配置
     
     不应该使用 android:launchMode="singleTask" 启动模式，可以考虑 singleTop
 -->
```

其中`android:configChanges` 新增的配置项，这是为适配 Android 分屏模式而添加的，更多内容参考[多窗口支持](https://developer.android.google.cn/guide/topics/ui/multi-window?hl=zh-cn#config)


**分屏（画中画）兼容**：游戏进入分屏再点击桌面图标启动，会重启游戏页面，而并没有完整的启动应用，这会导致部分功能异常，  
为了兼容这种情况，SDK 为应用默认设置了`android:resizeableActivity="false"`，即不支持分屏模式，  
<span style="color:red;font-weight:700">若游戏出于某些原因必须要覆盖此项，则务必按最低标准设置入口 Activity 和主 Activity的`android:configChanges`</span>，即
```xml
<activity
    android:name=".MainActivity"
    android:configChanges="orientation|screenSize|keyboardHidden|screenLayout|navigation|smallestScreenSize|fontScale"
/>
```

- *SDK 页面*，**方向支持横屏或竖屏，但不支持横竖屏切换**  
  通过`OperateConfig.setOrientation()`设置 SDK 页面方向，值是系统类`android.content.pm.ActivityInfo`定义的常量

| 方向参数                                           | 含义         |
|------------------------------------------------|------------|
| `SCREEN_ORIENTATION_LANDSCAPE`，`int`, 0        | 横屏         |
| `SCREEN_ORIENTATION_PORTRAIT`, `int`, 1        | 竖屏         |
| `SCREEN_ORIENTATION_SENSOR_LANDSCAPE`，`int`, 6 | 横屏，可180度旋转 |
| `SCREEN_ORIENTATION_SENSOR_PORTRAIT`，`int`, 7  | 竖屏         |

- *第三方页面*，其方向一般使用 SDK 默认设置即可，需要锁定可设置`android:screenOrientation`，如下所示

```xml
<!-- 
    4399 SDK：SDK 中除了以'cn.m4399.'开头的 Activity 都是第三方页面，
        假设游戏为横屏，需要锁定它们的方向为横屏，可以设置 android:screenOrientation，
        此设置应与初始化接口中设置的值保持对应，且若编译有错误，可以进一步设置‘tools:replace’
 -->
<activity android:name="com.cmic.gen.sdk.view.GenLoginAuthActivity"
    android:configChanges="orientation|keyboardHidden|screenSize" 
    android:launchMode="singleTop"
    android:screenOrientation="landscape" 
    android:theme="@style/AppTheme.NoActionBar"
    tools:replace="android:theme, android:screenOrientation" />
```

### 悬浮窗选项

悬浮窗会在用户登录后附着在游戏页面上层，点击后打开对页面展示个人信息、设置，和游戏论坛、官网等入口。

*极少数游戏登录 Activity 并不是游戏主 Activity ，则悬浮窗可能发生异常，建议游戏做相应调整*

- 悬浮窗风格  
  可根据游戏的风格选择合适的悬浮窗样式，并通过设置`OperateConfig.setPopLogoStyle()`设置。

| 样式类型|图示|
|-------------------------|----|
| `PopLogoStyle.POPLOGOSTYLE_ONE`（默认） |![image](https://4399public.oss-cn-beijing.aliyuncs.com/ope/logo_one_normal.png)|
| `PopLogoStyle.POPLOGOSTYLE_TWO` |![image](https://4399public.oss-cn-beijing.aliyuncs.com/ope/logo_two_normal.png)|
| `PopLogoStyle.POPLOGOSTYLE_THREE` |![image](https://4399public.oss-cn-beijing.aliyuncs.com/ope/logo_three_normal.png)|
| `PopLogoStyle.POPLOGOSTYLE_FOUR` |![image](https://4399public.oss-cn-beijing.aliyuncs.com/ope/logo_four_normal.png)|

* 悬浮窗初始位置  
  可根据游戏的风格选择合适的悬浮窗样式，并通过设置`OperateConfig.setPopWinPosition()`设置。

|样式类型|位置|
|--------|----|
|`PopWinPosition.POS_LEFT`（默认）| 屏幕左侧|
|`PopWinPosition.POS_RIGHT`|屏幕右侧|
|`PopWinPosition.POS_TOP`|屏幕上侧|
|`PopWinPosition.POS_BOTTOM`|屏幕下侧|

## 接口：账号相关

### 账号登录（必须）

**功能简介：** 需要登录时调用此接口，如果 SDK 已存在有效登录状态，则直接回调，否则将打开登录页面登录。  
**调用时机：** 初始化且回调后

```typescript
public login() {
    const operateCenter = OperateCenter.getInstance();
    if(operateCenter.isLogin()) {
        User user = operateCenter.getCurrentAccount();
    } else {
        operateCenter.login((success, resultCode, user) => {
        // 登录结束后的游戏逻辑，回调结果中	
        // success，是否登录成功，true 为成功
        // resultCode，结果状态，其含义可以参考文末 code 详情
        // user，用户信息，其字段见文末 api 详情
         });
    }
}
```

**最佳实践**：SDK 初始化时会检查已有登录状态有效，若有效`OperateCenter.isLogin()`会返回`true`，  
游戏直接通过`OperateCenter.getCurrentAccount()`获取用户信息接口即可，而不需要调用登录接口

在登录成功回调的用户信息，即`User`对象（参考 API 详情），包含`state`字段，此值可用于游戏服务端进行 [用户信息二次验证](https://sdkftp.4399doc.com/external/operate/3.14.4/server_api_1.html)

### 查询是否有已登录账号
**功能简介：** 查询 SDK 中是否已有登录状态，适用于游戏启动时查询
**调用时机：** 账号登录后  

```typescript
const operateCenter = OperateCenter.getInstance();
boolean isLogin = operateCenter.isLogin();
```

### 获取当前登录帐号信息

**功能简介：** 在 SDK 处于登录状态时，可通过该接口获取当前用户的信息。  
**调用时机：** 账号登录后  
<span style="color:red">注意：User 中的uid是 32 位整数字符串；若要转换为数值，注意溢出的可能性</span>

```typescript
const operateCenter = OperateCenter.getInstance();
const user = OperateCenter.getInstance().getCurrentAccount();
```

### 设置角色服务器 id

**功能简介：** 若游戏有区服，则应在角色进入分服时，通过以下接口设置所在服的 id。  
**调用时机：** 账号登录后  
__服务器 id 为不超过10位的数字字符串__

```typescript
public setServer() {
    const operateCenter = OperateCenter.getInstance();
    operateCenter.setServer(SERVER_ID);
}
```

### 账号切换（推荐）

**功能简介：** 用于在游戏内切换账号  
**调用时机：** 账号登录后

```typescript
public switchAccount() {
    const operateCenter = OperateCenter.getInstance();
    operateCenter.switchAccount((success, resultCode, user) => {
        // 用户账号切换结束后的游戏逻辑
        // 切换账号后的游戏逻辑，回调结果中	
        // success，是否切换成功，true 为成功
        // resultCode，结果状态，其含义可以参考文末 code 详情
        // user，用户信息，其字段见文末 api 详情
    });
}
```

有时账号切换是由于 SDK 内部发起的，切换成功后 SDK 调用`onSwitchUserAccountFinished`通知游戏

### 账号登出（推荐）

**功能简介：** 当需要登出账号时调用，SDK 立即清除本地用户状态、通知服务端，并调用`onUserAccountLogout`通知游戏  
**调用时机：** 账号登录后

```typescript
public logout() {
    const operateCenter = OperateCenter.getInstance();
    operateCenter.logout();
}
```

## 接口：充值（开通充值必须）
**功能简介：** 若游戏支持内购，调用此接口将支持用户完成支付流程，4399 充值系统支持在微信、QQ钱包、支付宝、云闪付等 APP 内完成支付  
**调用时机：** 账号登录后，用户点击游戏内充值按钮时

- 全局设置是否支持超出金额：`.setSupportExcess(SUPPORT_EXPRESS)`接口  
  可选，默认不支持，此接口也可以在单笔订单中设置

> **关于超出金额模式（超额）**  
> 由于卡类等充值渠道有金额限制，可能出现*充值金额*无法匹配任何金额的情况；  
> 如果游戏能处理超出*传入金额* 部分的货币，即将多余部分兑换为游戏币发放给用户，则可以设置`true`；  
> 否则，应设置为`false`或不设置，SDK将直接隐藏无匹配金额的充值渠道。

- 调用充值接口

```typescript
/*
 * 充值，需要传入参数：money，mark, commodity
 * 
 * money, 充值金额，整型，单位元，1 ～ 50000，必须
 * mark, 游戏方订单，必须，
 *       支持大小写字母、数字、'|'(竖线)、'-'（中划线）、'_'（下划线），最长32位，非空，唯一
 * commodity, 商品名，可选，不传时认为商品名是游戏币
 */
public recharge() {
    const operateCenter = OperateCenter.getInstance();
    operateCenter.recharge(3, "cid|uid-sid_0TzXveWi", "物品", (success, resultCode, msg) => {
        // 充值操作结果，回调结果中	
        // success，充值操作成功，true 为成功
        // resultCode，结果状态，其含义可以参考文末 code 详情
        // msg，结果描述
        if (success) {
            // 充值操作成功，根据服务端回调决定是否道具
            // 注意：操作成功包含了订单成功、订单处理中两种情况
        } else {
            // 充值失败逻辑，resultCode查看文档结尾
        }
    });
}
```

## 接口：其他

### 退出游戏（必须）

**功能简介：** 当用户按“返回键”或者点击游戏内“退出游戏”时，调用，SDK 会弹出对话框挽留，并展示论坛、礼包等游戏内容  
**调用时机：** 初始化且回调后，否则弹出的是默认弹窗，仅确认是否退出游戏

```typescript
onKeyDown(event: EventKeyboard){
    if (event.keyCode === KeyCode.MOBILE_BACK) {
        const operateCenter = OperateCenter.getInstance();
        operateCenter.shouldQuitGame((shouldQuit) => {
            // 点击退出对话框上的“退出游戏”时，shouldQuit为true，游戏处理退出业务逻辑
        });
    }
}
```

## 渠道支持

**功能简介：** 接入 SDK 的游戏，希望在多个渠道投放，并分别统计数据，有两种方式打入渠道标识  
**使用时机：** 静态配置，SDK 初始化即读取

- 打包工具自动打包：联系运营使用专用打包工具打入渠道标识，不需要游戏额外处理。  
  但此方式要求游戏关闭`v2SigningEnabled`校验

```groovy
// 游戏 apk 工程的 build.gradle中
signingConfigs {
    release {
        v2SigningEnabled false
    }
}  
```

- 代码写入`meta-data`字段：需要运营与开发人员配合完成，但不会有兼容问题

在游戏模块的 `AndroidManifest.xml`中，设置`FTNN_CHANNEL_ID`字段值，作为渠道标识

```xml
<meta-data android:name="FTNN_CHANNEL_ID" android:value="baidu" />
```

## API 详情

### User 类方法

`User`类中是一系列`getter`方法

```typescript
export class User {
     /**
     * uid
     */
    public getUid(): string;

    /**
     * 用户名
     */
    public getName(): string;

    /**
     * 昵称
     */
    public getNick(): string;

    /**
     * 登录状态
     */
    public getState(): string;

    /**
     * 绑定的手机号，已脱敏，若已绑定则此字段非空
     */
    public getPhone(): string;
```

### Code 列表

| code        | 含义                                   |
|-------------|--------------------------------------|
| `0`         | 成功，通用code                            |
| `1`         | 取消，通用code                            |
| `2`         | 处理中，通用code                           |
| `3`         | 失败，通用code                            |
| `4`         | 超时，通用code                            |
| `5`         | 中止，通用code                            |
| `16`        | 登录成功                                 |
| `17`        | 不能取得游戏盒认证（一般是号被封了）                   |
| `18`        | 登录取消                                 |
| `19`        | 游戏不存在，GameKey问题                      |
| `20`        | 已登录                                  |
| `21`        | 没有初始化                                |
| `22`        | 一键登录失败                               |
| `24`        | 拒绝用户协议与隐私政策                          |
| `25`        | 网络问题，或者服务端问题                         |  
| `50`        | 认证通过                                 |  
| `51`        | 待认证                                  |  
| `52`        | 拒绝认证                                 |  
| `53`        | 跳过认证，此时后台配置允许取消                      |  
| `54`        | 认证审核中                                |  
| `55`        | 取消认证                                 |  
| `56`        | 认证中断，一般由于账号被顶，或踢出                    |
| `57`        | 认证错误，具体消息看对应message                  |
| `6001`      | 充值取消                                 |
| `0`, `9000` | 充值成功                                 |
| `2`, `9001` | 订单处理中，游戏应等待服务端的充值回调状态                |
| `9002`      | 订单已提交，SDK 不能在有限时间内判断订单状态，游戏应以服务端状态为准 |