文章目錄
- 創建房間類
- 創建房間類
- 實現房間管理器
- 實現匹配器(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
)強制刷新,才能生效- 否則瀏覽器可能仍然在執行舊版本的代碼(瀏覽器自帶緩存)