├── .gitignore ├── BlockerPasses ├── BlockerPasses.cs └── BlockerPasses.csproj └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Rider ignored files 5 | /modules.xml 6 | /contentModel.xml 7 | /projectSettingsUpdater.xml 8 | /.idea.BlockerPasses.iml 9 | # Editor-based HTTP Client requests 10 | /httpRequests/ 11 | # Datasource local storage ignored files 12 | /dataSources/ 13 | /dataSources.local.xml 14 | BlockerPasses/.idea 15 | *.user 16 | *.cache 17 | *.editorconfig 18 | BlockerPasses/obj/Debug/net7.0/BlockerPasses.AssemblyInfo.cs 19 | BlockerPasses/obj/Debug/net7.0/.NETCoreApp,Version=v7.0.AssemblyAttributes.cs 20 | BlockerPasses/obj/Debug/net7.0/BlockerPasses.GlobalUsings.g.cs 21 | BlockerPasses/obj/BlockerPasses.csproj.nuget.dgspec.json 22 | BlockerPasses/obj/BlockerPasses.csproj.nuget.g.props 23 | BlockerPasses/obj/BlockerPasses.csproj.nuget.g.targets 24 | BlockerPasses/obj/project.assets.json 25 | BlockerPasses/obj/project.packagespec.json 26 | BlockerPasses/obj/rider.project.restore.info 27 | *.json 28 | *.pdb 29 | *.CopyComplete 30 | *.txt 31 | BlockerPasses/obj/release/net7.0/.NETCoreApp,Version=v7.0.AssemblyAttributes.cs 32 | BlockerPasses/obj/release/net7.0/BlockerPasses.AssemblyInfo.cs 33 | BlockerPasses/obj/release/net7.0/BlockerPasses.GlobalUsings.g.cs 34 | BlockerPasses/obj/BlockerPasses.csproj.EntityFrameworkCore.targets 35 | BlockerPasses/.idea/.idea.BlockerPasses.dir/.idea/discord-ij.xml 36 | BlockerPasses/.idea/.idea.BlockerPasses.dir/.idea/discord.xml 37 | BlockerPasses/.idea/.idea.BlockerPasses.dir/.idea/workspace.xml 38 | BlockerPasses/bin/release/net7.0/publish/BlockerPasses.zip 39 | BlockerPasses/obj/BlockerPasses.csproj.nuget.g.props 40 | BlockerPasses/obj/BlockerPasses.csproj.nuget.g.targets 41 | BlockerPasses/obj/Debug/net7.0/.NETCoreApp,Version=v7.0.AssemblyAttributes.cs 42 | BlockerPasses/obj/Debug/net7.0/BlockerPasses.AssemblyInfo.cs 43 | BlockerPasses/obj/release/net7.0/.NETCoreApp,Version=v7.0.AssemblyAttributes.cs 44 | BlockerPasses/obj/release/net7.0/BlockerPasses.AssemblyInfo.cs 45 | BlockerPasses/.idea/.idea.BlockerPasses.dir/.idea/vcs.xml 46 | BlockerPasses/.idea/.idea.BlockerPasses.dir/.idea/indexLayout.xml 47 | BlockerPasses/.idea/.idea.BlockerPasses.dir/.idea/projectSettingsUpdater.xml 48 | BlockerPasses/.idea/.idea.BlockerPasses.dir/.idea/indexLayout.xml 49 | BlockerPasses/.idea/.idea.BlockerPasses.dir/.idea/projectSettingsUpdater.xml 50 | BlockerPasses/.idea/.idea.BlockerPasses.dir/.idea/vcs.xml 51 | BlockerPasses/.idea/.idea.BlockerPasses.dir/.idea/workspace.xml 52 | BlockerPasses/obj/Debug/net7.0/.NETCoreApp,Version=v7.0.AssemblyAttributes.cs 53 | BlockerPasses/obj/Debug/net7.0/BlockerPasses.AssemblyInfo.cs 54 | BlockerPasses/obj/BlockerPasses.csproj.nuget.g.props 55 | BlockerPasses/obj/release/net7.0/.NETCoreApp,Version=v7.0.AssemblyAttributes.cs 56 | *.targets 57 | BlockerPasses/obj/release/net7.0/BlockerPasses.AssemblyInfo.cs 58 | BlockerPasses/bin/release/net7.0/publish/BlockerPasses.zip 59 | BlockerPasses/.idea/.idea.BlockerPasses.dir/.idea/discord-ij.xml 60 | *.xml 61 | *.info 62 | BlockerPasses/bin/release/net7.0/BlockerPasses.dll 63 | BlockerPasses/bin/release/net7.0/CounterStrikeSharp.API.dll 64 | BlockerPasses/bin/release/net7.0/publish/addons/counterstrikesharp/plugins/BlockerPasses/BlockerPasses.dll 65 | BlockerPasses/bin/release/net7.0/publish/BlockerPasses.dll 66 | BlockerPasses/bin/release/net7.0/publish/CounterStrikeSharp.API.dll 67 | BlockerPasses/obj/release/net7.0/BlockerPasses.dll 68 | BlockerPasses/obj/release/net7.0/ref/BlockerPasses.dll 69 | BlockerPasses/obj/release/net7.0/refint/BlockerPasses.dll 70 | BlockerPasses/bin/release/net7.0/publish/System.Diagnostics.EventLog.dll 71 | BlockerPasses/bin/release/net7.0/publish/McMaster.NETCore.Plugins.dll 72 | BlockerPasses/bin/release/net7.0/publish/Microsoft.DotNet.PlatformAbstractions.dll 73 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Configuration.Abstractions.dll 74 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Configuration.Binder.dll 75 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Configuration.CommandLine.dll 76 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Configuration.dll 77 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Configuration.EnvironmentVariables.dll 78 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Configuration.FileExtensions.dll 79 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Configuration.Json.dll 80 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Configuration.UserSecrets.dll 81 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.DependencyInjection.dll 82 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.DependencyInjection.Abstractions.dll 83 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.DependencyModel.dll 84 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.FileProviders.Physical.dll 85 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.FileProviders.Abstractions.dll 86 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.FileSystemGlobbing.dll 87 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Hosting.Abstractions.dll 88 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Hosting.dll 89 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Localization.Abstractions.dll 90 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Logging.Abstractions.dll 91 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Logging.Configuration.dll 92 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Logging.Debug.dll 93 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Logging.Console.dll 94 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Logging.EventLog.dll 95 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Logging.dll 96 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Logging.EventSource.dll 97 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Options.ConfigurationExtensions.dll 98 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Options.dll 99 | BlockerPasses/bin/release/net7.0/publish/runtimes/win/lib/net7.0/System.Diagnostics.EventLog.dll 100 | BlockerPasses/bin/release/net7.0/publish/Microsoft.Extensions.Primitives.dll 101 | BlockerPasses/bin/release/net7.0/publish/runtimes/win/lib/net7.0/System.Diagnostics.EventLog.Messages.dll 102 | BlockerPasses/bin/release/net7.0/publish/Serilog.dll 103 | BlockerPasses/bin/release/net7.0/publish/Scrutor.dll 104 | BlockerPasses/bin/release/net7.0/publish/Serilog.Sinks.Console.dll 105 | BlockerPasses/bin/release/net7.0/publish/Serilog.Extensions.Logging.dll 106 | BlockerPasses/bin/release/net7.0/publish/Serilog.Sinks.File.dll 107 | BlockerPasses/CounterStrikeSharp.API.dll 108 | -------------------------------------------------------------------------------- /BlockerPasses/BlockerPasses.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using System.Globalization; 3 | using System.Text.Json; 4 | using CounterStrikeSharp.API; 5 | using CounterStrikeSharp.API.Core; 6 | using CounterStrikeSharp.API.Core.Attributes; 7 | using CounterStrikeSharp.API.Core.Attributes.Registration; 8 | using CounterStrikeSharp.API.Modules.Admin; 9 | using CounterStrikeSharp.API.Modules.Commands; 10 | using CounterStrikeSharp.API.Modules.Menu; 11 | using CounterStrikeSharp.API.Modules.Utils; 12 | 13 | namespace BlockerPasses; 14 | 15 | [MinimumApiVersion(90)] 16 | public class BlockerPasses : BasePlugin 17 | { 18 | public override string ModuleAuthor => "thesamefabius"; 19 | public override string ModuleName => "Blocker Passes"; 20 | public override string ModuleVersion => "v1.0.3"; 21 | 22 | private Config _config = null!; 23 | 24 | public override void Load(bool hotReload) 25 | { 26 | _config = LoadConfig(); 27 | RegisterEventHandler(EventRoundStart); 28 | } 29 | 30 | [RequiresPermissions("@css/root")] 31 | [ConsoleCommand("css_bp_reload")] 32 | public void OnCmdReload(CCSPlayerController? player, CommandInfo info) 33 | { 34 | _config = LoadConfig(); 35 | 36 | const string msg = "Configuration successfully rebooted"; 37 | 38 | if (player == null) 39 | Console.WriteLine(msg); 40 | else 41 | player.PrintToChat(msg); 42 | } 43 | 44 | private HookResult EventRoundStart(EventRoundStart @event, GameEventInfo info) 45 | { 46 | var playersCount = Utilities.GetPlayers() 47 | .Where(u => u.PlayerPawn.Value != null && u.TeamNum != (int)CsTeam.None && 48 | u.TeamNum != (int)CsTeam.Spectator && u.PlayerPawn.Value.IsValid).ToList(); 49 | 50 | if (playersCount.Count >= _config.Players) return HookResult.Continue; 51 | 52 | if (!_config.Maps.TryGetValue(Server.MapName, out var entitiesMap)) return HookResult.Continue; 53 | 54 | foreach (var entity in entitiesMap) 55 | { 56 | var color = entity.Color; 57 | 58 | SpawnProp(entity.ModelPath, new[] { color[0], color[1], color[2] }, 59 | GetVectorFromString(entity.Origin), GetQAngleFromString(entity.Angles), entity.Scale); 60 | } 61 | 62 | Server.PrintToChatAll( 63 | " " + ReplaceColorTags(_config.Message.Replace("{MINPLAYERS}", _config.Players.ToString()))); 64 | 65 | return HookResult.Continue; 66 | } 67 | 68 | private Vector GetVectorFromString(string vector) => GetFromString(vector, (x, y, z) => new Vector(x, y, z)); 69 | 70 | private QAngle GetQAngleFromString(string angles) => GetFromString(angles, (x, y, z) => new QAngle(x, y, z)); 71 | 72 | private static T GetFromString(string values, Func createInstance) 73 | { 74 | var split = values.Split(' '); 75 | 76 | if (split.Length >= 3 && 77 | float.TryParse(split[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var x) && 78 | float.TryParse(split[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var y) && 79 | float.TryParse(split[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var z)) 80 | { 81 | return createInstance(x, y, z); 82 | } 83 | 84 | return default!; 85 | } 86 | 87 | private void SpawnProp(string modelPath, int[] color, Vector origin, QAngle angles, float? entityScale) 88 | { 89 | var prop = Utilities.CreateEntityByName("prop_dynamic_override"); 90 | 91 | if (prop == null) return; 92 | 93 | prop.Collision.SolidType = SolidType_t.SOLID_VPHYSICS; 94 | prop.Render = Color.FromArgb(color[0], color[1], color[2]); 95 | prop.Teleport(origin, angles, new Vector(0, 0, 0)); 96 | prop.DispatchSpawn(); 97 | Server.NextFrame(() => prop.SetModel(modelPath)); 98 | 99 | var bodyComponent = prop.CBodyComponent; 100 | if (bodyComponent is not { SceneNode: not null }) return; 101 | 102 | if (entityScale != null && entityScale != 0.0f) 103 | bodyComponent.SceneNode.GetSkeletonInstance().Scale = entityScale.Value; 104 | } 105 | 106 | private Config LoadConfig() 107 | { 108 | var configPath = Path.Combine(ModuleDirectory, "blocker_passes.json"); 109 | if (!File.Exists(configPath)) return CreateConfig(configPath); 110 | 111 | var config = JsonSerializer.Deserialize(File.ReadAllText(configPath))!; 112 | 113 | return config; 114 | } 115 | 116 | private Config CreateConfig(string configPath) 117 | { 118 | var config = new Config 119 | { 120 | Players = 6, 121 | Message = 122 | "[{BLUE} BlockerPasses {DEFAULT}] Some passageways are blocked. Unblocking requires {RED}{MINPLAYERS}{DEFAULT} players", 123 | Maps = new Dictionary> 124 | { 125 | { 126 | "de_mirage", new List 127 | { 128 | new() 129 | { 130 | ModelPath = 131 | "models/props/de_dust/hr_dust/dust_windows/dust_rollupdoor_96x128_surface_lod.vmdl", 132 | Color = new[] { 30, 144, 255 }, 133 | Origin = "-1600.46 -741.124 -172.965", 134 | Angles = "0 180 0", 135 | Scale = 0.0f 136 | }, 137 | new() 138 | { 139 | ModelPath = "models/props/de_mirage/small_door_b.vmdl", 140 | Color = new[] { 255, 255, 255 }, 141 | Origin = "588.428 704.941 -136.517", 142 | Angles = "0 270.256 0", 143 | Scale = 0.0f 144 | }, 145 | new() 146 | { 147 | ModelPath = "models/props/de_mirage/large_door_c.vmdl", 148 | Color = new[] { 255, 255, 255 }, 149 | Origin = "-1007.87 -359.812 -323.64", 150 | Angles = "0 270.106 0", 151 | Scale = 0.0f 152 | }, 153 | new() 154 | { 155 | ModelPath = 156 | "models/props/de_nuke/hr_nuke/chainlink_fence_001/chainlink_fence_001_256_capped.vmdl", 157 | Color = new[] { 255, 255, 255 }, 158 | Origin = "-961.146 -14.2419 -169.489", 159 | Angles = "0 269.966 0", 160 | Scale = 0.0f 161 | }, 162 | new() 163 | { 164 | ModelPath = 165 | "models/props/de_nuke/hr_nuke/chainlink_fence_001/chainlink_fence_001_256_capped.vmdl", 166 | Color = new[] { 255, 255, 255 }, 167 | Origin = "-961.146 -14.2419 -43.0083", 168 | Angles = "0 269.966 0", 169 | Scale = 0.0f 170 | } 171 | } 172 | } 173 | } 174 | }; 175 | 176 | File.WriteAllText(configPath, 177 | JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true })); 178 | 179 | return config; 180 | } 181 | 182 | private string ReplaceColorTags(string input) 183 | { 184 | string[] colorPatterns = 185 | { 186 | "{DEFAULT}", "{WHITE}", "{DARKRED}", "{GREEN}", "{LIGHTYELLOW}", "{LIGHTBLUE}", "{OLIVE}", "{LIME}", 187 | "{RED}", "{LIGHTPURPLE}", "{PURPLE}", "{GREY}", "{YELLOW}", "{GOLD}", "{SILVER}", "{BLUE}", "{DARKBLUE}", 188 | "{BLUEGREY}", "{MAGENTA}", "{LIGHTRED}", "{ORANGE}" 189 | }; 190 | 191 | string[] colorReplacements = 192 | { 193 | $"{ChatColors.Default}", $"{ChatColors.White}", $"{ChatColors.Darkred}", $"{ChatColors.Green}", 194 | $"{ChatColors.LightYellow}", $"{ChatColors.LightBlue}", $"{ChatColors.Olive}", $"{ChatColors.Lime}", 195 | $"{ChatColors.Red}", $"{ChatColors.LightPurple}", $"{ChatColors.Purple}", $"{ChatColors.Grey}", 196 | $"{ChatColors.Yellow}", $"{ChatColors.Gold}", $"{ChatColors.Silver}", $"{ChatColors.Blue}", 197 | $"{ChatColors.DarkBlue}", $"{ChatColors.BlueGrey}", $"{ChatColors.Magenta}", $"{ChatColors.LightRed}", 198 | $"{ChatColors.Orange}" 199 | }; 200 | 201 | for (var i = 0; i < colorPatterns.Length; i ++) 202 | input = input.Replace(colorPatterns[i], colorReplacements[i]); 203 | 204 | return input; 205 | } 206 | } 207 | 208 | public class Config 209 | { 210 | public int Players { get; init; } 211 | public required string Message { get; init; } 212 | public Dictionary> Maps { get; init; } = null!; 213 | } 214 | 215 | public class Entities 216 | { 217 | public required string ModelPath { get; init; } 218 | public int[] Color { get; init; } = { 255, 255, 255 }; 219 | public required string Origin { get; init; } 220 | public required string Angles { get; init; } 221 | public float? Scale { get; init; } 222 | } -------------------------------------------------------------------------------- /BlockerPasses/BlockerPasses.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | CounterStrikeSharp.API.dll 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cs2-BlockerPasses 2 | Blocks passages if there are not a certain number of players on the server 3 | 4 | # Installation 5 | 1. Install [CounterStrike Sharp](https://github.com/roflmuffin/CounterStrikeSharp), [Metamod:Source](https://www.sourcemm.net/downloads.php/?branch=master) and [ResourcePrecacher](https://github.com/KillStr3aK/ResourcePrecacher) 6 | 3. Download [BlockerPasses](https://github.com/partiusfabaa/cs2-BlockerPasses/releases/tag/v1.0.0) 7 | 4. Unzip the archive and upload it to the game server 8 | 9 | ### After installing ResourcePrecacher, all the paths you write in the config, write them there as well 10 | 11 | # Commands 12 | `css_bp_reload`, `!bp_reload` - reloads the configuration(only for `@css/root`) 13 | 14 | # Config 15 | 16 | ```json 17 | { 18 | "Players": 10, // The number of players after which the passes will open 19 | "Message": "...", // A message stating that the passageways are blocked (all the tags are at the bottom) 20 | "Maps": { 21 | "de_mirage": [ // Map name 22 | { 23 | "ModelPath": "models/props/de_dust/hr_dust/dust_windows/dust_rollupdoor_96x128_surface_lod.vmdl", // Path to the model 24 | "Color": [ 30, 144, 255 ], // RGB color in which the model will be colored 25 | "Origin": "-1600.46 -741.124 -172.965", // Position where the model will be placed 26 | "Angles": "0 180 0" // Which way the model will be turned 27 | }, 28 | { 29 | "ModelPath": "models/props/de_mirage/small_door_b.vmdl", 30 | "Color": [ 255, 255, 255 ], 31 | "Origin": "588.428 704.941 -136.517", 32 | "Angles": "0 270.256 0" 33 | }, 34 | { 35 | "ModelPath": "models/props/de_mirage/large_door_c.vmdl", 36 | "Color": [ 255, 255, 255 ], 37 | "Origin": "-1007.87 -359.812 -323.64", 38 | "Angles": "0 270.106 0" 39 | }, 40 | { 41 | "ModelPath": "models/props/de_nuke/hr_nuke/chainlink_fence_001/chainlink_fence_001_256_capped.vmdl", 42 | "Color": [ 255, 255, 255 ], 43 | "Origin": "-961.146 -14.2419 -169.489", 44 | "Angles": "0 269.966 0" 45 | }, 46 | { 47 | "ModelPath": "models/props/de_nuke/hr_nuke/chainlink_fence_001/chainlink_fence_001_256_capped.vmdl", 48 | "Color": [ 255, 255, 255 ], 49 | "Origin": "-961.146 -14.2419 -43.0083", 50 | "Angles": "0 269.966 0" 51 | } 52 | ], //if you need to add more maps, put a comma. But the last map doesn't need one! (example) 53 | "de_dust2": [ 54 | { 55 | "ModelPath": "", 56 | "Color": [ 255, 255, 255 ], 57 | "Origin": "", 58 | "Angles": "" 59 | } 60 | ] 61 | } 62 | } 63 | ``` 64 | 65 | # Tags 66 | Colors - `{DEFAULT}`, `{WHITE}`, `{DARKRED}`, `{GREEN}`, `{LIGHTYELLOW}`, `{LIGHTBLUE}`, `{OLIVE}`, `{LIME}`, `{RED}`, `{LIGHTPURPLE}`, `{PURPLE}`, `{GREY}`, `{YELLOW}`, `{GOLD}`, `{SILVER}`, `{BLUE}`, `{DARKBLUE}`, `{BLUEGREY}`, `{MAGENTA}`, `{LIGHTRED}`, `{ORANGE}` 67 | 68 | `{MINPLAYERS}` - minimum number of players 69 | --------------------------------------------------------------------------------