[网页五子棋][匹配模式]创建房间类、房间管理器、验证匹配功能,匹配模式小结
文章目录
- 创建房间类
- 创建房间类
- 实现房间管理器
- 实现匹配器(3)
- 验证匹配功能
- 问题:匹配按钮不改变
- 验证多开
- 小结
创建房间类
LOL
,通过匹配的方式,自动给你加入到一个房间,也可手动创建游戏房间
- 这一局游戏,进行的“场所”就可以称为是一个“游戏房间”,游戏房间中最关键的信息,就是玩家信息
- 一个游戏服务器,有同时存在了多个游戏房间
我们就需要一个“游戏房间管理器”来管理多个游戏房间
- 键值对的方式,给每个
room
生成一个唯一的roomId
,以键值对 (哈希表) 在room manager
中来进行管理
创建房间类
匹配成功之后,需要把对战的两个玩家放到同一个房间对象中
创建 game.Room
类
- 一个房间要包含一个房间 ID,使用
UUID
做为房间的唯一身份标识 - 房间内要记录对弈的玩家双方信息
UUID
表示“世界上唯一的身份标识”
- 通过一系列的算法,能够生成一串字符串(一组十六进制表示的数字)
- 两次/任意次调用这个算法,生产的这个字符串都是不同的
package org.example.java_gobang.game; import org.example.java_gobang.model.User; import java.util.UUID; // 表示一个游戏房间
public class Room { // 此处我们使用字符串的类型来表示,方便生成唯一值 private String roomId; private User user1; private User user2; public String getRoomId() { return roomId; } public void setRoomId(String roomId) { this.roomId = roomId; } public User getUser1() { return user1; } public void setUser1(User user1) { this.user1 = user1; } public User getUser2() { return user2; } public void setUser2(User user2) { this.user2 = user2; } public Room() { // 构造 Room 的时候,生成一个唯一的字符串来表示房间 id roomId = UUID.randomUUID().toString(); }
}
实现房间管理器
Room
对象会存在很多,每两个对弈的玩家,都对应一个 Room
对象,需要创建一个管理器对象来管理所有的 Room
创建 game.RoomManager
- 使用一个
Hash
表,保存所有房间对象key:roomId
value:Room对象
- 再使用一个
Hash
表,保存userId -> RoomId
的映射,方便根据玩家来查找所在的房间 - 提供增、删、查的
API
- 查询包含基于房间 ID 的查询和基于用户 ID 的查询
package org.example.java_gobang.game; import org.springframework.stereotype.Component; import java.util.concurrent.ConcurrentHashMap; // 房间管理器,这个类也希望有唯一实例
@Component
public class RoomManager { private ConcurrentHashMap<String, Room> rooms = new ConcurrentHashMap<>(); // 通过这个哈希表,把玩家和房间之间的关系维护起来 private ConcurrentHashMap<Integer, String> userIdToRoomId = new ConcurrentHashMap<>(); public void add(Room room, int userId1, int userId2) { rooms.put(room.getRoomId(), room); userIdToRoomId.put(userId1, room.getRoomId()); userIdToRoomId.put(userId2, room.getRoomId()); } public void remove(String roomId, int userId1, int userId2) { rooms.remove(roomId); userIdToRoomId.remove(userId1); userIdToRoomId.remove(userId2); } public Room getRoomByRoomId(String roomId) { return rooms.get(roomId); } // 根据用户id 定位房间 public Room getRoomByUserId(int userId) { String roomId = userIdToRoomId.get(userId); if(roomId == null) { // userId -> roomId 映射关系不存在,直接返回 null return null; } return rooms.get(roomId); }
}
实现匹配器(3)
完善刚才匹配逻辑中的 TODO
,并把玩家放到一个房间中
- 先给
Matcher
注入RoomManager
对象
@Component
public class Matcher {//......// 房间管理器@Autowiredprivate RoomManager roomManager;// ......
}然后修改 Matcher.handlerMatch,补完之前 TODO 的内容
private void handlerMatch(Queue<User> matchQueue) {// 4. 把这两个玩家放到一个游戏房间中 Room room = new Room(); roomManager.add(room, player1.getUserId(), player2.getUserId());// ......
}
验证匹配功能
问题:匹配按钮不改变
当前发现,玩家点击匹配之后,匹配按钮的文本不发生改变
- 分析之前写的代码,点击按钮的时候,仅仅是给服务器发送了一个
websocket
请求,告诉服务器我要开始匹配了 - 服务器会立即返回一个响应,“进入匹配队列成功”,然后页面再修改按钮的文本
出现问题的原因:
- 服务器在处理匹配请求的时候,按理说是要立即就返回一个
websocket
响应的 - 实际上在服务器代码这里构造了响应对象,但是忘记
sendMessage
,给发回去了
在红框中加入如下逻辑代码
// 将 response 先转换成 JSON 字符串,然后将其通过 sendMessage 发回客户端
String jsonString = objectMapper.writeValueAsString(response);
session.sendMessage(new TextMessage(jsonString));
就类似于:你网购买了个东西,商家都已经打包好了,但是最后忘记发货了
验证匹配功能的时候,模拟多个用户登录的情况,最好使用多个浏览器,避免同一个浏览器中的
cookie/session
信息互相干扰
- 如果只有一个浏览器,并且是
chrome
的话,chrome
有个无痕模式(不会记录历史记录,也不会记录cookie
,页面关闭的时候会自动清空)
验证多开
当我们打开两个页面,登录同一个账号的时候,后登录的页面的检查页面会出现提示,但是正常用户多开了在页面中却没有显示
- 当用户多开之后,连接就会直接关闭,不能再进行匹配了
当前情况下,防多开机制起到了作用,但是又感觉差了点意思
- 要是在第二个账号登录的时候,在页面中直接有提示就更好了
此时我们就可以调整前端代码,当检测到多开的时候,就给用户一个更加明确的提示
这样,在我们登录的时候,要是出现了多开的情况,就直接报错了,返回重新登录页面
- 当我们修改了
css
样式/JS
文件之后,往往要在浏览器中使用cmd+shift+R
(Windows:ctrl+f5
)强制刷新,才能生效- 否则浏览器可能仍然在执行旧版本的代码(浏览器自带缓存)