diff --git a/CSPracc/CSPracc.csproj b/CSPracc/CSPracc.csproj index 24e8529..4fdfcc0 100644 --- a/CSPracc/CSPracc.csproj +++ b/CSPracc/CSPracc.csproj @@ -1,12 +1,12 @@  - net7.0 + net8.0 enable enable x64 AnyCPU;x64 - 1.0.0.2 + 1.0.0.4 @@ -18,7 +18,7 @@ - + diff --git a/CSPracc/DataModules/Constants.cs b/CSPracc/DataModules/Constants.cs index 95be592..37cc83b 100644 --- a/CSPracc/DataModules/Constants.cs +++ b/CSPracc/DataModules/Constants.cs @@ -46,6 +46,7 @@ public class PREFIRE_COMMAND public class BASE_COMMAND { public const string MODE = ".menu"; + public const string PRAC = ".prac"; public const string PRACC = ".pracc"; public const string MATCH = ".match"; public const string DryRun = ".dryrun"; @@ -92,6 +93,7 @@ public class DRYRUN_COMMAND public class PRACC_COMMAND { + public const string NOCLIP = ".noclip"; public const string SPAWN = ".spawn"; public const string TSPAWN = ".tspawn"; public const string CTSPAWN = ".ctspawn"; @@ -145,6 +147,7 @@ public class PRACC_COMMAND public const string breakstuff = ".break"; public const string impacts = ".impacts"; + public const string restart_round = ".rr"; public const string mimic_menu = ".mimic"; public const string create_replay = ".createreplay"; @@ -157,6 +160,7 @@ public class PRACC_COMMAND public class AdminFlags { + public const string Root = "@css/root"; public const string Standard = "@CSPracc/admin"; } public class Methods diff --git a/CSPracc/Extensions/CCSPlayerControllerExtensions.cs b/CSPracc/Extensions/CCSPlayerControllerExtensions.cs index 1eea8c1..b33118f 100644 --- a/CSPracc/Extensions/CCSPlayerControllerExtensions.cs +++ b/CSPracc/Extensions/CCSPlayerControllerExtensions.cs @@ -23,7 +23,10 @@ public static bool IsAdmin(this CCSPlayerController playerController) { return true; } - return AdminManager.PlayerHasPermissions(playerController, AdminFlags.Standard); + + // check if they have permission (starting with least privileged) + return AdminManager.PlayerHasPermissions(playerController, AdminFlags.Standard) + || AdminManager.PlayerHasPermissions(playerController, AdminFlags.Root); } public static CsTeam GetCsTeam(this CCSPlayerController playerController) diff --git a/CSPracc/Logging.cs b/CSPracc/Logging.cs index 34395ed..0ce77a5 100644 --- a/CSPracc/Logging.cs +++ b/CSPracc/Logging.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; + namespace CSPracc { diff --git a/CSPracc/Managers/PracticeBotManager.cs b/CSPracc/Managers/PracticeBotManager.cs index d9bf58a..c191992 100644 --- a/CSPracc/Managers/PracticeBotManager.cs +++ b/CSPracc/Managers/PracticeBotManager.cs @@ -1,19 +1,10 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; -using CounterStrikeSharp.API.Core.Attributes.Registration; using CounterStrikeSharp.API.Modules.Utils; using CSPracc.DataModules; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using CounterStrikeSharp.API.Modules.Timers; using CSPracc.DataModules.Constants; -using CounterStrikeSharp.API.Modules.Entities; -using System.Net.Http.Headers; -using CounterStrikeSharp.API.Modules.Memory; -using System.Numerics; +using CounterStrikeSharp.API.Modules.Entities.Constants; using Vector = CounterStrikeSharp.API.Modules.Utils.Vector; using Microsoft.Extensions.Logging; using CSPracc.Extensions; @@ -30,6 +21,8 @@ public PracticeBotManager() { } private Dictionary> spawnedBots { get; set; } =new Dictionary>(); private Dictionary lastBotSpawned { get; set; } = new Dictionary(); + + private CounterStrikeSharp.API.Modules.Timers.Timer? collisionGroupTimer; /// /// Following code is heavily inspired by https://github.com/shobhit-pathak/MatchZy/blob/main/PracticeMode.cs @@ -65,7 +58,7 @@ public void AddBot(CCSPlayerController player,bool crouch = false,CsTeam team = CSPraccPlugin.Instance!.AddTimer(0.1f, () => SpawnBot(player,crouch)); Server.ExecuteCommand("bot_stop 1"); Server.ExecuteCommand("bot_freeze 1"); - Server.ExecuteCommand("bot_zombie 1"); + Server.ExecuteCommand("bot_zombie 1"); } /// @@ -276,6 +269,7 @@ private void SpawnBot(CCSPlayerController botOwner, bool crouch = false) spawnedBots[tempPlayer.PlayerName]["position"] = botOwnerPosition; spawnedBots[tempPlayer.PlayerName]["owner"] = botOwner; spawnedBots[tempPlayer.PlayerName]["crouchstate"] = crouch; + CCSPlayer_MovementServices movementService = new CCSPlayer_MovementServices(tempPlayer.PlayerPawn.Value.MovementServices!.Handle); CCSBot bot = tempPlayer.PlayerPawn.Value.Bot!; CSPraccPlugin.Instance!.AddTimer(0.1f, () => tempPlayer.PlayerPawn.Value.Teleport(botOwnerPosition.PlayerPosition, botOwnerPosition.PlayerAngle, new Vector(0, 0, 0))); @@ -287,6 +281,7 @@ private void SpawnBot(CCSPlayerController botOwner, bool crouch = false) } lastBotSpawned.SetOrAdd(botOwner.SteamID, tempPlayer.PlayerName); + TemporarilyDisableCollisions(botOwner, tempPlayer); unusedBotFound = true; } } @@ -295,6 +290,56 @@ private void SpawnBot(CCSPlayerController botOwner, bool crouch = false) Methods.MsgToServer($"Cannot add bots, the team is full! Use .nobots to remove the current bots."); } } + + public void TemporarilyDisableCollisions(CCSPlayerController p1, CCSPlayerController p2) + { + CSPraccPlugin.Instance.Logger.LogInformation($"[TemporarilyDisableCollisions] Disabling {p1.PlayerName} {p2.PlayerName}"); + // Reference collision code: https://github.com/Source2ZE/CS2Fixes/blob/f009e399ff23a81915e5a2b2afda20da2ba93ada/src/events.cpp#L150 + p1.PlayerPawn.Value!.Collision.CollisionAttribute.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_DEBRIS; + p1.PlayerPawn.Value.Collision.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_DEBRIS; + p2.PlayerPawn.Value!.Collision.CollisionAttribute.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_DEBRIS; + p2.PlayerPawn.Value.Collision.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_DEBRIS; + // TODO: call CollisionRulesChanged + var p1p = p1.PlayerPawn; + var p2p = p2.PlayerPawn; + collisionGroupTimer?.Kill(); + collisionGroupTimer = CSPraccPlugin.Instance.AddTimer(0.1f, () => + { + if (!p1p.IsValid || !p2p.IsValid || !p1p.Value.IsValid || !p2p.Value.IsValid) + { + CSPraccPlugin.Instance.Logger.LogWarning($"player handle invalid p1p {p1p.Value.IsValid} p2p {p2p.Value.IsValid}"); + collisionGroupTimer?.Kill(); + return; + } + + if (!DoPlayersCollide(p1p.Value, p2p.Value)) + { + // Once they no longer collide + p1p.Value.Collision.CollisionAttribute.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_PLAYER_MOVEMENT; + p1p.Value.Collision.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_PLAYER_MOVEMENT; + p2p.Value.Collision.CollisionAttribute.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_PLAYER_MOVEMENT; + p2p.Value.Collision.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_PLAYER_MOVEMENT; + // TODO: call CollisionRulesChanged + collisionGroupTimer?.Kill(); + } + + }, TimerFlags.REPEAT | TimerFlags.STOP_ON_MAPCHANGE); + } + + public bool DoPlayersCollide(CCSPlayerPawn p1, CCSPlayerPawn p2) + { + Vector p1min, p1max, p2min, p2max; + var p1pos = p1.AbsOrigin; + var p2pos = p2.AbsOrigin; + p1min = p1.Collision.Mins + p1pos!; + p1max = p1.Collision.Maxs + p1pos!; + p2min = p2.Collision.Mins + p2pos!; + p2max = p2.Collision.Maxs + p2pos!; + + return p1min.X <= p2max.X && p1max.X >= p2min.X && + p1min.Y <= p2max.Y && p1max.Y >= p2min.Y && + p1min.Z <= p2max.Z && p1max.Z >= p2min.Z; + } public HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) { diff --git a/CSPracc/Modes/BaseMode/BaseCommandHandler.cs b/CSPracc/Modes/BaseMode/BaseCommandHandler.cs index 4ac0bde..9d006e0 100644 --- a/CSPracc/Modes/BaseMode/BaseCommandHandler.cs +++ b/CSPracc/Modes/BaseMode/BaseCommandHandler.cs @@ -175,6 +175,7 @@ public virtual bool PlayerChat(EventPlayerChat @event, GameEventInfo info) CSPraccPlugin.SwitchMode(Enums.PluginMode.Base); break; } + case BASE_COMMAND.PRAC: case BASE_COMMAND.PRACC: { if (!player.IsAdmin()) diff --git a/CSPracc/Modes/PracticeMode/PracticeCommandHandler.cs b/CSPracc/Modes/PracticeMode/PracticeCommandHandler.cs index 335b647..fbfe828 100644 --- a/CSPracc/Modes/PracticeMode/PracticeCommandHandler.cs +++ b/CSPracc/Modes/PracticeMode/PracticeCommandHandler.cs @@ -1,22 +1,12 @@ -using CounterStrikeSharp.API.Core.Attributes.Registration; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using CounterStrikeSharp.API.Modules.Menu; +using CounterStrikeSharp.API.Modules.Commands; +using CounterStrikeSharp.API.Modules.Cvars; using CounterStrikeSharp.API.Modules.Utils; -using CounterStrikeSharp.API; +using CSPracc.DataModules; using CSPracc.DataModules.Constants; using CSPracc.Managers; -using CSPracc.DataModules; -using CounterStrikeSharp.API.Modules.Commands; -using CounterStrikeSharp.API.Modules.Memory; using CSPracc.Modes; -using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; -using System.Reflection.Metadata.Ecma335; -using CounterStrikeSharp.API.Modules.Cvars; namespace CSPracc.CommandHandler { @@ -68,6 +58,12 @@ public override bool PlayerChat(EventPlayerChat @event, GameEventInfo info) } switch (command) { + case PRACC_COMMAND.NOCLIP: + Utils.ToggleNoClip(player); + break; + case PRACC_COMMAND.restart_round: + Server.ExecuteCommand("mp_restartgame 1"); + break; case PRACC_COMMAND.SPAWN: { SpawnManager.TeleportToSpawn(player, args); diff --git a/CSPracc/Modes/PracticeMode/PracticeEventHandler.cs b/CSPracc/Modes/PracticeMode/PracticeEventHandler.cs index f4e6a4f..26bed31 100644 --- a/CSPracc/Modes/PracticeMode/PracticeEventHandler.cs +++ b/CSPracc/Modes/PracticeMode/PracticeEventHandler.cs @@ -1,22 +1,10 @@ -using CounterStrikeSharp.API; -using CounterStrikeSharp.API.Core; -using CounterStrikeSharp.API.Core.Attributes; -using CounterStrikeSharp.API.Core.Attributes.Registration; -using CounterStrikeSharp.API.Modules.Entities; -using CounterStrikeSharp.API.Modules.Events; +using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; +using static CounterStrikeSharp.API.Core.BasePlugin; using CSPracc.CommandHandler; -using CSPracc.DataModules; -using CSPracc.DataModules.Constants; using CSPracc.Extensions; +using CSPracc.DataModules.Constants; using CSPracc.Managers; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; -using static CounterStrikeSharp.API.Core.BasePlugin; namespace CSPracc.EventHandler { diff --git a/CSPracc/Modes/PracticeMode/PracticeMode.cs b/CSPracc/Modes/PracticeMode/PracticeMode.cs index d2cb75f..25de090 100644 --- a/CSPracc/Modes/PracticeMode/PracticeMode.cs +++ b/CSPracc/Modes/PracticeMode/PracticeMode.cs @@ -1,17 +1,12 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Cvars; using CounterStrikeSharp.API.Modules.Utils; using CSPracc.CommandHandler; using CSPracc.DataModules; using CSPracc.DataModules.Constants; using CSPracc.EventHandler; using CSPracc.Managers; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using static CSPracc.DataModules.Enums; namespace CSPracc.Modes { @@ -212,7 +207,30 @@ public PracticeMode() : base() projectileManager = new ProjectileManager(); PracticeBotManager = new PracticeBotManager(); SpawnManager = new SpawnManager(); - BotReplayManager = new BotReplayManager(ref PracticeBotManager, ref projectileManager); + BotReplayManager = new BotReplayManager(ref PracticeBotManager, ref projectileManager); + } + + private static void UnprotectCommands() + { + // allow commands to be run on the server even if `sv_cheats false` + var unprotectCommands = new List + { + "sv_grenade_trajectory_prac_pipreview", + "sv_infinite_ammo", + "sv_showimpacts", + }; + + foreach (var conVar in unprotectCommands) + { + var c = ConVar.Find(conVar); + if (c == null) + { + continue; + } + + c.Flags &= ~ConVarFlags.FCVAR_CHEAT; + c.Flags &= ~ConVarFlags.FCVAR_PROTECTED; + } } public void StartTimer(CCSPlayerController player) @@ -247,7 +265,11 @@ public void ShowCompleteNadeMenu(CCSPlayerController player) public override void ConfigureEnvironment() { DataModules.Constants.Methods.MsgToServer("Loading practice mode."); + + UnprotectCommands(); + Server.ExecuteCommand("exec CSPRACC\\pracc.cfg"); + EventHandler?.Dispose(); EventHandler = new PracticeEventHandler(CSPraccPlugin.Instance!, new PracticeCommandHandler(this, ref projectileManager,ref PracticeBotManager, ref SpawnManager),ref projectileManager, ref PracticeBotManager); } diff --git a/CSPracc/PraccPlugin.cs b/CSPracc/PraccPlugin.cs index 1ff3543..bb7f74f 100644 --- a/CSPracc/PraccPlugin.cs +++ b/CSPracc/PraccPlugin.cs @@ -1,30 +1,11 @@ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Net.Http; - using System.Threading; - using System.Threading.Tasks; -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core.Attributes; -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.Menu; -using CounterStrikeSharp.API.Modules.Utils; using CSPracc; using CSPracc.DataModules; -using CSPracc.DataModules.Constants; -using System.Xml; -using System.Xml.Serialization; +using static CSPracc.DataModules.Enums; using CSPracc.Managers; -using System.Drawing; using CSPracc.Modes; -using static CSPracc.DataModules.Enums; -using System.Resources; using Microsoft.Extensions.Logging; [MinimumApiVersion(80)] diff --git a/CSPracc/Utils.cs b/CSPracc/Utils.cs index 9fd7fad..0aaab11 100644 --- a/CSPracc/Utils.cs +++ b/CSPracc/Utils.cs @@ -1,20 +1,15 @@ -using CounterStrikeSharp.API.Core; +using System.Drawing; using CounterStrikeSharp.API; -using CSPracc.DataModules; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Drawing; -using CounterStrikeSharp.API.Modules.Entities; -using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; -using System.Runtime.InteropServices; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Memory; namespace CSPracc { public class Utils { + // https://github.com/alliedmodders/hl2sdk/blob/cs2/game/shared/shareddefs.h#L509 + private const uint EFL_NOCLIP_ACTIVE = (((uint)1) << 2); + /// /// Deleting Grenades from server /// @@ -92,15 +87,50 @@ public static void RemoveGrenadeEntities() } } } - - public static void RemoveNoClip(CCSPlayerController player) + + public static void ToggleNoClip(CCSPlayerController? player) { - if (player == null || !player.IsValid) return; + if (player == null || !player.IsValid || player.PlayerPawn.Value == null) + { + return; + } + if (player.PlayerPawn.Value.MoveType == MoveType_t.MOVETYPE_NOCLIP) + { + RemoveNoClip(player); + return; + } - if (player.PlayerPawn.Value!.MoveType == MoveType_t.MOVETYPE_NOCLIP) + player.PlayerPawn.Value.Flags |= EFL_NOCLIP_ACTIVE; + player.PlayerPawn.Value.MoveType = MoveType_t.MOVETYPE_NOCLIP; + + Schema.SetSchemaValue(player.PlayerPawn.Value.Handle, "CBaseEntity", "m_nActualMoveType", 8); // 7? + + Utilities.SetStateChanged(player.PlayerPawn.Value, "CBaseEntity", "m_MoveType"); + } + + public static void RemoveNoClip(CCSPlayerController? player) + { + if (player == null || !player.IsValid || player.PlayerPawn.Value == null) + { + return; + } + if (player.PlayerPawn.Value.MoveType == MoveType_t.MOVETYPE_WALK) { - player.PlayerPawn.Value.MoveType = MoveType_t.MOVETYPE_WALK; + return; } + + player.PlayerPawn.Value.Flags &= ~EFL_NOCLIP_ACTIVE; + player.PlayerPawn.Value.MoveType = MoveType_t.MOVETYPE_WALK; + + Schema.SetSchemaValue(player.PlayerPawn.Value.Handle, "CBaseEntity", "m_nActualMoveType", 2); + + Utilities.SetStateChanged(player.PlayerPawn.Value, "CBaseEntity", "m_MoveType"); + + // maybe needs calling?: + // NoClipStateChanged + // https://github.com/alliedmodders/hl2sdk/blob/cs2/game/server/player.h#L406 + // see: + // https://github.com/alliedmodders/hl2sdk/blob/cs2/game/server/client.cpp#L1207-L1286 } public static void ServerMessage(string message)