上期我们完成了websocket建立连接后的数据初始化,今天我们完成落子交互的具体代码:
这里我们先复习一下,之前约定好的落子请求与响应包含的字段:
1. 发送落子请求
我们在script.js文件中找到落子的相关方法,增加发送请求的代码:
chess.onclick = function (e) {if (over) {return;}if (!me) {return;}let x = e.offsetX;let y = e.offsetY;// 注意, 横坐标是列, 纵坐标是行let col = Math.floor(x / 30);let row = Math.floor(y / 30);if (chessBoard[row][col] == 0) {// TODO 发送坐标给服务器, 服务器要返回结果webSocket.send(JSON.stringify({message: 'putChess',userId: gameInfo.userId1,row: row,col: col}));oneStep(col, row, gameInfo.isBlack);chessBoard[row][col] = 1;}}
2. 处理请求发送响应
@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {//1. 获取用户信息User user = (User)session.getAttributes().get("user");//2. 获取请求信息GameRequest request = objectMapper.readValue(message.getPayload(),GameRequest.class);//3. 调用游戏房间中的putChess方法实现落子Room room = roomManager.getRoomByUserId(user.getUserId());room.putChess(request);}
实现putChess:
private static int ROW = 15;private static int COL = 15;//棋盘, 0表示未落子,1表示玩家1的子,2表示玩家2的子private int[][] board = new int[ROW][COL];//处理落子请求public void putChess(GameRequest request) throws IOException {//1. 判断落子玩家int chess = request.getUserId() == user1.getUserId() ? 1 : 2;int row = request.getRow();int col = request.getCol();//2. 落子if(board[row][col] != 0) {System.out.println("[" + row + "," + col + "]已经有子了");return;}board[row][col] = chess;//3. 判断是否获胜int winnerId = checkWinner(row, col) ? request.getUserId() : -1;//4. 给房间中的玩家返回响应GameResponse response = new GameResponse();response.setMessage("putChess");response.setUserId(request.getUserId());response.setRow(row);response.setCol(col);response.setWinnerId(winnerId);//通过OnlineUserManager获取房间中的玩家WebSocketSession session1 = onlineUserManager.getFromRoom(user1.getUserId());WebSocketSession session2 = onlineUserManager.getFromRoom(user2.getUserId());//判断是否有玩家下线if(session1 == null) {//玩家1下线,玩家2获胜response.setWinnerId(user2.getUserId());System.out.println("玩家1掉线");}if(session1 == null) {//玩家2下线,玩家1获胜response.setWinnerId(user1.getUserId());System.out.println("玩家2掉线");}String resp = objectMapper.writeValueAsString(response);if(session1 != null) {session1.sendMessage(new TextMessage(resp));}if(session2 != null) {session2.sendMessage(new TextMessage(resp));}if(response.getWinnerId() != -1) {System.out.println("分出胜负, 游戏房间:" + roomId + "即将销毁");roomManager.remove(roomId, user1.getUserId(), user2.getUserId());}}
注意:我们并没有把Room注册为Spring组件,进行依赖注入时,需要在构造方法中手动注入:
public Room() {roomId = UUID.randomUUID().toString();onlineUserManager = J20250110GoBangApplication.context.getBean(OnlineUserManager.class);objectMapper = J20250110GoBangApplication.context.getBean(ObjectMapper.class);roomManager = J20250110GoBangApplication.context.getBean(RoomManager.class);}
实现checkWinner方法检测是否获胜:
private boolean checkWinner(int row, int col) {int count = 1;//判断行是否五子连珠for(int i = col + 1; i < COL; i++) {if(board[row][i] == board[row][col]) {count++;}else{break;}}for(int i = col - 1; i >= 0; i--) {if(board[row][i] == board[row][col]) {count++;}else{break;}}if(count >= 5) {return true;}//判断列是否五子连珠count = 1;for(int i = row + 1; i < ROW; i++) {if(board[i][col] == board[row][col]) {count++;}else{break;}}for(int i = row - 1; i >= 0; i--) {if(board[i][col] == board[row][col]) {count++;}else{break;}}if(count >= 5) {return true;}//判断左上到右下斜线是否五子连珠count = 1;for(int i = row + 1, j = col + 1; i < ROW && j < COL; i++, j++) {if(board[i][j] == board[row][col]) {count++;}else{break;}}for(int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {if(board[i][j] == board[row][col]) {count++;}else{break;}}if(count >= 5) {return true;}//判断右上到左下斜线是否五子连珠count = 1;for(int i = row - 1, j = col + 1; i >= 0 && j < COL; i--, j++) {if(board[i][j] == board[row][col]) {count++;}else{break;}}for(int i = row + 1, j = col - 1; i < ROW && j >= 0; i++, j--) {if(board[i][j] == board[row][col]) {count++;}else{break;}}if(count >= 5) {return true;}return false;}
3. 处理落子响应
在之前初始化游戏的代码中,我们在游戏初始化完成时会调用initGame()方法:
于是我们可以在initGame方法末尾中修改onmessages使之变为处理落子响应的方法:
webSocket.onmessage = function (e) {let resp = JSON.parse(e.data);console.log(resp);if (resp.message != 'putChess') {console.log("响应类型错误");return;}//判断是谁落子if (resp.userId == gameInfo.userId1) {//根据对应棋子颜色绘制棋子oneStep(resp.col, resp.row, gameInfo.isBlack);} else if (resp.userId == gameInfo.userId2) {//根据对应棋子颜色绘制棋子oneStep(resp.col, resp.row, !gameInfo.isBlack);} else {console.log("响应出错 userId:" + resp.userId);return;}// 给对应的位置设置为1,表示有子chessBoard[resp.row][resp.col] = 1;//交互落子方me = !me;setScreenText(me);//判断游戏是否结束if (resp.winnerId == gameInfo.userId1) {over = true;alert("你赢了!!!");location.href = "/hall.html";}if (resp.winnerId == gameInfo.userId2) {over = true;alert("你输了");location.href = "/hall.html";}}