当前位置: 首页 > wzjs >正文

湖北省建设工程质量安全协会网站制作网站的最大公司

湖北省建设工程质量安全协会网站,制作网站的最大公司,在线图像制作,海外网站域名注册目录 GameHandler 创建请求响应对象 处理连接成功 玩家下线处理 处理落子请求 handleTextMessage putChess 落子 胜负判定 构造落子响应并返回 更新玩家分数 修改客户端代码 对战模块测试 在本篇文章中,我们继续实现对战模块的后端逻辑 GameHandler…

目录

GameHandler

创建请求响应对象

处理连接成功

玩家下线处理

处理落子请求

handleTextMessage

putChess

落子 

胜负判定 

构造落子响应并返回

更新玩家分数

修改客户端代码

对战模块测试


在本篇文章中,我们继续实现对战模块的后端逻辑

GameHandler

我们创建 GameHandler 处理 WebSocket 请求

@Component
@Slf4j
public class GameHandler extends TextWebSocketHandler {@Autowiredprivate OnlineUserManager onlineUserManager;@Autowiredprivate RoomManager roomManager;/*** 连接建立(游戏准备)* @param session* @throws Exception*/@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {}/*** 处理落子请求* @param session* @param message* @throws Exception*/@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {}/*** 处理异常情况* @param session* @param exception* @throws Exception*/@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {}/*** 连接关闭* @param session* @param status* @throws Exception*/@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {}}

并注入 RoomManager OnlineUserManager 

修改 WebSocketConfig 将 GameHandler 进行注册

@EnableWebSocket
@Configuration
public class WebSocketConfig implements WebSocketConfigurer {@Autowiredprivate MatchHandler matchHandler;@Autowiredprivate GameHandler gameHandler;@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(matchHandler, "/findMatch").addInterceptors(new HttpSessionHandshakeInterceptor()); // 添加拦截器registry.addHandler(gameHandler, "/game").addInterceptors(new HttpSessionHandshakeInterceptor());}
}

根据约定的前后端交互接口创建请求/响应对象

创建请求响应对象

落子请求 PutChessParam

@Data
public class PutChessParam implements Serializable {/*** 落子玩家 id*/private Long userId;/*** 落子位置——行*/private Integer row;/*** 落子位置——列*/private Integer col;
}

落子响应 MatchResult

@Data
public class MatchResult implements Serializable {/*** 匹配结果*/private String matchMessage;private Rival rival;@Datapublic static class Rival {/*** 对手姓名*/private String name;/*** 天梯分数*/private Long score;}public MatchResult() {}public MatchResult(String matchMessage) {this.matchMessage = matchMessage;}
}

游戏就绪响应 GameReadyResult

@Data
public class GameReadyResult implements Serializable {/*** 房间号*/private String roomId;/*** 玩家 id*/private Long thisUserId;/*** 对手 id*/private Long thatUserId;/*** 先手 id*/private Long whiteUserId;
}

 

处理连接成功

游戏房间 WebSocket 连接建立之后(afterConnectionEstablished)需要实现的业务逻辑:

1. 从 session 中获取登录时存储的用户信息(UserInfo),检查用户的登录情况

2. 通过 OnlineUserManager 进行多开判定

3. 判断当前玩家是否在游戏房间中

4. 将两个玩家放到对应的房间对象中,当两个玩家都建立了连接时,这个房间就满了,此时就可以通知双方准备就绪

5. 若有更多玩家尝试加入房间,提示游戏房间已满

    @Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {// 从 session 中获取用户信息UserInfo userInfo = (UserInfo) session.getAttributes().get(USER_INFO);// 用户是否登录if (null == userInfo) {session.sendMessage(new TextMessage(JacksonUtil.writeValueAsString(CommonResult.noLogin())));return;}// 判断用户是否处于在线状态if (null != onlineUserManager.getFromHall(userInfo.getUserId())) {session.sendMessage(new TextMessage(JacksonUtil.writeValueAsString(CommonResult.repeatConnection())));return;}// 获取游戏房间, 判断当前玩家是否在游戏房间中Room room = roomManager.getRoomByUserId(userInfo.getUserId());if (null == room) {session.sendMessage(new TextMessage(JacksonUtil.writeValueAsString(CommonResult.fail(HandlerErrorCodeConstants.GET_GAME_ROOM_ERROR))));return;}// 将用户设置为在线状态onlineUserManager.enterGameRoom(userInfo.getUserId(), session);// 将玩家加入到游戏房间(加锁)synchronized (room) {// 第一个玩家是否加入游戏房间if (null == room.getUser1()) {room.setUser1(userInfo);// 默认玩家1 作为先手room.setWhiteUserId(userInfo.getUserId());log.info("房间 {} 玩家1 {} 已就绪", room.getRoomId(), userInfo.getUserName());return;}// 第二个玩家是否加入游戏房间if (null == room.getUser2()) {room.setUser2(userInfo);log.info("房间 {} 玩家2 {} 已就绪", room.getRoomId(), userInfo.getUserName());// 通知玩家1 和 玩家2 游戏已就绪notifyGameReady(room, room.getUser1(), room.getUser2());notifyGameReady(room, room.getUser2(), room.getUser1());return;}}// 还有玩家尝试进入同一房间,提示房间已满// 理论上不会出现该情况,为了让程序更加的健壮, 还是进行判定和提示session.sendMessage(new TextMessage(JacksonUtil.writeValueAsString(CommonResult.fail(HandlerErrorCodeConstants.GAME_ROOM_IS_FULL))));// 日志打印log.info("玩家 {} 进入游戏房间", userInfo.getUserName());}

在将玩家加入游戏房间时,我们需要考虑到线程安全问题

若两个玩家同时与 WebSocket 建立连接,那么,在将玩家加入游戏房间时, 就很可能都判定 玩家1 为空,从而将其都设置为玩家1

因此,我们需要对 玩家加入游戏房间操作 进行加锁(可以直接对 Room 对象进行加锁)

 添加错误码:

public interface HandlerErrorCodeConstants {ErrorCode GET_GAME_ROOM_ERROR = new ErrorCode(300, "用户尚未匹配到游戏房间");ErrorCode GAME_ROOM_IS_FULL = new ErrorCode(301, "游戏房间已满");
}

实现通知玩家就绪 notifyGameReady 方法:

    /*** 通知玩家游戏已准备就绪* @param room 游戏房间* @param thisUser 玩家* @param thatUser 对手*/private void notifyGameReady(Room room, UserInfo thisUser, UserInfo thatUser) {try {// 构造游戏就绪响应GameReadyResult gameReadyResult = new GameReadyResult();gameReadyResult.setRoomId(room.getRoomId());gameReadyResult.setThisUserId(thisUser.getUserId());gameReadyResult.setThatUserId(thatUser.getUserId());gameReadyResult.setWhiteUserId(room.getWhiteUserId());// 发送游戏就绪响应WebSocketSession session = onlineUserManager.getFromRoom(thisUser.getUserId());session.sendMessage(new TextMessage(JacksonUtil.writeValueAsString(CommonResult.success(gameReadyResult))));} catch (Exception e) {log.warn("notifyGameReady 通知玩家游戏准备就绪异常 e: ", e);}}

接下来,我们先实现 连接关闭 和 异常关闭 的相关逻辑,最后再实现对落子请求的处理

玩家下线处理

我们实现 logoutFromRoom 方法,来对玩家下线进行处理:

1. 获取用户信息,判断用户是否登录

2. 获取游戏房间中存储的玩家信息,针对多开情况进行处理

3. 当玩家掉线时,此时对手直接胜利

4. 通知玩家对手胜利,并修改对应玩家分数

    /*** 退出游戏房间* @param session*/private void logoutFromRoom(WebSocketSession session) {// 获取用户信息UserInfo userInfo = (UserInfo) session.getAttributes().get(USER_INFO);// 判断用户是否登录if (null == userInfo) {return;}// 获取游戏房间中存储的玩家信息WebSocketSession onlineSession = onlineUserManager.getFromRoom(userInfo.getUserId());// 多开情况,直接退出if (session != onlineSession) {return;}// 玩家下线onlineUserManager.exitGameRoom(userInfo.getUserId());// 当前玩家下线,对手胜利notifyThatUserWin(userInfo);log.info("玩家 {} 从游戏房间退出", userInfo.getUserName());}

我们继续实现通知玩家对手胜利 notifyThatUserWin 方法:

要通知对手,我们首先需要查询到对手的相关 session 信息:

1. 查询玩家所在游戏房间

2. 判断游戏房间是否已经被销毁(表明此时游戏已经结束,或对手已经下线)

3. 判断对手是否下线

4. 为对手玩家返回响应

5. 销毁房间

6. 更新玩家分数

    /*** 通知对手胜利* @param userInfo*/private void notifyThatUserWin(UserInfo userInfo) {try {// 查找玩家下线所在房间Room room = roomManager.getRoomByUserId(userInfo.getUserId());// 判断游戏房间是否已经销毁if (null == room) {return;}// 判断对手是否已经下线UserInfo thatUser = (room.getUser1() == userInfo) ?room.getUser2() : room.getUser1();if (null == thatUser) {return;}// 对手会话是否存在WebSocketSession thatUserSession = onlineUserManager.getFromRoom(thatUser.getUserId());if (null == thatUserSession) {return;}// 返回响应PutChessResult result = new PutChessResult();result.setUserId(thatUser.getUserId());result.setRow(-1);result.setCol(-1);result.setWinner(thatUser.getUserId());thatUserSession.sendMessage(new TextMessage(JacksonUtil.writeValueAsString(CommonResult.success(result))));log.info("房间{} 玩家 {} 胜利!", room.getRoomId(), thatUser.getUserId());// 销毁游戏房间roomManager.remove(room.getRoomId(), room.getUser1().getUserId(), room.getUser2().getUserId());// TODO 更新玩家分数} catch (IOException e) {log.warn("通知玩家下线异常 e: ", e);} catch (Exception e) {log.warn("玩家下线异常 e: ", e);}}

关于玩家分数的更新,我们后续再进行实现

异常情况处理:

    @Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {// 打印错误信息log.error("游戏过程中出现异常: ", exception);logoutFromRoom(session);}

连接关闭:

    @Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {log.info("游戏连接断开, code: {}, reason: {}",status.getCode(), status.getReason());// 玩家下线logoutFromRoom(session);}

接下来,我们就可以处理落子请求了

处理落子请求

handleTextMessage

handleTextMessage 主要用于接收落子请求并对其进行处理:

1. 判断用户是否登录

2. 获取游戏房间,判断游戏房间是否存在

3. 将接收到的数据转化为 PutChessParam 对象

4. 根据请求进行落子

关于落子相关逻辑,我们定义 GameService,并创建 putChess 方法来进行处理

    @Autowiredprivate GameService gameService;@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {UserInfo userInfo = (UserInfo) session.getAttributes().get(USER_INFO);// 用户是否登录if (null == userInfo) {session.sendMessage(new TextMessage(JacksonUtil.writeValueAsString(CommonResult.noLogin())));return;}// 获取游戏房间Room room = roomManager.getRoomByUserId(userInfo.getUserId());if (null == room) {session.sendMessage(new TextMessage(JacksonUtil.writeValueAsString(CommonResult.fail(HandlerErrorCodeConstants.GET_GAME_ROOM_ERROR))));}// 获取用户端发送的数据String payload = message.getPayload();// 将 JSON 字符串转化为 java 对象PutChessParam putChessParam = JacksonUtil.readValue(payload, PutChessParam.class);// 根据请求落子gameService.putChess(putChessParam, userInfo);}

GameService 接口:

@Service
public interface GameService {/*** 处理落子请求* @param param* @param userInfo*/void putChess(PutChessParam param, UserInfo userInfo);
}

在 GameServiceImpl 中实现具体逻辑:

@Service
@Slf4j
public class GameServiceImpl implements GameService {@Autowiredprivate RoomManager roomManager;@Autowiredprivate OnlineUserManager onlineUserManager;/*** 处理落子请求* @param param* @param userInfo*/@Overridepublic void putChess(PutChessParam param, UserInfo userInfo) {}

 

putChess

putChess 需要实现的业务逻辑:

1. 获取玩家所在游戏房间对象

2. 判断当前玩家是 玩家1 还是 玩家2

3. 从请求参数中获取落子位置

4. 判断当前位置是否有子

5. 若无,则落子

6. 进行胜负判定

7. 通知双方玩家落子结果

8. 若胜负已分,则更新相关分数,并销毁游戏房间

落子 

 我们先实现前四个部分相关逻辑:

    @Overridepublic void putChess(PutChessParam param, UserInfo userInfo) {try {// 获取游戏房间Room room = roomManager.getRoomByUserId(userInfo.getUserId());// 判断当前玩家是 玩家1 还是 玩家2int chess = userInfo.getUserId() == room.getUser1().getUserId()? 1 : 2;// 获取落子位置int row = param.getRow();int col = param.getCol();// 判断当前位置是否已经有子if (!room.isEmpty(row, col)) {log.info("已禁止 玩家 {} 在 {} 行 {} 列重复落子!",userInfo.getUserName(), row, col);return;}// 落子room.putChess(row, col, chess);// 打印棋盘信息,方便观察落子情况room.printBoard();}

在对应位置落子需要对 room 对象中 board 的进行修改,因此,我们在 Room 中提供对应方法:

    /*** 判断当前位置是否已经有子* @param row* @param col* @return*/public boolean isEmpty(int row, int col) {return board[row][col] == 0;}/*** 在当前位置落子* @param row* @param col* @param chess*/public void putChess(int row, int col, int chess) {board[row][col] = chess;piecesNumber++;}

为了方便观察落子请求,我们可以对棋盘进行打印,观察落子结果:

    /*** 打印棋盘*/public void printBoard() {System.out.println("------------------棋盘-----------------------");for (int i = 0; i < MAX_ROW; i++) {for (int j = 0; j < MAX_COL; j++) {System.out.print(board[i][j] + " ");}System.out.println();}System.out.println("------------------棋盘-----------------------");}

 

胜负判定 

落子成功后,我们进行胜负判定:

            // 进行胜负判断Long winner = room.checkWinner(row, col, chess);

若玩家1胜利,则返回玩家1 id;

若玩家2 胜利,则返回玩家2 id;

若为平局,则返回 -1;

若未决出胜负,则返回 0 

我们在每次落下棋子之后,进行胜负判定,也就是判定棋盘上形成连续的五个同色棋子(可以是横排、竖排 或 斜排

在进行判定时,我们并不需要遍历整个棋盘,只需要考虑新落棋子周围位置

若还未落子就已经出现了 连续五个同色棋子 的情况,则说明前面的判定出现了异常

因此,我们只需要以 (row, col) 为中心,判定其是否与周围棋子形成 五个同色棋子

我们首先来考虑 横排:

此时,需要考虑新落的子(x)能否与当前行周围棋子形成五个同色棋子:

新落棋子为 x

判定 x 与 左边 4 个棋子 是否为同色棋子

同理:

判定 x 与 左边 3 个棋子,右边 1 个棋子是否为同色棋子

判定 x 与 左边 2 个棋子,右边 2 个棋子是否为同色棋子

判定 x 与 左边 1 个棋子,右边 3 个棋子是否为同色棋子

判定 x 与 右边 4 个棋子是否为同色棋子

我们通过 for 循环来进行判定:

        // 行for (int c = col - 4; c <= col; c++) {try {if(board[row][c] == chess&& board[row][c + 1] == chess&& board[row][c + 2] == chess&& board[row][c + 3] == chess&& board[row][c + 4] == chess) {return chess == user1.getUserId() ?user1.getUserId() : user2.getUserId();}} catch (ArrayIndexOutOfBoundsException e) {continue;}}

在判定过程中可能会出现数组越界的异常情况,我们捕获 数组越界异常,并继续判定

我们继续看竖排:

需要判定:

 x 与 上方 4 个棋子 是否为同色棋子

 x 与 上方 3 个棋子,下方 1 个棋子是否为同色棋子

 x 与 上方 2 个棋子,下方 2 个棋子是否为同色棋子

 x 与 上方 1 个棋子,下方 3 个棋子是否为同色棋子

 x 与 下方 4 个棋子是否为同色棋子

        // 列for (int r = row - 4; r <= row; r++) {try {if(board[r][col] == chess&& board[r + 1][col] == chess&& board[r + 2][col] == chess&& board[r + 3][col] == chess&& board[r + 4][col] == chess) {return chess == 1 ?user1.getUserId() : user2.getUserId();}} catch (ArrayIndexOutOfBoundsException e) {// 若出现数据下标越界的情况,则继续判断continue;}}

再来看斜方:

 

 左对角线 和 右对角线 都需要进行判定:

        // 左对角线for (int r = row - 4, c = col - 4; r <= row && c <= col; r++, c++) {try {if(board[r][c] == chess&& board[r + 1][c + 1] == chess&& board[r + 2][c + 2] == chess&& board[r + 3][c + 3] == chess&& board[r + 4][c + 4] == chess) {return chess == 1 ? user1.getUserId() : user2.getUserId();}} catch (ArrayIndexOutOfBoundsException e) {continue;}}// 右对角线for (int r = row - 4, c = col + 4; r <= row && c >= col; r++, c--) {try {if(board[r][c] == chess&& board[r + 1][c - 1] == chess&& board[r + 2][c - 2] == chess&& board[r + 3][c - 3] == chess&& board[r + 4][c - 4] == chess) {return chess == 1 ? user1.getUserId() : user2.getUserId();}} catch (ArrayIndexOutOfBoundsException e) {continue;}}

此外,若未决出胜负,但此时棋盘已满,则为平局:

        if(piecesNumber >= MAX_ROW * MAX_COL) {return -1L;}

判断落子数量是否已达到最大数量

若以上情况都不满足,则表明当前未决出胜负,还需继续落子,因此返回 0

胜负判断完整代码:

    /*** 判断胜负* @param row* @param col* @param chess* @return 0:尚未分出胜负 -1:平局*/public Long checkWinner(int row, int col, int chess) {// 行for (int c = col - 4; c <= col; c++) {try {if(board[row][c] == chess&& board[row][c + 1] == chess&& board[row][c + 2] == chess&& board[row][c + 3] == chess&& board[row][c + 4] == chess) {return chess == user1.getUserId() ?user1.getUserId() : user2.getUserId();}} catch (ArrayIndexOutOfBoundsException e) {continue;}}// 列for (int r = row - 4; r <= row; r++) {try {if(board[r][col] == chess&& board[r + 1][col] == chess&& board[r + 2][col] == chess&& board[r + 3][col] == chess&& board[r + 4][col] == chess) {return chess == 1 ?user1.getUserId() : user2.getUserId();}} catch (ArrayIndexOutOfBoundsException e) {// 若出现数据下标越界的情况,则继续判断continue;}}// 左对角线for (int r = row - 4, c = col - 4; r <= row && c <= col; r++, c++) {try {if(board[r][c] == chess&& board[r + 1][c + 1] == chess&& board[r + 2][c + 2] == chess&& board[r + 3][c + 3] == chess&& board[r + 4][c + 4] == chess) {return chess == 1 ? user1.getUserId() : user2.getUserId();}} catch (ArrayIndexOutOfBoundsException e) {continue;}}// 右对角线for (int r = row - 4, c = col + 4; r <= row && c >= col; r++, c--) {try {if(board[r][c] == chess&& board[r + 1][c - 1] == chess&& board[r + 2][c - 2] == chess&& board[r + 3][c - 3] == chess&& board[r + 4][c - 4] == chess) {return chess == 1 ? user1.getUserId() : user2.getUserId();}} catch (ArrayIndexOutOfBoundsException e) {continue;}}// 平局if(piecesNumber == MAX_ROW * MAX_COL) {return -1L;}piecesNumber++;return 0L;}

构造落子响应并返回

接着,需要构造落子响应并返回:

1. 获取 session1 和 session2

2. 判断是否存在玩家掉线的情况:若双方玩家均掉线,则结果设置为平局;若玩家1掉线,则玩家2胜利;若玩家2掉线,则玩家1胜利

3. 构造响应并返回

            // 通知玩家WebSocketSession session1 = onlineUserManager.getFromRoom(room.getUser1().getUserId());WebSocketSession session2 = onlineUserManager.getFromRoom(room.getUser2().getUserId());if (null == session1 && null == session2) {// 玩家均掉线winner = -1L;log.info("游戏房间 {} 玩家1 和 玩家2 均掉线", room.getRoomId());} else if (null == session1) {winner = room.getUser2().getUserId();log.info("游戏房间 {} 玩家1 掉线", room.getRoomId());} else if (null == session2){winner = room.getUser1().getUserId();log.info("游戏房间 {} 玩家2 掉线", room.getRoomId());}// 构造响应并返回PutChessResult result = new PutChessResult(userInfo.getUserId(), row, col, winner);if (null != session1) {session1.sendMessage(new TextMessage(JacksonUtil.writeValueAsString(CommonResult.success(result))));}if (null != session2) {session2.sendMessage(new TextMessage(JacksonUtil.writeValueAsString(CommonResult.success(result))));}

判断胜负是否已分,若胜负已分,则更新玩家分数,并销毁游戏房间

            // 若胜负已分,则更新玩家分数,并销毁房间if (winner != 0) {if (winner == -1) {log.info("房间 {} 游戏结束, 结果为平局");} else {log.info("房间 {} 游戏结束, 获胜方id为 {}", room.getRoomId(), winner);// TODO 更新分数}// 销毁游戏房间roomManager.remove(room.getRoomId(),room.getUser1().getUserId(), room.getUser2().getUserId());}

 

更新玩家分数

创建 updateScore 接口,更新玩家分数:

@Service
public interface GameService {/*** 处理落子请求* @param param* @param userInfo*/void putChess(PutChessParam param, UserInfo userInfo);/*** 更新玩家分数* @param winner* @param loser*/void updateScore(Long winner, Long loser);
}

调用 UserMapper 相关方法,更新分数: 

    @Autowiredprivate UserMapper userMapper;@Overridepublic void updateScore(Long winner, Long loser) {userMapper.updateWinner(winner);userMapper.updateLoser(loser);}

UserMapper

    @Update("update user set total_count = total_count + 1, win_count = win_count + 1, score = score + 30 " +" where id = #{id}")int updateWinner(@Param("id") Long winner);@Update("update user set total_count = total_count + 1, score = score - 30 " +" where id = #{id}")int updateLoser(@Param("id") Long loser);

 更新分数:

putchess 完整代码:

    /*** 处理落子请求* @param param* @param userInfo*/@Overridepublic void putChess(PutChessParam param, UserInfo userInfo) {try {// 获取游戏房间Room room = roomManager.getRoomByUserId(userInfo.getUserId());// 判断当前玩家是 玩家1 还是 玩家2int chess = userInfo.getUserId() == room.getUser1().getUserId()? 1 : 2;// 获取落子位置int row = param.getRow();int col = param.getCol();// 判断当前位置是否已经有子if (!room.isEmpty(row, col)) {log.info("已禁止 玩家 {} 在 {} 行 {} 列重复落子!",userInfo.getUserName(), row, col);return;}// 落子room.putChess(row, col, chess);// 打印棋盘信息,方便观察落子情况room.printBoard();// 进行胜负判断Long winner = room.checkWinner(row, col, chess);// 通知玩家WebSocketSession session1 = onlineUserManager.getFromRoom(room.getUser1().getUserId());WebSocketSession session2 = onlineUserManager.getFromRoom(room.getUser2().getUserId());if (null == session1 && null == session2) {// 玩家均掉线winner = -1L;log.info("游戏房间 {} 玩家1 和 玩家2 均掉线", room.getRoomId());} else if (null == session1) {winner = room.getUser2().getUserId();log.info("游戏房间 {} 玩家1 掉线", room.getRoomId());} else if (null == session2){winner = room.getUser1().getUserId();log.info("游戏房间 {} 玩家2 掉线", room.getRoomId());}// 构造响应并返回PutChessResult result = new PutChessResult(userInfo.getUserId(), row, col, winner);if (null != session1) {session1.sendMessage(new TextMessage(JacksonUtil.writeValueAsString(CommonResult.success(result))));}if (null != session2) {session2.sendMessage(new TextMessage(JacksonUtil.writeValueAsString(CommonResult.success(result))));}// 若胜负已分,则更新玩家分数,并销毁房间if (winner != 0) {if (winner == -1) {log.info("房间 {} 游戏结束, 结果为平局");} else {log.info("房间 {} 游戏结束, 获胜方id为 {}", room.getRoomId(), winner);// 更新分数Long loser = (room.getUser1().getUserId() == winner) ?room.getUser2().getUserId() : room.getUser1().getUserId();updateScore(winner , loser);}// 销毁游戏房间roomManager.remove(room.getRoomId(),room.getUser1().getUserId(), room.getUser2().getUserId());}} catch (IOException e) {log.warn("发送落子响应异常 e: ", e);} catch (Exception e) {log.warn("落子异常 e: ", e);}}

此外,在玩家掉线时,我们还遗留了一个部分——更新玩家分数,我们将其补充完整:

至此,对战模块的后端代码就基本实现完毕了,我们修改客户端代码并进行测试

修改客户端代码

修改 code != 200 的情况:

根据我们约定的错误码:

当 code = 401 或 code = 402 时,我们让其跳转到 登录页面

当 code = 300 或 code = 301 时,我们让其跳转到 游戏大厅页面

    if (resp.code != 200) {if (resp.code == 300 || resp.code == 301) {location.replace("/game_hall.html");} else if (resp.code == 402 || resp.code == 401){location.replace("/login.html")} else {alert("异常情况:" + resp.errorMessage);}return;}

游戏就绪响应处理 和 落子响应处理都需进行修改

此外,当有玩家退出游戏房间时,我们返回的 row 和 col 为 -1

因此,在标记棋子时,我们需要先进行判断:

        // 标记此处有棋子if (gameRes.row >= 0 && gameRes.col >= 0) {chessBoard[gameRes.row][gameRes.col] = 1;} else {// 提示对手掉线,并返回游戏大厅alert("对方已掉线,恭喜你!你赢了!");location.replace("/game_hall.html");}

 接下来,我们就来对对战模块进行测试

对战模块测试

运行程序,登录两个账号,让其进行匹配,匹配成功后,跳转到游戏房间:

当 玩家1 进入游戏大厅时:

 玩家2 进入游戏大厅:

玩家1 落子:

玩家2 落子:

游戏胜利:

 观察后端落子情况:

与客户端落子情况相同,符合预期

 测试玩家掉线:

但此时还存在一个问题,那就是 用户信息显示

上述我们测试了两种情况——玩家胜利 和 玩家掉线

此时,已经进行了两场游戏,但我们观察游戏大厅显示的用户信息:

此时的用户信息并未进行更新,我们观察后端代码:

由于我们是直接从 session 中获取的信息,因此,当我们更新数据库中的对应信息时,session 存储的信息并未改变,因此,我们需要对其进行修改,从数据库中获取用户信息:

 

根据从 session 中获取的用户 id(用户 id 始终不变),从而在数据库中查询用户信息 

添加 selectByUserId 方法:

    @Select("select * from user where id = #{id}")UserDO selectByUserId(@Param("id") Long id);
http://www.dtcms.com/wzjs/605932.html

相关文章:

  • 佛山网站建设明细ui设计app界面设计流程
  • 购物网站建设服务新乡网站
  • 大连金州新区规划建设局网站网络空间测绘
  • 做物流网站找哪家好企业咨询方案
  • 网站开发的问题有哪些在门户网站上做推广
  • 专题网站设计双流海外网络推广
  • 美术馆网站页面设计wordpress 公众号 采集
  • 外贸网站流量网站定制那个好
  • 自助游网站开发分析报告网络游戏举报投诉官网12318
  • 国外设计网站图片中学生做的网站
  • 浅谈旅游网站的规划与建设加工平台调平装置
  • 旅游电子商务网站开发项目进度表新加坡购物网站排名
  • 网站上的办公网站怎么做wordpress 用svn checkout
  • 广州建设网站优化推广服务
  • 建立购物网站 app用jsp怎么做的购物网站
  • 开封网站设计价格北京公司如何做网站
  • 昆明做网站建设找谁哈尔滨网站制作方案定制
  • 培训教育网站建设网页链接提取
  • 如何做影视网站的标题wordpress php7
  • 百度推广销售员的工作内容东莞seo优化培训
  • 鞍山网站建设找金航娄底市住房和城乡建设局官方网站
  • 网站群建设费用武进区建设局网站
  • 养老网站建设团员关系没转就作废吗
  • 上海建设小学网站杭州优化排名哪家好
  • 企业网站设计哪家好网站iis安全配置
  • 医院网站怎么建设门户网站建设工作
  • 基础建设期刊在哪个网站可以查wordpress屏蔽国外ip访问
  • 江阴网站建设工作室wordpress首页文件
  • vps怎么搭建网站南阳千牛网站建设
  • 新建的网站百度搜不到wordpress知识库主题