# 游戏服务-成就系统

## 一、模块说明

本模块功能自运营 SDK `3.13.0` 开始提供

游戏使用成就系统提供的 API，记录并上报玩家在游戏中的活动，并在个人事迹中展示活动及其成果

## 二、接入准备

在正式接入成就系统前，接入方需要确认以下流程已完善：

- 已经在 4399 平台注册好游戏，并完善了平台所要求的配置
- 已经按照一般流程引入了 4399 运营SDK，具体流程参考 [SDK 客户端接入文档](./online_java_guide.html)
- 已经设计好成就系统，并在 SDK 后台添加配置
- 已经获取客户端接口公钥（与云存档、排行榜等其他服务商同一个参数，获取一次即可）

### 如何获取接口公钥

向 SDK 开发人员提交游戏 Game Key，和 APK 签名的 sha256摘要，SDK 对接人员会协助填写，  
并将最后得到的公钥给游戏开发，**游戏应妥善保管此参数**

如何获取公钥参考：[获取 Android 应用签名证书指纹](check_developer_ca_sha256.md)

### 如何添加成就

对于单个成就，游戏应提供以下信息给 SDK 对接人员：

内容 |定义 |说明
:--- |:--- |:---
成就名称|成就名称 |后台需要上传，仅作为成就ID对应的名称记录
成就标识|成就唯一标识 |通过标识返回对应成就的数据（仅限数字+英文，最多100个字符）
达成条件|完成成就所需分数 |仅限数值类型，支持从0-100,000,000
是否隐藏|记录是否为隐藏成就 |供游戏方做成就分类，获取成就列表时带有该字段，1为是，0为否

为方便接入方提交信息，可以按照 [4399 SDK 成就系统配置模板](http://sdkftp.4399doc.com/internal/operate/3.13/%E6%88%90%E5%B0%B1%E7%B3%BB%E7%BB%9F%E9%85%8D%E7%BD%AE.xls)
填写成就配置

## 三、接口调用

### 0、前置接口

在调用本服务相关接口前，游戏应先调用下面前置接口：

- [初始化](online_java_guide.md)
- [登录](online_java_guide.md)

### 1、服务初始化

`Achievement.init`，初始化公钥，在使用成就系统其他接口前，应调用此接口进行初始化

```java
/**
 * 服务初始化
 *
 * @param publicKey 接口公钥，完成游戏服务配置得到
 */
public static void init(String publicKey) 
```

调用示例：

```java
Achievement.init(String publicKey);
```

### 2、初始化角色成就

`Achievement.initRole`，在成就系统中初始化角色信息，要获取个人成就数据，需要先对角色进行成就系统初始化

```java
/**
 * 初始化角色信息
 *
 * @param serverId 区服id，没有则传字符串0
 * @param roleId   游戏角色id，没有则传字符串0
 * @param listener 回调对象
 */
public static void initRole(String serverId,String roleId,@NonNull OpeDataListener<Void> listener);
```

调用示例：

```java
String serverId = "4399";
String roleId = "520";
OpeDataListener<Void> listener = new OpeDataListener<Void>() {
    @Override
    public void onFinished(int code, @Nullable String message, @Nullable Void unused) {
        // code:状态码，为0表示成功
    }
};
Achievement.initRole(serverId, roleId, listener);
```

可调用`Achievement.isRoleInit`接口判断是否初始化成功，示例代码：

```java
if (!Achievement.isRoleInit(serverId, roleId)) {
    //需要进行初始化
    String serverId = "4399";
    String roleId = "520";
    OpeDataListener<Void> listener = new OpeDataListener<Void>() {
        @Override
        public void onFinished(int code, @Nullable String message, @Nullable Void unused) {
            //code:状态码，为0表示成功
        }
    };
    Achievement.initRole(String server, String role, listener);
}
```

### 3、获取角色成就列表

`Achievement.getAchievementList`，获取游戏成就列表。调用结果通过回调的方式返回给调用者

```java
/**
 * 获取角色成就列表
 *
 * @param serverId 区服id，没有则传字符串0
 * @param roleId   游戏角色id，没有则传字符串0
 * @param listener 回调对象
 */
public static void getAchievementList(String serverId,String roleId,
    @NonNull OpeDataListener<List<AchievementDetail>>listener);
```

接口返回的角色成就列表，通过接口`OpeDataListener<List<AchievementDetail>>`接口返回    
`OpeDataListener`，通用泛型回调接口，接入方调用方法后通过接口方法接收并处理调用结果  
`AchievementDetail`，角色成就数据模型，里面包含了游戏所定义的成就列表信息  
*两个类的具体信息在本文档后文的 API 详情部分，接口示例中涉及的其他类也是如此*

调用示例：

```java
String serverId = "4399";
String roleId = "520";
OpeDataListener<List<AchievementDetail>> listener = new OpeDataListener<List<AchievementDetail>>() {
    @Override
    public void onFinished(int code, @Nullable String message, @Nullable List<AchievementDetail> data) {
        //code ： 状态码，data : 成就数据
        if (data != null) {
            //获取成就列表
            List<AchievementDetail> mAchievementList = data;
        }
    }
};
Achievement.getAchievementList(serverId, roleId, listener);
```

### 4、更新角色成就

`Achievement.updateAchievement`，更新角色成就，当用户达成特定条件，使用此接口更新成就

```java
/**
 * 更新角色成就
 *
 * @param serverId 区服id，没有则传字符串0
 * @param roleId   游戏角色id，没有则传字符串0
 * @param mode     更新模式，枚举变量，详情参考 UpdateMode 说明
 * @param id       应与目标成就在后台配置一致
 * @param step     需要更新的成就步数，一般是正整数
 *                 当更新模式为UpdateMode.UPDATE_MAX时传固定值 -1
 * @param listener 回调对象
public static void updateAchievement(String serverId, String roleId, 
    UpdateMode mode, String id, int step,
    @NonNull OpeDataListener<Void> listener);
```

调用示例：

```java
Achievement.UpdateMode updateMode = Achievement.UpdateMode.UPDATE_ADD;
String serverId = "4399";
String roleId = "520";
String achvId = "成就id1";
int step = 1;
OpeDataListener<Void> opeDataListener = new OpeDataListener<Void>() {
    @Override
    public void onFinished(int code, @Nullable String message, @Nullable Void unused) {

    }
};
Achievement.updateAchievement(serverId, roleId, updateMode, achvId, step,opeDataListener);
```

`UpdateMode`更新模式，类详情可参考附录，此处结合示例说明用法

* 更新模式有三种：`UPDATE_ADD`，`UPDATE_SET`，`UPDATE_MAX`；
    * `UPDATE_ADD`：步进式进度增加，如原本是0，加1后变成1，再加1则变成2
    * `UPDATE_SET`：阶段式进度增加，如需要特定三个条件完成成就，重复完成一个条件，进度不会累加，  
      需要分别完成不同的条件才能增加进度。  
      举个例子，该成就需要step参数分别传数字1、2、3才能完成成就，step为1的情况下重复传3次则成就进度只能到1/3
    * `UPDATE_MAX`：直接达成成就

### 5、获取游戏所有成就列表

`Achievement.getGameAchievement`，获取游戏所有成就列表，某些情况下，可能需要此接口获取游戏的所有成就列表

```java
/**
 * 获取本游戏成就列表
 *
 * @param listener 回调对象
 */
public static void getGameAchievement(@NonNull OpeDataListener<List<AchievementDetail>>listener);
```

示例代码：

```java
Achievement.getGameAchievement(new OpeDataListener<List<AchievementDetail>>() {
    @Override
    public void onFinished(int code, @Nullable String message, @Nullable List<AchievementDetail> data) {
        if (code == 0 && data != null) {
            //获取成就列表
            List<AchievementDetail> mAchievementList = data;
        }
    }
});
```

## 四、API 详情

### OpeDataListener

```java

/**
 * 数据监听器
 */
public interface OpeDataListener<T> {
    /**
     * @param code    0为请求成功，并返回数据, 其他code均为失败，失败时数据可能为空
     * @param message 说明文案
     * @param data    返回数据
     */
    void onFinished(int code, @Nullable String message, @Nullable T data);
}
```

#### code 列表

| status |          含义         |
| :----: | :-----------------: |
|    0   |          成功         |
|    1   |          取消         |
|    2   |      处理中，可能是延迟      |
|    3   | 失败，可从message中了解更多信息 |
|    4   |        接口返回超时       |
|    5   |     未知结果，作为保留字段     |
|   101  |         参数不全        |
|   110  |        游戏未配置        |
|   112  |         加密错误        |
|   113  |      数据JSON类型错误     |
|   114  |        数据参数错误       |
|   115  |        证书签名错误       |
|   116  |        登录状态错误       |
|   117  |        数据参数不全       |
|   118  |       数据成就ID错误      |
|   119  |        数据步数错误       |
|   121  |        该成就已完成       |
|   122  |         维护中         |
|   123  |       已被列入黑名单       |
|   126  |       已初始化       |

### AchievementDetail及其内部类

描述成就的数据模型

```java
/** 成就详情 */
public class AchievementDetail implements Serializable {
    /** 成就名称. */
    public String name;
    /** 成就Id. */
    public String id;
    /** 成就总步数. */
    public int totalStepCounted;
    /** 是否隐藏. */
    public boolean isHide;
    /** 成就稀有度 */
    public RarityStatistics rarityStatistics;
    /** 个人成就详情 */
    public PersonalAchieve personalAchieve;

    /** 成就稀有度 */
    public static class RarityStatistics implements Serializable {
        /** 已完成人数 */
        public int countCompleted;
        /** 参与完成成就总人数 */
        public int countAll;
    }

    /** 个人成就详情 */
    public static class PersonalAchieve implements Serializable {
        /**
         * 成就达成时间
         * 格式：yyyy/MM/dd HH:mm:ss
         * 举例 : 
         * 未达成 0 
         * 已达成 1960/01/01 00:00:00
         */
        public String achieveTime;
        /** 成就当前进度 */
        public String currentSteps;
    }
}
```

### Achievement.UpdateMode

更新成就的模式

```java
public class Achievement {
    /**
     * 更新成就的模式
     */
    public enum UpdateMode {
        /* 累加，将数值加到旧值上 */
        UPDATE_ADD,
        /* 覆盖，用新值覆盖旧值 */
        UPDATE_SET,
        /* 设为最大值 */
        UPDATE_MAX
    }
}
```