项目-双人五子棋对战:匹配模块的实现(3)

完整代码见: 邹锦辉个人所有代码: 测试仓库 - Gitee.com

模块详细讲解

功能需求

匹配就类似于大家平常玩的王者荣耀这样的匹配功能, 当玩家点击匹配之后, 就会进入到一个匹配队列, 当匹配到足够数量的玩家后, 就会进入确认页. 

在这里, 我们主要实现的是1 - 1匹配功能, 首先先有一个玩家点击匹配, 进入匹配队列, 然后如果有段位差不多的(就是根据我们之前讲到的天梯分数, 按分数来分段位, 后面也会实现)进入到匹配队列, 就匹配成功, 创建一个游戏房间, 双方进入游戏房间.

接下来我们来详细介绍一下具体的匹配实现原理.

具体原理

匹配这样的功能, 也需要依赖到我们之前讲到的消息推送.

 

1. 玩家1点击匹配按钮, 就会告诉服务器, 我要进行匹配, 同时进入匹配队列.

2. 玩家2(跟玩家1同一个段位)点击匹配按钮, 这时就完成了匹配.

3. 此时服务器需要告诉玩家1匹配结果. (正是因为服务器自己也不确定, 啥时候告诉玩家匹配的结果, 因此就需要依赖消息推送机制, 当服务器匹配成功之后, 就主动告诉排到的玩家, 你排到了).

具体实现

前后端接口的约定

前后端的交互接口, 也是基于websocket来展开的, websocket可以传输文本数据, 也可以传输二进制数据, 此处就直接设计成让websocket传输json格式的文本数据即可.

匹配请求:

客户端通过websocket给服务器发送一个json格式的文本数据.

ws://127.0.0.1:8080/findMatch

{

        message:'startMatch' / 'stopMatch', //开始/结束匹配

在通过websocket传输请求信息的时候, 数据中就不用包含用户的个人信息的, 因为此时个人信息在登录之后被存储在HttpSession中了. 通过websocket也是可以拿到之前登录的HttpSession里的信息的. 

匹配响应:

ws://127.0.0.1:8080/findMatch

{

        ok: true, //匹配成功

        reason: ' ', //匹配如果失败, 失败的原因信息

        message: 'startMatch' / 'stopMatch', 

}

这个响应是客户端给服务器发送匹配请求之后, 服务器立即返回的匹配响应.

 匹配响应2: 

ws://127.0.0.1:8080/findMatch

{

        ok: true,

        reason: ' ',

        message: 'matchSuccess'

}

这个响应是真正匹配到对手之后, 服务器主动推送回来的信息. 匹配到的对手不需要在这个响应中体现, 仍然都放到服务器这边来储存即可

前端代码的实现

页面大致展示:

这里我们不难发现, 客户端的工作主要是发送两个请求, 即获取用户信息, 以及开始/结束匹配; 以及接收服务器响应, 根据响应改变按钮文本即可.(再次强调, 前端使用websocket的作用是建立连接, 发送和接收数据(请求和响应) ) 下面围绕这两个内容, 我们来实现一下前端代码.

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><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_hall.css">
</head>
<body><div class="nav">五子棋对战</div><!-- 整个页面的容器元素 --><div class="container"><!-- 这个 div 在 container 中是处于垂直水平居中这样的位置的 --><div><!-- 展示用户信息 --><div id="screen"></div><!-- 匹配按钮 --><div id="match-button">开始匹配</div></div></div><script src="js/jquery.min.js"></script><script>//获取用户信息$.ajax({type: 'get',url: '/userInfo',success: function(body) {let screenDiv = document.querySelector('#screen');screenDiv.innerHTML = '玩家: ' + body.username + " 分数: " + body.score + "<br> 比赛场次: " + body.totalCount + " 获胜场数: " + body.winCount},error: function() {alert("获取用户信息失败!");}});// 此处进行初始化 websocket, 并且实现前端的匹配逻辑. let websocketUrl = 'ws://' + location.host + '/findMatch';let websocket = new WebSocket(websocketUrl);websocket.onopen = function() {console.log("onopen");}websocket.onclose = function() {console.log("onclose");}websocket.onerror = function() {console.log("onerror");}// 监听页面关闭事件. 当用户关闭该页面时, 就会同时关闭websocket. window.onbeforeunload = function() {websocket.close();}// 处理服务器返回的响应数据. 这个响应就是针对 "开始匹配" / "结束匹配" 来对应的websocket.onmessage = function(e) {// 解析得到的响应对象. 返回的数据是一个 JSON 字符串, 解析成 js 对象let resp = JSON.parse(e.data);//选中按钮标签, 根据情况变换文本.let matchButton = document.querySelector('#match-button');if (!resp.ok) {console.log("游戏大厅中接收到了失败响应! " + resp.reason);return;}if (resp.message == 'startMatch') {// 开始匹配请求发送成功console.log("进入匹配队列成功!");matchButton.innerHTML = '匹配中...(点击停止)'} else if (resp.message == 'stopMatch') {// 结束匹配请求发送成功console.log("离开匹配队列成功!");matchButton.innerHTML = '开始匹配';} else if (resp.message == 'matchSuccess') {// 已经匹配到对手了. console.log("匹配到对手! 进入游戏房间!");location.replace("/game_room.html");} else if (resp.message == 'repeatConnection') {alert("当前检测到多开! 请使用其他账号登录!");location.replace("/login.html");} else {console.log("收到了非法的响应! message=" + resp.message);}}// 给匹配按钮添加一个点击事件let matchButton = document.querySelector('#match-button');matchButton.onclick = function() {// 在触发 websocket 请求之前, 先确认下 websocket 连接是否好着呢~~ if (websocket.readyState == websocket.OPEN) {// 如果当前 readyState 处在 OPEN 状态, 说明连接好着的~// 这里发送的数据有两种可能, 开始匹配/停止匹配~if (matchButton.innerHTML == '开始匹配') {// 发送开始匹配请求websocket.send(JSON.stringify({message: 'startMatch',}));} else if (matchButton.innerHTML == '匹配中...(点击停止)') {// 发送停止匹配请求websocket.send(JSON.stringify({message: 'stopMatch',}));}} else {// 这是说明连接当前是异常的状态(比如未登录直接打开该页面)alert("当前您的连接已经断开! 请重新登录!");location.replace('/login.html');}}</script>
</body>
</html>

不难发现, 核心也就是发送请求和处理响应, 这个都是通过用户点击的按钮所发起的.

这里还有一个特殊的情况就是, 用户可能会同时登录多个账号(也就是我们常说的多开), 这时候也要进行处理(前端这里仅针对响应结果进行弹窗提示, 跳转页面处理),  还有一些防止未登录直接跳转至页面的特殊情况处理, 一会在后端代码中做详细讲解.

后端代码

我们知道, 前端已经通过websocket和后端进行了连接, 因此后端就需要通过websocket会话和前端保持连接, 通过这个可以向客户端发送信息. 然后前端发来的请求会以TextMessage的形式被后端接收到. 然后服务器进行响应, 响应也就包含刚才讲的(ok/reason/message)这三个参数.

下面来看一下后端WebSocket负责接收服务器数据的方法具体实现:

@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {// 实现处理开始匹配请求和停止匹配请求User user = (User) session.getAttributes().get("user");//获取到客户端给服务器的数据String payload = message.getPayload();// 当前这个数据载荷是一个JSON格式的字符串, 需要把它转换为 Java对象, MatchRequest(也就是字符串格式)// ObjectMapper就是完成类型转换的核心类MatchRequest request = objectMapper.readValue(payload, MatchRequest.class);MatchResponse response = new MatchResponse();if(request.getMessage().equals("startMatch")) {// 进入匹配队列matcher.add(user);// 把玩家信息放入到匹配队列之后, 就可以返回一个响应给客户端了response.setOk(true);response.setMessage("startMatch");} else if (request.getMessage().equals("stopMatch")) {// 退出匹配队列matcher.remove(user);// 移除之后了, 就可以返回一个响应给客户端了response.setOk(true);response.setMessage("stopMatch");} else {//非法情况response.setOk(false);response.setReason("非法的匹配请求");}//格式转换为JSON格式String jsonString = objectMapper.writeValueAsString(response);//返回响应session.sendMessage(new TextMessage(jsonString));}

这里与开始匹配/结束匹配相关的就是匹配队列, 它是在matcher类中实现的. 明天我们再来具体讲解一下这个, 以及对于一些特殊情况的处理.

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端应用开发实验:组件应用

目录 实验目的相关知识点实验内容及要求代码实现效果 实验目的 &#xff08;1&#xff09;掌握组件的创建方法&#xff08;全局组件、局部组件&#xff09;&#xff1b; &#xff08;2&#xff09;重点学会组件之间的数据传递&#xff08;prop传值、自定义事件&#xff09;&am…

一文了解JVM面试篇(上)

Java内存区域 1、如何解释 Java 堆空间及 GC? 当通过 Java 命令启动 Java 进程的时候,会为它分配内存。内存的一部分用于创建 堆空间,当程序中创建对象的时候,就从对空间中分配内存。GC 是 JVM 内部的一 个进程,回收无效对象的内存用于将来的分配。 2、JVM 的主要组成…

PPT文件损坏且无法读取怎样修复?文档损坏修复方法推荐

PPT文件已经成为工作汇报、商务演示、学术交流以及教学培训中最常用到的文件&#xff0c;随着文件数量的增多和存储设备的频繁使用&#xff0c;我们有时会遇到PPT文件损坏无法打开的情况&#xff0c;这无疑给工作和学习带来了极大的困扰。 PPT文件损坏的原因可能多种多样&#…

实验9 静态路由配置

实验9 静态路由配置 一、 原理描述二、 实验目的三、 实验内容四、 实验配置五、 实验步骤 一、 原理描述 网络中的每个路由器都会维护一张路由表或转发表。路由表的表项记录着目的网络信息以及下一跳I 地址。路由表可以手动配置&#xff0c;也可以通过路由算法动态生成。静态…

20240605在WIN99主板上安装从微软官网下载的WIN10的HOME版本通过kms方式激活

20240605在WIN99主板上安装从微软官网下载的WIN10的HOME版本通过kms方式激活 2024/6/5 18:45 https://blog.csdn.net/qq_28606555/article/details/131665546?utm_mediumdistribute.pc_relevant.none-task-blog-2~default~baidujs_utm_term~default-4-131665546-blog-13187568…

植物大战僵尸杂交版2.0.88最新版+防闪退工具V2+修改工具+高清工具

植物大战僵尸杂交版&#xff0c;不仅继承原作的经典玩法&#xff0c;而且引入了全新的植物融合玩法&#xff0c;将各式各样的植物进行巧妙的杂交&#xff0c;孕育出前所未有、功能各异的全新植物。 创新的杂交合成系统 游戏引入了创新的杂交合成系统&#xff0c;让玩家可以将不…

力扣 54.螺旋矩阵

题目描述&#xff1a; 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5]示例 2&#xff1a; 输入&#…

ESP32S3——多线程

一、环境&#xff1a; 平台&#xff1a;arduino IDE 或 VS Code PlatformIO 皆可。 我的是后者&#xff0c;具体为&#xff1a; 框架&#xff1a;VS PlatformIO Arduino 二、硬件准备&#xff1a; 一个esp32s3 本文用到的是U0RXD&#xff08;GPIO44 &#xff09;与U0TXD…

C++ primer例子1实现

问题 代码 Sales_item.h #include<iostream> #include<string> class Sales_item {public:Sales_item(){};Sales_item(std::string insid, int num, double price);friend std::istream& operator>>(std::istream& is, Sales_item& item);frie…

ARM的工作模式

ARM的几种工作模式 User : 非特权模式&#xff0c;大部分任务执行在这种模式 FIQ : 当一个高优先级&#xff08;fast) 中断产生时将会进入这种模式IRQ : 当一个低优先级&#xff08;normal) 中断产生时将会进入这种模式 Supervisor&#xff08;SVC&#xff09; : 当复位或软中断…

使用迭代最近点 (ICP) 算法在 Open3D 中对齐点云

一、Open3D 简介及其功能 Open3D 是一个现代库&#xff0c;它提供了用于处理 3D 数据的各种工具。在其功能中&#xff0c;它提供了高效的数据结构和算法来处理点云、网格等&#xff0c;使其成为在计算机视觉、机器人和图形领域工作的研究人员和从业人员的不错选择。Open3D 的特…

前端_防抖节流

目录 一、防抖&#xff08;debounce&#xff09; 1.使用场景 2.js代码实现 3.lodash工具库使用 二、节流&#xff08;throttle&#xff09; 1.使用场景 2.js代码实现 3.lodash工具库使用 前端做项目&#xff0c;为了防止用户因为网络不好数据响应慢&#xff0c;导致进行…

实时监控与报警:人员跌倒检测算法的实践

在全球范围内&#xff0c;跌倒事件对老年人和儿童的健康与安全构成了重大威胁。据统计&#xff0c;跌倒是老年人意外伤害和死亡的主要原因之一。开发人员跌倒检测算法的目的是通过技术手段及时发现和响应跌倒事件&#xff0c;减少因延迟救助而造成的严重后果。这不仅对老年人群…

WEB攻防-Python-PYC 反编译CTF 与 CMS-SSTI 模版注入

反编译pyc字节码文件 pyc文件是py文件编译后生成的字节码文件(byte code)&#xff0c;pyc文件经过python解释器最终会生成机器码运行。因此pyc文件是可以跨平台部署的&#xff0c;类似Java的.class文件&#xff0c;一般py文件改变后&#xff0c;都会重新生成pyc文件。 真题附件…

Android Studio的Gradle面板里不显示task,build ,assemble 无法出aar包

按照以下方式把对应开关打开就可以正常进行build/assemble进行aar的生成了

【Vue3-Element-Admin 动态路由】涉及到的配置

Vue3-Element-Admin 动态路由 涉及到的配置 0. Vue3-Element-Admin 项目地址1. router/index.ts2. Mock接口模拟数据3. store/permission4. api/menu5. plugins/permission 这篇文章讲的主要是 Vue3-Element-Admin 差不多内置的动态路由配置 (根据后端接口渲染) 先把开发环境&a…

vue 引用第三方库 Swpier轮播图

本文全程干货&#xff0c;没有废话 1.使用 npm 安装 swiper&#xff0c;使用 save 保存到 packjson 中 npm install --save swiper 2、把 swiper看成是第三方库或者是组件&#xff0c;然后按照&#xff0c;引用&#xff0c;挂载组件&#xff0c;使用组件三步法。 3、在 script…

完美的移动端 UI 风格

完美的移动端 UI 风格

文件上传漏洞之upload-labs

前提&#xff1a; 本文中的以xshell命名的均为以密码为admin的一句话木马&#xff0c;而shell命名的则是由冰蝎工具生成的木马。 pass-01&#xff1a;js前端验证 测试性的上传一个一句话木马&#xff0c;发现被拦截了&#xff0c;而且根据推测大概率是前端检测&#xff0c;于…

爬楼梯——动态规划第一步

本问题其实常规解法可以分成多个子问题&#xff0c;爬第 n 阶楼梯的方法数量&#xff0c;等于两个部分之和 爬上 n−1 阶楼梯的方法数量。因为再爬 1 阶就能到第 n 阶爬上 n−2 阶楼梯的方法数量&#xff0c;因为再爬 2 阶就能到第 n 阶 所以我们得到公式 dp[n] dp[n−1] d…