让多个用户,在游戏大厅中能够进行匹配,系统会把实力相近的两个玩家凑成一桌,进行对战
约定前后端交互接口
消息推送机制
匹配这样的功能,也是依赖消息推送机制的
- 玩家 1 点击开始匹配按钮,就会告诉服务器:我要开始进行匹配
- 玩家发送匹配请求,这个事情是确定(点击了匹配按钮,就会发送匹配请求),服务器什么时候告知玩家匹配结果(你到你排到了谁),就需要等待匹配结束的时候才能告知
- 正是因为服务器自己也不确定,什么时候能告知玩家匹配的结果,因此就需要依赖消息推送机制
- 当服务器这里匹配成功之后,就主动的告诉当前排到的所有玩家,“你排到了”
这里就需要用到消息推送机制,也就是需要用到 websocket
- 接下来约定和前后端交互接口,也都是基于
websocket
来展开的- 和前面的 HTTP 还是有点区别的
交互接口约定
websocket
可以传输文本数据,也能传输二进制数据。此处就直接设计成让 websocket
传输 JSON
格式的文本数据即可
匹配请求
- 客户端通过
websocket
给服务器发送一个JSON
格式的文本数据ws://127.0.0.1:8080/findMatch
{
message: 'startMarch' / 'stopMatch'
// 开始匹配/结束匹配
}
在通过 websocket
传输请求数据的时候,数据中是不必带有用户身份信息的,当前用户的身份信息,在前面登录完成后,就已经保存到 HttpSession
中了。websocket
里,也是能拿到之前登录好的 HttpSession
中的信息的
匹配响应 1
ws://127.0.0.1:8080/Match
{
ok: true, // 匹配成功
reason: ‘’, // 匹配如果失败,失败原因的信息
message: ‘startMatch’ / ‘stopMatch’,
}
这个响应,是客户端给服务器发送请求之后,服务器立即返回的匹配响应
匹配响应 2
ws://127.0.0.1:8080/findMatch
{
ok: true.
reason: ‘’,
message: ‘matchSuccess’
}
这个响应是真正匹配到对手之后,服务器主动推送回来的信息
- 匹配到的对手不需要在这个响应中体现,仍然都放到服务器这边来保存即可
客户端开发
匹配页面
game_hall. html
创建 game_hall. html
,主要包含
#screen
用于显示玩家的分数信息button#match-button
作为匹配按钮
<!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_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) {
// 将 user 对喜爱那个添加到页面的显示内容中
let screenDiv = document.querySelector("#screen");
screenDiv.innerHTML = '玩家: ' + body.username + '分数: ' + body.score
+ "<br> 比赛场次: " + body.totalCount + "获胜场次: " + body.winCount;
},
error: function() {
alert("获取用户信息失败!")
}
})
</script>
</body>
</html>
game_hall.css
.container {
width: 100%;
height: calc(100% - 50px);
display: flex;
align-items: center;
justify-content: center;
}
#screen {
width: 400px;
height: 200px;
font-size: 20px;
background-color: gray;
color: white;
border-radius: 10px;
text-align: center;
line-height: 100px;
}
#match-button {
width: 400px;
height: 50px;
font-size: 20px;
color: white;
background-color: orange;
border: none;
outline: none;
border-radius: 10px;
text-align: center;
line-height: 50px;
margin-top: 20px;
}
#match-button:active {
background-color: gray;
}
- 当我们修改了
css
样式/JS
文件之后,往往要在浏览器中使用cmd+shift+R
(Windows:ctrl+f5
)强制刷新,才能生效- 否则浏览器可能仍然在执行旧版本的代码(浏览器自带缓存)
匹配功能
编辑 game_hall.html
的 js
部分代码
- 点击匹配按钮,就会进入匹配逻辑,同时按钮上提示“
匹配中...(点击取消)
”字样 - 再次点击匹配按钮,则会取消匹配
- 当匹配成功后,服务器会返回匹配成功的响应,页面跳转到
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_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) {
// 将 user 对喜爱那个添加到页面的显示内容中
let screenDiv = document.querySelector("#screen");
screenDiv.innerHTML = '玩家: ' + body.username + '分数: ' + body.score
+ "<br> 比赛场次: " + body.totalCount + "获胜场次: " + body.winCount;
},
error: function() {
alert("获取用户信息失败!")
}
});
// 此处进行初始化 websocket,并且实现前端匹配逻辑
// 此处的路径必须写作 /findMatch,千万不要写作 /findMatch/ let websocket = new websocket('ws://127.0.0.1:8080/findMatch');
websocket.onopen = function() {
console.log("onopen");
}
websocket.onclose = function() {
console.log("onclose");
}
websocket.onerror = function() {
console.log("onerror");
}
// 监听页面关闭事件,在页面关闭之前,手动调用这里 websocket 的 close 方法
window.onbeforeunload = function() {
websocket.close();
}
// 一会重点来实现,要处理服务器返回的响应
websocket.onmessage = function(e) {
// 处理服务器返回的响应数据,这个响应就是针对“开始匹配”/“结束匹配”来对应的
// 解析得到的响应对象,返回的数据是一个 JSON 字符串,解析成 js 对象
let resp = JSON.parse(e.data);
let match-button = 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.assign('/game_room.html');
}else {
console.log("收到了非法的响应! message=" + resp.message);
}
}
// 给匹配按钮添加一个点击事件
let matchButton = document.querySelector('#match-button');
matchButton.onclick - function() {
// 在触发 websocket 请求之前,先确认下 websocket 连接是否好着
if (websocket.readyState == websocket.OPEN) {
// 如果当前 redyState 处于 OPEN 状态,说明连接好着的
// 这里发送的数据有两种可能,开始匹配/停止匹配
if (matchButton.innerHTML == '开始匹配') {
console.log("开始匹配");
websocket.send(JSON.stringify({
message: 'startMatch',
}));
} else if (matchButton.innerHTML == '匹配中...(点击停止)') {
console.log("停止匹配");
websocket.send(JSON.stringify({
message: 'stopMatch',
}));
}
}else {
// 这是说明连接当前是异常状态
alert("当前您的连接已经断开!请重新登录!");
location.assign('/login.html');
}
}
</script>
</body>
</html>
- 此部分主要逻辑从
36
行开始
JSON
字符串和JS
对象的转换
JSON
字符串转换成JS
对象:JSON.pares
JS
对象转换成JSON
对象:JSON.stringify
JSON
字符串和Java
对象的转换
JSON
字符串转换成Java
对象:ObjectMapper.readValue
Java
对象转成JSON
字符串:ObjectMapper.writeValueAsString