/* * This file is part of CounterStrikeSharp. * CounterStrikeSharp is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * CounterStrikeSharp is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with CounterStrikeSharp. If not, see . * */ using System; using System.IO; using System.Linq; using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core.Attributes.Registration; using CounterStrikeSharp.API.Modules.Commands; using CounterStrikeSharp.API.Modules.Entities; using CounterStrikeSharp.API.Modules.Events; using CounterStrikeSharp.API.Modules.Memory; using CounterStrikeSharp.API.Modules.Utils; namespace TestPlugin { public class SamplePlugin : BasePlugin { public override string ModuleName => "Sample Plugin"; public override string ModuleVersion => "v1.0.0"; public override void Load(bool hotReload) { Console.WriteLine( $"Test Plugin has been loaded, and the hot reload flag was {hotReload}, path is {ModulePath}"); // ValveInterface provides pointers to loaded modules via Interface Name exposed from the engine (e.g. Source2Server001) var server = ValveInterface.Server; Log($"Server pointer found @ {server.Pointer:X}"); // inline void(FASTCALL *ClientPrint)(CBasePlayerController *player, int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4); var sigVirtualFunc = VirtualFunction.CreateVoid( GameData.GetSignature("ClientPrint")); var printAllFunc = VirtualFunction.CreateVoid( GameData.GetSignature("UTIL_ClientPrintAll")); var switchTeamFunc = VirtualFunction.CreateVoid(GameData.GetSignature("CCSPlayerController_SwitchTeam")); // Register Game Event Handlers RegisterEventHandler(GenericEventHandler); RegisterEventHandler((@event, info) => { // You can use `info.DontBroadcast` to set the dont broadcast flag on the event. if (new Random().NextSingle() > 0.5f) { @event.Attacker.PrintToChat($"Skipping player_death broadcast at {Server.CurrentTime}"); info.DontBroadcast = true; } return HookResult.Continue; }); RegisterEventHandler((@event, info) => { sigVirtualFunc(@event.Userid.Handle, 2, "Test", IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); return HookResult.Continue; }); RegisterEventHandler((@event, info) => { if (!@event.Userid.IsValid) return 0; if (!@event.Userid.PlayerPawn.IsValid) return 0; Log($"Player spawned with entity index: {@event.Userid.EntityIndex} & User ID: {@event.Userid.UserId}"); return HookResult.Continue; }); RegisterEventHandler(GenericEventHandler); RegisterEventHandler((@event, info) => { var player = @event.Userid; var pawn = player.PlayerPawn.Value; var activeWeapon = @event.Userid.PlayerPawn.Value.WeaponServices?.ActiveWeapon.Value; var weapons = @event.Userid.PlayerPawn.Value.WeaponServices?.MyWeapons; Server.NextFrame(() => { player.PrintToCenter(string.Join("\n", weapons.Select(x => x.Value.DesignerName))); }); activeWeapon.ReserveAmmo[0] = 250; activeWeapon.Clip1 = 250; VirtualFunctions.GiveNamedItem(pawn.ItemServices.Handle, "weapon_ak47", 0, 0, 0, 0); Log( $"Pawn Position: {pawn.CBodyComponent?.SceneNode?.AbsOrigin} @{pawn.CBodyComponent?.SceneNode.Rotation}"); char randomColourChar = (char)new Random().Next(0, 16); printAllFunc(3, $"Random String with Random Colour: {randomColourChar}{new Random().Next()}", IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); pawn.Health += 5; Log( $"Found steamID {new SteamID(player.SteamID)} for player {player.PlayerName}:{pawn.Health}|{pawn.InBuyZone}"); Log($"{@event.Userid}, {@event.X},{@event.Y},{@event.Z}"); return HookResult.Continue; }); RegisterEventHandler((@event, info) => { // Grab all cs_player_controller entities and set their cash value to $1337. var playerEntities = Utilities.FindAllEntitiesByDesignerName("cs_player_controller"); Log($"cs_player_controller count: {playerEntities.Count()}"); foreach (var player in playerEntities) { //var player = new CCSPlayerController(entInst.Handle); if (player.InGameMoneyServices != null) player.InGameMoneyServices.Account = 1337; } // Grab everything starting with cs_, but we'll only mainpulate cs_gamerules. var csEntities = Utilities.FindAllEntitiesByDesignerName("cs_"); Log($"Amount of cs_* entities: {csEntities.Count()}"); foreach (var entity in csEntities) { if (entity.DesignerName != "cs_gamerules") continue; var gamerulesEnt = new CCSGameRules(entity.Handle); gamerulesEnt.CTTimeOutActive = true; } return HookResult.Continue; }); // Hook global listeners defined by CounterStrikeSharp RegisterListener(mapName => { Log($"Map {mapName} has started!"); }); RegisterListener((index, name, ip) => { Log($"Client {name} from {ip} has connected!"); }); RegisterListener((index, id) => { Log($"Client {index} with address {id}"); }); RegisterListener(entity => { var designerName = entity.DesignerName; if (designerName != "smokegrenade_projectile") return; var projectile = new CSmokeGrenadeProjectile(entity.Handle); Server.NextFrame(() => { projectile.SmokeColor.X = Random.Shared.NextSingle() * 255.0f; projectile.SmokeColor.X = Random.Shared.NextSingle() * 255.0f; projectile.SmokeColor.X = Random.Shared.NextSingle() * 255.0f; Log($"Smoke grenade spawned with color {projectile.SmokeColor}"); }); }); // You can use `ModuleDirectory` to get the directory of the plugin (for storing config files, saving database files etc.) File.WriteAllText(Path.Join(ModuleDirectory, "example.txt"), $"Test file created by TestPlugin at {DateTime.Now}"); // Execute a server command as if typed into the server console. Server.ExecuteCommand("find \"cssharp\""); // Adds a new server console command AddCommand("cssharp_info", "A test command", (player, info) => { if (player == null) return; Log( $"CounterStrikeSharp - a test command was called by {new SteamID(player.SteamID).SteamId2} with {info.ArgString}"); }); // Example vfunc call that usually gets the game event manager pointer // by calling the func at offset 91 then subtracting 8 from the result pointer. // This value is asserted against the native code that points to the same function. var virtualFunc = VirtualFunction.Create(server.Pointer, 91); var result = virtualFunc() - 8; Log($"Result of virtual func call is {result:X}"); } [GameEventHandler] public HookResult OnPlayerConnect(EventPlayerConnect @event, GameEventInfo info) { Log($"Player {@event.Name} has connected!"); return HookResult.Continue; } [ConsoleCommand("cssharp_attribute", "This is a custom attribute event")] public void OnCommand(CCSPlayerController? player, CommandInfo command) { Log("cssharp_attribute called!"); } private HookResult GenericEventHandler(T @event, GameEventInfo info) where T : GameEvent { Log($"Event found {@event.Handle:X}, event name: {@event.EventName} dont broadcast: {info.DontBroadcast}"); return HookResult.Continue; } private void Log(string message) { Console.BackgroundColor = ConsoleColor.DarkGray; Console.ForegroundColor = ConsoleColor.DarkMagenta; Console.WriteLine(message); Console.ResetColor(); } } }