[网页五子棋][对战模块]实现游戏房间页面,服务器开发(创建落子请求/响应对象)
实现游戏房间页面
创建 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
,这个页面就是匹配成功之后,要跳转到的新页面
<!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
先处理游戏就绪响应
// 此处写的路径要写作 /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
类,让后面进行消息发送的时候进行序列化 - 同时注入一个
RoomManager
和OnlineUserManager
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; }
}
相关文章:

[网页五子棋][对战模块]实现游戏房间页面,服务器开发(创建落子请求/响应对象)
实现游戏房间页面 创建 css/game_room.css #screen 用于显示当前的状态,例如“等待玩家连接中…”,“轮到你落子”,“轮到对方落子”等 #screen { width: 450px; height: 50px; margin-top: 10px; color: #8f4e19; font-size: 28px; …...
数据结构-代码总结
下面代码自己上完课写着玩的,除了克鲁斯卡尔那里完全ai,其他基本上都是自己写的,具体请参考书本,同时也欢迎各位大佬来纠错 线性表 //线性表--顺序存储结构 #include<iostream> using namespace std; template<typename T> …...
快速掌握 GO 之 RabbitMQ
更多个人笔记见: github个人笔记仓库 gitee 个人笔记仓库 个人学习,学习过程中还会不断补充~ (后续会更新在github和 gitee上) 文章目录 作用经典例子生产者(发送端)消费者(接收端&a…...
SQL Server 事务详解:概念、特性、隔离级别与实践
一、事务的基本概念 事务(Transaction)是数据库操作的基本单位,它是由一组SQL语句组成的逻辑工作单元。事务具有以下关键特性,通常被称为ACID特性: 原子性(Atomicity):事务…...
MAC软件游戏打开提示已损坏
打开「终端.app」,输入以下命令并回车,输入开机密码回车 sudo spctl --master-disable 按照上述步骤操作完成后,打开「系统偏好设置」-「安全与隐私」-「通用」,确保已经修改为「任何来源」。 打开「终端.app」,输入…...
React基础教程(13):路由的使用
文章目录 1、什么是路由?2、路由安装3、路由使用(1)路由方法导入和使用(2)定义路由以及重定向(3)嵌套路由(4)路由跳转方式(5)动态路由动态路由写法一动态路由写法二4、实现效果5、完整代码下载1、什么是路由? 路由是根据不同的url地址展示不同的内容或页面。 一个…...
力扣刷题(第四十三天)
灵感来源 - 保持更新,努力学习 - python脚本学习 解题思路 1. 逐位检查法:通过右移操作逐位检查每一位是否为1,统计计数 2. 位运算优化法:利用 n & (n-1) 操作消除最低位的1,减少循环次数 3. 内置函数法&…...

Centos环境下安装/重装MySQL完整教程
目录 一、卸载残留的MySQL环境: 二、安装MySQL: 1、下载MySQL官方的yum源: 2、更新系统yum源: 3、确保系统中有了对应的MySQL安装包: 4、安装MySQL服务: 5、密钥问题安装失败解决方法: …...

【Linux】环境变量完全解析
9.环境变量 文章目录 9.环境变量一、命令行参数二、获取环境变量程序中获取环境变量1. 使用命令行参数2. 使用系统调用函数getenv("字符串");3. 使用系统提供的全局变量environ 命令行中查询环境变量 三、常见环境变量1. HOME2. OLDPWD3. PATH4. SHELL 四、环境变量与…...
【Java】mybatis-plus乐观锁-基本使用
乐观锁(Optimistic Locking)是解决并发问题的重要机制。它通过在数据更新时验证数据版本来确保数据的一致性,从而避免并发冲突。与悲观锁不同,乐观锁并不依赖数据库的锁机制,而是通过检查数据的版本或标志字段来判断数…...

力扣每日一题——找到离给定两个节点最近的节点
目录 题目链接:2359. 找到离给定两个节点最近的节点 - 力扣(LeetCode) 题目描述 解法一:双指针路径交汇法 基本思路 关键步骤 为什么这样可行呢我请问了? 举个例子 特殊情况 Java写法: C写法&a…...
机器学习与深度学习03-逻辑回归01
目录 上集回顾1. 逻辑回归与线性回归的区别2.逻辑回归的常见目标函数3.逻辑回归如何分类4.Sigmoid函数详解5.逻辑回归模型的参数 上集回顾 上一节文章地址:链接 1. 逻辑回归与线性回归的区别 应用领域 线性回归通常⽤于解决回归问题,其中⽬标是预测⼀…...

卷积神经网络(CNN)入门学习笔记
什么是 CNN? CNN,全称 卷积神经网络(Convolutional Neural Network),是一种专门用来处理图片、语音、文本等结构化数据的神经网络。 它模仿人眼识别图像的方式: 从局部到整体,一步步提取特征&a…...
【优笔】基于STM32的多模态智能门禁系统
代码功能详细描述 该代码实现了一个基于STM32的多模态智能门禁系统,整合密码、指纹、人脸识别(预留)三种验证方式,并提供完善的管理功能。系统架构如下图所示: #mermaid-svg-Uufpcoeo5Lega096 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size…...
Metasploit工具使用详解(上)丨小白WEB安全入门笔记
Metasploit工具使用详解(上)丨小白WEB安全入门笔记 一、课程定位与工具概述 课程性质: 小白WEB安全入门课程聚焦基础操作,非深度专题(Metasploit专题可讲数十节课)目标:掌握基本概念和简单漏洞利用 Metasploit核心定…...
Femap许可证与网络安全策略
随着科技的快速发展,网络安全问题已成为各行各业关注的焦点。在电磁仿真领域,Femap作为一款领先的软件,其许可证的安全性和网络策略的重要性不言而喻。本文将探讨Femap许可证与网络安全策略的关系,确保您的电磁仿真工作能够在一个…...

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

深入探讨集合与数组转换方法
目录 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、实际应用建议…...
让大模型看得见自己的推理 — KnowTrace结构化知识追踪
让大模型“看得见”自己的推理 —— KnowTrace 结构化知识追踪式 RAG 全解析 一句话概括:把检索-推理“改造”成 动态知识图构建任务,再让 LLM 只关注这张不断精炼的小图 —— 这就是显式知识追踪的核心价值。 1. 背景:为什么 RAG 仍难以搞定多跳推理? 长上下文负担 传统 I…...

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

【Oracle】DCL语言
个人主页:Guiat 归属专栏:Oracle 文章目录 1. DCL概述1.1 什么是DCL?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.添加索引 (1)建表时添加 (2)建表后添加 5.删除索引 三、树 1.二叉树 2.红黑树 3.B树 4.B树 (1)为什么 MySQL 选择B树作为索引…...
stm32——SPI协议
stm32——SPI协议 STM32的SPI(Serial Peripheral Interface,串行外设接口)协议是一种高速、全双工、同步的串行通信协议,广泛评估微控制器与各种外设(如传感器、器件、显示器、模块等)之间的数据传输。STM3…...
Linux 下如何查看进程的资源限制信息?
简介 Linux 上的 cat /proc/$pid/limits 命令提供有关特定进程的资源限制的信息,其中 $pid 是相关进程的进程 ID (pid)。该文件是 /proc 文件系统的一部分,该文件系统是一个虚拟文件系统,提供有关进程和系统资源的信息…...

【备忘】php命令行异步执行超长时间任务
环境说明: 操作系统:windows10 IDE:phpstorm 开发语言:php7.4 框架:thinkphp5.1 测试环境:linuxwindows均测试通过。 初级方法: function longRunningTask() {$root_path Tools::get_ro…...
对于ARM开发各种手册的分类
手册名称全称主要内容适用范围是不是讲SysTick?Cortex-M3 Technical Reference Manual (TRM)Cortex-M3 Technical Reference Manual描述 Cortex-M3内核架构,如寄存器模型、总线接口、指令集、异常模型只适合 Cortex-M3 内核,不含外设❌ 没有C…...
java开发中#和$的区别
在Spring框架中,$ 和 # 是两种不同的表达式前缀,用于从不同的来源获取值或执行计算。下面详细解释它们的区别和用法: 一、$ 占位符(Property Placeholder) 1. 作用 从配置文件(如 application.propertie…...

在 RK3588 上通过 VSCode 远程开发配置指南
在 RK3588 上通过 VSCode 远程开发配置指南 RK3588 设备本身不具备可视化编程环境,但可以通过 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 ^ _ ^大家好…...