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)