C# Counterstrikesharp-MapChooser-for-CS2 1.0.0

Totul despre Counter-Strike Sharp. Tutoriale de instalare, scripting in C# si cele mai noi pluginuri pentru serverul tau de CS2.
Post Reply
User avatar
greenie
Full Member
Full Member
Posts: 2389
Joined: Sun Nov 21, 2021 4:55 pm
Jucator SA:MP ?: Nu
Nick:: greenie
Jucator CS ?: Da

C# Counterstrikesharp-MapChooser-for-CS2 1.0.0

Post by greenie »

MapChooser is a simple plugin for CS2 on CounterStrikeSharp that adds the !rtv command for voting on map changes. It supports regular maps and maps from the Steam Workshop.

Plugin storage path (Installation): Copy the plugin to the specified path and restart the server: addons/counterstrikesharp/plugins/MapChooser.

Upon first launch, a custom settings.json file is automatically created with a list of maps and plugin parameters.

Source code:
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes;
using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Cvars;
using CounterStrikeSharp.API.Modules.Menu;
using CounterStrikeSharp.API.Modules.Timers;
using CounterStrikeSharp.API.Modules.Utils;
using System.Text.Json;

namespace MapChooser;

[MinimumApiVersion(200)]
public class MapChooser : BasePlugin
{
public override string ModuleName => "MapChooser";
public override string ModuleVersion => "1.0.0";
public override string ModuleAuthor => "Alley";

private Config _config = null!;
private readonly Dictionary<int, UserSettings> _users = new();
private ChatMenu? _voteMenu;

private readonly Dictionary<string, int> _mapVotes = new();
private readonly HashSet<string> _nominatedMaps = new();
private readonly HashSet<string> _recentMaps = new();

private int _rtvCount = 0;
private bool _voteInProgress = false;
private bool _rtvAllowedByTime = true;
private string? _nextMap = null;

private float _mapStartTime = 0f;

public override void Load(bool hotReload)
{
_config = LoadConfig();

RegisterListener<Listeners.OnMapStart>(name =>
{
_mapStartTime = Server.CurrentTime;

_rtvCount = 0;
_voteInProgress = false;
_rtvAllowedByTime = true;
_nextMap = null;
_mapVotes.Clear();
_nominatedMaps.Clear();
_users.Clear();

var currentMapKey = _config.Maps.FirstOrDefault(m => m.MapId.Equals(name, StringComparison.OrdinalIgnoreCase))?.MapId ?? name;
_recentMaps.Add(currentMapKey);

if (_recentMaps.Count > _config.RecentMapsCount)
{
_recentMaps.Remove(_recentMaps.First());
}

if (_config.RtvDelayMinutes > 0)
{
_rtvAllowedByTime = false;
AddTimer(_config.RtvDelayMinutes * 60f, () => _rtvAllowedByTime = true);
}

var timeLimit = ConVar.Find("mp_timelimit")?.GetPrimitiveValue<float>() ?? 0f;
if (timeLimit > 0)
{
var delay = (timeLimit - _config.VoteStartMinutes) * 60f;
if (delay > 0)
{
AddTimer(delay, () => StartMapVote());
}
}
});

AddCommand("rtv", "Rock the vote", OnRtvCommand);
AddCommand("rockthevote", "Rock the vote", OnRtvCommand);
AddCommand("nominate", "Nominate a map", OnNominateCommand);
AddCommand("timeleft", "Show time left", OnTimeleftCommand);
AddCommand("nextmap", "Show next map", OnNextmapCommand);
AddCommand("maplist", "Show available maps", OnMapListCommand);
}

private void OnMapListCommand(CCSPlayerController? player, CommandInfo info)
{
if (player == null) return;

var available = GetAvailableMaps();
if (available.Count == 0)
{
player.PrintToChat($"{ChatColors.Red}{ChatColors.Green}[MapChooser]{ChatColors.Red} Нет доступных карт.");
return;
}

player.PrintToChat($"{ChatColors.LightBlue}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
player.PrintToChat($"{ChatColors.Gold} ДОСТУПНЫЕ КАРТЫ");
player.PrintToChat($"{ChatColors.LightBlue}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");

int index = 1;
foreach (var map in available)
{
var status = _nominatedMaps.Contains(map.MapId) ? $"{ChatColors.LightPurple} (номинирована)" : "";
player.PrintToChat($" {ChatColors.Grey}{index++}. {ChatColors.Green}{map.DisplayName} {ChatColors.Grey}→ {ChatColors.DarkBlue}{map.MapId}{status}");
}

player.PrintToChat($"{ChatColors.LightBlue}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
}

private void OnRtvCommand(CCSPlayerController? player, CommandInfo info)
{
if (player == null || !player.IsValid || player.IsBot) return;

var alivePlayers = Utilities.GetPlayers().Count(p => p is { IsValid: true, IsBot: false });

if (alivePlayers < _config.RtvMinPlayers && !_config.RtvAllowWhenLessThanMin)
{
player.PrintToChat($"{ChatColors.Red}{ChatColors.Green}[MapChooser]{ChatColors.Red} RTV доступен с {_config.RtvMinPlayers} игроков! (сейчас: {alivePlayers})");
return;
}

if (!_rtvAllowedByTime)
{
player.PrintToChat($"{ChatColors.Red}{ChatColors.Green}[MapChooser]{ChatColors.Red} RTV доступен через {_config.RtvDelayMinutes} мин после начала карты.");
return;
}

if (_voteInProgress)
{
player.PrintToChat($"{ChatColors.Red}{ChatColors.Green}[MapChooser]{ChatColors.Red} Голосование уже идёт!");
return;
}

if (_users.ContainsKey(player.Slot))
{
player.PrintToChat($"{ChatColors.Red}{ChatColors.Green}[MapChooser]{ChatColors.Red} Вы уже использовали RTV!");
return;
}

_users[player.Slot] = new UserSettings { HasRtved = true };
_rtvCount++;

var required = Math.Max(1, (int)Math.Ceiling(alivePlayers * _config.RtvRequiredPercent));
Server.PrintToChatAll($"{ChatColors.Green}[MapChooser] {ChatColors.LightBlue}{player.PlayerName} {ChatColors.Green}хочет сменить карту! {ChatColors.Gold}({_rtvCount}/{required})");

if (_rtvCount >= required)
{
StartMapVote(true);
}
}

private void OnNominateCommand(CCSPlayerController? player, CommandInfo info)
{
if (player == null || !player.IsValid || player.IsBot) return;

var available = GetAvailableMaps();
if (available.Count == 0)
{
player.PrintToChat($"{ChatColors.Red}{ChatColors.Green}[MapChooser]{ChatColors.Red} Нет карт для номинации.");
return;
}

var menu = new ChatMenu("Номинировать карту");

foreach (var map in available)
{
menu.AddMenuOption(map.DisplayName, (p, option) =>
{
if (_nominatedMaps.Contains(map.MapId)) return;

_nominatedMaps.Add(map.MapId);
Server.PrintToChatAll($"{ChatColors.Green}[MapChooser] {ChatColors.LightBlue}{p.PlayerName} {ChatColors.Green}номинировал: {ChatColors.Gold}{map.DisplayName}");
});
}

MenuManager.OpenChatMenu(player, menu);
}

private void OnTimeleftCommand(CCSPlayerController? player, CommandInfo info)
{
if (player == null) return;

var timeLimit = ConVar.Find("mp_timelimit")?.GetPrimitiveValue<float>() ?? 0f;
if (timeLimit > 0)
{
var remaining = timeLimit * 60f - (Server.CurrentTime - _mapStartTime);
if (remaining < 0) remaining = 0;
var mins = (int)remaining / 60;
var secs = (int)remaining % 60;
player.PrintToChat($"{ChatColors.Green}[MapChooser] Осталось: {ChatColors.Gold}{mins:D2}:{secs:D2}");
}
else
{
player.PrintToChat($"{ChatColors.Green}[MapChooser] Без лимита времени.");
}
}

private void OnNextmapCommand(CCSPlayerController? player, CommandInfo info)
{
if (player == null) return;

if (string.IsNullOrEmpty(_nextMap))
player.PrintToChat($"{ChatColors.Red}{ChatColors.Green}[MapChooser]{ChatColors.Red} Карта не выбрана.");
else
{
var map = _config.Maps.FirstOrDefault(m => m.MapId == _nextMap);
var name = map?.DisplayName ?? _nextMap;
player.PrintToChat($"{ChatColors.Green}[MapChooser] Следующая: {ChatColors.Gold}{name}");
}
}

private void StartMapVote(bool forced = false)
{
if (_voteInProgress) return;
_voteInProgress = true;

var maps = GetVoteMaps();

Server.PrintToChatAll($"{ChatColors.LightBlue}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
Server.PrintToChatAll($"{ChatColors.Gold} ГОЛОСОВАНИЕ ЗА КАРТУ");
Server.PrintToChatAll($"{ChatColors.LightBlue}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");

_voteMenu = new ChatMenu("Голосуйте за карту");

_voteMenu.AddMenuOption("Extend current map", (p, option) => VoteForMap("extend", p));

int index = 1;
foreach (var map in maps)
{
_voteMenu.AddMenuOption(map.DisplayName, (p, option) => VoteForMap(map.MapId, p));
Server.PrintToChatAll($" {ChatColors.Grey}{index++}. {ChatColors.Green}{map.DisplayName}");
}

Server.PrintToChatAll($" {ChatColors.Grey}0. {ChatColors.LightPurple}Extend current map");
Server.PrintToChatAll($"{ChatColors.Gold}Голосуйте в меню! Время: {_config.VoteTime} сек");
Server.PrintToChatAll($"{ChatColors.LightBlue}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");

foreach (var player in Utilities.GetPlayers().Where(p => p.IsValid && !p.IsBot))
{
MenuManager.OpenChatMenu(player, _voteMenu);
}

AddTimer(_config.VoteTime, EndVote);
}

private void VoteForMap(string mapId, CCSPlayerController player)
{
if (!_mapVotes.ContainsKey(mapId))
_mapVotes[mapId] = 0;

_mapVotes[mapId]++;

var map = _config.Maps.FirstOrDefault(m => m.MapId == mapId);
var name = mapId == "extend" ? "Extend current map" : (map?.DisplayName ?? mapId);
Server.PrintToChatAll($"{ChatColors.Green}[MapChooser] {ChatColors.LightBlue}{player.PlayerName} {ChatColors.Green}voted for {ChatColors.Gold}{name}");
}

private void EndVote()
{
Server.PrintToChatAll($"{ChatColors.LightBlue}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
Server.PrintToChatAll($"{ChatColors.Gold} VOTING ENDED!");
Server.PrintToChatAll($"{ChatColors.LightBlue}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");

var winner = _mapVotes.OrderByDescending(kv => kv.Value).FirstOrDefault();

if (winner.Key == "extend")
{
Server.PrintToChatAll($"{ChatColors.Green}[MapChooser] Map extended for {_config.ExtendMinutes} minutes!");
var current = ConVar.Find("mp_timelimit")!.GetPrimitiveValue<float>();
ConVar.Find("mp_timelimit")?.SetValue(current + _config.ExtendMinutes);
}
else if (!string.IsNullOrEmpty(winner.Key))
{
var map = _config.Maps.First(m => m.MapId == winner.Key);
_nextMap = winner.Key;

Server.PrintToChatAll($"{ChatColors.Green}[MapChooser] Winner: {ChatColors.Gold}{map.DisplayName} {ChatColors.Green}(votes: {winner.Value})!");
Server.PrintToChatAll($"{ChatColors.Green}[MapChooser] Map change in 5 seconds...");

AddTimer(5f, () => ChangeToMap(winner.Key));
}
else
{
var randomMap = GetAvailableMaps().OrderBy(_ => Random.Shared.Next()).FirstOrDefault();
if (randomMap != null)
{
_nextMap = randomMap.MapId;
Server.PrintToChatAll($"{ChatColors.Green}[MapChooser] Randomly selected: {ChatColors.Gold}{randomMap.DisplayName}");
Server.PrintToChatAll($"{ChatColors.Green}[MapChooser] Map change in 5 seconds...");

AddTimer(5f, () => ChangeToMap(randomMap.MapId));
}
}

_voteInProgress = false;
}

private List<MapEntry> GetVoteMaps()
{
var available = GetAvailableMaps();
var selected = _nominatedMaps
.Where(id => available.Any(m => m.MapId == id))
.Select(id => available.First(m => m.MapId == id))
.ToList();

while (selected.Count < 6 && available.Count > selected.Count)
{
var randomMap = available.Except(selected).OrderBy(_ => Random.Shared.Next()).FirstOrDefault();
if (randomMap != null) selected.Add(randomMap);
}

return selected;
}

private List<MapEntry> GetAvailableMaps()
{
return _config.Maps.Where(m => !_recentMaps.Contains(m.MapId)).ToList();
}

private void ChangeToMap(string mapId)
{
var map = _config.Maps.FirstOrDefault(m => m.MapId == mapId);
if (map == null)
{
Server.PrintToChatAll($"{ChatColors.Red}{ChatColors.Green}[MapChooser]{ChatColors.Red} Error: map not found!");
return;
}

Server.PrintToChatAll($"{ChatColors.Green}[MapChooser] Loading map: {ChatColors.Gold}{map.DisplayName}");

if (map.MapId.StartsWith("workshop_id/"))
{
var workshopId = map.MapId.Replace("workshop_id/", "").Trim();
Server.ExecuteCommand($"host_workshop_map {workshopId}");
}
else if (map.MapId.StartsWith("collection/"))
{
var collectionId = map.MapId.Replace("collection/", "");
Server.ExecuteCommand($"host_workshop_collection {collectionId}");
Server.ExecuteCommand("map random");
}
else if (map.MapId.StartsWith("workshop_start_map/"))
{
var startMapId = map.MapId.Replace("workshop_start_map/", "");
Server.ExecuteCommand($"workshop_start_map {startMapId}");
}
else if (map.MapId.StartsWith("workshop/"))
{
var parts = map.MapId.Split('/', 3);
if (parts.Length == 3)
{
var mapName = parts[2];
Server.ExecuteCommand($"ds_workshop_changelevel {mapName}");
}
}
else
{
Server.ExecuteCommand($"changelevel {map.MapId}");
}
}

private Config LoadConfig()
{
var path = Path.Combine(ModuleDirectory, "settings.json");

if (!File.Exists(path))
{
var defaultConfig = new Config
{
Maps = new List<MapEntry>
{
new() { DisplayName = "Mirage FPS", MapId = "workshop_id/3349182536" },
new() { DisplayName = "Mirage", MapId = "de_mirage" },
new() { DisplayName = "Italy", MapId = "cs_italy" },
new() { DisplayName = "Office", MapId = "cs_office" },
new() { DisplayName = "Inferno", MapId = "de_inferno" }
},
RtvMinPlayers = 4,
RtvAllowWhenLessThanMin = false,
RtvRequiredPercent = 0.6,
RtvDelayMinutes = 5,
VoteTime = 20f,
RecentMapsCount = 5,
VoteStartMinutes = 5f,
ExtendMinutes = 15f
};

var jsonOptions = new JsonSerializerOptions { WriteIndented = true };
File.WriteAllText(path, JsonSerializer.Serialize(defaultConfig, jsonOptions));

return defaultConfig;
}

var json = File.ReadAllText(path);
return JsonSerializer.Deserialize<Config>(json)!;
}
}

public class UserSettings
{
public bool HasRtved { get; set; }
}

public class MapEntry
{
public string DisplayName { get; set; } = "";
public string MapId { get; set; } = "";
}

public class Config
{
public List<MapEntry> Maps { get; set; } = new();
public int RtvMinPlayers { get; set; } = 4;
public bool RtvAllowWhenLessThanMin { get; set; } = false;
public double RtvRequiredPercent { get; set; } = 0.6;
public int RtvDelayMinutes { get; set; } = 5;
public float VoteTime { get; set; } = 20f;
public int RecentMapsCount { get; set; } = 5;
public float VoteStartMinutes { get; set; } = 5f;
public float ExtendMinutes { get; set; } = 15f;
}
Requirements
CounterStrikeSharp.API
Variables
settings.json list of maps and plugin parameters.
Teams
!rtv — launches voting for map change
Installation
Plugin storage path(Installation): Copy the plugin to the specified path and restart the server. : addons/counterstrikesharp/plugins/MapChooser

https://hlmod.net/resources/c-counterst ... 2/download
𝑴𝒂𝒚𝒃𝒆 𝒕𝒉𝒆 𝑬𝒂𝒓𝒕𝒉 𝒊𝒔 𝒕𝒉𝒆 𝑯𝒆𝒍𝒍 𝒐𝒇 𝑨𝒏𝒐𝒕𝒉𝒆𝒓 𝑷𝒍𝒂𝒏𝒆𝒕... ☢️
Post Reply

Return to “Counter-Strike Sharp CS2”