mirror of
https://github.com/roflmuffin/CounterStrikeSharp.git
synced 2025-12-06 16:06:37 -08:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b5eb7e38d | ||
|
|
2dd62c44d3 | ||
|
|
f811338ce4 | ||
|
|
194c340ae7 | ||
|
|
6b0912d3cd | ||
|
|
2d3aa09aa4 | ||
|
|
911084e71e | ||
|
|
5b99206568 | ||
|
|
4bfdf28beb | ||
|
|
9bcd0f7e92 | ||
|
|
11c6486ec5 | ||
|
|
ee69560a66 | ||
|
|
d37e5e194a | ||
|
|
c4740d1cc9 | ||
|
|
7e92f178fd | ||
|
|
107ca08132 | ||
|
|
8cda8d9a50 | ||
|
|
3d59a05de8 | ||
|
|
77b7040d6c | ||
|
|
75de9732ef | ||
|
|
575c859ddb | ||
|
|
e12a7cb17a | ||
|
|
7c7f52a219 | ||
|
|
cd593fb238 | ||
|
|
c5cc65be48 | ||
|
|
59928bbcc5 | ||
|
|
319b116c5f |
@@ -63,6 +63,10 @@ public class HelloWorldPlugin : BasePlugin
|
||||
|
||||
public override string ModuleVersion => "0.0.1";
|
||||
|
||||
public override string ModuleAuthor => "roflmuffin";
|
||||
|
||||
public override string ModuleDescription => "Simple hello world plugin";
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
Logger.LogInformation("Plugin loaded successfully!");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"PublicChatTrigger": [ "!" ],
|
||||
"SilentChatTrigger": [ "/" ],
|
||||
"FollowCS2ServerGuidelines": true
|
||||
"FollowCS2ServerGuidelines": true,
|
||||
"PluginHotReloadEnabled": true
|
||||
}
|
||||
@@ -71,12 +71,12 @@
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x48\\x89\\x5C\\x24\\x10\\x48\\x89\\x7C\\x24\\x20\\x55\\x48\\x8B\\xEC\\x48\\x83\\xEC\\x50",
|
||||
"linux": "\\x55\\x48\\x89\\xF2\\x48\\x89\\xE5\\x41\\x54\\x49\\x89\\xFC\\x48\\x8D\\x7D\\xE0\\x48\\x83\\xEC\\x18\\x48\\x8D\\x05\\xA5"
|
||||
"linux": "\\x55\\x48\\x89\\xF2\\x48\\x89\\xE5\\x41\\x54\\x49\\x89\\xFC\\x48\\x8D\\x2A\\x2A\\x48\\x83\\x2A\\x2A\\x2A\\x8D\\x05\\x2A\\x2A\\x2A\\x00\\x48\\x8B\\x30\\x48\\x8B\\x06\\xFF\\x2A\\x2A\\x48\\x8B\\x45\\x2A\\x48\\x8D\\x2A\\x2A\\x4C\\x89\\x2A\\x48\\x89\\x45\\x2A\\x2A\\x68\\xFC"
|
||||
}
|
||||
},
|
||||
"CCSPlayer_ItemServices_DropActivePlayerWeapon": {
|
||||
"offsets": {
|
||||
"windows": 20,
|
||||
"windows": 18,
|
||||
"linux": 19
|
||||
}
|
||||
},
|
||||
@@ -103,7 +103,7 @@
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x48\\x83\\xEC\\x48\\xC6\\x44\\x24\\x30\\x00\\x4C\\x8B\\xC1",
|
||||
"linux": "\\x48\\x8D\\x05\\xC9\\xC2\\xBC"
|
||||
"linux": "\\x48\\x8D\\x05\\x2A\\x2A\\x2A\\x2A\\x55\\x48\\x89\\xFA"
|
||||
}
|
||||
},
|
||||
"CBaseEntity_DispatchSpawn": {
|
||||
|
||||
@@ -68,19 +68,20 @@ Now build your project using your ide or the `dotnet build` command. You should
|
||||
|
||||
### Installing your Plugin
|
||||
|
||||
Locate the `plugins` folder in your CS2 dedicated server (`/game/csgo/addons/counterstrikesharp/plugins`) and create a new folder with the exact same name as your output .dll file. In this example it would be `HelloWorldPlugin.dll`, so I will make a new folder called `HelloWorldPlugin`. Inside of this folder, copy and paste all of the `.dll` files _except_ the `CounterStrikeSharp.API.dll` file. Once completed, the folder should look as follows:
|
||||
Locate the `plugins` folder in your CS2 dedicated server (`/game/csgo/addons/counterstrikesharp/plugins`) and create a new folder with the exact same name as your output .dll file. In this example it would be `HelloWorldPlugin.dll`, so I will make a new folder called `HelloWorldPlugin`. Inside of this folder, copy and paste the: `HelloWorldPlugin.deps.json`, `HelloWorldPlugin.dll`, and `HelloWorldPlugin.pdb` files. Once completed, the folder should look as follows:
|
||||
|
||||
```shell
|
||||
.
|
||||
└── SamplePlugin
|
||||
├── McMaster.NETCore.Plugins.dll
|
||||
├── Microsoft.DotNet.PlatformAbstractions.dll
|
||||
├── Microsoft.Extensions.DependencyModel.dll
|
||||
├── SamplePlugin.deps.json
|
||||
├── SamplePlugin.dll
|
||||
└── SamplePlugin.pdb
|
||||
└── HelloWorldPlugin
|
||||
├── HelloWorldPlugin.deps.json
|
||||
├── HelloWorldPlugin.dll
|
||||
└── HelloWorldPlugin.pdb
|
||||
```
|
||||
|
||||
:::caution
|
||||
If you have installed external nuget packages for your plugin, you may need to include their respective `.dll`s. For example, if you utilize the `Stateless` C# library, include `stateless.dll` in your `HelloWorld` plugin directory.
|
||||
:::
|
||||
|
||||
:::note
|
||||
Note that some of these dependencies may change depending on the version of CounterStrikeSharp being used.
|
||||
:::
|
||||
|
||||
@@ -25,6 +25,10 @@ public class HelloWorldPlugin : BasePlugin
|
||||
|
||||
public override string ModuleVersion => "0.0.1";
|
||||
|
||||
public override string ModuleAuthor => "roflmuffin";
|
||||
|
||||
public override string ModuleDescription => "Simple hello world plugin";
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
Logger.LogInformation("Plugin loaded successfully!");
|
||||
|
||||
@@ -23,4 +23,8 @@ receive a ban.
|
||||
|
||||
:::note
|
||||
Disable this option at your own risk.
|
||||
:::
|
||||
:::
|
||||
|
||||
## PluginHotReloadEnabled
|
||||
|
||||
When enabled, plugins are automatically reloaded when their .dll file is updated.
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace WarcraftPlugin
|
||||
var victim = @event.Userid;
|
||||
var headshot = @event.Headshot;
|
||||
|
||||
if (attacker.IsValid && victim.IsValid && (attacker.EntityIndex.Value.Value != victim.EntityIndex.Value.Value) && !attacker.IsBot)
|
||||
if (attacker.IsValid && victim.IsValid && (attacker != victim) && !attacker.IsBot)
|
||||
{
|
||||
var weaponName = attacker.PlayerPawn.Value.WeaponServices.ActiveWeapon.Value.DesignerName;
|
||||
|
||||
|
||||
@@ -30,5 +30,19 @@ namespace CounterStrikeSharp.API
|
||||
{
|
||||
Handle = pointer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new instance of the specified type using the pointer from the passed in object.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Useful for creating a new instance of a class that inherits from NativeObject.
|
||||
/// e.g. <code>var weaponServices = playerWeaponServices.As<CCSPlayer_WeaponServices>();</code>
|
||||
/// </remarks>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public T As<T>() where T : NativeObject
|
||||
{
|
||||
return (T)Activator.CreateInstance(typeof(T), this.Handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,6 +534,49 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
public static uint GetRefFromEntityPointer(IntPtr entitypointer){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(entitypointer);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0xAF13DA94);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
return (uint)ScriptContext.GlobalScriptContext.GetResult(typeof(uint));
|
||||
}
|
||||
}
|
||||
|
||||
public static IntPtr GetEntityPointerFromRef(uint entityref){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(entityref);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0xDBC17174);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
return (IntPtr)ScriptContext.GlobalScriptContext.GetResult(typeof(IntPtr));
|
||||
}
|
||||
}
|
||||
|
||||
public static IntPtr GetConcreteEntityListPointer(){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0x5756DB36);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
return (IntPtr)ScriptContext.GlobalScriptContext.GetResult(typeof(IntPtr));
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsRefValidEntity(uint entityref){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(entityref);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0x6E38A1FC);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
return (bool)ScriptContext.GlobalScriptContext.GetResult(typeof(bool));
|
||||
}
|
||||
}
|
||||
|
||||
public static void PrintToConsole(int index, string message){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
@@ -967,6 +1010,17 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetSchemaClassSize(string classname){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(classname);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0x9CE4FC56);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
return (int)ScriptContext.GlobalScriptContext.GetResult(typeof(int));
|
||||
}
|
||||
}
|
||||
|
||||
public static IntPtr GetEconItemSystem(){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
|
||||
@@ -152,17 +152,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
true);
|
||||
break;
|
||||
}
|
||||
|
||||
var plugin = _pluginContextQueryHandler.FindPluginByModulePath(info.GetArg(2));
|
||||
|
||||
if (plugin == null)
|
||||
{
|
||||
info.ReplyToCommand("Could not find plugin to load.");
|
||||
break;
|
||||
}
|
||||
|
||||
plugin.Load(false);
|
||||
|
||||
|
||||
// If our arugment doesn't end in ".dll" - try and construct a path similar to PluginName/PluginName.dll.
|
||||
// We'll assume we have a full path if we have ".dll".
|
||||
var path = info.GetArg(2);
|
||||
@@ -175,15 +165,23 @@ namespace CounterStrikeSharp.API.Core
|
||||
path = Path.Combine(_scriptHostConfiguration.RootPath, path);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// LoadPlugin(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError(e, "Failed to load plugin from {Path}", path);
|
||||
}
|
||||
var plugin = _pluginContextQueryHandler.FindPluginByModulePath(path);
|
||||
|
||||
if (plugin == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_pluginManager.LoadPlugin(path);
|
||||
} catch (Exception e)
|
||||
{
|
||||
info.ReplyToCommand($"Could not load plugin \"{path}\")", true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
plugin.Load(false);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,9 @@ namespace CounterStrikeSharp.API.Core
|
||||
|
||||
[JsonPropertyName("FollowCS2ServerGuidelines")]
|
||||
public bool FollowCS2ServerGuidelines { get; set; } = true;
|
||||
|
||||
[JsonPropertyName("PluginHotReloadEnabled")]
|
||||
public bool PluginHotReloadEnabled { get; set; } = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -80,6 +83,11 @@ namespace CounterStrikeSharp.API.Core
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public static bool FollowCS2ServerGuidelines => _coreConfig.FollowCS2ServerGuidelines;
|
||||
|
||||
/// <summary>
|
||||
/// When enabled, plugins are automatically reloaded when their .dll file is updated.
|
||||
/// </summary>
|
||||
public static bool PluginHotReloadEnabled => _coreConfig.PluginHotReloadEnabled;
|
||||
}
|
||||
|
||||
public partial class CoreConfig : IStartupService
|
||||
|
||||
@@ -125,6 +125,18 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
return new InputArgument(value);
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
public static implicit operator InputArgument(NativeObject value)
|
||||
{
|
||||
return new InputArgument(value.Handle);
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
public static implicit operator InputArgument(NativeEntity value)
|
||||
{
|
||||
return new InputArgument(value.Handle);
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
public static unsafe implicit operator InputArgument(void* value)
|
||||
|
||||
@@ -11,8 +11,7 @@ public partial class CCSPlayerController
|
||||
{
|
||||
get
|
||||
{
|
||||
if (EntityIndex == null) return null;
|
||||
return NativeAPI.GetUseridFromIndex((int)this.EntityIndex.Value.Value);
|
||||
return NativeAPI.GetUseridFromIndex((int)this.Index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +37,7 @@ public partial class CCSPlayerController
|
||||
|
||||
public void PrintToConsole(string message)
|
||||
{
|
||||
NativeAPI.PrintToConsole((int)EntityIndex.Value.Value, $"{message}\n\0");
|
||||
NativeAPI.PrintToConsole((int)Index, $"{message}\n\0");
|
||||
}
|
||||
|
||||
public void PrintToChat(string message)
|
||||
@@ -144,4 +143,8 @@ public partial class CCSPlayerController
|
||||
/// Gets the active pawns button state. Will work even if the player is dead or observing.
|
||||
/// </summary>
|
||||
public PlayerButtons Buttons => (PlayerButtons)Pawn.Value.MovementServices!.Buttons.ButtonStates[0];
|
||||
|
||||
public void ExecuteClientCommand(string command) => NativeAPI.IssueClientCommand(Slot, command);
|
||||
|
||||
public int Slot => (int)Index - 1;
|
||||
}
|
||||
@@ -12,18 +12,31 @@ namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CEntityInstance : IEquatable<CEntityInstance>
|
||||
{
|
||||
public bool IsValid => Handle != IntPtr.Zero;
|
||||
public CEntityInstance(IntPtr pointer) : base(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
public CEntityIndex? EntityIndex => IsValid ? Entity?.EntityHandle.Index : null;
|
||||
public CEntityInstance(uint rawHandle) : base(rawHandle)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks that the entity handle is valid and the handle points to a valid entity
|
||||
/// </summary>
|
||||
public bool IsValid => EntityHandle.IsValid && Handle != IntPtr.Zero;
|
||||
|
||||
[Obsolete("Use Index instead", true)]
|
||||
public CEntityIndex? EntityIndex => new CEntityIndex(EntityHandle.Index);
|
||||
|
||||
public uint Index => EntityHandle.Index;
|
||||
|
||||
public string DesignerName => IsValid ? Entity?.DesignerName : null;
|
||||
|
||||
public void Remove() => VirtualFunctions.UTIL_Remove(this.Handle);
|
||||
|
||||
|
||||
|
||||
public bool Equals(CEntityInstance? other)
|
||||
{
|
||||
return this.Handle == other?.Handle;
|
||||
return this.EntityHandle == other?.EntityHandle;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
@@ -50,5 +63,5 @@ public partial class CEntityInstance : IEquatable<CEntityInstance>
|
||||
public partial class CEntityIdentity
|
||||
{
|
||||
public unsafe CEntityInstance EntityInstance => new(Unsafe.Read<IntPtr>((void*)Handle));
|
||||
public unsafe CHandle<CEntityInstance> EntityHandle => new(Handle + 0x10);
|
||||
public unsafe CHandle<CEntityInstance> EntityHandle => new(this.Handle + 0x10);
|
||||
}
|
||||
@@ -9420,10 +9420,8 @@ public partial class CEntityIdentity : NativeObject
|
||||
|
||||
}
|
||||
|
||||
public partial class CEntityInstance : NativeObject
|
||||
public partial class CEntityInstance : NativeEntity
|
||||
{
|
||||
public CEntityInstance (IntPtr pointer) : base(pointer) {}
|
||||
|
||||
// m_iszPrivateVScripts
|
||||
public string PrivateVScripts
|
||||
{
|
||||
|
||||
@@ -5,5 +5,6 @@ namespace CounterStrikeSharp.API.Core.Plugin.Host;
|
||||
public interface IPluginManager
|
||||
{
|
||||
public void Load();
|
||||
public void LoadPlugin(string path);
|
||||
public IEnumerable<PluginContext> GetLoadedPlugins();
|
||||
}
|
||||
@@ -45,7 +45,7 @@ public class PluginManager : IPluginManager
|
||||
return _loadedPluginContexts;
|
||||
}
|
||||
|
||||
private void LoadPlugin(string path)
|
||||
public void LoadPlugin(string path)
|
||||
{
|
||||
var plugin = new PluginContext(_serviceProvider, _scriptHostConfiguration, path, _loadedPluginContexts.Select(x => x.PluginId).DefaultIfEmpty(0).Max() + 1);
|
||||
_loadedPluginContexts.Add(plugin);
|
||||
|
||||
@@ -64,23 +64,26 @@ namespace CounterStrikeSharp.API.Core.Plugin
|
||||
config.IsUnloadable = true;
|
||||
});
|
||||
|
||||
_fileWatcher = new FileSystemWatcher
|
||||
if (CoreConfig.PluginHotReloadEnabled)
|
||||
{
|
||||
Path = Path.GetDirectoryName(path)
|
||||
};
|
||||
|
||||
_fileWatcher.Deleted += async (s, e) =>
|
||||
{
|
||||
if (e.FullPath == path)
|
||||
_fileWatcher = new FileSystemWatcher
|
||||
{
|
||||
_logger.LogInformation("Plugin {Name} has been deleted, unloading...", Plugin.ModuleName);
|
||||
Unload(true);
|
||||
}
|
||||
};
|
||||
Path = Path.GetDirectoryName(path)
|
||||
};
|
||||
|
||||
_fileWatcher.Filter = "*.dll";
|
||||
_fileWatcher.EnableRaisingEvents = true;
|
||||
Loader.Reloaded += async (s, e) => await OnReloadedAsync(s, e);
|
||||
_fileWatcher.Deleted += async (s, e) =>
|
||||
{
|
||||
if (e.FullPath == path)
|
||||
{
|
||||
_logger.LogInformation("Plugin {Name} has been deleted, unloading...", Plugin.ModuleName);
|
||||
Unload(true);
|
||||
}
|
||||
};
|
||||
|
||||
_fileWatcher.Filter = "*.dll";
|
||||
_fileWatcher.EnableRaisingEvents = true;
|
||||
Loader.Reloaded += async (s, e) => await OnReloadedAsync(s, e);
|
||||
}
|
||||
}
|
||||
|
||||
private Task OnReloadedAsync(object sender, PluginReloadedEventArgs eventargs)
|
||||
|
||||
@@ -205,6 +205,16 @@ namespace CounterStrikeSharp.API.Core
|
||||
|
||||
return;
|
||||
}
|
||||
else if (arg is NativeObject nativeObject)
|
||||
{
|
||||
Push(context, (InputArgument)nativeObject);
|
||||
return;
|
||||
}
|
||||
else if (arg is NativeEntity nativeValue)
|
||||
{
|
||||
Push(context, (InputArgument)nativeValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Marshal.SizeOf(arg.GetType()) <= 8)
|
||||
{
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace CounterStrikeSharp.API.Modules.Config
|
||||
{
|
||||
_logger.LogError(ex, "Failed to generate configuration file for {PluginName}", pluginName);
|
||||
}
|
||||
} else if (File.Exists(exampleConfigPath))
|
||||
} else if (File.Exists(exampleConfigPath) && !File.Exists(configPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
public static class EntitySystem
|
||||
{
|
||||
private static Lazy<IntPtr> ConcreteEntityListPointer = new(NativeAPI.GetConcreteEntityListPointer);
|
||||
|
||||
private const int MaxEntities = 32768;
|
||||
private const int MaxEntitiesPerChunk = 512;
|
||||
private const int MaxChunks = MaxEntities / MaxEntitiesPerChunk;
|
||||
private const int SizeOfEntityIdentity = 0x78;
|
||||
private const int HandleOffset = 0x10;
|
||||
private const uint InvalidEHandleIndex = 0xFFFFFFFF;
|
||||
|
||||
static unsafe Span<IntPtr> IdentityChunks => new((void*)ConcreteEntityListPointer.Value, MaxChunks);
|
||||
public static IntPtr FirstActiveEntity => Marshal.ReadIntPtr(ConcreteEntityListPointer.Value, MaxEntitiesPerChunk);
|
||||
|
||||
public static IntPtr? GetEntityByHandle(uint raw)
|
||||
{
|
||||
return GetEntityByHandle(new CHandle<CEntityInstance>(raw));
|
||||
}
|
||||
|
||||
public static IntPtr? GetEntityByHandle<T>(CHandle<T> handle) where T : NativeEntity
|
||||
{
|
||||
if (!handle.IsValid)
|
||||
return null;
|
||||
|
||||
IntPtr pChunkToUse = IdentityChunks[(int)(handle.Index / MaxEntitiesPerChunk)];
|
||||
if (pChunkToUse == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
IntPtr pIdentityPtr = IntPtr.Add(pChunkToUse, SizeOfEntityIdentity * (int)(handle.Index % MaxEntitiesPerChunk));
|
||||
|
||||
if (pIdentityPtr == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
var foundHandle = new CEntityHandle(pIdentityPtr + HandleOffset);
|
||||
|
||||
if (foundHandle.Raw != handle.Raw)
|
||||
return null;
|
||||
|
||||
return Marshal.ReadIntPtr(pIdentityPtr);
|
||||
}
|
||||
|
||||
public static IntPtr? GetEntityByIndex(uint index)
|
||||
{
|
||||
if ((int)index <= -1 || index >= MaxEntities - 1) return null;
|
||||
|
||||
IntPtr pChunkToUse = IdentityChunks[(int)(index / MaxEntitiesPerChunk)];
|
||||
if (pChunkToUse == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
IntPtr pIdentityPtr = IntPtr.Add(pChunkToUse, SizeOfEntityIdentity * (int)(index % MaxEntitiesPerChunk));
|
||||
|
||||
if (pIdentityPtr == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
var foundHandle = new CEntityHandle(pIdentityPtr + HandleOffset);
|
||||
|
||||
if (foundHandle.Index != index)
|
||||
return null;
|
||||
|
||||
return Marshal.ReadIntPtr(pIdentityPtr);
|
||||
}
|
||||
|
||||
public static uint GetRawHandleFromEntityPointer(IntPtr pointer)
|
||||
{
|
||||
if (pointer == IntPtr.Zero)
|
||||
return InvalidEHandleIndex;
|
||||
|
||||
return Schema.GetPointer<CEntityIdentity?>(pointer, "CEntityInstance", "m_pEntity")?.EntityHandle.Raw ??
|
||||
InvalidEHandleIndex;
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,7 @@ namespace CounterStrikeSharp.API.Modules.Events
|
||||
break;
|
||||
case var _ when value is CCSPlayerController player:
|
||||
// When I was testing this, the code seems to expect a slot, even though it is called index
|
||||
SetEntityIndex(name, (int)player.EntityIndex.Value.Value - 1);
|
||||
SetEntityIndex(name, (int)player.Index - 1);
|
||||
break;
|
||||
case var _ when value is string s:
|
||||
SetString(name, s);
|
||||
@@ -124,6 +124,6 @@ namespace CounterStrikeSharp.API.Modules.Events
|
||||
|
||||
public void FireEvent(bool dontBroadcast) => NativeAPI.FireEvent(Handle, dontBroadcast);
|
||||
|
||||
public void FireEventToClient(CCSPlayerController player) => NativeAPI.FireEventToClient(Handle, (int)player.EntityIndex.Value.Value);
|
||||
public void FireEventToClient(CCSPlayerController player) => NativeAPI.FireEventToClient(Handle, (int)player.Index);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Memory;
|
||||
@@ -49,6 +50,8 @@ public class Schema
|
||||
"m_nActiveCoinRank",
|
||||
"m_nMusicID",
|
||||
};
|
||||
|
||||
public static int GetClassSize(string className) => NativeAPI.GetSchemaClassSize(className);
|
||||
|
||||
public static short GetSchemaOffset(string className, string propertyName)
|
||||
{
|
||||
@@ -92,9 +95,9 @@ public class Schema
|
||||
return ref Unsafe.AsRef<T>((void*)(pointer + GetSchemaOffset(className, memberName)));
|
||||
}
|
||||
|
||||
public static unsafe T GetPointer<T>(IntPtr pointer)
|
||||
public static T GetPointer<T>(IntPtr pointer)
|
||||
{
|
||||
var pointerTo = Unsafe.Read<IntPtr>((void*)pointer);
|
||||
var pointerTo = Marshal.ReadIntPtr(pointer);
|
||||
if (pointerTo == IntPtr.Zero)
|
||||
{
|
||||
return default;
|
||||
@@ -103,9 +106,9 @@ public class Schema
|
||||
return (T)Activator.CreateInstance(typeof(T), pointerTo);
|
||||
}
|
||||
|
||||
public static unsafe T GetPointer<T>(IntPtr pointer, string className, string memberName)
|
||||
public static T GetPointer<T>(IntPtr pointer, string className, string memberName)
|
||||
{
|
||||
var pointerTo = Unsafe.Read<IntPtr>((void*)(pointer + GetSchemaOffset(className, memberName)));
|
||||
var pointerTo = Marshal.ReadIntPtr(pointer + GetSchemaOffset(className, memberName));
|
||||
if (pointerTo == IntPtr.Zero)
|
||||
{
|
||||
return default;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
@@ -9,22 +11,85 @@ public readonly record struct CEntityIndex(uint Value)
|
||||
public override string ToString() => $"Entity Index {Value}";
|
||||
}
|
||||
|
||||
public class CHandle<T> : NativeObject
|
||||
public class CHandle<T> : IEquatable<CHandle<T>> where T : NativeEntity
|
||||
{
|
||||
public CHandle(IntPtr pointer) : base(pointer)
|
||||
private uint _raw;
|
||||
private IntPtr? _pointer;
|
||||
|
||||
public uint Raw
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_pointer != null)
|
||||
{
|
||||
return (uint)Marshal.ReadInt32(_pointer.Value);
|
||||
}
|
||||
|
||||
return _raw;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_pointer != null)
|
||||
{
|
||||
Marshal.WriteInt32(_pointer.Value, (int)value);
|
||||
return;
|
||||
}
|
||||
|
||||
_raw = value;
|
||||
}
|
||||
}
|
||||
|
||||
public T Value => (T)Activator.CreateInstance(typeof(T), NativeAPI.GetEntityPointerFromHandle(Handle));
|
||||
public CHandle(uint raw)
|
||||
{
|
||||
Raw = raw;
|
||||
}
|
||||
|
||||
public unsafe ref ulong Raw => ref Unsafe.AsRef<ulong>((void*)Handle);
|
||||
public CHandle(IntPtr raw)
|
||||
{
|
||||
_pointer = raw;
|
||||
}
|
||||
|
||||
public override string ToString() => IsValid ? $"Index = {Index.Value}, Serial = {SerialNum}" : "<invalid>";
|
||||
public T? Value => (T)Activator.CreateInstance(typeof(T), EntitySystem.GetEntityByHandle(this));
|
||||
|
||||
public bool IsValid => Index.Value != (Utilities.MaxEdicts - 1);
|
||||
public override string ToString() => IsValid ? $"Index = {Index}, Serial = {SerialNum}" : "<invalid>";
|
||||
|
||||
public CEntityIndex Index => new((uint)(Raw & (Utilities.MaxEdicts - 1)));
|
||||
public uint SerialNum => (uint)(Raw >> Utilities.MaxEdictBits);
|
||||
public bool IsValid => Index != (Utilities.MaxEdicts - 1);
|
||||
|
||||
public uint Index => (uint)(Raw & (Utilities.MaxEdicts - 1));
|
||||
public uint SerialNum => Raw >> Utilities.MaxEdictBits;
|
||||
|
||||
public static implicit operator uint(CHandle<T> handle) => handle.Raw;
|
||||
|
||||
public bool Equals(CHandle<T>? other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return Raw == other.Raw;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((CHandle<T>)obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (int)Raw;
|
||||
}
|
||||
}
|
||||
|
||||
public class CEntityHandle : CHandle<CEntityInstance>
|
||||
{
|
||||
public CEntityHandle(uint raw) : base(raw)
|
||||
{
|
||||
}
|
||||
|
||||
public CEntityHandle(IntPtr raw) : base (raw)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class PointerTo<T> : NativeObject where T : NativeObject
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
|
||||
@@ -7,30 +8,57 @@ namespace CounterStrikeSharp.API.Modules.Utils
|
||||
{
|
||||
public static class EnumUtils
|
||||
{
|
||||
public static string? GetEnumMemberAttributeValue<T>(T enumValue)
|
||||
/// <summary>
|
||||
/// Brute force search using Enum.GetNames as enum members pointing to other enum members do not have the correct attributes.
|
||||
/// </summary>
|
||||
public static T? GetEnumMemberAttribute<T>(this Enum enumValue) where T : Attribute
|
||||
{
|
||||
var type = enumValue.GetType();
|
||||
foreach (var name in Enum.GetNames(type))
|
||||
{
|
||||
var field = type.GetField(name);
|
||||
if (field == null) continue;
|
||||
|
||||
var fieldValue = field.GetValue(null)!;
|
||||
if (fieldValue.Equals(enumValue))
|
||||
{
|
||||
var attribute = field.GetCustomAttribute<T>();
|
||||
if (attribute != null)
|
||||
{
|
||||
return attribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string? GetEnumMemberAttributeValue<T>(T? enumValue) where T : Enum
|
||||
{
|
||||
var enumType = typeof(T);
|
||||
|
||||
if(!enumType.IsEnum || enumValue == null)
|
||||
if (!enumType.IsEnum || enumValue == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var enumString = enumValue.ToString();
|
||||
|
||||
if(string.IsNullOrWhiteSpace(enumString))
|
||||
if (string.IsNullOrWhiteSpace(enumString))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var memberInfo = enumType.GetMember(enumString);
|
||||
var enumMemberAttribute = memberInfo.FirstOrDefault()?.GetCustomAttributes(false).OfType<EnumMemberAttribute>().FirstOrDefault();
|
||||
var enumMemberAttribute = memberInfo.FirstOrDefault()?.GetCustomAttributes(false)
|
||||
.OfType<EnumMemberAttribute>().FirstOrDefault();
|
||||
if (enumMemberAttribute != null)
|
||||
{
|
||||
return enumMemberAttribute.Value;
|
||||
}
|
||||
|
||||
return null;
|
||||
// Brute force search by name if we still can't find it.
|
||||
return enumValue.GetEnumMemberAttribute<EnumMemberAttribute>()?.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
managed/CounterStrikeSharp.API/NativeEntity.cs
Normal file
21
managed/CounterStrikeSharp.API/NativeEntity.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CounterStrikeSharp.API;
|
||||
|
||||
public abstract class NativeEntity : NativeObject
|
||||
{
|
||||
public new IntPtr Handle => EntitySystem.GetEntityByHandle(EntityHandle) ?? IntPtr.Zero;
|
||||
public CEntityHandle EntityHandle { get; }
|
||||
|
||||
public NativeEntity(IntPtr pointer) : base(pointer)
|
||||
{
|
||||
EntityHandle = new(EntitySystem.GetRawHandleFromEntityPointer(pointer));
|
||||
}
|
||||
|
||||
public NativeEntity(uint rawHandle) : base(EntitySystem.GetEntityByHandle(rawHandle) ?? IntPtr.Zero)
|
||||
{
|
||||
EntityHandle = new(rawHandle);
|
||||
}
|
||||
}
|
||||
@@ -22,15 +22,17 @@ using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using CounterStrikeSharp.API.Modules.Commands.Targeting;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CounterStrikeSharp.API
|
||||
{
|
||||
public static class Utilities
|
||||
{
|
||||
// https://github.com/dotabuff/manta/blob/master/entity.go#L186-L190
|
||||
public const int MaxEdictBits = 14;
|
||||
public const int MaxEdictBits = 15;
|
||||
public const int MaxEdicts = 1 << MaxEdictBits;
|
||||
public const int NumEHandleSerialNumberBits = 17;
|
||||
public const uint InvalidEHandleIndex = 0xFFFFFFFF;
|
||||
|
||||
public static IEnumerable<T> FlagsToList<T>(this T flags) where T : Enum
|
||||
{
|
||||
@@ -75,7 +77,7 @@ namespace CounterStrikeSharp.API
|
||||
|
||||
public static IEnumerable<T> FindAllEntitiesByDesignerName<T>(string designerName) where T : CEntityInstance
|
||||
{
|
||||
var pEntity = new CEntityIdentity(NativeAPI.GetFirstActiveEntity());
|
||||
var pEntity = new CEntityIdentity(EntitySystem.FirstActiveEntity);
|
||||
for (; pEntity != null && pEntity.Handle != IntPtr.Zero; pEntity = pEntity.Next)
|
||||
{
|
||||
if (!pEntity.DesignerName.Contains(designerName)) continue;
|
||||
@@ -83,6 +85,15 @@ namespace CounterStrikeSharp.API
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<CEntityInstance> GetAllEntities()
|
||||
{
|
||||
var pEntity = new CEntityIdentity(EntitySystem.FirstActiveEntity);
|
||||
for (; pEntity != null && pEntity.Handle != IntPtr.Zero; pEntity = pEntity.Next)
|
||||
{
|
||||
yield return new PointerTo<CEntityInstance>(pEntity.Handle).Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of <see cref="CCSPlayerController"/> that are valid and have a valid <see cref="CCSPlayerController.UserId"/> >= 0
|
||||
/// </summary>
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace TestPlugin
|
||||
[JsonPropertyName("LogPrefix")] public string LogPrefix { get; set; } = "CSSharp";
|
||||
}
|
||||
|
||||
[MinimumApiVersion(33)]
|
||||
[MinimumApiVersion(80)]
|
||||
public class SamplePlugin : BasePlugin, IPluginConfig<SampleConfig>
|
||||
{
|
||||
public override string ModuleName => "Sample Plugin";
|
||||
@@ -137,7 +137,7 @@ namespace TestPlugin
|
||||
VirtualFunctions.UTIL_RemoveFunc.Hook(hook =>
|
||||
{
|
||||
var entityInstance = hook.GetParam<CEntityInstance>(0);
|
||||
Logger.LogInformation("Removed entity {EntityIndex}", entityInstance.EntityIndex.Value.Value);
|
||||
Logger.LogInformation("Removed entity {EntityIndex}", entityInstance.Index);
|
||||
|
||||
return HookResult.Continue;
|
||||
}, HookMode.Post);
|
||||
@@ -217,7 +217,7 @@ namespace TestPlugin
|
||||
if (!@event.Userid.PlayerPawn.IsValid) return 0;
|
||||
|
||||
Logger.LogInformation("Player spawned with entity index: {EntityIndex} & User ID: {UserId}",
|
||||
@event.Userid.EntityIndex, @event.Userid.UserId);
|
||||
@event.Userid.Index, @event.Userid.UserId);
|
||||
|
||||
return HookResult.Continue;
|
||||
});
|
||||
@@ -229,27 +229,21 @@ namespace TestPlugin
|
||||
var activeWeapon = @event.Userid.PlayerPawn.Value.WeaponServices?.ActiveWeapon.Value;
|
||||
var weapons = @event.Userid.PlayerPawn.Value.WeaponServices?.MyWeapons;
|
||||
|
||||
Server.NextFrame(() =>
|
||||
{
|
||||
var weaponServices = player.PlayerPawn.Value.WeaponServices.As<CCSPlayer_WeaponServices>();
|
||||
player.PrintToChat(weaponServices.ActiveWeapon.Value?.DesignerName);
|
||||
});
|
||||
|
||||
// Set player to random colour
|
||||
player.PlayerPawn.Value.Render = Color.FromArgb(Random.Shared.Next(0, 255),
|
||||
Random.Shared.Next(0, 255), Random.Shared.Next(0, 255));
|
||||
|
||||
Server.NextFrame(() =>
|
||||
{
|
||||
player.PrintToCenter(string.Join("\n", weapons.Select(x => x.Value.DesignerName)));
|
||||
});
|
||||
|
||||
activeWeapon.ReserveAmmo[0] = 250;
|
||||
activeWeapon.Clip1 = 250;
|
||||
|
||||
Logger.LogInformation("Pawn Position: {AbsOrigin}-{Rotation}", pawn.AbsOrigin, pawn.AbsRotation);
|
||||
|
||||
char randomColourChar = (char)new Random().Next(0, 16);
|
||||
Server.PrintToChatAll($"Random String with Random Colour: {randomColourChar}{new Random().Next()}");
|
||||
|
||||
pawn.Health += 5;
|
||||
|
||||
Logger.LogInformation("Bullet Impact: {X},{Y},{Z}", @event.X, @event.Y, @event.Z);
|
||||
|
||||
return HookResult.Continue;
|
||||
});
|
||||
RegisterEventHandler<EventRoundStart>((@event, info) =>
|
||||
@@ -304,7 +298,7 @@ namespace TestPlugin
|
||||
switch (designerName)
|
||||
{
|
||||
case "smokegrenade_projectile":
|
||||
var projectile = new CSmokeGrenadeProjectile(entity.Handle);
|
||||
var projectile = entity.As<CSmokeGrenadeProjectile>();
|
||||
|
||||
Server.NextFrame(() =>
|
||||
{
|
||||
@@ -316,7 +310,7 @@ namespace TestPlugin
|
||||
});
|
||||
return;
|
||||
case "flashbang_projectile":
|
||||
var flashbang = new CBaseCSGrenadeProjectile(entity.Handle);
|
||||
var flashbang = entity.As<CBaseCSGrenadeProjectile>();
|
||||
|
||||
Server.NextFrame(() => { flashbang.Remove(); });
|
||||
return;
|
||||
@@ -470,6 +464,20 @@ namespace TestPlugin
|
||||
}
|
||||
}
|
||||
|
||||
[ConsoleCommand("css_entities", "List entities")]
|
||||
public void OnCommandEntities(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
foreach (var entity in Utilities.GetAllEntities())
|
||||
{
|
||||
command.ReplyToCommand($"{entity.Index}:{entity.DesignerName}");
|
||||
}
|
||||
|
||||
foreach (var entity in Utilities.FindAllEntitiesByDesignerName<CBaseEntity>("cs_"))
|
||||
{
|
||||
command.ReplyToCommand($"{entity.Index}:{entity.DesignerName}");
|
||||
}
|
||||
}
|
||||
|
||||
[ConsoleCommand("css_colors", "List Chat Colors")]
|
||||
public void OnCommandColors(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
@@ -481,6 +489,15 @@ namespace TestPlugin
|
||||
command.ReplyToCommand($" {(char)i}Color 0x{i:x}");
|
||||
}
|
||||
}
|
||||
|
||||
[ConsoleCommand("css_sound", "Play a sound to client")]
|
||||
public void OnCommandSound(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.PlayerPawn.IsValid) return;
|
||||
|
||||
player.ExecuteClientCommand($"play sounds/ui/counter_beep.vsnd");
|
||||
}
|
||||
|
||||
[ConsoleCommand("css_pause", "Pause Game")]
|
||||
public void OnCommandPause(CCSPlayerController? player, CommandInfo command)
|
||||
|
||||
@@ -47,9 +47,10 @@ bool CCoreConfig::Init(char* conf_error, int conf_error_size)
|
||||
m_json = json::parse(ifs);
|
||||
|
||||
try {
|
||||
PublicChatTrigger = m_json["PublicChatTrigger"];
|
||||
SilentChatTrigger = m_json["SilentChatTrigger"];
|
||||
FollowCS2ServerGuidelines = m_json["FollowCS2ServerGuidelines"];
|
||||
PublicChatTrigger = m_json.value("PublicChatTrigger", PublicChatTrigger);
|
||||
SilentChatTrigger = m_json.value("SilentChatTrigger", SilentChatTrigger);
|
||||
FollowCS2ServerGuidelines = m_json.value("FollowCS2ServerGuidelines", FollowCS2ServerGuidelines);
|
||||
PluginHotReloadEnabled = m_json.value("PluginHotReloadEnabled", PluginHotReloadEnabled);
|
||||
} catch (const std::exception& ex) {
|
||||
V_snprintf(conf_error, conf_error_size, "Failed to parse CoreConfig file: %s", ex.what());
|
||||
return false;
|
||||
|
||||
@@ -28,6 +28,7 @@ class CCoreConfig
|
||||
std::vector<std::string> PublicChatTrigger = { std::string("!") };
|
||||
std::vector<std::string> SilentChatTrigger = { std::string("/") };
|
||||
bool FollowCS2ServerGuidelines = true;
|
||||
bool PluginHotReloadEnabled = true;
|
||||
|
||||
using json = nlohmann::json;
|
||||
CCoreConfig(const std::string& path);
|
||||
|
||||
@@ -53,6 +53,52 @@ void* GetEntityPointerFromHandle(ScriptContext& scriptContext) {
|
||||
return globals::entitySystem->GetBaseEntity(*handle);
|
||||
}
|
||||
|
||||
void* GetEntityPointerFromRef(ScriptContext& scriptContext) {
|
||||
auto ref = scriptContext.GetArgument<unsigned int>(0);
|
||||
|
||||
if (ref == INVALID_EHANDLE_INDEX) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CBaseHandle hndl(ref);
|
||||
|
||||
return globals::entitySystem->GetBaseEntity(hndl);
|
||||
}
|
||||
|
||||
unsigned int GetRefFromEntityPointer(ScriptContext& scriptContext) {
|
||||
auto* pEntity = scriptContext.GetArgument<CBaseEntity*>(0);
|
||||
|
||||
if (pEntity == nullptr)
|
||||
{
|
||||
return INVALID_EHANDLE_INDEX;
|
||||
}
|
||||
|
||||
auto hndl = pEntity->GetRefEHandle();
|
||||
|
||||
if (hndl == INVALID_EHANDLE_INDEX)
|
||||
{
|
||||
return INVALID_EHANDLE_INDEX;
|
||||
}
|
||||
|
||||
return hndl.ToInt();
|
||||
}
|
||||
|
||||
bool IsRefValidEntity(ScriptContext& scriptContext) {
|
||||
auto ref = scriptContext.GetArgument<unsigned int>(0);
|
||||
|
||||
if (ref == INVALID_EHANDLE_INDEX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CBaseHandle hndl(ref);
|
||||
|
||||
if (!hndl.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return globals::entitySystem->GetBaseEntity(hndl) != nullptr;
|
||||
}
|
||||
|
||||
void PrintToConsole(ScriptContext& scriptContext) {
|
||||
auto index = scriptContext.GetArgument<int>(0);
|
||||
auto message = scriptContext.GetArgument<const char*>(1);
|
||||
@@ -64,12 +110,20 @@ CEntityIdentity* GetFirstActiveEntity(ScriptContext& script_context) {
|
||||
return globals::entitySystem->m_EntityList.m_pFirstActiveEntity;
|
||||
}
|
||||
|
||||
void* GetConcreteEntityListPointer(ScriptContext& script_context) {
|
||||
return &globals::entitySystem->m_EntityList;
|
||||
}
|
||||
|
||||
REGISTER_NATIVES(entities, {
|
||||
ScriptEngine::RegisterNativeHandler("GET_ENTITY_FROM_INDEX", GetEntityFromIndex);
|
||||
ScriptEngine::RegisterNativeHandler("GET_USERID_FROM_INDEX", GetUserIdFromIndex);
|
||||
ScriptEngine::RegisterNativeHandler("GET_DESIGNER_NAME", GetDesignerName);
|
||||
ScriptEngine::RegisterNativeHandler("GET_ENTITY_POINTER_FROM_HANDLE",
|
||||
GetEntityPointerFromHandle);
|
||||
ScriptEngine::RegisterNativeHandler("GET_ENTITY_POINTER_FROM_REF", GetEntityPointerFromRef);
|
||||
ScriptEngine::RegisterNativeHandler("GET_REF_FROM_ENTITY_POINTER", GetRefFromEntityPointer);
|
||||
ScriptEngine::RegisterNativeHandler("GET_CONCRETE_ENTITY_LIST_POINTER", GetConcreteEntityListPointer);
|
||||
ScriptEngine::RegisterNativeHandler("IS_REF_VALID_ENTITY", IsRefValidEntity);
|
||||
ScriptEngine::RegisterNativeHandler("PRINT_TO_CONSOLE", PrintToConsole);
|
||||
ScriptEngine::RegisterNativeHandler("GET_FIRST_ACTIVE_ENTITY", GetFirstActiveEntity);
|
||||
})
|
||||
|
||||
@@ -2,5 +2,9 @@ GET_ENTITY_FROM_INDEX: index:int -> pointer
|
||||
GET_USERID_FROM_INDEX: index:int -> int
|
||||
GET_DESIGNER_NAME: pointer:pointer -> string
|
||||
GET_ENTITY_POINTER_FROM_HANDLE: entityHandlePointer:pointer -> pointer
|
||||
GET_REF_FROM_ENTITY_POINTER: entityPointer:pointer -> uint
|
||||
GET_ENTITY_POINTER_FROM_REF: entityRef:uint -> pointer
|
||||
GET_CONCRETE_ENTITY_LIST_POINTER: -> pointer
|
||||
IS_REF_VALID_ENTITY: entityRef:uint -> bool
|
||||
PRINT_TO_CONSOLE: index:int, message:string -> void
|
||||
GET_FIRST_ACTIVE_ENTITY: -> pointer
|
||||
@@ -24,6 +24,9 @@
|
||||
#include "schema.h"
|
||||
#include "core/function.h"
|
||||
#include "core/coreconfig.h"
|
||||
#include "interfaces/cschemasystem.h"
|
||||
#include "core/cs2_sdk/interfaces/cschemasystem.h"
|
||||
#include "interfaces/cs2_interfaces.h"
|
||||
|
||||
namespace counterstrikesharp {
|
||||
|
||||
@@ -39,6 +42,20 @@ int16 GetSchemaOffset(ScriptContext& script_context)
|
||||
return m_key.offset;
|
||||
}
|
||||
|
||||
int GetSchemaClassSize(ScriptContext& script_context)
|
||||
{
|
||||
auto className = script_context.GetArgument<const char*>(0);
|
||||
|
||||
CSchemaSystemTypeScope* pType =
|
||||
interfaces::pSchemaSystem->FindTypeScopeForModule(MODULE_PREFIX "server" MODULE_EXT);
|
||||
|
||||
SchemaClassInfoData_t* pClassInfo = pType->FindDeclaredClass(className);
|
||||
if (!pClassInfo)
|
||||
return -1;
|
||||
|
||||
return pClassInfo->m_size;
|
||||
}
|
||||
|
||||
void GetSchemaValueByName(ScriptContext& script_context)
|
||||
{
|
||||
auto instancePointer = script_context.GetArgument<void*>(0);
|
||||
@@ -224,5 +241,6 @@ REGISTER_NATIVES(schema, {
|
||||
ScriptEngine::RegisterNativeHandler("GET_SCHEMA_OFFSET", GetSchemaOffset);
|
||||
ScriptEngine::RegisterNativeHandler("GET_SCHEMA_VALUE_BY_NAME", GetSchemaValueByName);
|
||||
ScriptEngine::RegisterNativeHandler("SET_SCHEMA_VALUE_BY_NAME", SetSchemaValueByName);
|
||||
ScriptEngine::RegisterNativeHandler("GET_SCHEMA_CLASS_SIZE", GetSchemaClassSize);
|
||||
})
|
||||
} // namespace counterstrikesharp
|
||||
@@ -1,3 +1,4 @@
|
||||
GET_SCHEMA_OFFSET: className:string, propName:string -> short
|
||||
GET_SCHEMA_VALUE_BY_NAME: instance:pointer, returnType:int, className:string, propName:string -> any
|
||||
SET_SCHEMA_VALUE_BY_NAME: instance:pointer, returnType:int, className:string, propName:string, value:any -> void
|
||||
SET_SCHEMA_VALUE_BY_NAME: instance:pointer, returnType:int, className:string, propName:string, value:any -> void
|
||||
GET_SCHEMA_CLASS_SIZE: className:string -> int
|
||||
Reference in New Issue
Block a user