mirror of
https://github.com/roflmuffin/CounterStrikeSharp.git
synced 2025-12-05 23:58:24 -08:00
feat: extract baseclass, automatic de-registration
This commit is contained in:
@@ -129,6 +129,8 @@ namespace CounterStrikeSharp.API.Core
|
||||
internal readonly Dictionary<Delegate, EntityIO.EntityOutputCallback> EntitySingleOutputHooks =
|
||||
new Dictionary<Delegate, EntityIO.EntityOutputCallback>();
|
||||
|
||||
internal readonly List<ConVarBase> ConVars = [];
|
||||
|
||||
public readonly List<CommandDefinition> CommandDefinitions = new List<CommandDefinition>();
|
||||
|
||||
public readonly List<Timer> Timers = new List<Timer>();
|
||||
@@ -367,6 +369,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
this.RegisterAttributeHandlers(instance);
|
||||
this.RegisterConsoleCommandAttributeHandlers(instance);
|
||||
this.RegisterEntityOutputAttributeHandlers(instance);
|
||||
this.RegisterConVars(instance);
|
||||
this.RegisterFakeConVars(instance);
|
||||
}
|
||||
|
||||
@@ -524,6 +527,19 @@ namespace CounterStrikeSharp.API.Core
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterConVars(Type type, object instance = null)
|
||||
{
|
||||
var convars = type
|
||||
.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
|
||||
.Where(prop => prop.FieldType.IsGenericType &&
|
||||
prop.FieldType.GetGenericTypeDefinition() == typeof(ConVar<>));
|
||||
|
||||
foreach (var prop in convars)
|
||||
{
|
||||
ConVars.Add(prop.GetValue(instance) as ConVarBase); // ConVar<?> instance
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to bind a fake ConVar to a plugin command. Only required for ConVars that are not public properties of the plugin class.
|
||||
@@ -535,6 +551,10 @@ namespace CounterStrikeSharp.API.Core
|
||||
RegisterFakeConVars(instance.GetType(), instance);
|
||||
}
|
||||
|
||||
public void RegisterConVars(object instance) {
|
||||
RegisterConVars(instance.GetType(), instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hooks an <a href="https://developer.valvesoftware.com/wiki/Inputs_and_Outputs">entity output</a>.
|
||||
/// </summary>
|
||||
@@ -657,6 +677,11 @@ namespace CounterStrikeSharp.API.Core
|
||||
subscriber.Dispose();
|
||||
}
|
||||
|
||||
foreach (var convar in ConVars)
|
||||
{
|
||||
convar.Delete();
|
||||
}
|
||||
|
||||
foreach (var definition in CommandDefinitions)
|
||||
{
|
||||
CommandManager.RemoveCommand(definition);
|
||||
|
||||
@@ -2,14 +2,9 @@ using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Cvars;
|
||||
|
||||
public class ConVar<T>
|
||||
public class ConVarBase
|
||||
{
|
||||
public ushort AccessIndex { get; private set; }
|
||||
|
||||
public ConVar(ushort accessIndex)
|
||||
{
|
||||
AccessIndex = accessIndex;
|
||||
}
|
||||
public ushort AccessIndex { get; protected set; }
|
||||
|
||||
public string Name => NativeAPI.GetConvarName(AccessIndex);
|
||||
public string Description => NativeAPI.GetConvarHelpText(AccessIndex);
|
||||
@@ -28,6 +23,95 @@ public class ConVar<T>
|
||||
set => NativeAPI.SetConvarFlags(AccessIndex, (ulong)value);
|
||||
}
|
||||
|
||||
public string ValueAsString
|
||||
{
|
||||
get => NativeAPI.GetConvarValueAsString(AccessIndex);
|
||||
set => NativeAPI.SetConvarValueAsString(AccessIndex, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shorthand for checking the <see cref="ConVarFlags.FCVAR_NOTIFY"/> flag.
|
||||
/// </summary>
|
||||
public bool Public
|
||||
{
|
||||
get => Flags.HasFlag(ConVarFlags.FCVAR_NOTIFY);
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
Flags |= ConVarFlags.FCVAR_NOTIFY;
|
||||
}
|
||||
else
|
||||
{
|
||||
Flags &= ~ConVarFlags.FCVAR_NOTIFY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Delete()
|
||||
{
|
||||
if (AccessIndex == 0)
|
||||
throw new InvalidOperationException("Cannot delete a ConVar that has not been created or found.");
|
||||
|
||||
NativeAPI.DeleteConvar(AccessIndex);
|
||||
AccessIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public class ConVar<T> : ConVarBase
|
||||
{
|
||||
public ConVar(ushort accessIndex)
|
||||
{
|
||||
AccessIndex = accessIndex;
|
||||
}
|
||||
|
||||
public ConVar(string name, string description, T defaultValue = default(T), ConVarFlags flags = ConVarFlags.FCVAR_NONE,
|
||||
T? minValue = default, T? maxValue = default) : this(new ConVarCreationOptions
|
||||
{
|
||||
Name = name,
|
||||
DefaultValue = defaultValue,
|
||||
Description = description,
|
||||
Flags = flags,
|
||||
MinValue = minValue,
|
||||
MaxValue = maxValue
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public ConVar(ConVarCreationOptions options)
|
||||
{
|
||||
var type = typeof(T);
|
||||
var conVarType = type switch
|
||||
{
|
||||
_ when type == typeof(bool) => ConVarType.Bool,
|
||||
_ when type == typeof(float) => ConVarType.Float32,
|
||||
_ when type == typeof(double) => ConVarType.Float64,
|
||||
_ when type == typeof(ushort) => ConVarType.UInt16,
|
||||
_ when type == typeof(short) => ConVarType.Int16,
|
||||
_ when type == typeof(uint) => ConVarType.UInt32,
|
||||
_ when type == typeof(int) => ConVarType.Int32,
|
||||
_ when type == typeof(long) => ConVarType.Int64,
|
||||
_ when type == typeof(ulong) => ConVarType.UInt64,
|
||||
_ when type == typeof(string) => ConVarType.String,
|
||||
_ when type == typeof(QAngle) => ConVarType.Qangle,
|
||||
_ when type == typeof(Vector2D) => ConVarType.Vector2,
|
||||
_ when type == typeof(Vector) => ConVarType.Vector3,
|
||||
_ when type == typeof(Vector4D) => ConVarType.Vector4,
|
||||
_ => throw new InvalidOperationException($"Unsupported type: {type}")
|
||||
};
|
||||
|
||||
AccessIndex = NativeAPI.CreateConvar(options.Name, (short)conVarType, options.Description, (UInt64)options.Flags,
|
||||
options.MinValue != null, options.MaxValue != null,
|
||||
options.DefaultValue,
|
||||
options.MinValue,
|
||||
options.MaxValue);
|
||||
|
||||
if (AccessIndex == 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to create ConVar '{options.Name}' with type '{type}'.");
|
||||
}
|
||||
}
|
||||
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
@@ -114,12 +198,6 @@ public class ConVar<T>
|
||||
set => NativeAPI.SetConvarValue(AccessIndex, value);
|
||||
}
|
||||
|
||||
public string ValueAsString
|
||||
{
|
||||
get => NativeAPI.GetConvarValueAsString(AccessIndex);
|
||||
set => NativeAPI.SetConvarValueAsString(AccessIndex, value);
|
||||
}
|
||||
|
||||
public static ConVar<T>? Find(string name)
|
||||
{
|
||||
var accessIndex = NativeAPI.GetConvarAccessIndexByName(name);
|
||||
@@ -128,25 +206,6 @@ public class ConVar<T>
|
||||
return new ConVar<T>(accessIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shorthand for checking the <see cref="ConVarFlags.FCVAR_NOTIFY"/> flag.
|
||||
/// </summary>
|
||||
public bool Public
|
||||
{
|
||||
get => Flags.HasFlag(ConVarFlags.FCVAR_NOTIFY);
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
Flags |= ConVarFlags.FCVAR_NOTIFY;
|
||||
}
|
||||
else
|
||||
{
|
||||
Flags &= ~ConVarFlags.FCVAR_NOTIFY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"ConVar [name={Name}, value={Value}, description={Description}, type={Type}, flags={Flags}]";
|
||||
@@ -161,53 +220,4 @@ public class ConVar<T>
|
||||
public T? MinValue { get; init; }
|
||||
public T? MaxValue { get; init; }
|
||||
}
|
||||
|
||||
public static ConVar<T>? Create(ConVarCreationOptions options)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(options.Name))
|
||||
throw new ArgumentException("ConVar name cannot be null or whitespace.", nameof(options.Name));
|
||||
|
||||
return Create(options.Name, options.DefaultValue, options.Description, options.Flags, options.MinValue, options.MaxValue);
|
||||
}
|
||||
|
||||
public static ConVar<T>? Create(string name, T defaultValue, string description = "", ConVarFlags flags = ConVarFlags.FCVAR_NONE,
|
||||
T? minValue = default, T? maxValue = default)
|
||||
{
|
||||
var type = typeof(T);
|
||||
var conVarType = type switch
|
||||
{
|
||||
_ when type == typeof(bool) => ConVarType.Bool,
|
||||
_ when type == typeof(float) => ConVarType.Float32,
|
||||
_ when type == typeof(double) => ConVarType.Float64,
|
||||
_ when type == typeof(ushort) => ConVarType.UInt16,
|
||||
_ when type == typeof(short) => ConVarType.Int16,
|
||||
_ when type == typeof(uint) => ConVarType.UInt32,
|
||||
_ when type == typeof(int) => ConVarType.Int32,
|
||||
_ when type == typeof(long) => ConVarType.Int64,
|
||||
_ when type == typeof(ulong) => ConVarType.UInt64,
|
||||
_ when type == typeof(string) => ConVarType.String,
|
||||
_ when type == typeof(QAngle) => ConVarType.Qangle,
|
||||
_ when type == typeof(Vector2D) => ConVarType.Vector2,
|
||||
_ when type == typeof(Vector) => ConVarType.Vector3,
|
||||
_ when type == typeof(Vector4D) => ConVarType.Vector4,
|
||||
_ => throw new InvalidOperationException($"Unsupported type: {type}")
|
||||
};
|
||||
|
||||
var accessIndex = NativeAPI.CreateConvar(name, (short)conVarType, description, (UInt64)flags, minValue != null, maxValue != null,
|
||||
defaultValue,
|
||||
minValue,
|
||||
maxValue);
|
||||
if (accessIndex == 0) return null;
|
||||
|
||||
return new ConVar<T>(accessIndex);
|
||||
}
|
||||
|
||||
public void Delete()
|
||||
{
|
||||
if (AccessIndex == 0)
|
||||
throw new InvalidOperationException("Cannot delete a ConVar that has not been created or found.");
|
||||
|
||||
NativeAPI.DeleteConvar(AccessIndex);
|
||||
AccessIndex = 0;
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ public class ConVarTests
|
||||
{
|
||||
ConVar<bool>.Find("test_bool_convar")?.Delete();
|
||||
|
||||
var conVar = ConVar<bool>.Create("test_bool_convar", true, "Test boolean ConVar", ConVarFlags.FCVAR_NOTIFY);
|
||||
var conVar = new ConVar<bool>("test_bool_convar", "Test boolean ConVar", true, ConVarFlags.FCVAR_NOTIFY);
|
||||
Assert.NotNull(conVar);
|
||||
Assert.Equal("test_bool_convar", conVar.Name);
|
||||
Assert.Equal(ConVarType.Bool, conVar.Type);
|
||||
@@ -112,7 +112,7 @@ public class ConVarTests
|
||||
{
|
||||
ConVar<Vector>.Find("test_vector_convar")?.Delete();
|
||||
|
||||
var conVar = ConVar<Vector>.Create(new()
|
||||
var conVar = new ConVar<Vector>(new ConVar<Vector>.ConVarCreationOptions()
|
||||
{
|
||||
Name = "test_vector_convar",
|
||||
DefaultValue = new Vector(1, 2, 3),
|
||||
@@ -151,7 +151,7 @@ public class ConVarTests
|
||||
{
|
||||
ConVar<string>.Find("test_string_convar")?.Delete();
|
||||
|
||||
var conVar = ConVar<string>.Create("test_string_convar", "default_value", "Test string ConVar", ConVarFlags.FCVAR_NOTIFY);
|
||||
var conVar = new ConVar<string>("test_string_convar", "Test string ConVar", "default_value", ConVarFlags.FCVAR_NOTIFY);
|
||||
Assert.NotNull(conVar);
|
||||
Assert.Equal("test_string_convar", conVar.Name);
|
||||
Assert.Equal(ConVarType.String, conVar.Type);
|
||||
@@ -172,7 +172,7 @@ public class ConVarTests
|
||||
{
|
||||
ConVar<float>.Find("test_float_convar")?.Delete();
|
||||
|
||||
var conVar = ConVar<float>.Create(new()
|
||||
var conVar = new ConVar<float>(new ConVar<float>.ConVarCreationOptions()
|
||||
{
|
||||
Name = "test_float_convar",
|
||||
DefaultValue = 1.23f,
|
||||
@@ -209,7 +209,7 @@ public class ConVarTests
|
||||
{
|
||||
ConVar<int>.Find("test_int_convar")?.Delete();
|
||||
|
||||
var conVar = ConVar<int>.Create(new()
|
||||
var conVar = new ConVar<int>(new ConVar<int>.ConVarCreationOptions()
|
||||
{
|
||||
Name = "test_int_convar",
|
||||
DefaultValue = 42,
|
||||
|
||||
@@ -65,6 +65,15 @@ namespace TestPlugin
|
||||
|
||||
private TestInjectedClass _testInjectedClass;
|
||||
|
||||
public ConVar<float> MyExampleConvar = new ConVar<float>(
|
||||
"example_convar",
|
||||
"An example ConVar for testing purposes",
|
||||
42.0f,
|
||||
ConVarFlags.FCVAR_NONE,
|
||||
0.0f,
|
||||
100.0f
|
||||
);
|
||||
|
||||
public SamplePlugin(TestInjectedClass testInjectedClass)
|
||||
{
|
||||
_testInjectedClass = testInjectedClass;
|
||||
|
||||
Reference in New Issue
Block a user