这是一个最简化的匹配系统,目前仅支持RANK进行匹配,无法执行BAN图、投票选队等。
注意:工程测试代码,仅用于需求较小实现,此代码不具备大规模匹配,若有需求自行添加JWT授权、Websocket完成最小项目匹配系统。
有何作用?
可在插件中实现匹配、在服务器本地开放一个端口装载ASP.NET服务后用GET、POST进行请求。当然这个代码需要完善,还没有JWT鉴权操作,可能存在非法请求问题。
请求:
向StartMatchmaking函数请求:UserId(这里建议STEAMID)、MapsNameGrounp(string[])
查询匹配结果(建议6s轮询):GetMatchInfo(StartMatchmaking返回的MATCH)
匹配成功:
{
Console.WriteLine("Match found! Info:");
Console.WriteLine("Match ID: " + matchId);
Console.WriteLine("Server IP: " + serverIp);
Console.WriteLine("Server Port: " + serverPort);
Console.WriteLine("Your team: " + team);
}
代码后面我会更新,加入ELO机制方便精准匹配,暂时不在V2做WSS。
Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
MatchmakingContoller.cs 文件
using DS_Matchmaking_V3.Models;
using Microsoft.AspNetCore.Mvc;
namespace DS_Matchmaking_V3.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class MatchmakingController : ControllerBase
{
[HttpPost]
public IActionResult StartMatchmaking([FromBody] MatchmakingRequest request)
{
string matchId = MatchmakingManager.Instance.GenerateMatchId();
MatchmakingManager.Instance.AddPlayer(request.UserId, request.Maps, matchId); // 将MatchId与UserId一起写入Player
Console.WriteLine($"匹配模块确认UserID:{request.UserId} 资格");
return Ok(new { MatchId = matchId });
}
[HttpGet("{matchId}")]
public IActionResult GetMatchInfo(string matchId)
{
MatchInfo matchInfo = MatchmakingManager.Instance.GetMatchInfo(matchId);
if (matchInfo != null)
{
return Ok(matchInfo);
}
else
{
return NotFound();
}
}
}
public class MatchmakingRequest
{
public string UserId { get; set; }
public string[] Maps { get; set; }
}
public class MatchInfo
{
public string MatchId { get; set; }
public string ServerIp { get; set; }
public int ServerPort { get; set; }
public string[] Team1 { get; set; }
public string[] Team2 { get; set; }
}
}
MatchmakingManger.cs
using DS_Matchmaking_V3.Controllers;
using DS_Matchmaking_V3.Service;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Numerics;
namespace DS_Matchmaking_V3.Models
{
public class MatchmakingManager
{
private static MatchmakingManager _instance;
private static readonly int TEAM_SIZE = 5;
private static readonly string[] MAPS = { "de_dust2", "de_mirage", "de_inferno", "de_nuke", "de_overpass", "de_train" };
private static readonly int MIN_PLAYERS_FOR_MATCH = 10;
private Queue<Player> _waitingPlayers = new Queue<Player>();
private Dictionary<string, MatchInfo> _matches = new Dictionary<string, MatchInfo>();
private MatchmakingManager()
{
// 初始化
}
public static MatchmakingManager Instance
{
get
{
if (_instance == null)
{
_instance = new MatchmakingManager();
}
return _instance;
}
}
public void AddPlayer(string userId, string[] maps, string matchId)
{
_waitingPlayers.Enqueue(new Player(userId, maps, matchId)); // 将MatchId与UserId一起写入Player
// 开始匹配
StartMatchmaking();
}
public string GenerateMatchId()
{
// 基于请求时间生成唯一匹配ID
string matchId = DateTime.Now.Ticks.ToString();
return matchId;
}
private void StartMatchmaking()
{
if (_waitingPlayers.Count < MIN_PLAYERS_FOR_MATCH)
{
return;
}
HashSet<Player>[] mapPlayers = new HashSet<Player>[MAPS.Length];
for (int i = 0; i < mapPlayers.Length; i++)
{
mapPlayers[i] = new HashSet<Player>();
}
while (_waitingPlayers.Count > 0)
{
// 先将玩家从等待队列中取出
Player player = _waitingPlayers.Dequeue();
// 根据玩家想玩的地图加入相应的HashSet中
foreach (string map in player.Maps)
{
int index = Array.IndexOf(MAPS, map);
if (index >= 0 && index < mapPlayers.Length)
{
mapPlayers[index].Add(player);
}
else
{
// 处理数组越界异常,例如打印日志或抛出异常
Console.WriteLine("Index out of range: " + index);
}
}
}
List<Player> matchPlayers = new List<Player>();
// 遍历所有地图的玩家列表,找到所有有相同想玩地图的玩家
foreach (HashSet<Player> mapPlayerSet in mapPlayers)
{
if (mapPlayerSet.Count >= MIN_PLAYERS_FOR_MATCH)
{
// 如果有多个玩家集合都满足人数,选择数量最多的
matchPlayers = mapPlayerSet.ToList();
break;
}
else if (matchPlayers.Count == 0 && mapPlayerSet.Count >= TEAM_SIZE * 2)
{
// 如果没有符合人数的集合,则选择最大的集合,但要求人数不少于两个队伍
matchPlayers = mapPlayerSet.ToList();
}
}
// 如果玩家数量不足10,将所有玩家重新插入等待队列中
if (matchPlayers.Count < MIN_PLAYERS_FOR_MATCH)
{
foreach (Player player in matchPlayers)
{
_waitingPlayers.Enqueue(player);
}
return;
}
// 分配队伍
matchPlayers.Shuffle();
List<Player> team1 = new List<Player>(matchPlayers.Take(TEAM_SIZE));
List<Player> team2 = new List<Player>(matchPlayers.Skip(TEAM_SIZE).Take(TEAM_SIZE));
// 创建对局服务器,分配IP、端口
string serverIp = "YOU SERVER INFO";
int serverPort = GetNextAvailablePort();
MatchInfo matchInfo = new MatchInfo
{
MatchId = Guid.NewGuid().ToString(),
ServerIp = serverIp,
ServerPort = serverPort,
Team1 = team1.Select(p => p.UserId).ToArray(),
Team2 = team2.Select(p => p.UserId).ToArray()
};
_matches.Add(matchInfo.MatchId, matchInfo);
// 返回查询信息数据
foreach (Player player in matchPlayers)
{
_matches.Add(player.MatchId, matchInfo);//将时间Match加入查询池
Console.WriteLine("-----匹配成功-----");
Console.WriteLine($"追踪对局ID:{matchInfo.MatchId}");
Console.WriteLine($"对局服务器IP地址:{matchInfo.ServerIp}");
Console.WriteLine($"对局服务器端口:{matchInfo.ServerPort}");
Console.WriteLine($"CT阵营玩家:{string.Join(",", matchInfo.Team1)}");
Console.WriteLine($"T阵营玩家:{string.Join(",", matchInfo.Team2)}");
player.Client.OnMatchReady(matchInfo.MatchId, serverIp, serverPort, string.Join(",", matchInfo.Team1));
}
ConcurrentQueue<Player> _waitingPlayerss = new ConcurrentQueue<Player>();
// 将玩家移出等待队列
foreach (Player player in matchPlayers)
{
Player removedPlayer;
if (_waitingPlayerss.TryDequeue(out removedPlayer))
{
player.IsInMatch = true;
}
}
}
private int GetNextAvailablePort()//反馈游戏端口
{
return 27015;
}
public MatchInfo GetMatchInfo(string matchId)
{
if (_matches.ContainsKey(matchId))
{
return _matches[matchId];
}
else
{
return null;
}
}
public List<Player> GetPlayersInMatch(string matchId)
{
List<Player> players = new List<Player>();
foreach (Player player in _waitingPlayers)
{
if (player.MatchId == matchId)
{
players.Add(player);
}
}
return players;
}
}
public class Player
{
public string UserId { get; private set; }
public string[] Maps { get; private set; }
public string Team { get; private set; }
public bool IsInMatch { get; set; }
public string MatchId { get; set; }
public IMatchmakingClient Client { get; set; }
public Player(string userId, string[] maps, string matchId)
{
UserId = userId;
Maps = maps;
MatchId = matchId;
Client = new MatchmakingClient();
}
public void AssignTeam(string team)
{
Team = team;
Client.OnTeamAssigned(team);
}
}
public interface IMatchmakingClient
{
void OnMatchReady(string matchId, string serverIp, int serverPort, string team);
void OnTeamAssigned(string team);
}
public static class Extensions
{
private static Random rng = new Random();
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1)
{
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
}
}
MatchmakingClient.cs
using DS_Matchmaking_V3.Controllers;
using DS_Matchmaking_V3.Models;
using Newtonsoft.Json;
using System.Net.Http.Headers;
using System.Text;
namespace DS_Matchmaking_V3.Service
{
public class MatchmakingClient : IMatchmakingClient
{
public void OnMatchReady(string matchId, string serverIp, int serverPort, string team)
{
Console.WriteLine("Match found! Info:");
Console.WriteLine("Match ID: " + matchId);
Console.WriteLine("Server IP: " + serverIp);
Console.WriteLine("Server Port: " + serverPort);
Console.WriteLine("Your team: " + team);
}
public void OnTeamAssigned(string team)
{
Console.WriteLine("You have been assigned to team: " + team);
}
}
}
收藏的用户(0)
X
正在加载信息~