玩家登录
介绍
本篇,我们将介绍玩家登录功能。学习完后,你将可以完成登录、顶号、禁止重复登录等功能。
登录
我们定义了两个数据协议 LoginVerify 和 UserInfo,用于处理登录业务。
- code 10,flowContext.bindingUserId 方法是登录的关键代码, 目的是在对外服的 channel 中设置用户的真实 userId。 之后用户的每次请求,可以通过 FlowContext 来获取该玩家的相关信息, FlowContext 是玩家这一次请求的上下文。
 
@ActionController(1)
public class LoginAction {
    @ActionMethod(1)
    public UserInfo loginVerify(LoginVerify loginVerify, FlowContext flowContext) {
        String jwt = loginVerify.jwt;
        // get user by jwt
        UserInfo userInfo = ...;
        long userId = userInfo.id;
        boolean success = flowContext.bindingUserId(userId);
        if (!success) {
            // assert code
            ...
        }
        return userInfo;
    }
}
@ProtobufClass
public class LoginVerify {
    public String jwt;
}
@ProtobufClass
public class UserInfo {
    public long userId;
    public String name;
}
warning
一定要调用框架提供的这个方法才算是用户验证成功,否则后续在 FlowContext 上下文中拿不到 userId。
禁止重复登录
禁止重复登录指的是相同的号已经在线上了,不能重复登录该账号了。 登录相关的代码,在综合示例中。
- code 8,检测玩家是否在线。
 - code 9,断言+异常机制,如果玩家在线就返回错误码给请求端。
 - code 11,登录。
 
@ActionMethod(1)
public UserInfo loginVerify(LoginVerify loginVerify, FlowContext flowContext) {
    // get user by jwt
    UserInfo userInfo = ...
    long userId = userInfo.id;
    boolean existUser = ExternalCommunicationKit.existUser(userId);
    GameCode.accountOnline.assertTrueThrows(existUser);
    boolean success = flowContext.bindingUserId(userId);
    ...
    return userInfo;
}
@Getter
public enum GameCode implements MsgExceptionInfo {
    accountOnline(100, "User Online");
    final int code;
    final String msg;
    GameCode(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}
顶号
顶号指的是相同的号已经在线上了,把在线上的号顶下线,以最后一次登录为准。
- code 8,强制指定玩家下线。
 - code 10,登录。
 
@ActionMethod(1)
public UserInfo loginVerify(LoginVerify loginVerify, FlowContext flowContext) {
    // get user by jwt
    UserInfo userInfo = ...
    long userId = userInfo.id;
    ExternalCommunicationKit.forcedOffline(userId);
    boolean success = flowContext.bindingUserId(userId);
    ...
    return userInfo;
}
UserId
登录后,可以通过 FlowContext 来获取玩家 userId。
@ActionMethod(2)
public long hello(FlowContext flowContext) {
    return flowContext.getUserId();
}
userId 可以支持 String 类型的 Id 吗?
业务需求: 如果 userId 不是 long 类型的怎么办?我使用的是 MongoDB objectId 是 String 类型的。
ioGame 是一个扩展性极强的框架,这样的需求也是支持扩展的。扩展步骤如下
- 给用户数据关联一个 long 类型的 id,比如 Snowflake。
 - 将 objectId 存放到元信息-附加信息中
 - 配合自定义FlowContext
 
@ProtobufClass
public class MyAttachment implements Attachment {
    @Getter
    long userId;
    /** MongoDB objectId */
    public String objectId;
}
public class MyFlowContext extends FlowContext {
    public String getUserIdString() {
        var attachment = this.getAttachment(MyAttachment.class);
        return attachment.objectId;
    }
}