mirror of
https://github.com/roflmuffin/CounterStrikeSharp.git
synced 2025-12-07 16:36:35 -08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a6cdf0da3 | ||
|
|
a5399dd5fe | ||
|
|
ab996c34e9 |
5
docfx/examples/WithFakeConvars.md
Normal file
5
docfx/examples/WithFakeConvars.md
Normal file
@@ -0,0 +1,5 @@
|
||||
[!INCLUDE [WithFakeConvars](../../examples/WithFakeConvars/README.md)]
|
||||
|
||||
<a href="https://github.com/roflmuffin/CounterStrikeSharp/tree/main/examples/WithFakeConvars" class="btn btn-secondary">View project on Github <i class="bi bi-github"></i></a>
|
||||
|
||||
[!code-csharp[](../../examples/WithFakeConvars/WithFakeConvarsPlugin.cs)]
|
||||
@@ -3,6 +3,8 @@ items:
|
||||
href: HelloWorld.md
|
||||
- name: Commands
|
||||
href: WithCommands.md
|
||||
- name: Fake ConVars
|
||||
href: WithFakeConvars.md
|
||||
- name: Config
|
||||
href: WithConfig.md
|
||||
- name: Dependency Injection
|
||||
|
||||
9
examples/WithFakeConvars/ConVars.cs
Normal file
9
examples/WithFakeConvars/ConVars.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
|
||||
namespace WithFakeConvars;
|
||||
|
||||
public static class ConVars
|
||||
{
|
||||
// This convar is registered from the plugin instance but can be used anywhere.
|
||||
public static FakeConVar<int> ExampleStaticCvar = new("example_static", "An example static cvar");
|
||||
}
|
||||
21
examples/WithFakeConvars/EvenNumberValidator.cs
Normal file
21
examples/WithFakeConvars/EvenNumberValidator.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
|
||||
namespace WithFakeConvars;
|
||||
|
||||
// This is an example of a custom validator that checks if a number is even.
|
||||
public class EvenNumberValidator : IValidator<int>
|
||||
{
|
||||
public bool Validate(int value, out string? errorMessage)
|
||||
{
|
||||
if (value % 2 == 0)
|
||||
{
|
||||
errorMessage = null;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = "Value must be an even number";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
examples/WithFakeConvars/README.md
Normal file
2
examples/WithFakeConvars/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# With Fake Convars
|
||||
This is an example that shows how to register "fake" convars, which are actually console commands that track their internal state.
|
||||
12
examples/WithFakeConvars/WithFakeConvars.csproj
Normal file
12
examples/WithFakeConvars/WithFakeConvars.csproj
Normal file
@@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\managed\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
58
examples/WithFakeConvars/WithFakeConvarsPlugin.cs
Normal file
58
examples/WithFakeConvars/WithFakeConvarsPlugin.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
|
||||
namespace WithFakeConvars;
|
||||
|
||||
[MinimumApiVersion(168)]
|
||||
public class WithFakeConvarsPlugin : BasePlugin
|
||||
{
|
||||
public override string ModuleName => "Example: With Fake Convars";
|
||||
public override string ModuleVersion => "1.0.0";
|
||||
public override string ModuleAuthor => "CounterStrikeSharp & Contributors";
|
||||
public override string ModuleDescription => "A simple plugin that registers some console variables";
|
||||
|
||||
// FakeConVar is a class that can be used to create custom console variables.
|
||||
// You can specify a name, description, default value, and custom validators.
|
||||
public FakeConVar<bool> BoolCvar = new("example_bool", "An example boolean cvar", true);
|
||||
|
||||
// Range validator is an inbuilt validator that can be used to ensure that a value is within a certain range.
|
||||
public FakeConVar<int> ExampleIntCvar = new("example_int", "An example integer cvar", 10, flags: ConVarFlags.FCVAR_NONE, new RangeValidator<int>(0, 100));
|
||||
|
||||
public FakeConVar<float> ExampleFloatCvar = new("example_float", "An example float cvar", 10, flags: ConVarFlags.FCVAR_NONE, new RangeValidator<float>(5, 20));
|
||||
public FakeConVar<string> ExampleStringCvar = new("example_string", "An example string cvar", "default");
|
||||
|
||||
// Replicated, Cheat & Protected flags are supported.
|
||||
public FakeConVar<float> ExamplePublicCvar = new("example_public_float", "An example public float cvar", 5,
|
||||
ConVarFlags.FCVAR_REPLICATED);
|
||||
|
||||
// Can only be changed if sv_cheats is enabled.
|
||||
public FakeConVar<float> ExampleCheatCvar = new("example_cheat_float", "An example cheat float cvar", 5,
|
||||
ConVarFlags.FCVAR_CHEAT);
|
||||
|
||||
// Protected cvars do not output their value when queried.
|
||||
public FakeConVar<float> ExampleProtectedCvar = new("example_protected_float", "An example cheat float cvar", 5,
|
||||
ConVarFlags.FCVAR_PROTECTED);
|
||||
|
||||
// You can create your own custom validators by implementing the IValidator interface.
|
||||
public FakeConVar<int> ExampleEvenNumberCvar = new("example_even_number", "An example even number cvar", 0, flags: ConVarFlags.FCVAR_NONE, new EvenNumberValidator());
|
||||
|
||||
public FakeConVar<int> RequiresRestartCvar = new("example_requires_restart", "A cvar that requires a restart when changed");
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
// You can subscribe to the ValueChanged event to execute code when the value of a cvar changes.
|
||||
// In this example, we restart the game when the value of RequiresRestartCvar is greater than 5.
|
||||
RequiresRestartCvar.ValueChanged += (sender, value) =>
|
||||
{
|
||||
if (value > 5)
|
||||
{
|
||||
Server.ExecuteCommand("mp_restartgame 1");
|
||||
}
|
||||
};
|
||||
|
||||
RegisterFakeConVars(typeof(ConVars));
|
||||
}
|
||||
}
|
||||
@@ -427,6 +427,18 @@
|
||||
<Left>.\ApiCompat\v151.dll</Left>
|
||||
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0002</DiagnosticId>
|
||||
<Target>M:CounterStrikeSharp.API.Core.NativeAPI.QueueTaskForNextFrame(System.IntPtr)</Target>
|
||||
<Left>.\ApiCompat\v151.dll</Left>
|
||||
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0002</DiagnosticId>
|
||||
<Target>M:CounterStrikeSharp.API.Core.NativeAPI.QueueTaskForNextWorldUpdate(System.IntPtr)</Target>
|
||||
<Left>.\ApiCompat\v151.dll</Left>
|
||||
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0002</DiagnosticId>
|
||||
<Target>M:CounterStrikeSharp.API.Core.Plugin.Host.PluginManager.#ctor(CounterStrikeSharp.API.Core.Hosting.IScriptHostConfiguration,Microsoft.Extensions.Logging.ILogger{CounterStrikeSharp.API.Core.Plugin.Host.PluginManager},System.IServiceProvider)</Target>
|
||||
@@ -1039,4 +1051,16 @@
|
||||
<Left>.\ApiCompat\v151.dll</Left>
|
||||
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP1002</DiagnosticId>
|
||||
<Target>Microsoft.Extensions.Localization.Abstractions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60</Target>
|
||||
<Left>.\ApiCompat\v151.dll</Left>
|
||||
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP1002</DiagnosticId>
|
||||
<Target>Serilog, Version=2.0.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10</Target>
|
||||
<Left>.\ApiCompat\v151.dll</Left>
|
||||
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
|
||||
</Suppression>
|
||||
</Suppressions>
|
||||
@@ -23,6 +23,7 @@ namespace CounterStrikeSharp.API
|
||||
[Flags]
|
||||
public enum ConVarFlags : Int64
|
||||
{
|
||||
FCVAR_NONE = 0,
|
||||
FCVAR_LINKED_CONCOMMAND = (1 << 0),
|
||||
|
||||
FCVAR_DEVELOPMENTONLY =
|
||||
|
||||
@@ -502,20 +502,20 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
public static void QueueTaskForNextFrame(IntPtr callback){
|
||||
public static void QueueTaskForNextFrame(InputArgument callback){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(callback);
|
||||
ScriptContext.GlobalScriptContext.Push((InputArgument)callback);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0x9FE394D8);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
}
|
||||
}
|
||||
|
||||
public static void QueueTaskForNextWorldUpdate(IntPtr callback){
|
||||
public static void QueueTaskForNextWorldUpdate(InputArgument callback){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(callback);
|
||||
ScriptContext.GlobalScriptContext.Push((InputArgument)callback);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0xAD51A0C9);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
|
||||
@@ -28,6 +28,7 @@ using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Events;
|
||||
using CounterStrikeSharp.API.Modules.Timers;
|
||||
using CounterStrikeSharp.API.Modules.Config;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -308,6 +309,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
this.RegisterAttributeHandlers(instance);
|
||||
this.RegisterConsoleCommandAttributeHandlers(instance);
|
||||
this.RegisterEntityOutputAttributeHandlers(instance);
|
||||
this.RegisterFakeConVars(instance);
|
||||
}
|
||||
|
||||
public void InitializeConfig(object instance, Type pluginType)
|
||||
@@ -410,6 +412,43 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterFakeConVars(Type type, object instance = null)
|
||||
{
|
||||
var convars = type
|
||||
.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
|
||||
.Where(prop => prop.FieldType.IsGenericType &&
|
||||
prop.FieldType.GetGenericTypeDefinition() == typeof(FakeConVar<>));
|
||||
|
||||
foreach (var prop in convars)
|
||||
{
|
||||
object propValue = prop.GetValue(instance); // FakeConvar<?> instance
|
||||
var propValueType = prop.FieldType.GenericTypeArguments[0];
|
||||
var name = prop.FieldType.GetProperty("Name", BindingFlags.Public | BindingFlags.Instance)
|
||||
.GetValue(propValue);
|
||||
|
||||
var description = prop.FieldType.GetProperty("Description", BindingFlags.Public | BindingFlags.Instance)
|
||||
.GetValue(propValue);
|
||||
|
||||
MethodInfo executeCommandMethod = prop.FieldType
|
||||
.GetMethod("ExecuteCommand", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
this.AddCommand((string)name, (string) description, (caller, command) =>
|
||||
{
|
||||
executeCommandMethod.Invoke(propValue, new object[] {caller, command});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to bind a fake ConVar to a plugin command. Only required for ConVars that are not public properties of the plugin class.
|
||||
/// </summary>
|
||||
/// <param name="convar"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public void RegisterFakeConVars(object instance)
|
||||
{
|
||||
RegisterFakeConVars(instance.GetType(), instance);
|
||||
}
|
||||
|
||||
public void HookEntityOutput(string classname, string outputName, EntityIO.EntityOutputHandler handler, HookMode mode = HookMode.Pre)
|
||||
{
|
||||
var subscriber = new CallbackSubscriber(handler, handler,
|
||||
|
||||
@@ -22,9 +22,22 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the lifetime of a function reference.
|
||||
/// </summary>
|
||||
public enum FunctionLifetime
|
||||
{
|
||||
/// <summary>Delegate will be removed after the first invocation.</summary>
|
||||
SingleUse,
|
||||
/// <summary>Delegate will remain in memory for the lifetime of the application.</summary>
|
||||
Permanent
|
||||
}
|
||||
|
||||
public class FunctionReference
|
||||
{
|
||||
private readonly Delegate m_method;
|
||||
|
||||
public FunctionLifetime Lifetime { get; set; } = FunctionLifetime.Permanent;
|
||||
|
||||
public unsafe delegate void CallbackDelegate(fxScriptContext* context);
|
||||
private CallbackDelegate s_callback;
|
||||
@@ -59,12 +72,13 @@ namespace CounterStrikeSharp.API.Core
|
||||
if (typeof(NativeObject).IsAssignableFrom(param.ParameterType))
|
||||
{
|
||||
obj = Activator.CreateInstance(param.ParameterType,
|
||||
new[] {scriptContext.GetArgument(typeof(IntPtr), i)});
|
||||
new[] { scriptContext.GetArgument(typeof(IntPtr), i) });
|
||||
}
|
||||
else
|
||||
{
|
||||
obj = scriptContext.GetArgument(param.ParameterType, i);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}).ToArray();
|
||||
|
||||
@@ -79,6 +93,15 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
Application.Instance.Logger.LogError(e, "Error invoking callback");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Lifetime == FunctionLifetime.SingleUse)
|
||||
{
|
||||
Remove(Identifier);
|
||||
if (references.ContainsKey(m_method))
|
||||
references.Remove(m_method);
|
||||
}
|
||||
}
|
||||
});
|
||||
s_callback = dg;
|
||||
}
|
||||
@@ -98,7 +121,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
var referenceId = Register(reference);
|
||||
|
||||
reference.Identifier = referenceId;
|
||||
|
||||
|
||||
return reference;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
@@ -6,14 +7,20 @@ namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CBaseEntity
|
||||
{
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void Teleport(Vector position, QAngle angles, Vector velocity)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunction.CreateVoid<IntPtr, IntPtr, IntPtr, IntPtr>(Handle, GameData.GetOffset("CBaseEntity_Teleport"))(
|
||||
Handle, position.Handle, angles.Handle, velocity.Handle);
|
||||
}
|
||||
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void DispatchSpawn()
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunctions.CBaseEntity_DispatchSpawn(Handle, IntPtr.Zero);
|
||||
}
|
||||
|
||||
@@ -25,7 +32,13 @@ public partial class CBaseEntity
|
||||
/// <summary>
|
||||
/// Shorthand for accessing an entity's CBodyComponent?.SceneNode?.AbsRotation;
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public QAngle? AbsRotation => CBodyComponent?.SceneNode?.AbsRotation;
|
||||
|
||||
public T? GetVData<T>() where T : CEntitySubclassVDataBase => (T)Activator.CreateInstance(typeof(T), Marshal.ReadIntPtr(SubclassID.Handle + 4));
|
||||
public T? GetVData<T>() where T : CEntitySubclassVDataBase
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
return (T) Activator.CreateInstance(typeof(T), Marshal.ReadIntPtr(SubclassID.Handle + 4));
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,11 @@ namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CBaseModelEntity
|
||||
{
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void SetModel(string model)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunctions.SetModel(Handle, model);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
@@ -10,10 +9,13 @@ namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CBasePlayerController
|
||||
{
|
||||
public void SetPawn(CBasePlayerPawn? pawn)
|
||||
{
|
||||
if (pawn is null) return;
|
||||
if (!pawn.IsValid) return;
|
||||
VirtualFunctions.CBasePlayerController_SetPawnFunc.Invoke(this, pawn, true, false);
|
||||
}
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void SetPawn(CBasePlayerPawn? pawn)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
if (pawn is null) return;
|
||||
if (!pawn.IsValid) return;
|
||||
VirtualFunctions.CBasePlayerController_SetPawnFunc.Invoke(this, pawn, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,17 +10,24 @@ public partial class CBasePlayerPawn
|
||||
/// </summary>
|
||||
/// <param name="explode"></param>
|
||||
/// <param name="force"></param>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void CommitSuicide(bool explode, bool force)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunction.CreateVoid<IntPtr, bool, bool>(Handle, GameData.GetOffset("CBasePlayerPawn_CommitSuicide"))(Handle, explode, force);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Remove Player Item
|
||||
/// </summary>
|
||||
/// <param name="weapon"></param>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void RemovePlayerItem(CBasePlayerWeapon weapon)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
Guard.IsValidEntity(weapon);
|
||||
|
||||
VirtualFunctions.RemovePlayerItemVirtual(Handle, weapon.Handle);
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,11 @@ public partial class CCSPlayerController
|
||||
public int? UserId => NativeAPI.GetUseridFromIndex((int)Index);
|
||||
public CsTeam Team => (CsTeam)TeamNum;
|
||||
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public IntPtr GiveNamedItem(string item)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
if (!PlayerPawn.IsValid) return 0;
|
||||
if (PlayerPawn.Value == null) return 0;
|
||||
if (!PlayerPawn.Value.IsValid) return 0;
|
||||
@@ -34,25 +37,37 @@ public partial class CCSPlayerController
|
||||
return GiveNamedItem(itemString);
|
||||
}
|
||||
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void PrintToConsole(string message)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
NativeAPI.PrintToConsole((int)Index, $"{message}\n\0");
|
||||
}
|
||||
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void PrintToChat(string message)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunctions.ClientPrint(Handle, HudDestination.Chat, message, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void PrintToCenter(string message)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunctions.ClientPrint(Handle, HudDestination.Center, message, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
public void PrintToCenterHtml(string message) => PrintToCenterHtml(message, 5);
|
||||
|
||||
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void PrintToCenterHtml(string message, int duration)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
var @event = new EventShowSurvivalRespawnStatus(true)
|
||||
{
|
||||
LocToken = message,
|
||||
@@ -65,8 +80,10 @@ public partial class CCSPlayerController
|
||||
/// <summary>
|
||||
/// Drops the active player weapon on the ground.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void DropActiveWeapon()
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
if (!PlayerPawn.IsValid) return;
|
||||
if (PlayerPawn.Value == null) return;
|
||||
if (!PlayerPawn.Value.IsValid) return;
|
||||
@@ -83,8 +100,10 @@ public partial class CCSPlayerController
|
||||
/// <summary>
|
||||
/// Removes every weapon from the player.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void RemoveWeapons()
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
if (!PlayerPawn.IsValid) return;
|
||||
if (PlayerPawn.Value == null) return;
|
||||
if (!PlayerPawn.Value.IsValid) return;
|
||||
@@ -99,8 +118,10 @@ public partial class CCSPlayerController
|
||||
/// </summary>
|
||||
/// <param name="explode"></param>
|
||||
/// <param name="force"></param>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void CommitSuicide(bool explode, bool force)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
if (!PlayerPawn.IsValid) return;
|
||||
if (PlayerPawn.Value == null) return;
|
||||
if (!PlayerPawn.Value.IsValid) return;
|
||||
@@ -111,8 +132,10 @@ public partial class CCSPlayerController
|
||||
/// <summary>
|
||||
/// Respawn player
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void Respawn()
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
if (!PlayerPawn.IsValid) return;
|
||||
if (PlayerPawn.Value == null) return;
|
||||
if (!PlayerPawn.Value.IsValid) return;
|
||||
@@ -128,8 +151,11 @@ public partial class CCSPlayerController
|
||||
/// Forcibly switches the team of the player, the player will remain alive and keep their weapons.
|
||||
/// </summary>
|
||||
/// <param name="team">The team to switch to</param>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void SwitchTeam(CsTeam team)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunctions.SwitchTeam(Handle, (byte)team);
|
||||
}
|
||||
|
||||
@@ -140,8 +166,11 @@ public partial class CCSPlayerController
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
/// <param name="team">The team to change to</param>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void ChangeTeam(CsTeam team)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunction.CreateVoid<IntPtr, CsTeam>(Handle, GameData.GetOffset("CCSPlayerController_ChangeTeam"))(Handle,
|
||||
team);
|
||||
}
|
||||
@@ -151,8 +180,11 @@ public partial class CCSPlayerController
|
||||
/// </summary>
|
||||
/// <param name="conVar">Name of the convar to retrieve</param>
|
||||
/// <returns>ConVar string value</returns>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public string GetConVarValue(string conVar)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
return NativeAPI.GetClientConvarValue(Slot, conVar);
|
||||
}
|
||||
|
||||
@@ -171,13 +203,12 @@ public partial class CCSPlayerController
|
||||
/// </summary>
|
||||
/// <param name="conVar">Console variable name</param>
|
||||
/// <param name="value">String value to set</param>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
/// <exception cref="InvalidOperationException">Player is not a bot</exception>
|
||||
public void SetFakeClientConVar(string conVar, string value)
|
||||
{
|
||||
if (!IsBot)
|
||||
{
|
||||
throw new InvalidOperationException("'SetFakeClientConVar' can only be called for fake clients (bots)");
|
||||
}
|
||||
Guard.IsValidEntity(this);
|
||||
if (!IsBot) throw new InvalidOperationException("'SetFakeClientConVar' can only be called for fake clients (bots)");
|
||||
|
||||
NativeAPI.SetFakeClientConvarValue(Slot, conVar, value);
|
||||
}
|
||||
@@ -207,27 +238,45 @@ public partial class CCSPlayerController
|
||||
/// Note: Only works for some commands, marked with the FCVAR_CLIENT_CAN_EXECUTE flag (not many).
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
public void ExecuteClientCommand(string command) => NativeAPI.IssueClientCommand(Slot, command);
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void ExecuteClientCommand(string command)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
NativeAPI.IssueClientCommand(Slot, command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Issue the specified command directly from the server (mimics the server executing the command with the given player context).
|
||||
/// <remarks>Works with server commands like `kill`, `explode`, `noclip`, etc. </remarks>
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
public void ExecuteClientCommandFromServer(string command) => NativeAPI.IssueClientCommandFromServer(Slot, command);
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void ExecuteClientCommandFromServer(string command)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
NativeAPI.IssueClientCommandFromServer(Slot, command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides who a player can hear in voice chat.
|
||||
/// </summary>
|
||||
/// <param name="sender">Player talking in the voice chat</param>
|
||||
/// <param name="override">Whether the talker should be heard</param>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void SetListenOverride(CCSPlayerController sender, ListenOverride @override)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
NativeAPI.SetClientListening(Handle, sender.Handle, (Byte)@override);
|
||||
}
|
||||
|
||||
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public ListenOverride GetListenOverride(CCSPlayerController sender)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
return NativeAPI.GetClientListening(Handle, sender.Handle);
|
||||
}
|
||||
|
||||
@@ -236,27 +285,31 @@ public partial class CCSPlayerController
|
||||
/// <summary>
|
||||
/// Returns the authorized SteamID of this user which has been validated with the SteamAPI.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public SteamID? AuthorizedSteamID
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsValid) return null;
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
var authorizedSteamId = NativeAPI.GetPlayerAuthorizedSteamid(Slot);
|
||||
if ((long)authorizedSteamId == -1) return null;
|
||||
|
||||
return (SteamID)authorizedSteamId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the IP address (and possibly port) of this player.
|
||||
/// <remarks>Returns 127.0.0.1 if the player is a bot.</remarks>
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public string? IpAddress
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsValid) return null;
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
var ipAddress = NativeAPI.GetPlayerIpAddress(Slot);
|
||||
if (string.IsNullOrWhiteSpace(ipAddress)) return null;
|
||||
|
||||
@@ -267,9 +320,20 @@ public partial class CCSPlayerController
|
||||
/// <summary>
|
||||
/// Determines how the player interacts with voice chat.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public VoiceFlags VoiceFlags
|
||||
{
|
||||
get => (VoiceFlags)NativeAPI.GetClientVoiceFlags(Handle);
|
||||
set => NativeAPI.SetClientVoiceFlags(Handle, (Byte)value);
|
||||
get
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
return (VoiceFlags)NativeAPI.GetClientVoiceFlags(Handle);
|
||||
}
|
||||
set
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
NativeAPI.SetClientVoiceFlags(Handle, (Byte)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,16 +23,26 @@ public partial class CCSPlayer_ItemServices
|
||||
/// <summary>
|
||||
/// Drops the active player weapon on the ground.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">ItemServices points to null</exception>
|
||||
public void DropActivePlayerWeapon(CBasePlayerWeapon activeWeapon)
|
||||
{
|
||||
if(Handle == IntPtr.Zero)
|
||||
throw new InvalidOperationException("ItemServices points to null.");
|
||||
|
||||
Guard.IsValidEntity(activeWeapon);
|
||||
|
||||
VirtualFunction.CreateVoid<nint, nint>(Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon"))(Handle, activeWeapon.Handle);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Removes every weapon from the player.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">ItemServices points to null</exception>
|
||||
public void RemoveWeapons()
|
||||
{
|
||||
if (Handle == IntPtr.Zero)
|
||||
throw new InvalidOperationException("ItemServices points to null.");
|
||||
|
||||
VirtualFunction.CreateVoid<nint>(Handle, GameData.GetOffset("CCSPlayer_ItemServices_RemoveWeapons"))(Handle);
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,12 @@ public partial class CEntityInstance : IEquatable<CEntityInstance>
|
||||
|
||||
public string DesignerName => IsValid ? Entity?.DesignerName : null;
|
||||
|
||||
public void Remove() => VirtualFunctions.UTIL_Remove(this.Handle);
|
||||
public void Remove()
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunctions.UTIL_Remove(this.Handle);
|
||||
}
|
||||
|
||||
public bool Equals(CEntityInstance? other)
|
||||
{
|
||||
@@ -58,7 +63,7 @@ public partial class CEntityInstance : IEquatable<CEntityInstance>
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calls a named input method on an entity.
|
||||
/// <example>
|
||||
@@ -72,8 +77,11 @@ public partial class CEntityInstance : IEquatable<CEntityInstance>
|
||||
/// <param name="caller">Entity that is sending the event, <see langword="null"/> for no entity</param>
|
||||
/// <param name="value">String variant value to send with the event</param>
|
||||
/// <param name="outputId">Unknown, defaults to 0</param>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void AcceptInput(string inputName, CEntityInstance? activator = null, CEntityInstance? caller = null, string value = "", int outputId = 0)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunctions.AcceptInput(Handle, inputName, activator?.Handle ?? IntPtr.Zero, caller?.Handle ?? IntPtr.Zero, value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,11 @@ public partial class CGameSceneNode
|
||||
/// <summary>
|
||||
/// Gets the <see cref="CSkeletonInstance"/> instance from the node.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">GameSceneNode points to null</exception>
|
||||
public CSkeletonInstance GetSkeletonInstance()
|
||||
{
|
||||
if (Handle == IntPtr.Zero) throw new InvalidOperationException("GameSceneNode points to null.");
|
||||
|
||||
return new CSkeletonInstance(VirtualFunction.Create<nint, nint>(Handle, GameData.GetOffset("CGameSceneNode_GetSkeletonInstance"))(Handle));
|
||||
}
|
||||
}
|
||||
11
managed/CounterStrikeSharp.API/Guard.cs
Normal file
11
managed/CounterStrikeSharp.API/Guard.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace CounterStrikeSharp.API
|
||||
{
|
||||
public static class Guard
|
||||
{
|
||||
public static void IsValidEntity(CEntityInstance ent)
|
||||
{
|
||||
if (!ent.IsValid)
|
||||
throw new InvalidOperationException("Entity is not valid");
|
||||
}
|
||||
}
|
||||
}
|
||||
140
managed/CounterStrikeSharp.API/Modules/Cvars/FakeConVar.cs
Normal file
140
managed/CounterStrikeSharp.API/Modules/Cvars/FakeConVar.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Cvars;
|
||||
|
||||
public class FakeConVar<T> where T : IComparable<T>
|
||||
{
|
||||
private readonly IEnumerable<IValidator<T>>? _customValidators;
|
||||
|
||||
public FakeConVar(string name, string description, T defaultValue = default(T), ConVarFlags flags = ConVarFlags.FCVAR_NONE,
|
||||
params IValidator<T>[] customValidators)
|
||||
{
|
||||
_customValidators = customValidators;
|
||||
Name = name;
|
||||
Description = description;
|
||||
Value = defaultValue;
|
||||
Flags = flags;
|
||||
}
|
||||
|
||||
public ConVarFlags Flags { get; set; }
|
||||
|
||||
public string Name { get; }
|
||||
public string Description { get; }
|
||||
|
||||
public event EventHandler<T> ValueChanged;
|
||||
|
||||
private T _value;
|
||||
|
||||
public T Value
|
||||
{
|
||||
get => _value;
|
||||
set => SetValue(value);
|
||||
}
|
||||
|
||||
internal void ExecuteCommand(CCSPlayerController? player, CommandInfo args)
|
||||
{
|
||||
if (player != null && !Flags.HasFlag(ConVarFlags.FCVAR_REPLICATED))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.ArgCount < 2)
|
||||
{
|
||||
if (Flags.HasFlag(ConVarFlags.FCVAR_PROTECTED) && player != null)
|
||||
{
|
||||
args.ReplyToCommand($"{args.GetArg(0)} = <protected>");
|
||||
}
|
||||
else
|
||||
{
|
||||
args.ReplyToCommand($"{args.GetArg(0)} = {Value.ToString()}");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Flags.HasFlag(ConVarFlags.FCVAR_CHEAT))
|
||||
{
|
||||
var cheats = ConVar.Find("sv_cheats")!.GetPrimitiveValue<bool>();
|
||||
if (!cheats)
|
||||
{
|
||||
args.ReplyToCommand($"SV: Convar '{Name}' is cheat protected, change ignored");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (player != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// TODO(dotnet8): Replace with IParsable<T>
|
||||
bool success = true;
|
||||
T parsedValue = default(T);
|
||||
TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
|
||||
if (converter.CanConvertFrom(typeof(string)))
|
||||
{
|
||||
try
|
||||
{
|
||||
parsedValue = (T)converter.ConvertFromString(args.ArgString);
|
||||
}
|
||||
catch
|
||||
{
|
||||
success = typeof(T) == typeof(bool) && TryConvertCustomBoolean(args.ArgString, out parsedValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
args.ReplyToCommand($"Error: String '{args.GetArg(1)}' can't be converted to {typeof(T).Name}");
|
||||
args.ReplyToCommand($"Failed to parse input ConVar '{Name}' from string '{args.GetArg(1)}'");
|
||||
return;
|
||||
}
|
||||
|
||||
SetValue(parsedValue);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
args.ReplyToCommand($"Error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryConvertCustomBoolean(string input, out T result)
|
||||
{
|
||||
input = input.Trim().ToLowerInvariant();
|
||||
if (input == "1" || input == "true")
|
||||
{
|
||||
result = (T)(object)true;
|
||||
return true;
|
||||
}
|
||||
else if (input == "0" || input == "false")
|
||||
{
|
||||
result = (T)(object)false;
|
||||
return true;
|
||||
}
|
||||
|
||||
result = default(T);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetValue(T value)
|
||||
{
|
||||
if (_customValidators != null)
|
||||
{
|
||||
foreach (var validator in _customValidators)
|
||||
{
|
||||
if (!validator.Validate(value, out var error))
|
||||
{
|
||||
throw new ArgumentException($"{error ?? "Invalid value provided"}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_value = value;
|
||||
ValueChanged?.Invoke(this, _value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
|
||||
public interface IValidator<in T>
|
||||
{
|
||||
bool Validate(T value, out string? errorMessage);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
|
||||
public class RangeValidator<T> : IValidator<T> where T : IComparable<T>
|
||||
{
|
||||
private readonly T _min;
|
||||
private readonly T _max;
|
||||
|
||||
public RangeValidator(T min, T max)
|
||||
{
|
||||
_min = min;
|
||||
_max = max;
|
||||
}
|
||||
|
||||
public bool Validate(T value, out string? errorMessage)
|
||||
{
|
||||
if (value.CompareTo(_min) >= 0 && value.CompareTo(_max) <= 0)
|
||||
{
|
||||
errorMessage = null;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = $"Value must be between {_min} and {_max}";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
@@ -78,11 +79,15 @@ public class Schema
|
||||
|
||||
public static T GetSchemaValue<T>(IntPtr handle, string className, string propertyName)
|
||||
{
|
||||
if (handle == IntPtr.Zero) throw new ArgumentNullException(nameof(handle), "Schema target points to null.");
|
||||
|
||||
return NativeAPI.GetSchemaValueByName<T>(handle, (int)typeof(T).ToDataType(), className, propertyName);
|
||||
}
|
||||
|
||||
public static void SetSchemaValue<T>(IntPtr handle, string className, string propertyName, T value)
|
||||
{
|
||||
if (handle == IntPtr.Zero) throw new ArgumentNullException(nameof(handle), "Schema target points to null.");
|
||||
|
||||
if (CoreConfig.FollowCS2ServerGuidelines && _cs2BadList.Contains(propertyName))
|
||||
{
|
||||
throw new Exception($"Cannot set or get '{className}::{propertyName}' with \"FollowCS2ServerGuidelines\" option enabled.");
|
||||
@@ -93,11 +98,15 @@ public class Schema
|
||||
|
||||
public static T GetDeclaredClass<T>(IntPtr pointer, string className, string memberName)
|
||||
{
|
||||
if (pointer == IntPtr.Zero) throw new ArgumentNullException(nameof(pointer), "Schema target points to null.");
|
||||
|
||||
return (T)Activator.CreateInstance(typeof(T), pointer + GetSchemaOffset(className, memberName));
|
||||
}
|
||||
|
||||
public static unsafe ref T GetRef<T>(IntPtr pointer, string className, string memberName)
|
||||
{
|
||||
if (pointer == IntPtr.Zero) throw new ArgumentNullException(nameof(pointer), "Schema target points to null.");
|
||||
|
||||
return ref Unsafe.AsRef<T>((void*)(pointer + GetSchemaOffset(className, memberName)));
|
||||
}
|
||||
|
||||
@@ -114,6 +123,8 @@ public class Schema
|
||||
|
||||
public static T GetPointer<T>(IntPtr pointer, string className, string memberName)
|
||||
{
|
||||
if (pointer == IntPtr.Zero) throw new ArgumentNullException(nameof(pointer), "Schema target points to null.");
|
||||
|
||||
var pointerTo = Marshal.ReadIntPtr(pointer + GetSchemaOffset(className, memberName));
|
||||
if (pointerTo == IntPtr.Zero)
|
||||
{
|
||||
@@ -125,6 +136,8 @@ public class Schema
|
||||
|
||||
public static unsafe Span<T> GetFixedArray<T>(IntPtr pointer, string className, string memberName, int count)
|
||||
{
|
||||
if (pointer == IntPtr.Zero) throw new ArgumentNullException(nameof(pointer), "Schema target points to null.");
|
||||
|
||||
Span<T> span = new((void*)(pointer + GetSchemaOffset(className, memberName)), count);
|
||||
return span;
|
||||
}
|
||||
|
||||
@@ -40,10 +40,6 @@ namespace CounterStrikeSharp.API
|
||||
public static float GameFrameTime => NativeAPI.GetGameFrameTime();
|
||||
public static double EngineTime => NativeAPI.GetEngineTime();
|
||||
public static void PrecacheModel(string name) => NativeAPI.PrecacheModel(name);
|
||||
// public static void PrecacheSound(string name) => Sound.PrecacheSound(name);
|
||||
|
||||
// Currently only used to keep the delegate from being garbage collected
|
||||
private static List<Action> nextFrameTasks = new List<Action>();
|
||||
|
||||
/// <summary>
|
||||
/// Queue a task to be executed on the next game frame.
|
||||
@@ -51,9 +47,9 @@ namespace CounterStrikeSharp.API
|
||||
/// </summary>
|
||||
public static void NextFrame(Action task)
|
||||
{
|
||||
nextFrameTasks.Add(task);
|
||||
var ptr = Marshal.GetFunctionPointerForDelegate(task);
|
||||
NativeAPI.QueueTaskForNextFrame(ptr);
|
||||
var functionReference = FunctionReference.Create(task);
|
||||
functionReference.Lifetime = FunctionLifetime.SingleUse;
|
||||
NativeAPI.QueueTaskForNextFrame(functionReference);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -63,9 +59,9 @@ namespace CounterStrikeSharp.API
|
||||
/// <param name="task"></param>
|
||||
public static void NextWorldUpdate(Action task)
|
||||
{
|
||||
nextFrameTasks.Add(task);
|
||||
var ptr = Marshal.GetFunctionPointerForDelegate(task);
|
||||
NativeAPI.QueueTaskForNextWorldUpdate(ptr);
|
||||
var functionReference = FunctionReference.Create(task);
|
||||
functionReference.Lifetime = FunctionLifetime.SingleUse;
|
||||
NativeAPI.QueueTaskForNextWorldUpdate(functionReference);
|
||||
}
|
||||
|
||||
public static void PrintToChatAll(string message)
|
||||
|
||||
@@ -209,8 +209,11 @@ namespace CounterStrikeSharp.API
|
||||
/// <param name="className" example="CBaseEntity">Schema field class name</param>
|
||||
/// <param name="fieldName" example="m_iHealth">Schema field name</param>
|
||||
/// <param name="extraOffset">Any additional offset to the schema field</param>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public static void SetStateChanged(CBaseEntity entity, string className, string fieldName, int extraOffset = 0)
|
||||
{
|
||||
Guard.IsValidEntity(entity);
|
||||
|
||||
if (!Schema.IsSchemaFieldNetworked(className, fieldName))
|
||||
{
|
||||
Application.Instance.Logger.LogWarning("Field {ClassName}:{FieldName} is not networked, but SetStateChanged was called on it.", className, fieldName);
|
||||
|
||||
@@ -32,6 +32,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithTranslations", "..\exam
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithVoiceOverrides", "..\examples\WithVoiceOverrides\WithVoiceOverrides.csproj", "{6FA3107D-42AF-42A0-BF51-2230D13268B5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithFakeConvars", "..\examples\WithFakeConvars\WithFakeConvars.csproj", "{1309954E-FAF7-47A5-9FF9-C7263B33E4E3}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -98,6 +100,10 @@ Global
|
||||
{6FA3107D-42AF-42A0-BF51-2230D13268B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6FA3107D-42AF-42A0-BF51-2230D13268B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6FA3107D-42AF-42A0-BF51-2230D13268B5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1309954E-FAF7-47A5-9FF9-C7263B33E4E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1309954E-FAF7-47A5-9FF9-C7263B33E4E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1309954E-FAF7-47A5-9FF9-C7263B33E4E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1309954E-FAF7-47A5-9FF9-C7263B33E4E3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{57E64289-5D69-4AA1-BEF0-D0D96A55EE8F} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
@@ -111,5 +117,6 @@ Global
|
||||
{31EABE0B-871F-497B-BF36-37FFC6FAD15F} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{BB44E08E-CCA8-4E22-A132-11B2F69D1890} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{6FA3107D-42AF-42A0-BF51-2230D13268B5} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{1309954E-FAF7-47A5-9FF9-C7263B33E4E3} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -21,8 +21,8 @@ TRACE_FILTER_PROXY_SET_TRACE_TYPE_CALLBACK: trace_filter:pointer, callback:point
|
||||
TRACE_FILTER_PROXY_SET_SHOULD_HIT_ENTITY_CALLBACK: trace_filter:pointer, callback:pointer -> void
|
||||
NEW_TRACE_RESULT: -> pointer
|
||||
GET_TICKED_TIME: -> double
|
||||
QUEUE_TASK_FOR_NEXT_FRAME: callback:pointer -> void
|
||||
QUEUE_TASK_FOR_NEXT_WORLD_UPDATE: callback:pointer -> void
|
||||
QUEUE_TASK_FOR_NEXT_FRAME: callback:func -> void
|
||||
QUEUE_TASK_FOR_NEXT_WORLD_UPDATE: callback:func -> void
|
||||
GET_VALVE_INTERFACE: interfaceType:int, interfaceName:string -> pointer
|
||||
GET_COMMAND_PARAM_VALUE: param:string, dataType:DataType_t, defaultValue:any -> any
|
||||
PRINT_TO_SERVER_CONSOLE: msg:string -> void
|
||||
@@ -29,6 +29,12 @@
|
||||
namespace counterstrikesharp {
|
||||
|
||||
CBaseEntity* GetEntityFromIndex(ScriptContext& script_context) {
|
||||
if (!globals::entitySystem)
|
||||
{
|
||||
script_context.ThrowNativeError("Entity system is not yet initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto entityIndex = script_context.GetArgument<int>(0);
|
||||
|
||||
return globals::entitySystem->GetBaseEntity(CEntityIndex(entityIndex));
|
||||
@@ -47,6 +53,11 @@ const char* GetDesignerName(ScriptContext& scriptContext) {
|
||||
}
|
||||
|
||||
void* GetEntityPointerFromHandle(ScriptContext& scriptContext) {
|
||||
if (!globals::entitySystem) {
|
||||
scriptContext.ThrowNativeError("Entity system is not yet initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto handle = scriptContext.GetArgument<CEntityHandle*>(0);
|
||||
|
||||
if (!handle->IsValid()) {
|
||||
@@ -57,6 +68,11 @@ void* GetEntityPointerFromHandle(ScriptContext& scriptContext) {
|
||||
}
|
||||
|
||||
void* GetEntityPointerFromRef(ScriptContext& scriptContext) {
|
||||
if (!globals::entitySystem) {
|
||||
scriptContext.ThrowNativeError("Entity system yet is not initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto ref = scriptContext.GetArgument<unsigned int>(0);
|
||||
|
||||
if (ref == INVALID_EHANDLE_INDEX) {
|
||||
@@ -87,6 +103,11 @@ unsigned int GetRefFromEntityPointer(ScriptContext& scriptContext) {
|
||||
}
|
||||
|
||||
bool IsRefValidEntity(ScriptContext& scriptContext) {
|
||||
if (!globals::entitySystem) {
|
||||
scriptContext.ThrowNativeError("Entity system yet is not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ref = scriptContext.GetArgument<unsigned int>(0);
|
||||
|
||||
if (ref == INVALID_EHANDLE_INDEX) {
|
||||
@@ -110,10 +131,20 @@ void PrintToConsole(ScriptContext& scriptContext) {
|
||||
}
|
||||
|
||||
CEntityIdentity* GetFirstActiveEntity(ScriptContext& script_context) {
|
||||
if (!globals::entitySystem) {
|
||||
script_context.ThrowNativeError("Entity system yet is not initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return globals::entitySystem->m_EntityList.m_pFirstActiveEntity;
|
||||
}
|
||||
|
||||
void* GetConcreteEntityListPointer(ScriptContext& script_context) {
|
||||
if (!globals::entitySystem) {
|
||||
script_context.ThrowNativeError("Entity system yet is not initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &globals::entitySystem->m_EntityList;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user