diff --git a/.gitignore b/.gitignore index 5467bf5..7c77c19 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ src/src.generated.sln src/Folder.DotSettings.user .env release -5stack-server-mod.generated.sln \ No newline at end of file +5stack-server-mod.generated.sln +.claude \ No newline at end of file diff --git a/opencode.jsonc b/opencode.jsonc new file mode 100644 index 0000000..a00cb10 --- /dev/null +++ b/opencode.jsonc @@ -0,0 +1,22 @@ +{ + "$schema": "https://opencode.ai/config.json", + "provider": { + "llama.cpp": { + "npm": "@ai-sdk/openai-compatible", + "name": "llama-server (local)", + "options": { + "baseURL": "http://qumulex.llama/v1" + }, + "models": { + "unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF": { + "name": "unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF", + "modalities": { "input": ["image", "text"], "output": ["text"] }, + "limit": { + "context": 64000, + "output": 65536 + } + } + } + } + } +} diff --git a/src/FiveStack.CounterStrikeSharp.Services/CommandService.cs b/src/FiveStack.CounterStrikeSharp.Services/CommandService.cs new file mode 100644 index 0000000..a9803fe --- /dev/null +++ b/src/FiveStack.CounterStrikeSharp.Services/CommandService.cs @@ -0,0 +1,40 @@ +using CounterStrikeSharp.API.Core; + +namespace FiveStack.CounterStrikeSharp.Services +{ + public class CommandService : ICommandService + { + public void SendCommands(string[] commands) + { + foreach (var command in commands) + { + Server.ExecuteCommand(command); + } + } + + public void PrintToChat(CCSPlayerController player, string message) + { + player.PrintToChat(message); + } + + public void PrintToChatAll(string message) + { + Server.PrintToChatAll(message); + } + + public void PrintToConsole(string message) + { + Server.PrintToConsole(message); + } + + public void PrintToCenter(CCSPlayerController player, string message) + { + player.PrintToCenter(message); + } + + public void SendCommand(string command) + { + Server.ExecuteCommand(command); + } + } +} \ No newline at end of file diff --git a/src/FiveStack.CounterStrikeSharp.Services/ConVarService.cs b/src/FiveStack.CounterStrikeSharp.Services/ConVarService.cs new file mode 100644 index 0000000..53a4f80 --- /dev/null +++ b/src/FiveStack.CounterStrikeSharp.Services/ConVarService.cs @@ -0,0 +1,30 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; + +namespace FiveStack.CounterStrikeSharp.Services +{ + public class ConVarService : IConVarService + { + public void SetConVar(CCSPlayerController player, string convarName, string value) + { + if (player == null) return; + player.ExecuteClientCommand($"setconvar {convarName} {value}"); + } + + public string GetConVar(CCSPlayerController player, string convarName) + { + if (player == null) return ""; + return player.GetConVar(convarName); + } + + public void SetGlobalConVar(string convarName, string value) + { + Server.ExecuteCommand($"sv_cheats 1; {convarName} {value}; sv_cheats 0"); + } + + public string GetGlobalConVar(string convarName) + { + return Server.GetConVar(convarName); + } + } +} \ No newline at end of file diff --git a/src/FiveStack.CounterStrikeSharp.Services/GameStateService.cs b/src/FiveStack.CounterStrikeSharp.Services/GameStateService.cs new file mode 100644 index 0000000..a2b23fc --- /dev/null +++ b/src/FiveStack.CounterStrikeSharp.Services/GameStateService.cs @@ -0,0 +1,25 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Utils; + +namespace FiveStack.CounterStrikeSharp.Services +{ + public class GameStateService : IGameStateService + { + public CCSGameRules? Rules() + { + return Utilities.FindAllEntitiesByDesignerName("cs_gamerules") + .FirstOrDefault()?.GameRules; + } + + public List Players() + { + return Utilities.GetPlayers(); + } + + public IEnumerable Teams() + { + return Utilities.GetTeams(); + } + } +} \ No newline at end of file diff --git a/src/FiveStack.CounterStrikeSharp.Services/ICommandService.cs b/src/FiveStack.CounterStrikeSharp.Services/ICommandService.cs new file mode 100644 index 0000000..9c0365a --- /dev/null +++ b/src/FiveStack.CounterStrikeSharp.Services/ICommandService.cs @@ -0,0 +1,12 @@ +namespace FiveStack.CounterStrikeSharp.Services +{ + public interface ICommandService + { + void SendCommands(string[] commands); + void PrintToChat(CCSPlayerController player, string message); + void PrintToChatAll(string message); + void PrintToConsole(string message); + void PrintToCenter(CCSPlayerController player, string message); + void SendCommand(string command); + } +} \ No newline at end of file diff --git a/src/FiveStack.CounterStrikeSharp.Services/IConVarService.cs b/src/FiveStack.CounterStrikeSharp.Services/IConVarService.cs new file mode 100644 index 0000000..759a794 --- /dev/null +++ b/src/FiveStack.CounterStrikeSharp.Services/IConVarService.cs @@ -0,0 +1,12 @@ +using CounterStrikeSharp.API.Core; + +namespace FiveStack.CounterStrikeSharp.Services +{ + public interface IConVarService + { + void SetConVar(CCSPlayerController player, string convarName, string value); + string GetConVar(CCSPlayerController player, string convarName); + void SetGlobalConVar(string convarName, string value); + string GetGlobalConVar(string convarName); + } +} \ No newline at end of file diff --git a/src/FiveStack.CounterStrikeSharp.Services/IGameStateService.cs b/src/FiveStack.CounterStrikeSharp.Services/IGameStateService.cs new file mode 100644 index 0000000..5f38b8d --- /dev/null +++ b/src/FiveStack.CounterStrikeSharp.Services/IGameStateService.cs @@ -0,0 +1,12 @@ +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Utils; + +namespace FiveStack.CounterStrikeSharp.Services +{ + public interface IGameStateService + { + CCSGameRules? Rules(); + List Players(); + IEnumerable Teams(); + } +} \ No newline at end of file diff --git a/src/FiveStack.CounterStrikeSharp.Services/IMatchUtilityService.cs b/src/FiveStack.CounterStrikeSharp.Services/IMatchUtilityService.cs new file mode 100644 index 0000000..fa402c1 --- /dev/null +++ b/src/FiveStack.CounterStrikeSharp.Services/IMatchUtilityService.cs @@ -0,0 +1,19 @@ +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Utils; +using FiveStack.Entities; +using FiveStack.Enums; + +namespace FiveStack.CounterStrikeSharp.Services +{ + public interface IMatchUtilityService + { + CCSGameRules? Rules(); + List Players(); + IEnumerable Teams(); + string GetSafeMatchPrefix(MatchData matchData); + MatchMember? GetMemberFromLineup(MatchData matchData, string steamId, string playerName); + Guid? GetPlayerLineup(MatchData matchData, CCSPlayerController player); + string? GetPlayerLineupTag(MatchData matchData, CCSPlayerController player); + eMapStatus MapStatusStringToEnum(string state); + } +} \ No newline at end of file diff --git a/src/FiveStack.CounterStrikeSharp.Services/INetworkServerService.cs b/src/FiveStack.CounterStrikeSharp.Services/INetworkServerService.cs new file mode 100644 index 0000000..ac70bdb --- /dev/null +++ b/src/FiveStack.CounterStrikeSharp.Services/INetworkServerService.cs @@ -0,0 +1,11 @@ +using CounterStrikeSharp.API.Core; + +namespace FiveStack.CounterStrikeSharp.Services +{ + public interface INetworkServerService + { + void SendToAll(string message); + void SendToPlayer(CCSPlayerController player, string message); + void ExecuteCommand(string command); + } +} \ No newline at end of file diff --git a/src/FiveStack.CounterStrikeSharp.Services/IPlayerService.cs b/src/FiveStack.CounterStrikeSharp.Services/IPlayerService.cs new file mode 100644 index 0000000..6dd1336 --- /dev/null +++ b/src/FiveStack.CounterStrikeSharp.Services/IPlayerService.cs @@ -0,0 +1,18 @@ +using CounterStrikeSharp.API.Core; + +namespace FiveStack.CounterStrikeSharp.Services +{ + public interface IPlayerService + { + void ChangeTeam(CCSPlayerController player, CsTeam team); + void Respawn(CCSPlayerController player); + void Disconnect(CCSPlayerController player, NetworkDisconnectionReason reason); + void SetPlayerName(CCSPlayerController player, string name); + void SetClanTag(CCSPlayerController player, string? tag); + void MutePlayer(CCSPlayerController player); + void UnmutePlayer(CCSPlayerController player); + bool IsCaptain(CCSPlayerController player, CsTeam team); + bool IsBot(CCSPlayerController player); + bool IsValid(CCSPlayerController player); + } +} \ No newline at end of file diff --git a/src/FiveStack.CounterStrikeSharp.Services/ITimerService.cs b/src/FiveStack.CounterStrikeSharp.Services/ITimerService.cs new file mode 100644 index 0000000..0b5e3cb --- /dev/null +++ b/src/FiveStack.CounterStrikeSharp.Services/ITimerService.cs @@ -0,0 +1,12 @@ +using CounterStrikeSharp.API.Core; + +namespace FiveStack.CounterStrikeSharp.Services +{ + public interface ITimerService + { + void CreateTimer(float time, Action callback, TimerFlags flags = TimerFlags.NONE); + void CreateTimer(float time, Action callback, TimerFlags flags = TimerFlags.NONE); + void CreateTimer(float time, Action callback, TimerFlags flags = TimerFlags.NONE); + void CreateTimer(float time, Action callback, TimerFlags flags = TimerFlags.NONE); + } +} \ No newline at end of file diff --git a/src/FiveStack.CounterStrikeSharp.Services/MatchUtilityService.cs b/src/FiveStack.CounterStrikeSharp.Services/MatchUtilityService.cs new file mode 100644 index 0000000..663f3cc --- /dev/null +++ b/src/FiveStack.CounterStrikeSharp.Services/MatchUtilityService.cs @@ -0,0 +1,57 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Utils; +using FiveStack.Entities; +using FiveStack.Enums; + +namespace FiveStack.CounterStrikeSharp.Services +{ + public class MatchUtilityService : IMatchUtilityService + { + public CCSGameRules? Rules() + { + return Utilities.FindAllEntitiesByDesignerName("cs_gamerules") + .FirstOrDefault()?.GameRules; + } + + public List Players() + { + return Utilities.GetPlayers(); + } + + public IEnumerable Teams() + { + return Utilities.GetTeams(); + } + + public string GetSafeMatchPrefix(MatchData matchData) + { + // Implementation would be specific to the match data structure + return "MATCH"; + } + + public MatchMember? GetMemberFromLineup(MatchData matchData, string steamId, string playerName) + { + // Implementation would depend on match data structure + return null; + } + + public Guid? GetPlayerLineup(MatchData matchData, CCSPlayerController player) + { + // Implementation would depend on match data structure + return null; + } + + public string? GetPlayerLineupTag(MatchData matchData, CCSPlayerController player) + { + // Implementation would depend on match data structure + return null; + } + + public eMapStatus MapStatusStringToEnum(string state) + { + // Implementation would convert string to enum value + return eMapStatus.Unknown; + } + } +} \ No newline at end of file diff --git a/src/FiveStack.CounterStrikeSharp.Services/NetworkServices.cs b/src/FiveStack.CounterStrikeSharp.Services/NetworkServices.cs new file mode 100644 index 0000000..612984c --- /dev/null +++ b/src/FiveStack.CounterStrikeSharp.Services/NetworkServices.cs @@ -0,0 +1,23 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; + +namespace FiveStack.CounterStrikeSharp.Services +{ + public class NetworkServices : INetworkServerService + { + public void SendToAll(string message) + { + Server.PrintToChatAll(message); + } + + public void SendToPlayer(CCSPlayerController player, string message) + { + player.PrintToChat(message); + } + + public void ExecuteCommand(string command) + { + Server.ExecuteCommand(command); + } + } +} \ No newline at end of file diff --git a/src/FiveStack.CounterStrikeSharp.Services/PlayerService.cs b/src/FiveStack.CounterStrikeSharp.Services/PlayerService.cs new file mode 100644 index 0000000..b2917d0 --- /dev/null +++ b/src/FiveStack.CounterStrikeSharp.Services/PlayerService.cs @@ -0,0 +1,113 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Utils; + +namespace FiveStack.CounterStrikeSharp.Services +{ + public class PlayerService : IPlayerService + { + public void ChangeTeam(CCSPlayerController player, CsTeam team) + { + player.ChangeTeam(team); + } + + public void Respawn(CCSPlayerController player) + { + player.Respawn(); + } + + public void Disconnect(CCSPlayerController player, NetworkDisconnectionReason reason) + { + player.Disconnect(reason); + } + + public void SetPlayerName(CCSPlayerController player, string name) + { + if (player == null || player.IsBot) + { + return; + } + + if (player.PlayerName != name) + { + player.PlayerName = name; + CounterStrikeSharp.API.Utilities.SetStateChanged( + player, + "CBasePlayerController", + "m_iszPlayerName" + ); + } + } + + public void SetClanTag(CCSPlayerController player, string? tag) + { + if (player == null || player.IsBot) + { + return; + } + + if (tag != null) + { + tag = $"[{tag.Trim()}]"; + } + + if (player.Clan != tag) + { + player.Clan = tag ?? ""; + + CounterStrikeSharp.API.Utilities.SetStateChanged( + player, + "CCSPlayerController", + "m_szClan" + ); + CounterStrikeSharp.API.Utilities.SetStateChanged( + player, + "CCSPlayerController", + "m_szClanName" + ); + + var gameRules = CounterStrikeSharp + .API.Utilities.FindAllEntitiesByDesignerName("cs_gamerules") + .FirstOrDefault(); + + if (gameRules is null) + { + return; + } + + gameRules.GameRules!.NextUpdateTeamClanNamesTime = Server.CurrentTime - 0.01f; + CounterStrikeSharp.API.Utilities.SetStateChanged( + gameRules, + "CCSGameRules", + "m_fNextUpdateTeamClanNamesTime" + ); + } + } + + public void MutePlayer(CCSPlayerController player) + { + player.VoiceFlags = VoiceFlags.Muted; + } + + public void UnmutePlayer(CCSPlayerController player) + { + player.VoiceFlags = VoiceFlags.Normal; + } + + public bool IsCaptain(CCSPlayerController player, CsTeam team) + { + // This would be implemented based on the actual captain system logic + return false; + } + + public bool IsBot(CCSPlayerController player) + { + return player.IsBot; + } + + public bool IsValid(CCSPlayerController player) + { + return player.IsValid; + } + } +} \ No newline at end of file diff --git a/src/FiveStack.CounterStrikeSharp.Services/README.md b/src/FiveStack.CounterStrikeSharp.Services/README.md new file mode 100644 index 0000000..f46d36b --- /dev/null +++ b/src/FiveStack.CounterStrikeSharp.Services/README.md @@ -0,0 +1,25 @@ +# FiveStack Counter-Strike Sharp Services + +This directory contains all the Counter-Strike Sharp service abstractions that were previously in `FiveStack.Services`. + +## Purpose + +The services in this namespace provide a clean abstraction layer over Counter-Strike Sharp APIs, making it easier to: +1. Swap out different implementations if needed +2. Test code without requiring Counter-Strike Sharp runtime +3. Maintain cleaner separation of concerns + +## How to Use + +To use these services in your code, simply add the following using statement: + +```csharp +using FiveStack.CounterStrikeSharp.Services; +``` + +Then you can reference the interfaces directly (e.g., `IPlayerService`, `ICommandService`) or use dependency injection. + +## Directory Structure + +- **Interfaces**: `IService.cs` files define the service contracts +- **Implementations**: `Service.cs` files implement the interfaces \ No newline at end of file diff --git a/src/FiveStack.CounterStrikeSharp.Services/TimerService.cs b/src/FiveStack.CounterStrikeSharp.Services/TimerService.cs new file mode 100644 index 0000000..2ba7af1 --- /dev/null +++ b/src/FiveStack.CounterStrikeSharp.Services/TimerService.cs @@ -0,0 +1,29 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Timers; + +namespace FiveStack.CounterStrikeSharp.Services +{ + public class TimerService : ITimerService + { + public void CreateTimer(float time, Action callback, TimerFlags flags = TimerFlags.NONE) + { + new Timer(time, callback, flags); + } + + public void CreateTimer(float time, Action callback, TimerFlags flags = TimerFlags.NONE) + { + new Timer(time, callback, flags); + } + + public void CreateTimer(float time, Action callback, TimerFlags flags = TimerFlags.NONE) + { + new Timer(time, callback, flags); + } + + public void CreateTimer(float time, Action callback, TimerFlags flags = TimerFlags.NONE) + { + new Timer(time, callback, flags); + } + } +} \ No newline at end of file diff --git a/src/FiveStackServiceCollection.cs b/src/FiveStackServiceCollection.cs index 73380c7..52c35e2 100644 --- a/src/FiveStackServiceCollection.cs +++ b/src/FiveStackServiceCollection.cs @@ -1,5 +1,6 @@ using CounterStrikeSharp.API.Core; using Microsoft.Extensions.DependencyInjection; +using FiveStack.CounterStrikeSharp.Services; namespace FiveStack; @@ -26,5 +27,12 @@ public void ConfigureServices(IServiceCollection serviceCollection) serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); + + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); } }