DS-MATCH-V2 匹配系统

The_518651 一级用户组 2023-8-4 650

这是一个最简化的匹配系统,目前仅支持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);
        }
    }
}



CSGO插件分享-申明 1、本网站名称:CSGO插件分享-中文站  网址:https://bbs.csgocn.net
2、本站的宗旨在于为CSGO玩家提供一个插件分享的中文资源平台,多数插件来源于SourceMod论坛,并配以中文介绍和安装教程。
3、欢迎有能力的朋友共享有趣的CSGO插件资源。
4、本站资源大多为百度网盘,如发现链接失效,可以点: 这里进行反馈,我们会第一时间更新。
最新回复 (1)
返回