[网页五子棋][对战模块]实现游戏房间页面,服务器开发(创建落子请求/响应对象)

实现游戏房间页面

创建 css/game_room.css

  • #screen 用于显示当前的状态,例如"等待玩家连接中...","轮到你落子","轮到对方落子"等
css 复制代码
#screen {  
    width: 450px;  
    height: 50px;  
    margin-top: 10px;  
    color: #8f4e19;  
    font-size: 28px;  
    font-weight: 700;  
    line-height: 50px;  
    text-align: center;  
}

实现 game_room.html,这个页面就是匹配成功之后,要跳转到的新页面

java 复制代码
<!DOCTYPE html>  
<html lang="en">  
<head>  
    <meta charset="UTF-8">  
    <meta name="viewport" content="width=device-width, initial-scale=1.0">  
    <title>游戏房间</title>  
    <link rel="stylesheet" href="css/common.css">  
    <link rel="stylesheet" href="css/game_room.css">  
</head>  
<body>  
    <div class="nav">五子棋对战</div>  
    <div class="container">  
         <div>            
         	<!-- 棋盘区域,需要基于 canva 进行实现 -->  
            <canvas id="chess" width="450px" height="450px">  
  
            </canvas>             
            <!-- 显示区域 -->  
            <div id="screen">等待玩家连接中...</div>  
         </div>    
        </div>    
    <script src="js/app.js"></script>  
</body>  
</html>

页面效果

初始化 websocket

js/app.js 中,加入 websocket 的连接代码,实现前后端交互

  • 先删掉原来 initGame() 函数的调用,一会在获取到服务器反馈的就绪响应之后,再初始化棋盘
  • 创建 websocket 对象,并注册 onopen/onclose/onerror 函数
    • 其中在 onopen 中做一个跳转到大厅的逻辑,当网络断开时,则返回大厅
  • 实现 onmessage 方法,onmessage 先处理游戏就绪响应
js 复制代码
// 此处写的路径要写作 /game,不要写作 /game/let websocket = new WebSocket("ws://127.0.0.1:8080/game");  
  
websocket.onopen = function() {  
    console.log("连接游戏房间成功!");  
}  
  
websocket.onclose = function() {  
    console.log("和游戏服务器断开连接!");  
}  
  
websocket.onerror = function() {  
    console.log("和服务器的连接出现异常!");  
}  
  
// 用户点击关闭关闭页面,就断开网络连接  
window.onbeforeunload = function() {  
    websocket.close();  
}  
  
// 处理服务器返回的响应数据  
websocket.onmessage = function(event) {  
    console.log("[handlerGameReady] " + event.data);  
    let resp = JSON.parse(event.data);  
  
    if(resp.message != 'gameReady') {  
        console.log("响应类型错误!");  
        return;  
    }  
  
    if(!resp.ok) {  
        alert("连接游戏失败! reason: " + resp.reason);  
        // 如果出现连接失败的情况,就回到游戏大厅  
        location.assign("/game_hall.html");  
        return;  
    }  
  
    gameInfo.roomId = resp.roomId;  
    gameInfo.thisUserId = resp.thisUserId;  
    gameInfo.thatUserId = resp.thatUserId;  
    gameInfo.isWhite = resp.isWhite;  
  
    // 初始化棋盘  
    initGame();  
    // 设置显示区域的内容  
    setScreenText(gameInfo.isWhite);  
}

服务器开发

创建并注册 GameAPI 类

创建 api.GameAPI,处理 websocket 请求

  • 这里准备一个 ObjectMapper 类,让后面进行消息发送的时候进行序列化
  • 同时注入一个 RoomManagerOnlineUserManager
java 复制代码
package org.example.java_gobang.api;  
  
import org.springframework.stereotype.Component;  
import org.springframework.web.socket.CloseStatus;  
import org.springframework.web.socket.TextMessage;  
import org.springframework.web.socket.WebSocketSession;  
import org.springframework.web.socket.handler.TextWebSocketHandler;  
  
@Component  
public class GameAPI extends TextWebSocketHandler {  

	@Autowired  
	private ObjectMapper objectMapper = new ObjectMapper();  
	@Autowired  
	private RoomManager roomManager;
	
    @Override  
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {  
  
    }  
  
    @Override  
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {  
    }  
  
    @Override  
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {  
    }  
  
    @Override  
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {  
    }  
}

修改 WebSocketConfig,将 GameAPI 进行注册

java 复制代码
package org.example.java_gobang.config;  
  
import org.example.java_gobang.api.GameAPI;  
import org.example.java_gobang.api.MatchAPI;  
import org.example.java_gobang.api.TestAPI;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.web.socket.config.annotation.EnableWebSocket;  
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;  
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;  
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;  
  
  
/**  
 * 这个类是用来注册 WebSocketHandler 的配置类  
 * 这个类是来告诉 Spring(websocket),哪一个类是和哪一个路径相匹配的  
 */  
@Configuration  
@EnableWebSocket // 让 Spring 框架知道这个类是用来配置 websocket 的  
public class WebSocketConfig implements WebSocketConfigurer {  
    @Autowired  
    private TestAPI testAPI;  
  
    @Autowired  
    private MatchAPI matchAPI;  
  
    @Autowired  
    private GameAPI gameAPI;  
  
    @Override  
    public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {  
        // 当我们的客户端连接到 /test 路径的时候,就会触发到当前的 TestAPI,然后调用执行里面的方法  
        webSocketHandlerRegistry.addHandler(testAPI, "/test");  
        // 通过 .addInterceptors(new HttpSessionHandshakeInterceptor() 这个操作,  
        // 来把 HttpSession ⾥的属性放到 WebSocket 的 session 中  
        // 然后就可以在 WebSocket 代码中 WebSocketSession ⾥拿到 HttpSession 中的 attribute        webSocketHandlerRegistry.addHandler(matchAPI,"/findMatch")  
                .addInterceptors(new HttpSessionHandshakeInterceptor());  
        webSocketHandlerRegistry.addHandler(gameAPI, "/game")  
                .addInterceptors(new HttpSessionHandshakeInterceptor());  
    }  
}

创建落子请求/响应对象

这部分内容要和约定的前后端交互接口匹配

创建 game.GameReadyResponse

java 复制代码
package org.example.java_gobang.game;  
  
// 客户端连接到游戏房间后,服务器返回的响应  
  
public class GameReadyResponse {  
    private String message;  
    private boolean ok;  
    private String reason;  
    private String roomId;  
    private int thisUserId;  
    private int thatUserId;  
    private int whiteUser;  
  
    public boolean isOk() {  
        return ok;  
    }  
  
    public void setOk(boolean ok) {  
        this.ok = ok;  
    }  
  
    public String getMessage() {  
        return message;  
    }  
  
    public void setMessage(String message) {  
        this.message = message;  
    }  
  
    public String getReason() {  
        return reason;  
    }  
  
    public void setReason(String reason) {  
        this.reason = reason;  
    }  
  
    public String getRoomId() {  
        return roomId;  
    }  
  
    public void setRoomId(String roomId) {  
        this.roomId = roomId;  
    }  
  
    public int getThatUserId() {  
        return thatUserId;  
    }  
  
    public void setThatUserId(int thatUserId) {  
        this.thatUserId = thatUserId;  
    }  
  
    public int getThisUserId() {  
        return thisUserId;  
    }  
  
    public void setThisUserId(int thisUserId) {  
        this.thisUserId = thisUserId;  
    }  
  
    public int getWhiteUser() {  
        return whiteUser;  
    }  
  
    public void setWhiteUser(int whiteUser) {  
        this.whiteUser = whiteUser;  
    }  
}

创建 game.GameRequest

  • 表示落子请求
java 复制代码
package org.example.java_gobang.game;  
  
// 这个类表示落子请求  
public class GameRequest {  
	// 如果不给 message 设置 getter / setter, 则不会被 jackson 序列化
    private String message;  
    private int userId;  
    private int row;  
    private int col;  
  
    public String getMessage() {  
        return message;  
    }  
  
    public void setMessage(String message) {  
        this.message = message;  
    }  
  
    public int getUserId() {  
        return userId;  
    }  
  
    public void setUserId(int userId) {  
        this.userId = userId;  
    }  
  
    public int getRow() {  
        return row;  
    }  
  
    public void setRow(int row) {  
        this.row = row;  
    }  
  
    public int getCol() {  
        return col;  
    }  
  
    public void setCol(int col) {  
        this.col = col;  
    }  
}

创建 game.Response

  • 表示落子响应
java 复制代码
package org.example.java_gobang.game;  
  
// 这个类表示 落子响应  
public class GameResponse {  
    // 如果不给 message 设置 getter / setter, 则不会被 jackson 序列化  
    private String message;  
    private int userId;  
    private int row;  
    private int col;  
    private int winner;  
  
    public String getMessage() {  
        return message;  
    }  
  
    public void setMessage(String message) {  
        this.message = message;  
    }  
  
    public int getUserId() {  
        return userId;  
    }  
  
    public void setUserId(int userId) {  
        this.userId = userId;  
    }  
  
    public int getRow() {  
        return row;  
    }  
  
    public void setRow(int row) {  
        this.row = row;  
    }  
  
    public int getCol() {  
        return col;  
    }  
  
    public void setCol(int col) {  
        this.col = col;  
    }  
  
    public int getWinner() {  
        return winner;  
    }  
  
    public void setWinner(int winner) {  
        this.winner = winner;  
    }  
}
相关推荐
K____End4 分钟前
Spring 中的disposableBean介绍
java·spring·rpc
考虑考虑39 分钟前
JPA自定义sql参数为空和postgresql遇到问题
spring boot·后端·spring
冰橙子id40 分钟前
linux——账号和权限的管理
linux·运维·服务器
紫气东来,茉上花开2 小时前
[特殊字符] Spring Boot底层原理深度解析与高级面试题精析
spring boot·后端·spring
风麒麟3 小时前
13. springCloud AlibabaSeata处理分布式事务
分布式·spring·spring cloud
王道长服务器 | AWS3 小时前
CloudFront 加速详解:AWS CDN 怎么用?
服务器·云计算·aws
CHANG_THE_WORLD3 小时前
编译 Linux openssl
linux·运维·服务器
Insist7533 小时前
linux操作系统---网络协议
linux·运维·服务器
小跌—3 小时前
Linux:理解库制作与原理
linux·服务器