anlingyi / xechat-idea

让你能够在IDEA里实现聊天、下棋、斗地主!
https://xeblog.cn/?tag=xechat-idea
Apache License 2.0
742 stars 149 forks source link

当通讯发生异常时,如果发生的用户是玩家,此时用户所在游戏房房主仍为游戏中状态,实际应该是鱼 #73

Closed smilesnake closed 2 years ago

smilesnake commented 2 years ago

XEChatServerHandler

  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    ChannelAction.cleanUser(ctx);
    ctx.close();
    log.error("error:", cause);
  }

ChannelAction

/**
   * 清理指定用户
   * @param ctx 会话处理器上下文
   */
  public static void cleanUser(ChannelHandlerContext ctx) {
    cleanUser(getId(ctx));
  }

  /**
   * 通过id清理指定用户
   * @param id  用户id
   * @return 清理的用户信息
   */
  public static User cleanUser(String id) {
    log.debug("清理用户, id -> {}", id);

    User user = getUser(id);
    if (user == null) {
      return null;
    }

    log.debug("清理用户, username -> {}", user.getUsername());

    // 获取游戏房间号
    GameRoom gameRoom = GameRoomCache.getGameRoomByUserId(user.getId());
    if (gameRoom != null) {
      gameRoom.getUsers().forEach((k, v) -> {
        if (v.getId().equals(user.getId())) {
          return;
        }

        // 通知客户端玩家离开房间
        User player = UserCache.get(v.getId());
        if (player != null) {
          player.send(ResponseBuilder.build(user, new GameRoomMsgDTO(GameRoomMsgDTO.MsgType.PLAYER_LEFT, null), MessageType.GAME_ROOM));
        }
      });
      // 玩家离开房间
      GameRoomCache.leftRoom(gameRoom.getId(), user);
    }

    // 设置用户离线并移除相关缓存
    UserCache.remove(id);
    sendUserState(user, UserStateMsgDTO.State.OFFLINE);

    return user;
  }

GameRoomCache

  /**
     * 玩家离开房间
     *
     * @param roomId 房间ID
     * @param user   玩家
     * @return
     */
    public static boolean leftRoom(String roomId, User user) {
        // 查询游戏房间
        GameRoom gameRoom = GAME_ROOM_MAP.get(roomId);
        if (gameRoom == null) {
            return false;
        }

        // 从游戏房间移除玩家
        if (gameRoom.removeUser(user)) {
            // 从用户与房间的缓存中移除,即移除玩家与游戏房间的关联
            USER_ROOM_MAP.remove(user.getId());
            // 如果房间没人或者当前用户是房主
            if (gameRoom.getCurrentNums() == 0 || gameRoom.isHomeowner(user.getUsername())) {
                // 移除房间
                removeRoom(gameRoom.getId());
            }
            return true;
        }

        return false;
    }

    /**
     * 移除房间
     *
     * @param roomId 房间ID
     */
    public static void removeRoom(String roomId) {
        GameRoom gameRoom = getGameRoom(roomId);
        if (gameRoom == null) {
            return;
        }

        log.debug("游戏房间关闭 -> {}", gameRoom);

        // 先移除房间与房号的关联,再移除房间与玩家的关联
        GAME_ROOM_MAP.remove(roomId);
        if (gameRoom.getUsers().size() > 0) {
            gameRoom.getUsers().forEach((k, v) -> {
                USER_ROOM_MAP.remove(v.getId());
            });
        }

        //
        // 邀请的用户列表
        Set<User> userSet = new HashSet<>(gameRoom.getInviteUsers());
        // 房间内有玩家
        gameRoom.getUsers().forEach((k, v) -> {
            User user = UserCache.get(v.getId());
            if (user != null) {
                userSet.add(user);
            }
        });

        // 将非房主的玩家设置为“鱼”的状态
        if (userSet.size() > 0) {
            userSet.forEach(player -> {
                if (gameRoom.isHomeowner(player.getUsername())) {
                    return;
                }

                player.setStatus(UserStatus.FISHING);
                ChannelAction.updateUserStatus(player);
            });
        }
    }

removeRoom在这个方法中:

if (gameRoom.isHomeowner(player.getUsername())) {
return;
}

这里直接把房主过滤了