游戲匹配實現


設計思路

客戶端向服務器發送匹配請求,服務端接收后將客戶端Session放入匹配隊列中,匹配完成時通知用戶。

使用觀察者設計模式可以實現這個功能。

代碼

觀察者代碼:

Java util 包擁有Observer 接口,這里根據實際應用自定義參數 Map。

import java.util.Map;
​
public interface Observer {
​
  public void update(Map<String, String> map);
​
}

  

 

被觀察者接口:

import java.util.List;
​
public interface Observerable {
​
  public void registerObserver(MatchNode o);
​
  public void removeObserver(MatchNode o);
​
  public void notifyObserver(List<MatchNode> matchNodes);
​
​
}

 

匹配節點:

每個節點存放用戶數據,用於進行匹配和查找。

import java.util.Map;
​
public class MatchNode implements Observer {
​
  private Long userId;
​
  private Long sessionId;
​
  private Long ruleId;
​
​
​
  public Long getUserId() {
      return userId;
  }
​
  public void setUserId(Long userId) {
      this.userId = userId;
  }
​
  public Long getSessionId() {
      return sessionId;
  }
​
  public void setSessionId(Long sessionId) {
      this.sessionId = sessionId;
  }
​
  public Long getRuleId() {
      return ruleId;
  }
​
  public void setRuleId(Long ruleId) {
      this.ruleId = ruleId;
  }
​
​
  @Override
  public void update(Map<String, String> map) {
​
  }
}

  

 

匹配線程

使用被觀察接口 registerObserver 和 registerObserver 可以對匹配隊列中的用戶進行管理,當匹配成功后,會將兩個匹配成功的用戶移除匹配集合,並對匹配成功的用戶發送通知。

使用單例模式意味着可以在任意位置獲取匹配線程的狀態,在程序初始化時啟動匹配線程即可。

用戶在匹配成功時,由匹配線程生成一個 groupId 用於標記這對用戶,可以通過groupId 在后續功能開發中可以起到查詢的作用。

package common.thread;
​
import cn.hutool.core.util.IdUtil;
import common.util.RandomTable;
import lombok.extern.slf4j.Slf4j;
​
import java.util.*;
import java.util.stream.Collectors;
​
@Slf4j
public class MatchQueueThread implements Runnable, Observerable {
​
  List<MatchNode> matchNodes;
​
​
  @Override
  public void registerObserver(MatchNode o) {
      matchNodes.add(o);
  }
​
  @Override
  public void removeObserver(MatchNode o) {
      if (!matchNodes.isEmpty())
          matchNodes.remove(o);
  }
​
  @Override
  public void notifyObserver(List<MatchNode> matchNodes) {
      //從匹配隊列中移除
      this.matchNodes.removeAll(matchNodes);
      String groupId = IdUtil.randomUUID();
      Map<String, String> dataMap = new HashMap<>();
      dataMap.put("groupId", groupId);
      dataMap.put("tables", RandomTable.randomTableSerial());
      matchNodes.forEach(matchNode -> {
          matchNode.update(dataMap);
      });
  }
​
  //單例
  private static class SingletonClassInstance {
      private static final MatchQueueThread instance = new MatchQueueThread();
  }
​
  private MatchQueueThread() {
      matchNodes = new ArrayList<>();
  }
​
  public static MatchQueueThread getInstance() {
      return SingletonClassInstance.instance;
  }
​
  public int getMatchCount() {
      return matchNodes.size();
  }
​
  /**
    * |
    * 匹配邏輯
    */
  @Override
  public void run() {
      Map<Long, List<MatchNode>> result = matchNodes.parallelStream()
              .collect(Collectors.groupingBy(MatchNode::getRuleId));
      for (Map.Entry<Long, List<MatchNode>> group : result.entrySet()) {
          if (group.getValue().size() > 1) {
              List<MatchNode> matchNodes = group.getValue().subList(0, 2);
              notifyObserver(matchNodes);
              log.info("匹配成功 : " + matchNodes.get(0).getUserId() + " " + matchNodes.get(1).getUserId());
          }
      }
  }
}

  

以上代碼尚未經過實際運行測試。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM