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

news2025/6/4 15:31:36

实现游戏房间页面

创建 css/game_room.css

  • #screen 用于显示当前的状态,例如“等待玩家连接中…”,“轮到你落子”,“轮到对方落子”等
#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,这个页面就是匹配成功之后,要跳转到的新页面image.png|439

<!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>

页面效果

image.png|439

初始化 websocket

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

  • 先删掉原来 initGame() 函数的调用,一会在获取到服务器反馈的就绪响应之后,再初始化棋盘
  • 创建 websocket 对象,并注册 onopen/onclose/onerror 函数
    • 其中在 onopen 中做一个跳转到大厅的逻辑,当网络断开时,则返回大厅
  • 实现 onmessage 方法,onmessage 先处理游戏就绪响应
// 此处写的路径要写作 /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
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 进行注册

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

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

  • 表示落子请求
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

  • 表示落子响应
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;  
    }  
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2394538.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Centos环境下安装/重装MySQL完整教程

目录 一、卸载残留的MySQL环境&#xff1a; 二、安装MySQL&#xff1a; 1、下载MySQL官方的yum源&#xff1a; 2、更新系统yum源&#xff1a; 3、确保系统中有了对应的MySQL安装包&#xff1a; 4、安装MySQL服务&#xff1a; 5、密钥问题安装失败解决方法&#xff1a; …

【Linux】环境变量完全解析

9.环境变量 文章目录 9.环境变量一、命令行参数二、获取环境变量程序中获取环境变量1. 使用命令行参数2. 使用系统调用函数getenv("字符串");3. 使用系统提供的全局变量environ 命令行中查询环境变量 三、常见环境变量1. HOME2. OLDPWD3. PATH4. SHELL 四、环境变量与…

力扣每日一题——找到离给定两个节点最近的节点

目录 题目链接&#xff1a;2359. 找到离给定两个节点最近的节点 - 力扣&#xff08;LeetCode&#xff09; 题目描述 解法一&#xff1a;双指针路径交汇法​ 基本思路 关键步骤 为什么这样可行呢我请问了&#xff1f; 举个例子 特殊情况 Java写法&#xff1a; C写法&a…

卷积神经网络(CNN)入门学习笔记

什么是 CNN&#xff1f; CNN&#xff0c;全称 卷积神经网络&#xff08;Convolutional Neural Network&#xff09;&#xff0c;是一种专门用来处理图片、语音、文本等结构化数据的神经网络。 它模仿人眼识别图像的方式&#xff1a; 从局部到整体&#xff0c;一步步提取特征&a…

VLAN的作用和原理

1. 为什么要有vlan&#xff1f; 分割广播域&#xff0c;避免广播风暴&#xff0c;造成网络资源的浪费 可以灵活的组网&#xff0c;便于管理&#xff0c;同时还有安全加固的功能 2. vlan是怎么实现的&#xff1f;端口的原理&#xff1f; 设置VLAN后&#xff0c;流量之间的转…

深入探讨集合与数组转换方法

目录 1、Arrays.asList() 1.1、方法作用 1.2、内部实现 1.3、修改元素的影响 1.4、注意事项 2、list.toArray() 2.1、方法作用 2.2、内部实现 2.3、修改元素的影响 2.4、特殊情况 1、对象引用 2、数组copy 3、对比总结 4、常见误区与解决方案 5、实际应用建议…

【HarmonyOS 5应用架构详解】深入理解应用程序包与多Module设计机制

⭐本期内容&#xff1a;【HarmonyOS 5应用架构详解】深入理解应用程序包与多Module设计机制 &#x1f3c6;系列专栏&#xff1a;鸿蒙HarmonyOS&#xff1a;探索未来智能生态新纪元 文章目录 前言应用与应用程序包应用程序的基本概念应用程序包的类型标识机制应用安装流程 应用的…

【Oracle】DCL语言

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. DCL概述1.1 什么是DCL&#xff1f;1.2 DCL的核心功能 2. 用户管理2.1 创建用户2.2 修改用户2.3 删除用户2.4 用户信息查询 3. 权限管理3.1 系统权限3.1.1 授予系统权限3.1.2 撤销系统权限 3.2 对象权限3.2.1…

MySQL强化关键_017_索引

目 录 一、概述 二、索引 1.主键索引 2.唯一索引 3.查看索引 4.添加索引 &#xff08;1&#xff09;建表时添加 &#xff08;2&#xff09;建表后添加 5.删除索引 三、树 1.二叉树 2.红黑树 3.B树 4.B树 &#xff08;1&#xff09;为什么 MySQL 选择B树作为索引…

【备忘】php命令行异步执行超长时间任务

环境说明&#xff1a; 操作系统&#xff1a;windows10 IDE&#xff1a;phpstorm 开发语言&#xff1a;php7.4 框架&#xff1a;thinkphp5.1 测试环境&#xff1a;linuxwindows均测试通过。 初级方法&#xff1a; function longRunningTask() {$root_path Tools::get_ro…

在 RK3588 上通过 VSCode 远程开发配置指南

在 RK3588 上通过 VSCode 远程开发配置指南 RK3588 设备本身不具备可视化编程环境&#xff0c;但可以通过 VSCode 的 Remote - SSH 插件 实现远程代码编写与调试。以下是完整的配置流程。 一、连接 RK3588 1. 安装 Debian 系统 先在 RK3588 上安装 Debian 操作系统。 2. 安…

OpenHarmony标准系统-HDF框架之音频驱动开发

文章目录 引言OpenHarmony音频概述OpenHarmony音频框图HDF音频驱动框架概述HDF音频驱动框图HDF音频驱动框架分析之音频设备驱动HDF音频驱动框架分析之supportlibs实现HDF音频驱动框架分析之hdi-passthrough实现HDF音频驱动框架分析之hdi-bindev实现HDF音频驱动加载过程HDF音频驱…

HTML Day03

Day03 0. 引言1. CSS1.1 CSS的3种使用方法1.2 内联样式1.3 内部样式表1.4 外部CSS文件 2. 图像3. 表格3.1单元格间距和单元格边框 4. 列表4.1 有序表格的不同类型4.2 不同类型的无序表格4.3 嵌套列表 5. 区块6. 布局6.1 div布局6.2 表格布局 0. 引言 HELLO ^ _ ^大家好&#xf…

篇章六 数据结构——链表(二)

目录 1. LinkedList的模拟实现 1.1 双向链表结构图​编辑 1.2 三个简单方法的实现 1.3 头插法 1.4 尾插法 1.5 中间插入 1.6 删除 key 1.7 删除所有key 1.8 clear 2.LinkedList的使用 2.1 什么是LinkedList 5.2 LinkedList的使用 1.LinkedList的构造 2. LinkedList的…

吴恩达MCP课程(3):mcp_chatbot

原课程代码是用Anthropic写的&#xff0c;下面代码是用OpenAI改写的&#xff0c;模型则用阿里巴巴的模型做测试 .env 文件为&#xff1a; OPENAI_API_KEYsk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx OPENAI_API_BASEhttps://dashscope.aliyuncs.com/compatible-mode…

【清晰教程】查看和修改Git配置情况

目录 查看安装版本 查看特定配置 查看全局配置 查看本地仓库配置 设置或修改配置 查看安装版本 打开命令行工具&#xff0c;通过version命令检查Git版本号。 git --version 如果显示出 Git 的版本号&#xff0c;说明 Git 已经成功安装。 查看特定配置 如果想要查看特定…

JAVA 常用 API 正则表达式

1 正则表达式作用 作用一&#xff1a;校验字符串是否满足规则作用二&#xff1a;在一段文本中查找满足要求的内容 2 正则表达式规则 2.1 字符类 package com.bjpowernode.test14;public class RegexDemo1 {public static void main(String[] args) {//public boolean matche…

光电设计大赛智能车激光对抗方案分享:低成本高效备赛攻略

一、赛题核心难点与备赛痛点解析 全国大学生光电设计竞赛的 “智能车激光对抗” 赛题&#xff0c;要求参赛队伍设计具备激光对抗功能的智能小车&#xff0c;需实现光电避障、目标识别、轨迹规划及激光精准打击等核心功能。从历年参赛情况看&#xff0c;选手普遍面临三大挑战&a…

Python实现P-PSO优化算法优化BP神经网络回归模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在当今数据驱动的时代&#xff0c;回归分析作为预测和建模的重要工具&#xff0c;在科学研究和工业应用中占据着重要…

Microsoft的在word中选择文档中的所有表格进行字体和格式的调整时的解决方案

找到宏 创建 并粘贴 使用 Sub 全选所有表格() Dim t As Table an MsgBox("即将选择选区内所有表格&#xff0c;若无选区&#xff0c;则选择全文表格。", vbYesNo, "reboot提醒您!") If an - 6 Then Exit Sub Set rg IIf(Selection.Type wdSelectionIP, …