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

实现游戏房间页面

创建 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;  
    }  
}
相关推荐
Danileaf_Guo4 小时前
256台H100服务器算力中心的带外管理网络建设方案
运维·服务器
拾贰_C6 小时前
【Linux | Windows | Terminal Command】 Linux---grep | Windows--- findstr
linux·运维·服务器
alengan8 小时前
linux上面写python3日志服务器
linux·运维·服务器
北城以北88889 小时前
Spring定时任务与Spring MVC拦截器
spring boot·spring·mvc
小卒过河01049 小时前
使用apache nifi 从数据库文件表路径拉取远程文件至远程服务器目的地址
运维·服务器·数据库
WizLC9 小时前
【Java】各种IO流知识详解
java·开发语言·后端·spring·intellij idea
Mr.朱鹏9 小时前
SQL深度分页问题案例实战
java·数据库·spring boot·sql·spring·spring cloud·kafka
土星云SaturnCloud9 小时前
液冷“内卷”:在局部优化与系统重构之间,寻找第三条路
服务器·人工智能·ai·计算机外设
星星不打輰9 小时前
SSM项目--SweetHouse 甜蜜蛋糕屋
java·spring·mybatis·ssm·springmvc