diff --git a/README.md b/README.md index c6ff5d1..928160c 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,28 @@ -![GitHub tag (with filter)](https://img.shields.io/github/v/tag/K4ryuu/Project_Template?style=for-the-badge&label=Version) -![GitHub Repo stars](https://img.shields.io/github/stars/K4ryuu/Project_Template?style=for-the-badge) -![GitHub issues](https://img.shields.io/github/issues/K4ryuu/Project_Template?style=for-the-badge) -![GitHub](https://img.shields.io/github/license/K4ryuu/Project_Template?style=for-the-badge) -![GitHub contributors](https://img.shields.io/github/contributors/K4ryuu/Project_Template?style=for-the-badge) -![GitHub all releases](https://img.shields.io/github/downloads/K4ryuu/Project_Template/total?style=for-the-badge) -![GitHub last commit (branch)](https://img.shields.io/github/last-commit/K4ryuu/Project_Template/dev?style=for-the-badge) +![GitHub tag (with filter)](https://img.shields.io/github/v/tag/K4ryuu/CS2_SteamRestrict?style=for-the-badge&label=Version) +![GitHub Repo stars](https://img.shields.io/github/stars/K4ryuu/CS2_SteamRestrict?style=for-the-badge) +![GitHub issues](https://img.shields.io/github/issues/K4ryuu/CS2_SteamRestrict?style=for-the-badge) +![GitHub](https://img.shields.io/github/license/K4ryuu/CS2_SteamRestrict?style=for-the-badge) +![GitHub contributors](https://img.shields.io/github/contributors/K4ryuu/CS2_SteamRestrict?style=for-the-badge) +![GitHub all releases](https://img.shields.io/github/downloads/K4ryuu/CS2_SteamRestrict/total?style=for-the-badge) +![GitHub last commit (branch)](https://img.shields.io/github/last-commit/K4ryuu/CS2_SteamRestrict/dev?style=for-the-badge)

K4ryuu

-

PLACE OF TITLE

+

CS2 Chat Relay

An awesome CS2 server addon created with CounterStrikeSharp

- Download + Download · - Report Bug + Report Bug · - Request Feature + Request Feature

@@ -62,7 +62,7 @@ ## About The Project -DESCRIPTION HERE +Enhance your game server's control with this plugin. Fetch and analyze player data from their Steam profiles, enforce minimum CS:GO playtime and Steam level requirements, and customize restrictions for Prime and non-Prime players. Flexible and powerful server management tool.

(back to top)

@@ -71,7 +71,6 @@ DESCRIPTION HERE To use this server addon, you'll need the following dependencies installed: - [**CounterStrikeSharp**](https://github.com/roflmuffin/CounterStrikeSharp/actions/workflows/cmake-single-platform.yml): CounterStrikeSharp allows you to write server plugins in C# for Counter-Strike 2/Source2/CS2 -- **MySQL Database (Version 5.2 or higher):** This project requires a MySQL database to store and manage data. You can host your own MySQL server or use a third-party hosting service. Make sure it's at least version 5.2 or higher.

(back to top)

@@ -106,13 +105,12 @@ Before you begin, ensure you have the following prerequisites: - A working CS2 (Counter-Strike 2) server. - CounterStrikeSharp is up to date and is running on your server. -- A compatible MySQL database (Version 5.2 or higher) set up and configured.

(back to top)

### Installation -1. **Download the Addon:** Start by downloading the addon from the [GitHub Releases Page](https://github.com/K4ryuu/Project_Template/releases). Choose the latest release version. +1. **Download the Addon:** Start by downloading the addon from the [GitHub Releases Page](https://github.com/K4ryuu/CS2_SteamRestrict/releases). Choose the latest release version. 2. **Extract the Addon:** After downloading, extract the contents of the addon to the counterstrikesharp/plugins directory on your server. Inside the plugins folder, you should have a folder named exactly as the project dll. From the releases, you find it pre zipped with the correct name. @@ -128,13 +126,13 @@ The addon provides several commands and console variables (convars) to customize ### Commands -- **Command 1:** Describe the first command and how to use it. +- None

(back to top)

### Console Variables (Convars) -- **Convar 1:** Describe the first convar and how to set its value. +- None

(back to top)

@@ -142,7 +140,7 @@ The addon provides several commands and console variables (convars) to customize ## Roadmap -- [ ] No plans for now +- [ ] No plans yet

(back to top)

diff --git a/src/.gitkeep b/src/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/CFG.cs b/src/CFG.cs new file mode 100644 index 0000000..011fe51 --- /dev/null +++ b/src/CFG.cs @@ -0,0 +1,57 @@ +using System.Text.Json; + +internal class CFG +{ + public static Config config = new(); + + public void CheckConfig(string moduleDirectory) + { + string path = Path.Join(moduleDirectory, "config.json"); + + if (!File.Exists(path)) + { + CreateAndWriteFile(path); + } + + using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) + using (StreamReader sr = new StreamReader(fs)) + { + // Deserialize the JSON from the file and load the configuration. + config = JsonSerializer.Deserialize(sr.ReadToEnd())!; + } + } + + private static void CreateAndWriteFile(string path) + { + + using (FileStream fs = File.Create(path)) + { + // File is created, and fs will automatically be disposed when the using block exits. + } + + Console.WriteLine($"File created: {File.Exists(path)}"); + + Config config = new Config + { + SteamWebAPI = "-", + }; + + // Serialize the config object to JSON and write it to the file. + string jsonConfig = JsonSerializer.Serialize(config, new JsonSerializerOptions() + { + WriteIndented = true + }); + File.WriteAllText(path, jsonConfig); + } +} + +internal class Config +{ + public string? SteamWebAPI { get; set; } + public int MinimumHourNonPrime { get; set; } + public int MinimumHourPrime { get; set; } + public int MinimumLevelNonPrime { get; set; } + public int MinimumLevelPrime { get; set; } + public bool BlockPrivateProfile { get; set; } + public bool BlockNonPrime { get; set; } +} \ No newline at end of file diff --git a/src/CounterStrikeSharp.API.dll b/src/CounterStrikeSharp.API.dll new file mode 100644 index 0000000..656b1e9 Binary files /dev/null and b/src/CounterStrikeSharp.API.dll differ diff --git a/src/K4ryuuSteamRestrict.cs b/src/K4ryuuSteamRestrict.cs new file mode 100644 index 0000000..15ceb60 --- /dev/null +++ b/src/K4ryuuSteamRestrict.cs @@ -0,0 +1,206 @@ +using System.Text; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Core.Attributes; +using CounterStrikeSharp.API.Core.Attributes.Registration; +using CounterStrikeSharp.API.Modules.Utils; +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; +using CounterStrikeSharp.API; + +namespace K4ryuuSteamRestrict +{ + [MinimumApiVersion(16)] + public class SteamRestrictPlugin : BasePlugin + { + public class SteamUserInfo + { + public int SteamLevel { get; set; } + public int CSGOPlaytime { get; set; } + public bool IsPrivate { get; set; } + public bool HasPrime { get; set; } + } + + public override string ModuleName => "Steam Restrict"; + public override string ModuleVersion => "1.0.0"; + public override string ModuleAuthor => "K4ryuu"; + + public override void Load(bool hotReload) + { + new CFG().CheckConfig(ModuleDirectory); + } + + [GameEventHandler] + public HookResult OnClientConnect(EventPlayerConnectFull @event, GameEventInfo info) + { + CCSPlayerController player = @event.Userid; + + if (player == null || !player.IsValid || player.IsBot || CFG.config.SteamWebAPI == "-") + return HookResult.Continue; + + _ = FetchSteamUserInfo(player); + + + return HookResult.Continue; + } + + private async Task FetchSteamUserInfo(CCSPlayerController player) + { + SteamUserInfo userInfo = new SteamUserInfo(); + + using (HttpClient httpClient = new HttpClient()) + { + string steamId = player.SteamID.ToString(); + string steamWebAPIKey = CFG.config.SteamWebAPI!; + + string gamesUrl = $"https://api.steampowered.com/IPlayerService/GetOwnedGames/v1/?key={steamWebAPIKey}&steamid={steamId}&format=json"; + HttpResponseMessage gamesResponse = await httpClient.GetAsync(gamesUrl); + + if (gamesResponse.IsSuccessStatusCode) + { + string gamesJson = await gamesResponse.Content.ReadAsStringAsync(); + userInfo.CSGOPlaytime = ParseCS2Playtime(gamesJson) / 60; + + JArray games = (JObject.Parse(gamesJson)["response"]!["games"] as JArray)!; + bool hasPrime = games.Any(game => (int)game["appid"]! == 54029); + userInfo.HasPrime = hasPrime; + } + else + { + userInfo.HasPrime = false; + userInfo.CSGOPlaytime = 0; + } + + // Fetch Steam level + string levelUrl = $"http://api.steampowered.com/IPlayerService/GetSteamLevel/v1/?key={steamWebAPIKey}&steamid={steamId}"; + HttpResponseMessage levelResponse = await httpClient.GetAsync(levelUrl); + + if (levelResponse.IsSuccessStatusCode) + { + string levelJson = await levelResponse.Content.ReadAsStringAsync(); + userInfo.SteamLevel = ParseSteamLevel(levelJson); + } + else + { + userInfo.SteamLevel = 0; + } + + // Fetch other user information + string userUrl = $"https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/?key={steamWebAPIKey}&steamids={steamId}"; + HttpResponseMessage userResponse = await httpClient.GetAsync(userUrl); + + if (userResponse.IsSuccessStatusCode) + { + string userJson = await userResponse.Content.ReadAsStringAsync(); + ParseSteamUserInfo(userJson, userInfo); + } + else + { + userInfo.IsPrivate = false; + } + + if (IsRestrictionViolated(userInfo)) + { + Server.ExecuteCommand($"kickid {player.UserId} \"You have been kicked for not meeting the minimum requirements.\""); + } + } + + return userInfo; + } + + private bool IsRestrictionViolated(SteamUserInfo userInfo) + { + bool isViolated = false; + + if (userInfo.HasPrime) + { + if (CFG.config.MinimumHourPrime != -1 && userInfo.CSGOPlaytime < CFG.config.MinimumHourPrime + || CFG.config.MinimumLevelPrime != -1 && userInfo.SteamLevel < CFG.config.MinimumLevelPrime) + { + isViolated = true; + } + } + else + { + if (CFG.config.MinimumHourNonPrime != -1 && userInfo.CSGOPlaytime < CFG.config.MinimumHourNonPrime + || CFG.config.MinimumLevelNonPrime != -1 && userInfo.SteamLevel < CFG.config.MinimumLevelNonPrime) + { + isViolated = true; + } + } + + if (CFG.config.BlockPrivateProfile && userInfo.IsPrivate) + { + isViolated = true; + } + + return isViolated; + } + + private int ParseCS2Playtime(string json) + { + int csPlaytime = 0; + + try + { + JObject data = JObject.Parse(json); + JArray games = (data["response"]!["games"] as JArray)!; + + if (games != null) + { + foreach (var game in games) + { + int appId = (int)game["appid"]!; + if (appId == 730) + { + csPlaytime = (int)game["playtime_forever"]!; + } + } + } + } + catch (Exception ex) + { + Console.WriteLine($"Error parsing CS:GO playtime: {ex.Message}"); + } + + return csPlaytime; + } + + private int ParseSteamLevel(string json) + { + int steamLevel = 0; + + try + { + JObject data = JObject.Parse(json); + steamLevel = (int)data["response"]!["player_level"]!; + } + catch (Exception ex) + { + Console.WriteLine($"Error parsing Steam level: {ex.Message}"); + } + + return steamLevel; + } + + private void ParseSteamUserInfo(string json, SteamUserInfo userInfo) + { + try + { + JObject data = JObject.Parse(json); + JArray players = (data["response"]!["players"] as JArray)!; + + if (players != null && players.Count > 0) + { + var player = players[0]; + + userInfo.IsPrivate = (int)player["communityvisibilitystate"]! != 3; + } + } + catch (Exception ex) + { + Console.WriteLine($"Error parsing Steam user info: {ex.Message}"); + } + } + } +} diff --git a/src/K4ryuuSteamRestrict.csproj b/src/K4ryuuSteamRestrict.csproj new file mode 100644 index 0000000..c2a336e --- /dev/null +++ b/src/K4ryuuSteamRestrict.csproj @@ -0,0 +1,12 @@ + + + + net7.0 + enable + enable + + + + + + \ No newline at end of file