mirror of
https://github.com/roflmuffin/CounterStrikeSharp.git
synced 2025-12-07 00:16:36 -08:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d3aa09aa4 | ||
|
|
911084e71e | ||
|
|
5b99206568 | ||
|
|
4bfdf28beb | ||
|
|
9bcd0f7e92 | ||
|
|
11c6486ec5 | ||
|
|
ee69560a66 | ||
|
|
d37e5e194a | ||
|
|
c4740d1cc9 | ||
|
|
7e92f178fd | ||
|
|
107ca08132 | ||
|
|
8cda8d9a50 | ||
|
|
3d59a05de8 | ||
|
|
77b7040d6c | ||
|
|
75de9732ef | ||
|
|
575c859ddb | ||
|
|
e12a7cb17a | ||
|
|
7c7f52a219 | ||
|
|
cd593fb238 | ||
|
|
c5cc65be48 | ||
|
|
59928bbcc5 | ||
|
|
319b116c5f | ||
|
|
e0dc053d22 | ||
|
|
f2e0dac32d | ||
|
|
4e8c18abc7 | ||
|
|
8d1891a3a8 | ||
|
|
6bc43444f7 | ||
|
|
f0c7869f4a | ||
|
|
3e38ed3c77 | ||
|
|
7e9e7c6665 | ||
|
|
9a018f295b | ||
|
|
8b725d435f | ||
|
|
ea3596417a | ||
|
|
123f41914e | ||
|
|
8f69076405 | ||
|
|
44a85d1201 |
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -17,3 +17,9 @@
|
||||
[submodule "libraries/GameTracking-CS2"]
|
||||
path = libraries/GameTracking-CS2
|
||||
url = https://github.com/SteamDatabase/GameTracking-CS2
|
||||
[submodule "libraries/DynoHook"]
|
||||
path = libraries/DynoHook
|
||||
url = git@github.com:qubka/DynoHook.git
|
||||
[submodule "libraries/asmjit"]
|
||||
path = libraries/asmjit
|
||||
url = git@github.com:asmjit/asmjit.git
|
||||
|
||||
@@ -8,7 +8,9 @@ include("makefiles/shared.cmake")
|
||||
add_subdirectory(libraries/spdlog)
|
||||
add_subdirectory(libraries/dyncall)
|
||||
add_subdirectory(libraries/funchook)
|
||||
add_subdirectory(libraries/DynoHook)
|
||||
|
||||
set_property(TARGET dynohook PROPERTY DYNO_ARCH_X86 64)
|
||||
set_property(TARGET funchook-static PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
SET(SOURCE_FILES
|
||||
@@ -81,6 +83,7 @@ SET(SOURCE_FILES
|
||||
src/core/managers/server_manager.h
|
||||
src/scripting/natives/natives_server.cpp
|
||||
libraries/nlohmann/json.hpp
|
||||
src/scripting/natives/natives_dynamichooks.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -96,16 +99,21 @@ set(PROTO_DIRS -I${CMAKE_CURRENT_SOURCE_DIR}/libraries/GameTracking-CS2/Protobuf
|
||||
file(GLOB PROTOS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/GameTracking-CS2/Protobufs/*.proto")
|
||||
|
||||
## Generate protobuf source & headers
|
||||
#add_custom_command(
|
||||
# OUTPUT protobuf_output_stamp
|
||||
# COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/compile.sh
|
||||
# COMMENT "Generating protobuf files using compile.sh script"
|
||||
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf
|
||||
# DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/compile.sh
|
||||
# VERBATIM
|
||||
#)
|
||||
#
|
||||
#SET(SOURCE_FILES ${SOURCE_FILES} protobuf_output_stamp)
|
||||
if (LINUX)
|
||||
set(PROTOC_EXECUTABLE ${CMAKE_CURRENT_SOURCE_DIR}/libraries/hl2sdk-cs2/devtools/bin/linux/protoc)
|
||||
elseif(WIN32)
|
||||
set(PROTOC_EXECUTABLE ${CMAKE_CURRENT_SOURCE_DIR}/libraries/hl2sdk-cs2/devtools/bin/protoc.exe)
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT protobuf_output_stamp
|
||||
COMMAND ${PROTOC_EXECUTABLE} --proto_path=thirdparty/protobuf-3.21.8/src --proto_path=common --cpp_out=common common/network_connection.proto
|
||||
COMMENT "Generating protobuf file"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/libraries/hl2sdk-cs2
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
SET(SOURCE_FILES ${SOURCE_FILES} protobuf_output_stamp)
|
||||
|
||||
# Sources
|
||||
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${NATIVES_SOURCES} ${CONVERSIONS_SOURCES} ${CONVERSIONS_HEADERS})
|
||||
|
||||
@@ -26,6 +26,26 @@
|
||||
"linux": 89
|
||||
}
|
||||
},
|
||||
"CCSPlayerController_Respawn": {
|
||||
"offsets": {
|
||||
"windows": 241,
|
||||
"linux": 243
|
||||
}
|
||||
},
|
||||
"CCSPlayerPawn_Respawn": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x40\\x53\\x48\\x83\\xEC\\x20\\x8B\\x91\\x38\\x0B\\x00\\x00\\x48\\x8B\\xD9",
|
||||
"linux": "\\x8B\\x8F\\x40\\x0E\\x00\\x00\\x83\\xF9\\xFF\\x0F\\x84\\xD9\\x01"
|
||||
}
|
||||
},
|
||||
"CCSPlayerPawnBase_PostThink": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x48\\x8B\\xC4\\x48\\x89\\x48\\x08\\x55\\x53\\x56\\x57\\x41\\x56\\x48\\x8D\\xA8\\xD8\\xFE\\xFF\\xFF",
|
||||
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x49\\x89\\xFF\\x41\\x56\\x41\\x55\\x41\\x54\\x53\\x48\\x81\\xEC\\x2A\\x2A\\x2A\\x2A\\xE8\\x2A\\x2A\\x2A\\x2A\\x4C"
|
||||
}
|
||||
},
|
||||
"GiveNamedItem": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
@@ -51,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
|
||||
}
|
||||
},
|
||||
@@ -83,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": {
|
||||
@@ -112,6 +132,27 @@
|
||||
"linux": 147
|
||||
}
|
||||
},
|
||||
"CBaseEntity_TakeDamageOld": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x40\\x56\\x57\\x48\\x83\\xEC\\x58\\x48\\x8B\\x41\\x10",
|
||||
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x41\\x55\\x41\\x54\\x49\\x89\\xFC\\x53\\x48\\x83\\xEC\\x38\\x4C\\x8D\\x2D\\x2A\\x2A\\x2A\\x2A\\x49\\x8B\\x7D\\x00\\x48\\x85\\xFF\\x0F\\x84\\x2A\\x2A\\x2A\\x2A"
|
||||
}
|
||||
},
|
||||
"CBaseTrigger_StartTouch": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x41\\x56\\x41\\x57\\x48\\x83\\xEC\\x58\\x48\\x8B\\x01",
|
||||
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x56\\x49\\x89\\xF6\\x41\\x55\\x49\\x89\\xFD\\x41\\x54\\x53\\xBB"
|
||||
}
|
||||
},
|
||||
"CBaseTrigger_EndTouch": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x40\\x53\\x57\\x41\\x55\\x48\\x83\\xEC\\x40",
|
||||
"linux": "\\x55\\xBA\\xFF\\xFF\\xFF\\xFF\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x41\\x55\\x49"
|
||||
}
|
||||
},
|
||||
"GameEntitySystem": {
|
||||
"offsets": {
|
||||
"windows": 88,
|
||||
|
||||
@@ -16,6 +16,10 @@ Adding an Admin is as simple as creating a new entry in the `configs/admins.json
|
||||
"ZoNiCaL": {
|
||||
"identity": "76561198808392634",
|
||||
"flags": ["@css/changemap", "@css/generic"]
|
||||
},
|
||||
"another ZoNiCaL": {
|
||||
"identity": "STEAM_0:1:1",
|
||||
"flags": ["@css/generic"]
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -54,3 +58,4 @@ However there is a somewhat standardized list of flags that it is advised you us
|
||||
:::note
|
||||
CounterStrikeSharp does not implement traditional admin command such as `!slay`, `!kick`, and `!ban`. It is up to individual plugins to implement these commands.
|
||||
:::
|
||||
|
||||
|
||||
@@ -18,8 +18,25 @@ Groups can be created to group a series of permissions together under one tag. T
|
||||
}
|
||||
```
|
||||
|
||||
You can add admins to groups using the `groups` array in `configs/admins.json`
|
||||
```json
|
||||
{
|
||||
"erikj": {
|
||||
"identity": "76561198808392634",
|
||||
"flags": ["@mycustomplugin/admin"],
|
||||
"groups": ["#css/simple-admin"]
|
||||
},
|
||||
"Another erikj": {
|
||||
"identity": "STEAM_0:1:1",
|
||||
"flags": ["@mycustomplugin/admin"],
|
||||
"groups": ["#css/simple-admin"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::note
|
||||
All group names MUST start with a hashtag # character, otherwise CounterStrikeSharp will recognize the group.
|
||||
All group names MUST start with a hashtag # character, otherwise CounterStrikeSharp won't recognize the group.
|
||||
:::
|
||||
|
||||
Admins can be assigned to multiple groups and they will inherit their flags. You can manually assign groups to players in code with `AdminManager.AddPlayerToGroup` and `AdminManager.RemovePlayerFromGroup`.
|
||||
|
||||
|
||||
@@ -17,8 +17,13 @@ The first parameter type must be a subclass of the `GameEvent` class. The names
|
||||
[GameEventHandler]
|
||||
public HookResult OnPlayerConnect(EventPlayerConnect @event, GameEventInfo info)
|
||||
{
|
||||
// Userid will give you a reference to a CCSPlayerController class
|
||||
// Userid will give you a reference to a CCSPlayerController class.
|
||||
// Before accessing any of its fields, you must first check if the Userid
|
||||
// handle is actually valid, otherwise you may run into runtime exceptions.
|
||||
// See the documentation section on Referencing Players for details.
|
||||
if (@event.Userid.IsValid) {
|
||||
Logger.LogInformation("Player {Name} has connected!", @event.Userid.PlayerName);
|
||||
}
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
71
docs/src/content/docs/guides/dependency-injection.md
Normal file
71
docs/src/content/docs/guides/dependency-injection.md
Normal file
@@ -0,0 +1,71 @@
|
||||
---
|
||||
title: Dependency Injection
|
||||
description: How to make use of dependency injection in CounterStrikeSharp
|
||||
sidebar:
|
||||
order: 1
|
||||
---
|
||||
|
||||
`CounterStrikeSharp` uses a standard <a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-8.0" target="_blank">`IServiceCollection`</a> to allow for dependency injection in plugins.
|
||||
|
||||
There are a handful of standard services that are predefined for you (`ILogger` for logging for instance), with more to come in the future. To add your own scoped & singleton services to the container, you can create a new class that implements the `IPluginServiceCollection<T>` interface for your plugin.
|
||||
|
||||
```csharp
|
||||
public class TestPlugin : BasePlugin
|
||||
{
|
||||
// Plugin code...
|
||||
}
|
||||
|
||||
public class TestPluginServiceCollection : IPluginServiceCollection<TestPlugin>
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection serviceCollection)
|
||||
{
|
||||
serviceCollection.AddScoped<ExampleInjectedClass>();
|
||||
serviceCollection.AddLogging(builder => ...);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
CounterStrikeSharp will search your assembly for any implementations of `IPlugin` and then any implementations of `IPluginServiceCollection<T>` where `T` is your plugin. It will then configure the service provider and then request a singleton instance of your plugin before proceeding to the load step.
|
||||
|
||||
In this way, any dependencies that are listed in your plugin class constructor will automatically get injected at instantation time (before load).
|
||||
|
||||
### Example
|
||||
|
||||
```csharp
|
||||
public class TestInjectedClass
|
||||
{
|
||||
private readonly ILogger<TestInjectedClass> _logger;
|
||||
|
||||
public TestInjectedClass(ILogger<TestInjectedClass> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Hello()
|
||||
{
|
||||
_logger.LogInformation("Hello World from Test Injected Class");
|
||||
}
|
||||
}
|
||||
|
||||
public class TestPluginServiceCollection : IPluginServiceCollection<SamplePlugin>
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection serviceCollection)
|
||||
{
|
||||
serviceCollection.AddScoped<TestInjectedClass>();
|
||||
}
|
||||
}
|
||||
|
||||
public class SamplePlugin : BasePlugin
|
||||
{
|
||||
private readonly TestInjectedClass _testInjectedClass;
|
||||
public SamplePlugin(TestInjectedClass testInjectedClass)
|
||||
{
|
||||
_testInjectedClass = testInjectedClass;
|
||||
}
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
_testInjectedClass.Hello();
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,6 +1,8 @@
|
||||
---
|
||||
title: Getting Started
|
||||
description: How to get started installing & using CounterStrikeSharp.
|
||||
sidebar:
|
||||
order: 0
|
||||
---
|
||||
|
||||
# Installation
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
---
|
||||
title: Hello World Plugin
|
||||
description: How to write your first plugin for CounterStrikeSharp
|
||||
sidebar:
|
||||
order: 0
|
||||
---
|
||||
|
||||
## Creating a New Project
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
1
libraries/DynoHook
Submodule
1
libraries/DynoHook
Submodule
Submodule libraries/DynoHook added at d7f8ebb059
1
libraries/asmjit
Submodule
1
libraries/asmjit
Submodule
Submodule libraries/asmjit added at 0dd16b0a98
Submodule libraries/hl2sdk-cs2 updated: 492b8f9663...1d394d3365
@@ -16,7 +16,8 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-offsetof -Wno-reorder")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -msse -fno-strict-aliasing")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-threadsafe-statics -v -fvisibility=default")
|
||||
|
||||
set(COUNTER_STRIKE_SHARP_LINK_LIBRARIES
|
||||
SET(
|
||||
COUNTER_STRIKE_SHARP_LINK_LIBRARIES
|
||||
${SOURCESDK_LIB}/linux64/libtier0.so
|
||||
${SOURCESDK_LIB}/linux64/tier1.a
|
||||
${SOURCESDK_LIB}/linux64/interfaces.a
|
||||
@@ -26,4 +27,5 @@ set(COUNTER_STRIKE_SHARP_LINK_LIBRARIES
|
||||
dyncall_s
|
||||
distorm
|
||||
funchook-static
|
||||
dynohook
|
||||
)
|
||||
@@ -14,6 +14,8 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING
|
||||
# TODO: Use C++20 instead.
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
Set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||
Set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
|
||||
set(CMAKE_STATIC_LIBRARY_PREFIX "")
|
||||
|
||||
set(SOURCESDK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libraries/hl2sdk-cs2)
|
||||
@@ -26,6 +28,7 @@ add_definitions(-DMETA_IS_SOURCE2)
|
||||
|
||||
include_directories(
|
||||
${SOURCESDK}
|
||||
${SOURCESDK}/thirdparty/protobuf-3.21.8/src
|
||||
${SOURCESDK}/common
|
||||
${SOURCESDK}/game/shared
|
||||
${SOURCESDK}/game/server
|
||||
@@ -44,6 +47,7 @@ include_directories(
|
||||
libraries/spdlog/include
|
||||
libraries/tl
|
||||
libraries/funchook/include
|
||||
libraries/DynoHook/src
|
||||
libraries
|
||||
)
|
||||
|
||||
|
||||
@@ -17,4 +17,5 @@ set(COUNTER_STRIKE_SHARP_LINK_LIBRARIES
|
||||
dyncall_s
|
||||
distorm
|
||||
funchook-static
|
||||
dynohook
|
||||
)
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
67
managed/CounterStrikeSharp.API/Bootstrap.cs
Normal file
67
managed/CounterStrikeSharp.API/Bootstrap.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Hosting;
|
||||
using CounterStrikeSharp.API.Core.Logging;
|
||||
using CounterStrikeSharp.API.Core.Plugin;
|
||||
using CounterStrikeSharp.API.Core.Plugin.Host;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Serilog;
|
||||
|
||||
namespace CounterStrikeSharp.API;
|
||||
|
||||
public static class Bootstrap
|
||||
{
|
||||
[UnmanagedCallersOnly]
|
||||
// Used by .NET Host in C++ to initiate loading
|
||||
public static int Run()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Path to /game/csgo/addons/counterstrikesharp
|
||||
var contentRoot = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.Parent.FullName;
|
||||
|
||||
using var host = Host.CreateDefaultBuilder()
|
||||
.UseContentRoot(contentRoot)
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddLogging(builder =>
|
||||
{
|
||||
builder.ClearProviders();
|
||||
builder.AddCoreLogging(contentRoot);
|
||||
});
|
||||
|
||||
services.AddSingleton<IScriptHostConfiguration, ScriptHostConfiguration>();
|
||||
services.AddScoped<Application>();
|
||||
services.AddSingleton<IPluginManager, PluginManager>();
|
||||
services.AddScoped<IPluginContextQueryHandler, PluginContextQueryHandler>();
|
||||
|
||||
services.Scan(i => i.FromCallingAssembly()
|
||||
.AddClasses(c => c.AssignableTo<IStartupService>())
|
||||
.AsSelfWithInterfaces()
|
||||
.WithSingletonLifetime());
|
||||
})
|
||||
.Build();
|
||||
|
||||
using IServiceScope scope = host.Services.CreateScope();
|
||||
|
||||
// TODO: Improve static singleton access
|
||||
GameData.GameDataProvider = scope.ServiceProvider.GetRequiredService<GameDataProvider>();
|
||||
|
||||
var application = scope.ServiceProvider.GetRequiredService<Application>();
|
||||
application.Start();
|
||||
|
||||
return 1;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.Error.WriteLine(e);
|
||||
Log.Fatal(e, "Failed to start application");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,6 +157,56 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
public static T DynamicHookGetReturn<T>(IntPtr hook, int datatype){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(hook);
|
||||
ScriptContext.GlobalScriptContext.Push(datatype);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0x4F5B80D0);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
return (T)ScriptContext.GlobalScriptContext.GetResult(typeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
public static void DynamicHookSetReturn<T>(IntPtr hook, int datatype, T value){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(hook);
|
||||
ScriptContext.GlobalScriptContext.Push(datatype);
|
||||
ScriptContext.GlobalScriptContext.Push(value);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0xDB297E44);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
}
|
||||
}
|
||||
|
||||
public static T DynamicHookGetParam<T>(IntPtr hook, int datatype, int paramindex){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(hook);
|
||||
ScriptContext.GlobalScriptContext.Push(datatype);
|
||||
ScriptContext.GlobalScriptContext.Push(paramindex);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0x5F5ABDD5);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
return (T)ScriptContext.GlobalScriptContext.GetResult(typeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
public static void DynamicHookSetParam<T>(IntPtr hook, int datatype, int paramindex, T value){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(hook);
|
||||
ScriptContext.GlobalScriptContext.Push(datatype);
|
||||
ScriptContext.GlobalScriptContext.Push(paramindex);
|
||||
ScriptContext.GlobalScriptContext.Push(value);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0xA96CFBC1);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetMapName(){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
@@ -484,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();
|
||||
@@ -803,6 +896,30 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
public static void HookFunction(IntPtr function, InputArgument hook, bool post){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(function);
|
||||
ScriptContext.GlobalScriptContext.Push((InputArgument)hook);
|
||||
ScriptContext.GlobalScriptContext.Push(post);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0xA6C8BA9B);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
}
|
||||
}
|
||||
|
||||
public static void UnhookFunction(IntPtr function, InputArgument hook, bool post){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(function);
|
||||
ScriptContext.GlobalScriptContext.Push((InputArgument)hook);
|
||||
ScriptContext.GlobalScriptContext.Push(post);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0x2051B00);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
}
|
||||
}
|
||||
|
||||
public static T ExecuteVirtualFunction<T>(IntPtr function, object[] arguments){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
@@ -893,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();
|
||||
|
||||
254
managed/CounterStrikeSharp.API/Core/Application.cs
Normal file
254
managed/CounterStrikeSharp.API/Core/Application.cs
Normal file
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* This file is part of CounterStrikeSharp.
|
||||
* CounterStrikeSharp is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* CounterStrikeSharp is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
|
||||
*/
|
||||
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using CounterStrikeSharp.API.Core.Hosting;
|
||||
using CounterStrikeSharp.API.Core.Plugin;
|
||||
using CounterStrikeSharp.API.Core.Plugin.Host;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Menu;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
public sealed class Application
|
||||
{
|
||||
private static Application _instance = null!;
|
||||
public ILogger Logger { get; }
|
||||
public static Application Instance => _instance!;
|
||||
|
||||
public static string RootDirectory => Instance._scriptHostConfiguration.RootPath;
|
||||
|
||||
private readonly IScriptHostConfiguration _scriptHostConfiguration;
|
||||
private readonly GameDataProvider _gameDataProvider;
|
||||
private readonly CoreConfig _coreConfig;
|
||||
private readonly IPluginManager _pluginManager;
|
||||
private readonly IPluginContextQueryHandler _pluginContextQueryHandler;
|
||||
|
||||
public Application(ILoggerFactory loggerFactory, IScriptHostConfiguration scriptHostConfiguration,
|
||||
GameDataProvider gameDataProvider, CoreConfig coreConfig, IPluginManager pluginManager,
|
||||
IPluginContextQueryHandler pluginContextQueryHandler)
|
||||
{
|
||||
Logger = loggerFactory.CreateLogger("Core");
|
||||
_scriptHostConfiguration = scriptHostConfiguration;
|
||||
_gameDataProvider = gameDataProvider;
|
||||
_coreConfig = coreConfig;
|
||||
_pluginManager = pluginManager;
|
||||
_pluginContextQueryHandler = pluginContextQueryHandler;
|
||||
_instance = this;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
Logger.LogInformation("CounterStrikeSharp is starting up...");
|
||||
|
||||
_coreConfig.Load();
|
||||
_gameDataProvider.Load();
|
||||
|
||||
var adminGroupsPath = Path.Combine(_scriptHostConfiguration.RootPath, "configs", "admin_groups.json");
|
||||
Logger.LogInformation("Loading Admin Groups from {Path}", adminGroupsPath);
|
||||
AdminManager.LoadAdminGroups(adminGroupsPath);
|
||||
|
||||
var adminPath = Path.Combine(_scriptHostConfiguration.RootPath, "configs", "admins.json");
|
||||
Logger.LogInformation("Loading Admins from {Path}", adminPath);
|
||||
AdminManager.LoadAdminData(adminPath);
|
||||
|
||||
var overridePath = Path.Combine(_scriptHostConfiguration.RootPath, "configs", "admin_overrides.json");
|
||||
Logger.LogInformation("Loading Admin Command Overrides from {Path}", overridePath);
|
||||
AdminManager.LoadCommandOverrides(overridePath);
|
||||
|
||||
AdminManager.MergeGroupPermsIntoAdmins();
|
||||
|
||||
_pluginManager.Load();
|
||||
|
||||
for (var i = 1; i <= 9; i++)
|
||||
{
|
||||
CommandUtils.AddStandaloneCommand("css_" + i, "Command Key Handler", (player, info) =>
|
||||
{
|
||||
if (player == null) return;
|
||||
var key = Convert.ToInt32(info.GetArg(0).Split("_")[1]);
|
||||
ChatMenus.OnKeyPress(player, key);
|
||||
});
|
||||
}
|
||||
|
||||
RegisterPluginCommands();
|
||||
}
|
||||
|
||||
[RequiresPermissions("@css/generic")]
|
||||
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
private void OnCSSCommand(CCSPlayerController? caller, CommandInfo info)
|
||||
{
|
||||
var currentVersion = Api.GetVersion();
|
||||
|
||||
info.ReplyToCommand(
|
||||
" CounterStrikeSharp was created and is maintained by Michael \"roflmuffin\" Wilson.\n" +
|
||||
" Counter-Strike Sharp uses code borrowed from SourceMod, Source.Python, FiveM, Saul Rennison and CS2Fixes.\n" +
|
||||
" See ACKNOWLEDGEMENTS.md for more information.\n" +
|
||||
" Current API Version: " + currentVersion, true);
|
||||
return;
|
||||
}
|
||||
|
||||
[RequiresPermissions("@css/generic")]
|
||||
[CommandHelper(minArgs: 1,
|
||||
usage: "[option]\n" +
|
||||
" list - List all plugins currently loaded.\n" +
|
||||
" start / load - Loads a plugin not currently loaded.\n" +
|
||||
" stop / unload - Unloads a plugin currently loaded.\n" +
|
||||
" restart / reload - Reloads a plugin currently loaded.",
|
||||
whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
private void OnCSSPluginCommand(CCSPlayerController? caller, CommandInfo info)
|
||||
{
|
||||
switch (info.GetArg(1))
|
||||
{
|
||||
case "list":
|
||||
{
|
||||
info.ReplyToCommand(
|
||||
$" List of all plugins currently loaded by CounterStrikeSharp: {_pluginManager.GetLoadedPlugins().Count()} plugins loaded.",
|
||||
true);
|
||||
|
||||
foreach (var plugin in _pluginManager.GetLoadedPlugins())
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat(" [#{0}:{1}]: \"{2}\" ({3})", plugin.PluginId,
|
||||
plugin.State.ToString().ToUpper(), plugin.Plugin.ModuleName,
|
||||
plugin.Plugin.ModuleVersion);
|
||||
if (!string.IsNullOrEmpty(plugin.Plugin.ModuleAuthor))
|
||||
sb.AppendFormat(" by {0}", plugin.Plugin.ModuleAuthor);
|
||||
if (!string.IsNullOrEmpty(plugin.Plugin.ModuleDescription))
|
||||
{
|
||||
sb.Append("\n");
|
||||
sb.Append(" ");
|
||||
sb.Append(plugin.Plugin.ModuleDescription);
|
||||
}
|
||||
|
||||
info.ReplyToCommand(sb.ToString(), true);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "start":
|
||||
case "load":
|
||||
{
|
||||
if (info.ArgCount < 2)
|
||||
{
|
||||
info.ReplyToCommand(
|
||||
"Valid usage: css_plugins start/load [relative plugin path || absolute plugin path] (e.g \"TestPlugin\", \"plugins/TestPlugin/TestPlugin.dll\")\n",
|
||||
true);
|
||||
break;
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (!path.EndsWith(".dll"))
|
||||
{
|
||||
path = Path.Combine(_scriptHostConfiguration.RootPath, $"plugins/{path}/{path}.dll");
|
||||
}
|
||||
else
|
||||
{
|
||||
path = Path.Combine(_scriptHostConfiguration.RootPath, 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;
|
||||
}
|
||||
|
||||
case "stop":
|
||||
case "unload":
|
||||
{
|
||||
if (info.ArgCount < 2)
|
||||
{
|
||||
info.ReplyToCommand(
|
||||
"Valid usage: css_plugins stop/unload [plugin name || #plugin id] (e.g \"TestPlugin\", \"1\")\n",
|
||||
true);
|
||||
break;
|
||||
}
|
||||
|
||||
var pluginIdentifier = info.GetArg(2);
|
||||
IPluginContext? plugin = _pluginContextQueryHandler.FindPluginByIdOrName(pluginIdentifier);
|
||||
if (plugin == null)
|
||||
{
|
||||
info.ReplyToCommand($"Could not unload plugin \"{pluginIdentifier}\")", true);
|
||||
break;
|
||||
}
|
||||
|
||||
plugin.Unload(false);
|
||||
break;
|
||||
}
|
||||
|
||||
case "restart":
|
||||
case "reload":
|
||||
{
|
||||
if (info.ArgCount < 2)
|
||||
{
|
||||
info.ReplyToCommand(
|
||||
"Valid usage: css_plugins restart/reload [plugin name || #plugin id] (e.g \"TestPlugin\", \"#1\")\n",
|
||||
true);
|
||||
break;
|
||||
}
|
||||
|
||||
var pluginIdentifier = info.GetArg(2);
|
||||
var plugin = _pluginContextQueryHandler.FindPluginByIdOrName(pluginIdentifier);
|
||||
|
||||
if (plugin == null)
|
||||
{
|
||||
info.ReplyToCommand($"Could not reload plugin \"{pluginIdentifier}\")", true);
|
||||
break;
|
||||
}
|
||||
|
||||
plugin.Unload(true);
|
||||
plugin.Load(true);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
info.ReplyToCommand("Valid usage: css_plugins [option]\n" +
|
||||
" list - List all plugins currently loaded.\n" +
|
||||
" start / load - Loads a plugin not currently loaded.\n" +
|
||||
" stop / unload - Unloads a plugin currently loaded.\n" +
|
||||
" restart / reload - Reloads a plugin currently loaded."
|
||||
, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterPluginCommands()
|
||||
{
|
||||
CommandUtils.AddStandaloneCommand("css", "Counter-Strike Sharp options.", OnCSSCommand);
|
||||
CommandUtils.AddStandaloneCommand("css_plugins", "Counter-Strike Sharp plugin options.",
|
||||
OnCSSPluginCommand);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,25 +18,19 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Loader;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Core.Attributes.Registration;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Events;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Listeners;
|
||||
using CounterStrikeSharp.API.Modules.Timers;
|
||||
using McMaster.NETCore.Plugins;
|
||||
using CounterStrikeSharp.API.Modules.Config;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
public abstract class BasePlugin : IPlugin, IDisposable
|
||||
public abstract class BasePlugin : IPlugin
|
||||
{
|
||||
private bool _disposed;
|
||||
|
||||
@@ -51,7 +45,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
|
||||
public virtual string ModuleDescription { get; }
|
||||
|
||||
public string ModulePath { get; internal set; }
|
||||
public string ModulePath { get; set; }
|
||||
|
||||
public string ModuleDirectory => Path.GetDirectoryName(ModulePath);
|
||||
public ILogger Logger { get; set; }
|
||||
@@ -316,7 +310,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
.Select(p => p.GetCustomAttribute<CastFromAttribute>()?.Type)
|
||||
.ToArray();
|
||||
|
||||
GlobalContext.Instance.Logger.LogDebug("Registering listener for {ListenerName} with {ParameterCount} parameters",
|
||||
Application.Instance.Logger.LogDebug("Registering listener for {ListenerName} with {ParameterCount} parameters",
|
||||
listenerName, parameterTypes.Length);
|
||||
|
||||
var wrappedHandler = new Action<ScriptContext>(context =>
|
||||
|
||||
@@ -19,12 +19,13 @@ using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using System.Collections.Generic;
|
||||
using CounterStrikeSharp.API.Core.Hosting;
|
||||
using CounterStrikeSharp.API.Core.Logging;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
@@ -34,17 +35,20 @@ namespace CounterStrikeSharp.API.Core
|
||||
/// </summary>
|
||||
internal sealed partial class CoreConfigData
|
||||
{
|
||||
[JsonPropertyName("PublicChatTrigger")] public IEnumerable<string> PublicChatTrigger { get; set; } = new HashSet<string>() { "!" };
|
||||
[JsonPropertyName("PublicChatTrigger")]
|
||||
public IEnumerable<string> PublicChatTrigger { get; set; } = new HashSet<string>() { "!" };
|
||||
|
||||
[JsonPropertyName("SilentChatTrigger")] public IEnumerable<string> SilentChatTrigger { get; set; } = new HashSet<string>() { "/" };
|
||||
[JsonPropertyName("SilentChatTrigger")]
|
||||
public IEnumerable<string> SilentChatTrigger { get; set; } = new HashSet<string>() { "/" };
|
||||
|
||||
[JsonPropertyName("FollowCS2ServerGuidelines")] public bool FollowCS2ServerGuidelines { get; set; } = true;
|
||||
[JsonPropertyName("FollowCS2ServerGuidelines")]
|
||||
public bool FollowCS2ServerGuidelines { get; set; } = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configuration related to the Core API.
|
||||
/// </summary>
|
||||
public static partial class CoreConfig
|
||||
public partial class CoreConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// List of characters to use for public chat triggers.
|
||||
@@ -78,49 +82,56 @@ namespace CounterStrikeSharp.API.Core
|
||||
public static bool FollowCS2ServerGuidelines => _coreConfig.FollowCS2ServerGuidelines;
|
||||
}
|
||||
|
||||
public static partial class CoreConfig
|
||||
public partial class CoreConfig : IStartupService
|
||||
{
|
||||
private static CoreConfigData _coreConfig = new CoreConfigData();
|
||||
|
||||
// TODO: ServiceCollection
|
||||
private static ILogger _logger = CoreLogging.Factory.CreateLogger("CoreConfig");
|
||||
private readonly ILogger<CoreConfig> _logger;
|
||||
|
||||
static CoreConfig()
|
||||
private readonly string _coreConfigPath;
|
||||
|
||||
public CoreConfig(IScriptHostConfiguration scriptHostConfiguration, ILogger<CoreConfig> logger)
|
||||
{
|
||||
CommandUtils.AddStandaloneCommand("css_core_reload", "Reloads the core configuration file.", ReloadCoreConfigCommand);
|
||||
_logger = logger;
|
||||
_coreConfigPath = Path.Join(scriptHostConfiguration.ConfigsPath, "core.json");
|
||||
}
|
||||
|
||||
[RequiresPermissions("@css/config")]
|
||||
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
private static void ReloadCoreConfigCommand(CCSPlayerController? player, CommandInfo command)
|
||||
private void ReloadCoreConfigCommand(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
var rootDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.Parent;
|
||||
Load(Path.Combine(rootDir.FullName, "configs", "core.json"));
|
||||
Load();
|
||||
}
|
||||
|
||||
public static void Load(string coreConfigPath)
|
||||
public void Load()
|
||||
{
|
||||
if (!File.Exists(coreConfigPath))
|
||||
CommandUtils.AddStandaloneCommand("css_core_reload", "Reloads the core configuration file.",
|
||||
ReloadCoreConfigCommand);
|
||||
|
||||
if (!File.Exists(_coreConfigPath))
|
||||
{
|
||||
_logger.LogWarning("Core configuration could not be found at path \"{CoreConfigPath}\", fallback values will be used.", coreConfigPath);
|
||||
_logger.LogWarning(
|
||||
"Core configuration could not be found at path \"{CoreConfigPath}\", fallback values will be used.",
|
||||
_coreConfigPath);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var data = JsonSerializer.Deserialize<CoreConfigData>(File.ReadAllText(coreConfigPath), new JsonSerializerOptions() { ReadCommentHandling = JsonCommentHandling.Skip });
|
||||
var data = JsonSerializer.Deserialize<CoreConfigData>(File.ReadAllText(_coreConfigPath),
|
||||
new JsonSerializerOptions() { ReadCommentHandling = JsonCommentHandling.Skip });
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
_coreConfig = data;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Successfully loaded core configuration.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to load core configuration, fallback values will be used");
|
||||
}
|
||||
|
||||
_logger.LogInformation("Successfully loaded core configuration");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
GlobalContext.Instance.Logger.LogError(e, "Error invoking callback");
|
||||
Application.Instance.Logger.LogError(e, "Error invoking callback");
|
||||
}
|
||||
});
|
||||
s_callback = dg;
|
||||
@@ -141,7 +141,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
ms_references.Remove(reference);
|
||||
|
||||
GlobalContext.Instance.Logger.LogDebug("Removing function/callback reference: {Reference}", reference);
|
||||
Application.Instance.Logger.LogDebug("Removing function/callback reference: {Reference}", reference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,12 @@ using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using CounterStrikeSharp.API.Core.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
class LoadedGameData
|
||||
public class LoadedGameData
|
||||
{
|
||||
[JsonPropertyName("signatures")] public Signatures? Signatures { get; set; }
|
||||
[JsonPropertyName("offsets")] public Offsets? Offsets { get; set; }
|
||||
@@ -31,33 +32,45 @@ public class Offsets
|
||||
[JsonPropertyName("linux")] public int Linux { get; set; }
|
||||
}
|
||||
|
||||
public static class GameData
|
||||
public sealed class GameDataProvider : IStartupService
|
||||
{
|
||||
private static Dictionary<string, LoadedGameData> _methods;
|
||||
private readonly string _gameDataFilePath;
|
||||
public Dictionary<string,LoadedGameData> Methods;
|
||||
private readonly ILogger<GameDataProvider> _logger;
|
||||
|
||||
public static void Load(string gameDataPath)
|
||||
public GameDataProvider(IScriptHostConfiguration scriptHostConfiguration, ILogger<GameDataProvider> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_gameDataFilePath = Path.Join(scriptHostConfiguration.GameDataPath, "gamedata.json");
|
||||
}
|
||||
|
||||
public void Load()
|
||||
{
|
||||
try
|
||||
{
|
||||
_methods = JsonSerializer.Deserialize<Dictionary<string, LoadedGameData>>(File.ReadAllText(gameDataPath))!;
|
||||
|
||||
GlobalContext.Instance.Logger.LogInformation("Loaded game data with {Count} methods.", _methods.Count);
|
||||
Methods = JsonSerializer.Deserialize<Dictionary<string, LoadedGameData>>(File.ReadAllText(_gameDataFilePath))!;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
GlobalContext.Instance.Logger.LogError(ex, "Failed to load game data");
|
||||
_logger.LogError(ex, "Failed to load game data");
|
||||
}
|
||||
|
||||
_logger.LogInformation("Successfully loaded {Count} game data entries from {Path}", Methods.Count, _gameDataFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
public static class GameData
|
||||
{
|
||||
internal static GameDataProvider GameDataProvider { get; set; } = null!;
|
||||
public static string GetSignature(string key)
|
||||
{
|
||||
GlobalContext.Instance.Logger.LogDebug("Getting signature: {Key}", key);
|
||||
if (!_methods.ContainsKey(key))
|
||||
Application.Instance.Logger.LogDebug("Getting signature: {Key}", key);
|
||||
if (!GameDataProvider.Methods.ContainsKey(key))
|
||||
{
|
||||
throw new ArgumentException($"Method {key} not found in gamedata.json");
|
||||
}
|
||||
|
||||
var methodMetadata = _methods[key];
|
||||
var methodMetadata = GameDataProvider.Methods[key];
|
||||
if (methodMetadata.Signatures == null)
|
||||
{
|
||||
throw new InvalidOperationException($"No signatures found for {key} in gamedata.json");
|
||||
@@ -79,12 +92,12 @@ public static class GameData
|
||||
|
||||
public static int GetOffset(string key)
|
||||
{
|
||||
if (!_methods.ContainsKey(key))
|
||||
if (!GameDataProvider.Methods.ContainsKey(key))
|
||||
{
|
||||
throw new Exception($"Method {key} not found in gamedata.json");
|
||||
}
|
||||
|
||||
var methodMetadata = _methods[key];
|
||||
var methodMetadata = GameDataProvider.Methods[key];
|
||||
|
||||
if (methodMetadata.Offsets == null)
|
||||
{
|
||||
|
||||
@@ -1,355 +0,0 @@
|
||||
/*
|
||||
* This file is part of CounterStrikeSharp.
|
||||
* CounterStrikeSharp is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* CounterStrikeSharp is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using CounterStrikeSharp.API.Core.Logging;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Menu;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Serilog;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
public sealed class GlobalContext
|
||||
{
|
||||
private static GlobalContext _instance = null;
|
||||
|
||||
public ILogger Logger { get; }
|
||||
public static GlobalContext Instance => _instance;
|
||||
|
||||
public static string RootDirectory => _instance.rootDir.FullName;
|
||||
private DirectoryInfo rootDir;
|
||||
|
||||
private readonly List<PluginContext> _loadedPlugins = new();
|
||||
|
||||
public GlobalContext()
|
||||
{
|
||||
rootDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.Parent;
|
||||
_instance = this;
|
||||
Logger = CoreLogging.Factory.CreateLogger("Core");
|
||||
|
||||
Logger.LogInformation("CounterStrikeSharp is starting up...");
|
||||
}
|
||||
|
||||
~GlobalContext()
|
||||
{
|
||||
foreach (var plugin in _loadedPlugins)
|
||||
{
|
||||
plugin.Unload();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnNativeUnload()
|
||||
{
|
||||
foreach (var plugin in _loadedPlugins)
|
||||
{
|
||||
plugin.Unload();
|
||||
}
|
||||
}
|
||||
public void InitGlobalContext()
|
||||
{
|
||||
var coreConfigPath = Path.Combine(rootDir.FullName, "configs", "core.json");
|
||||
Logger.LogInformation("Loading CoreConfig from {Path}", coreConfigPath);
|
||||
CoreConfig.Load(coreConfigPath);
|
||||
|
||||
var gameDataPath = Path.Combine(rootDir.FullName, "gamedata", "gamedata.json");
|
||||
Logger.LogInformation("Loading GameData from {Path}", gameDataPath);
|
||||
GameData.Load(gameDataPath);
|
||||
|
||||
|
||||
var adminGroupsPath = Path.Combine(rootDir.FullName, "configs", "admin_groups.json");
|
||||
Logger.LogInformation("Loading Admin Groups from {Path}", adminGroupsPath);
|
||||
AdminManager.LoadAdminGroups(adminGroupsPath);
|
||||
|
||||
var adminPath = Path.Combine(rootDir.FullName, "configs", "admins.json");
|
||||
Logger.LogInformation("Loading Admins from {Path}", adminPath);
|
||||
AdminManager.LoadAdminData(adminPath);
|
||||
|
||||
var overridePath = Path.Combine(rootDir.FullName, "configs", "admin_overrides.json");
|
||||
Logger.LogInformation("Loading Admin Command Overrides from {Path}", overridePath);
|
||||
AdminManager.LoadCommandOverrides(overridePath);
|
||||
|
||||
AdminManager.MergeGroupPermsIntoAdmins();
|
||||
|
||||
for (var i = 1; i <= 9; i++)
|
||||
{
|
||||
CommandUtils.AddStandaloneCommand("css_" + i, "Command Key Handler", (player, info) =>
|
||||
{
|
||||
if (player == null) return;
|
||||
var key = Convert.ToInt32(info.GetArg(0).Split("_")[1]);
|
||||
ChatMenus.OnKeyPress(player, key);
|
||||
});
|
||||
}
|
||||
|
||||
Logger.LogInformation("Loading C# plugins...");
|
||||
var pluginCount = LoadAllPlugins();
|
||||
Logger.LogInformation("All managed modules were loaded. {PluginCount} plugins loaded.", pluginCount);
|
||||
|
||||
RegisterPluginCommands();
|
||||
}
|
||||
|
||||
private void LoadPlugin(string path)
|
||||
{
|
||||
var existingPlugin = FindPluginByModulePath(path);
|
||||
if (existingPlugin != null)
|
||||
{
|
||||
throw new FileLoadException("Plugin is already loaded.");
|
||||
}
|
||||
|
||||
var plugin = new PluginContext(path, _loadedPlugins.Select(x => x.PluginId).DefaultIfEmpty(0).Max() + 1);
|
||||
plugin.Load();
|
||||
_loadedPlugins.Add(plugin);
|
||||
}
|
||||
|
||||
private int LoadAllPlugins()
|
||||
{
|
||||
DirectoryInfo modulesDirectoryInfo;
|
||||
try
|
||||
{
|
||||
modulesDirectoryInfo = new DirectoryInfo(Path.Combine(rootDir.FullName, "plugins"));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError(e, "Error finding plugin path");
|
||||
return 0;
|
||||
}
|
||||
|
||||
DirectoryInfo[] properModulesDirectories;
|
||||
try
|
||||
{
|
||||
properModulesDirectories = modulesDirectoryInfo.GetDirectories();
|
||||
}
|
||||
catch
|
||||
{
|
||||
properModulesDirectories = Array.Empty<DirectoryInfo>();
|
||||
}
|
||||
|
||||
|
||||
var filePaths = properModulesDirectories
|
||||
.Where(d => d.GetFiles().Any((f) => f.Name == d.Name + ".dll"))
|
||||
.Select(d => d.GetFiles().First((f) => f.Name == d.Name + ".dll").FullName)
|
||||
.ToArray();
|
||||
|
||||
|
||||
foreach (var path in filePaths)
|
||||
{
|
||||
try
|
||||
{
|
||||
LoadPlugin(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError(e, "Failed to load plugin from {Path}", path);
|
||||
}
|
||||
}
|
||||
|
||||
return _loadedPlugins.Count;
|
||||
}
|
||||
|
||||
public void UnloadAllPlugins()
|
||||
{
|
||||
foreach (var plugin in _loadedPlugins)
|
||||
{
|
||||
plugin.Unload();
|
||||
_loadedPlugins.Remove(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
private PluginContext? FindPluginByType(Type moduleClass)
|
||||
{
|
||||
return _loadedPlugins.FirstOrDefault(x => x.PluginType == moduleClass);
|
||||
}
|
||||
|
||||
private PluginContext? FindPluginById(int id)
|
||||
{
|
||||
return _loadedPlugins.FirstOrDefault(x => x.PluginId == id);
|
||||
}
|
||||
|
||||
private PluginContext? FindPluginByModuleName(string name)
|
||||
{
|
||||
return _loadedPlugins.FirstOrDefault(x => x.Name == name);
|
||||
}
|
||||
|
||||
private PluginContext? FindPluginByModulePath(string path)
|
||||
{
|
||||
return _loadedPlugins.FirstOrDefault(x => x.PluginPath == path);
|
||||
}
|
||||
|
||||
private PluginContext? FindPluginByIdOrName(string query)
|
||||
{
|
||||
|
||||
PluginContext? plugin = null;
|
||||
if (Int32.TryParse(query, out var pluginNumber))
|
||||
{
|
||||
plugin = FindPluginById(pluginNumber);
|
||||
if (plugin != null) return plugin;
|
||||
}
|
||||
|
||||
plugin = FindPluginByModuleName(query);
|
||||
|
||||
return plugin;
|
||||
}
|
||||
|
||||
[RequiresPermissions("@css/generic")]
|
||||
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
private void OnCSSCommand(CCSPlayerController? caller, CommandInfo info)
|
||||
{
|
||||
var currentVersion = Api.GetVersion();
|
||||
|
||||
info.ReplyToCommand(" CounterStrikeSharp was created and is maintained by Michael \"roflmuffin\" Wilson.\n" +
|
||||
" Counter-Strike Sharp uses code borrowed from SourceMod, Source.Python, FiveM, Saul Rennison and CS2Fixes.\n" +
|
||||
" See ACKNOWLEDGEMENTS.md for more information.\n" +
|
||||
" Current API Version: " + currentVersion, true);
|
||||
return;
|
||||
}
|
||||
|
||||
[RequiresPermissions("@css/generic")]
|
||||
[CommandHelper(minArgs: 1,
|
||||
usage: "[option]\n" +
|
||||
" list - List all plugins currently loaded.\n" +
|
||||
" start / load - Loads a plugin not currently loaded.\n" +
|
||||
" stop / unload - Unloads a plugin currently loaded.\n" +
|
||||
" restart / reload - Reloads a plugin currently loaded.",
|
||||
whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
private void OnCSSPluginCommand(CCSPlayerController? caller, CommandInfo info)
|
||||
{
|
||||
switch (info.GetArg(1))
|
||||
{
|
||||
case "list":
|
||||
{
|
||||
info.ReplyToCommand($" List of all plugins currently loaded by CounterStrikeSharp: {_loadedPlugins.Count} plugins loaded.", true);
|
||||
|
||||
foreach (var plugin in _loadedPlugins)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat(" [#{0}]: \"{1}\" ({2})", plugin.PluginId, plugin.Name, plugin.Version);
|
||||
if (!string.IsNullOrEmpty(plugin.Author)) sb.AppendFormat(" by {0}", plugin.Author);
|
||||
if (!string.IsNullOrEmpty(plugin.Description))
|
||||
{
|
||||
sb.Append("\n");
|
||||
sb.Append(" ");
|
||||
sb.Append(plugin.Description);
|
||||
}
|
||||
info.ReplyToCommand(sb.ToString(), true);
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "start":
|
||||
case "load":
|
||||
{
|
||||
if (info.ArgCount < 2)
|
||||
{
|
||||
info.ReplyToCommand("Valid usage: css_plugins start/load [relative plugin path || absolute plugin path] (e.g \"TestPlugin\", \"plugins/TestPlugin/TestPlugin.dll\")\n", true);
|
||||
break;
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (!path.EndsWith(".dll"))
|
||||
{
|
||||
path = Path.Combine(rootDir.FullName, $"plugins/{path}/{path}.dll");
|
||||
}
|
||||
else
|
||||
{
|
||||
path = Path.Combine(rootDir.FullName, path);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
LoadPlugin(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError(e, "Failed to load plugin from {Path}", path);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "stop":
|
||||
case "unload":
|
||||
{
|
||||
if (info.ArgCount < 2)
|
||||
{
|
||||
info.ReplyToCommand("Valid usage: css_plugins stop/unload [plugin name || #plugin id] (e.g \"TestPlugin\", \"1\")\n", true);
|
||||
break;
|
||||
}
|
||||
|
||||
var pluginIdentifier = info.GetArg(2);
|
||||
PluginContext? plugin = FindPluginByIdOrName(pluginIdentifier);
|
||||
if (plugin == null)
|
||||
{
|
||||
info.ReplyToCommand($"Could not unload plugin \"{pluginIdentifier}\")", true);
|
||||
break;
|
||||
}
|
||||
plugin.Unload();
|
||||
_loadedPlugins.Remove(plugin);
|
||||
break;
|
||||
}
|
||||
|
||||
case "restart":
|
||||
case "reload":
|
||||
{
|
||||
if (info.ArgCount < 2)
|
||||
{
|
||||
info.ReplyToCommand("Valid usage: css_plugins restart/reload [plugin name || #plugin id] (e.g \"TestPlugin\", \"#1\")\n", true);
|
||||
break;
|
||||
}
|
||||
|
||||
var pluginIdentifier = info.GetArg(2);
|
||||
var plugin = FindPluginByIdOrName(pluginIdentifier);
|
||||
|
||||
if (plugin == null)
|
||||
{
|
||||
info.ReplyToCommand($"Could not reload plugin \"{pluginIdentifier}\")", true);
|
||||
break;
|
||||
}
|
||||
plugin.Unload(true);
|
||||
plugin.Load(true);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
info.ReplyToCommand("Valid usage: css_plugins [option]\n" +
|
||||
" list - List all plugins currently loaded.\n" +
|
||||
" start / load - Loads a plugin not currently loaded.\n" +
|
||||
" stop / unload - Unloads a plugin currently loaded.\n" +
|
||||
" restart / reload - Reloads a plugin currently loaded."
|
||||
, true);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void RegisterPluginCommands()
|
||||
{
|
||||
CommandUtils.AddStandaloneCommand("css", "Counter-Strike Sharp options.", OnCSSCommand);
|
||||
CommandUtils.AddStandaloneCommand("css_plugins", "Counter-Strike Sharp plugin options.", OnCSSPluginCommand);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -22,53 +22,12 @@ using System.Security;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
public class MethodAttribute<T> where T : Attribute
|
||||
{
|
||||
public MethodAttribute(T attribute, MethodInfo method)
|
||||
{
|
||||
Attribute = attribute;
|
||||
Method = method;
|
||||
}
|
||||
|
||||
public T Attribute;
|
||||
public MethodInfo Method;
|
||||
}
|
||||
|
||||
public static class Helpers
|
||||
{
|
||||
private static MethodAttribute<T>[] FindMethodAttributes<T>(BasePlugin plugin) where T: Attribute
|
||||
{
|
||||
return plugin
|
||||
.GetType()
|
||||
.GetMethods()
|
||||
.Where(m => m.GetCustomAttributes(typeof(T), false).Length > 0)
|
||||
.Select(x => new MethodAttribute<T>(x.GetCustomAttribute<T>(), x))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private const string dllPath = "counterstrikesharp";
|
||||
|
||||
[SecurityCritical]
|
||||
[DllImport(dllPath, EntryPoint = "InvokeNative")]
|
||||
public static extern void InvokeNative(IntPtr ptr);
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
// Used by .NET Host in C++ to initiate loading
|
||||
public static int LoadAllPlugins()
|
||||
{
|
||||
try
|
||||
{
|
||||
var globalContext = new GlobalContext();
|
||||
globalContext.InitGlobalContext();
|
||||
return 1;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void Callback();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace CounterStrikeSharp.API.Core.Hosting;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides information about the CounterStrikeSharp host configuration.
|
||||
/// </summary>
|
||||
public interface IScriptHostConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the absolute path to the directory that contains CounterStrikeSharp files.
|
||||
/// e.g. /game/csgo/addons/counterstrikesharp
|
||||
/// </summary>
|
||||
string RootPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the absolute path to the directory that contains CounterStrikeSharp plugins.
|
||||
/// e.g. /game/csgo/addons/counterstrikesharp/plugins
|
||||
/// </summary>
|
||||
string PluginPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the absolute path to the directory that contains CounterStrikeSharp configs.
|
||||
/// e.g. /game/csgo/addons/counterstrikesharp/configs
|
||||
/// </summary>
|
||||
string ConfigsPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the absolute path to the directory that contains CounterStrikeSharp game data.
|
||||
/// e.g. /game/csgo/addons/counterstrikesharp/gamedata
|
||||
/// </summary>
|
||||
string GameDataPath { get; }
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Hosting;
|
||||
|
||||
internal sealed class ScriptHostConfiguration : IScriptHostConfiguration
|
||||
{
|
||||
public string RootPath { get; }
|
||||
public string PluginPath { get; }
|
||||
public string ConfigsPath { get; }
|
||||
public string GameDataPath { get; }
|
||||
|
||||
public ScriptHostConfiguration(IHostEnvironment hostEnvironment)
|
||||
{
|
||||
RootPath = Path.Join(new[] { hostEnvironment.ContentRootPath });
|
||||
PluginPath = Path.Join(new[] { hostEnvironment.ContentRootPath, "plugins" });
|
||||
ConfigsPath = Path.Join(new[] { hostEnvironment.ContentRootPath, "configs" });
|
||||
GameDataPath = Path.Join(new[] { hostEnvironment.ContentRootPath, "gamedata" });
|
||||
}
|
||||
}
|
||||
@@ -14,28 +14,30 @@
|
||||
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface which every CounterStrikeSharp plugin must implement. Module will be created with parameterless constructor and then Load method will be called.
|
||||
/// </summary>
|
||||
public interface IPlugin
|
||||
public interface IPlugin : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the plugin.
|
||||
/// </summary>
|
||||
string ModuleName
|
||||
{
|
||||
get;
|
||||
}
|
||||
string ModuleName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Module version.
|
||||
/// </summary>
|
||||
string ModuleVersion
|
||||
{
|
||||
get;
|
||||
}
|
||||
string ModuleVersion { get; }
|
||||
|
||||
string ModuleAuthor { get; }
|
||||
|
||||
string ModuleDescription { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This method is called by CounterStrikeSharp on plugin load and should be treated as plugin constructor.
|
||||
@@ -48,5 +50,13 @@ namespace CounterStrikeSharp.API.Core
|
||||
/// Event handlers, listeners etc. will automatically be deregistered.
|
||||
/// </summary>
|
||||
void Unload(bool hotReload);
|
||||
|
||||
string ModulePath { get; internal set; }
|
||||
|
||||
ILogger Logger { get; set; }
|
||||
|
||||
void RegisterAllAttributes(object instance);
|
||||
|
||||
void InitializeConfig(object instance, Type pluginType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a service collection configuration for a plugin.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IPluginServiceCollection<T> where T : IPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to configure services exposed for dependency injection.
|
||||
/// </summary>
|
||||
public void ConfigureServices(IServiceCollection serviceCollection);
|
||||
}
|
||||
6
managed/CounterStrikeSharp.API/Core/IStartupService.cs
Normal file
6
managed/CounterStrikeSharp.API/Core/IStartupService.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public interface IStartupService
|
||||
{
|
||||
public void Load();
|
||||
}
|
||||
@@ -126,6 +126,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)
|
||||
{
|
||||
|
||||
@@ -2,28 +2,40 @@ using System;
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Serilog;
|
||||
using Serilog.Core;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Logging;
|
||||
|
||||
public static class CoreLogging
|
||||
{
|
||||
public static ILoggerFactory Factory { get; }
|
||||
public static ILoggerFactory Factory { get; private set; }
|
||||
private static Logger? SerilogLogger { get; set; }
|
||||
|
||||
static CoreLogging()
|
||||
public static void AddCoreLogging(this ILoggingBuilder builder, string contentRoot)
|
||||
{
|
||||
var logger = new LoggerConfiguration()
|
||||
if (SerilogLogger == null)
|
||||
{
|
||||
SerilogLogger = new LoggerConfiguration()
|
||||
.Enrich.FromLogContext()
|
||||
.Enrich.With<SourceContextEnricher>()
|
||||
.WriteTo.Console(outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u4}] (cssharp:{SourceContext}) {Message:lj}{NewLine}{Exception}")
|
||||
.WriteTo.File(Path.Join(new[] {GlobalContext.RootDirectory, "logs", $"log-cssharp.txt"}), rollingInterval: RollingInterval.Day, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u4}] (cssharp:{SourceContext}) {Message:lj}{NewLine}{Exception}")
|
||||
.WriteTo.File(Path.Join(new[] {GlobalContext.RootDirectory, "logs", $"log-all.txt"}), rollingInterval: RollingInterval.Day, shared: true, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u4}] (cssharp:{SourceContext}) {Message:lj}{NewLine}{Exception}")
|
||||
.WriteTo.Console(
|
||||
outputTemplate:
|
||||
"{Timestamp:HH:mm:ss} [{Level:u4}] (cssharp:{SourceContext}) {Message:lj}{NewLine}{Exception}")
|
||||
.WriteTo.File(Path.Join(new[] { contentRoot, "logs", $"log-cssharp.txt" }),
|
||||
rollingInterval: RollingInterval.Day,
|
||||
outputTemplate:
|
||||
"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u4}] (cssharp:{SourceContext}) {Message:lj}{NewLine}{Exception}")
|
||||
.WriteTo.File(Path.Join(new[] { contentRoot, "logs", $"log-all.txt" }),
|
||||
rollingInterval: RollingInterval.Day, shared: true,
|
||||
outputTemplate:
|
||||
"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u4}] (cssharp:{SourceContext}) {Message:lj}{NewLine}{Exception}")
|
||||
.CreateLogger();
|
||||
|
||||
Factory =
|
||||
LoggerFactory.Create(builder =>
|
||||
{
|
||||
builder.AddSerilog(logger);
|
||||
});
|
||||
LoggerFactory.Create(builder => { builder.AddSerilog(SerilogLogger); });
|
||||
}
|
||||
|
||||
builder.AddSerilog(SerilogLogger);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Serilog;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Logging;
|
||||
|
||||
public class PluginLogging
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a logger scoped to a specific plugin
|
||||
/// <remarks>Eventually this should probably come from a service collection</remarks>
|
||||
/// </summary>
|
||||
public static ILogger CreatePluginLogger(PluginContext pluginContext)
|
||||
{
|
||||
var logger = new LoggerConfiguration()
|
||||
.Enrich.FromLogContext()
|
||||
.Enrich.With(new PluginNameEnricher(pluginContext))
|
||||
.WriteTo.Console(outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u4}] (plugin:{PluginName}) {Message:lj}{NewLine}{Exception}")
|
||||
.WriteTo.File(Path.Join(new[] {GlobalContext.RootDirectory, "logs", $"log-{pluginContext.PluginType.Name}.txt"}), rollingInterval: RollingInterval.Day, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u4}] plugin:{PluginName} {Message:lj}{NewLine}{Exception}")
|
||||
.WriteTo.File(Path.Join(new[] {GlobalContext.RootDirectory, "logs", $"log-all.txt"}), rollingInterval: RollingInterval.Day, shared: true, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u4}] plugin:{PluginName} {Message:lj}{NewLine}{Exception}")
|
||||
.CreateLogger();
|
||||
|
||||
using ILoggerFactory loggerFactory =
|
||||
LoggerFactory.Create(builder =>
|
||||
{
|
||||
builder.AddSerilog(logger);
|
||||
});
|
||||
|
||||
return loggerFactory.CreateLogger(pluginContext.PluginType);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using CounterStrikeSharp.API.Core.Plugin;
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
|
||||
@@ -16,7 +17,7 @@ public class PluginNameEnricher : ILogEventEnricher
|
||||
|
||||
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
|
||||
{
|
||||
var property = propertyFactory.CreateProperty(PropertyName, Context.PluginType.Name);
|
||||
var property = propertyFactory.CreateProperty(PropertyName, Context.Plugin.ModuleName);
|
||||
logEvent.AddPropertyIfAbsent(property);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,11 @@ namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CBasePlayerPawn
|
||||
{
|
||||
/// <summary>
|
||||
/// Force player suicide
|
||||
/// </summary>
|
||||
/// <param name="explode"></param>
|
||||
/// <param name="force"></param>
|
||||
public void CommitSuicide(bool explode, bool force)
|
||||
{
|
||||
VirtualFunction.CreateVoid<IntPtr, bool, bool>(Handle, GameData.GetOffset("CBasePlayerPawn_CommitSuicide"))(Handle, explode, force);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
@@ -10,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,20 @@ public partial class CCSPlayerController
|
||||
return VirtualFunctions.GiveNamedItem(PlayerPawn.Value.ItemServices.Handle, item, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
public IntPtr GiveNamedItem(CsItem item)
|
||||
{
|
||||
string? itemString = EnumUtils.GetEnumMemberAttributeValue(item);
|
||||
if (string.IsNullOrWhiteSpace(itemString))
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
return this.GiveNamedItem(itemString);
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -79,6 +90,31 @@ public partial class CCSPlayerController
|
||||
itemServices.RemoveWeapons();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force player suicide
|
||||
/// </summary>
|
||||
/// <param name="explode"></param>
|
||||
/// <param name="force"></param>
|
||||
public void CommitSuicide(bool explode, bool force)
|
||||
{
|
||||
if (!PlayerPawn.IsValid) return;
|
||||
if (!PlayerPawn.Value.IsValid) return;
|
||||
|
||||
PlayerPawn.Value.CommitSuicide(explode, force);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Respawn player
|
||||
/// </summary>
|
||||
public void Respawn()
|
||||
{
|
||||
if (!PlayerPawn.IsValid) return;
|
||||
if (!PlayerPawn.Value.IsValid) return;
|
||||
|
||||
VirtualFunctions.CCSPlayerPawn_Respawn(PlayerPawn.Value.Handle);
|
||||
VirtualFunction.CreateVoid<IntPtr>(Handle, GameData.GetOffset("CCSPlayerController_Respawn"))(Handle);
|
||||
}
|
||||
|
||||
public bool IsBot => ((PlayerFlags)Flags).HasFlag(PlayerFlags.FL_FAKECLIENT);
|
||||
|
||||
/// <summary>
|
||||
@@ -107,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;
|
||||
}
|
||||
19
managed/CounterStrikeSharp.API/Core/Model/CCSPlayerPawn.cs
Normal file
19
managed/CounterStrikeSharp.API/Core/Model/CCSPlayerPawn.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CCSPlayerPawn
|
||||
{
|
||||
/// <summary>
|
||||
/// Respawn player
|
||||
/// </summary>
|
||||
public void Respawn()
|
||||
{
|
||||
if (!Controller.IsValid) return;
|
||||
if (!Controller.Value.IsValid) return;
|
||||
|
||||
VirtualFunctions.CCSPlayerPawn_Respawn(Handle);
|
||||
VirtualFunction.CreateVoid<IntPtr>(Controller.Value.Handle, GameData.GetOffset("CCSPlayerController_Respawn"))(Controller.Value.Handle);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CPlayerPawnComponent
|
||||
{
|
||||
public PointerTo<CBasePlayerPawn> Pawn => new PointerTo<CBasePlayerPawn>(this.Handle + 0x30);
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
@@ -19806,6 +19804,63 @@ public partial class CTablet : CCSWeaponBase
|
||||
|
||||
}
|
||||
|
||||
public partial class CTakeDamageInfo : NativeObject
|
||||
{
|
||||
public CTakeDamageInfo (IntPtr pointer) : base(pointer) {}
|
||||
|
||||
// m_vecDamageForce
|
||||
public Vector DamageForce => Schema.GetDeclaredClass<Vector>(this.Handle, "CTakeDamageInfo", "m_vecDamageForce");
|
||||
|
||||
// m_vecDamagePosition
|
||||
public Vector DamagePosition => Schema.GetDeclaredClass<Vector>(this.Handle, "CTakeDamageInfo", "m_vecDamagePosition");
|
||||
|
||||
// m_vecReportedPosition
|
||||
public Vector ReportedPosition => Schema.GetDeclaredClass<Vector>(this.Handle, "CTakeDamageInfo", "m_vecReportedPosition");
|
||||
|
||||
// m_vecDamageDirection
|
||||
public Vector DamageDirection => Schema.GetDeclaredClass<Vector>(this.Handle, "CTakeDamageInfo", "m_vecDamageDirection");
|
||||
|
||||
// m_hInflictor
|
||||
public CHandle<CBaseEntity> Inflictor => Schema.GetDeclaredClass<CHandle<CBaseEntity>>(this.Handle, "CTakeDamageInfo", "m_hInflictor");
|
||||
|
||||
// m_hAttacker
|
||||
public CHandle<CBaseEntity> Attacker => Schema.GetDeclaredClass<CHandle<CBaseEntity>>(this.Handle, "CTakeDamageInfo", "m_hAttacker");
|
||||
|
||||
// m_hAbility
|
||||
public CHandle<CBaseEntity> Ability => Schema.GetDeclaredClass<CHandle<CBaseEntity>>(this.Handle, "CTakeDamageInfo", "m_hAbility");
|
||||
|
||||
// m_flDamage
|
||||
public ref float Damage => ref Schema.GetRef<float>(this.Handle, "CTakeDamageInfo", "m_flDamage");
|
||||
|
||||
// m_bitsDamageType
|
||||
public ref Int32 BitsDamageType => ref Schema.GetRef<Int32>(this.Handle, "CTakeDamageInfo", "m_bitsDamageType");
|
||||
|
||||
// m_iDamageCustom
|
||||
public ref Int32 DamageCustom => ref Schema.GetRef<Int32>(this.Handle, "CTakeDamageInfo", "m_iDamageCustom");
|
||||
|
||||
// m_iAmmoType
|
||||
public ref byte AmmoType => ref Schema.GetRef<byte>(this.Handle, "CTakeDamageInfo", "m_iAmmoType");
|
||||
|
||||
// m_flOriginalDamage
|
||||
public ref float OriginalDamage => ref Schema.GetRef<float>(this.Handle, "CTakeDamageInfo", "m_flOriginalDamage");
|
||||
|
||||
// m_bShouldBleed
|
||||
public ref bool ShouldBleed => ref Schema.GetRef<bool>(this.Handle, "CTakeDamageInfo", "m_bShouldBleed");
|
||||
|
||||
// m_bShouldSpark
|
||||
public ref bool ShouldSpark => ref Schema.GetRef<bool>(this.Handle, "CTakeDamageInfo", "m_bShouldSpark");
|
||||
|
||||
// m_nDamageFlags
|
||||
public ref TakeDamageFlags_t DamageFlags => ref Schema.GetRef<TakeDamageFlags_t>(this.Handle, "CTakeDamageInfo", "m_nDamageFlags");
|
||||
|
||||
// m_nNumObjectsPenetrated
|
||||
public ref Int32 NumObjectsPenetrated => ref Schema.GetRef<Int32>(this.Handle, "CTakeDamageInfo", "m_nNumObjectsPenetrated");
|
||||
|
||||
// m_bInTakeDamageFlow
|
||||
public ref bool InTakeDamageFlow => ref Schema.GetRef<bool>(this.Handle, "CTakeDamageInfo", "m_bInTakeDamageFlow");
|
||||
|
||||
}
|
||||
|
||||
public partial class CTankTargetChange : CPointEntity
|
||||
{
|
||||
public CTankTargetChange (IntPtr pointer) : base(pointer) {}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace CounterStrikeSharp.API.Core.Plugin.Host;
|
||||
|
||||
public interface IPluginContextQueryHandler
|
||||
{
|
||||
IPluginContext? FindPluginByType(Type moduleClass);
|
||||
|
||||
IPluginContext? FindPluginById(int id);
|
||||
|
||||
IPluginContext? FindPluginByModuleName(string name);
|
||||
|
||||
IPluginContext? FindPluginByModulePath(string path);
|
||||
|
||||
IPluginContext? FindPluginByIdOrName(string query);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Plugin.Host;
|
||||
|
||||
public interface IPluginManager
|
||||
{
|
||||
public void Load();
|
||||
public void LoadPlugin(string path);
|
||||
public IEnumerable<PluginContext> GetLoadedPlugins();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Plugin.Host;
|
||||
|
||||
public class PluginContextQueryHandler : IPluginContextQueryHandler
|
||||
{
|
||||
private readonly IPluginManager _pluginManager;
|
||||
|
||||
public PluginContextQueryHandler(IPluginManager pluginManager)
|
||||
{
|
||||
_pluginManager = pluginManager;
|
||||
}
|
||||
|
||||
public IPluginContext? FindPluginByType(Type moduleClass)
|
||||
{
|
||||
return _pluginManager.GetLoadedPlugins().FirstOrDefault(x => x.Plugin.GetType() == moduleClass);
|
||||
}
|
||||
|
||||
public IPluginContext? FindPluginById(int id)
|
||||
{
|
||||
return _pluginManager.GetLoadedPlugins().FirstOrDefault(x => x.PluginId == id);
|
||||
}
|
||||
|
||||
public IPluginContext? FindPluginByModuleName(string name)
|
||||
{
|
||||
return _pluginManager.GetLoadedPlugins().FirstOrDefault(x => x.Plugin.ModuleName == name);
|
||||
}
|
||||
|
||||
public IPluginContext? FindPluginByModulePath(string path)
|
||||
{
|
||||
return _pluginManager.GetLoadedPlugins().FirstOrDefault(x => x.Plugin.ModulePath == path);
|
||||
}
|
||||
|
||||
public IPluginContext? FindPluginByIdOrName(string query)
|
||||
{
|
||||
return _pluginManager.GetLoadedPlugins().FirstOrDefault(x => x.PluginId.ToString() == query || x.Plugin.ModuleName == query);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CounterStrikeSharp.API.Core.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Plugin.Host;
|
||||
|
||||
public class PluginManager : IPluginManager
|
||||
{
|
||||
private readonly HashSet<PluginContext> _loadedPluginContexts = new();
|
||||
private readonly IScriptHostConfiguration _scriptHostConfiguration;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ILogger<PluginManager> _logger;
|
||||
|
||||
public PluginManager(IScriptHostConfiguration scriptHostConfiguration, ILogger<PluginManager> logger, IServiceProvider serviceProvider)
|
||||
{
|
||||
_scriptHostConfiguration = scriptHostConfiguration;
|
||||
_logger = logger;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public void Load()
|
||||
{
|
||||
var pluginDirectories = Directory.GetDirectories(_scriptHostConfiguration.PluginPath);
|
||||
var pluginAssemblyPaths = pluginDirectories
|
||||
.Select(dir => Path.Combine(dir, Path.GetFileName(dir) + ".dll"))
|
||||
.Where(File.Exists)
|
||||
.ToArray();
|
||||
|
||||
foreach (var path in pluginAssemblyPaths)
|
||||
{
|
||||
try
|
||||
{
|
||||
LoadPlugin(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Failed to load plugin from {Path}", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<PluginContext> GetLoadedPlugins()
|
||||
{
|
||||
return _loadedPluginContexts;
|
||||
}
|
||||
|
||||
public void LoadPlugin(string path)
|
||||
{
|
||||
var plugin = new PluginContext(_serviceProvider, _scriptHostConfiguration, path, _loadedPluginContexts.Select(x => x.PluginId).DefaultIfEmpty(0).Max() + 1);
|
||||
_loadedPluginContexts.Add(plugin);
|
||||
plugin.Load();
|
||||
}
|
||||
}
|
||||
11
managed/CounterStrikeSharp.API/Core/Plugin/IPluginContext.cs
Normal file
11
managed/CounterStrikeSharp.API/Core/Plugin/IPluginContext.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace CounterStrikeSharp.API.Core.Plugin;
|
||||
|
||||
public interface IPluginContext
|
||||
{
|
||||
PluginState State { get; }
|
||||
IPlugin Plugin { get; }
|
||||
int PluginId { get; }
|
||||
|
||||
void Load(bool hotReload);
|
||||
void Unload(bool hotReload);
|
||||
}
|
||||
206
managed/CounterStrikeSharp.API/Core/Plugin/PluginContext.cs
Normal file
206
managed/CounterStrikeSharp.API/Core/Plugin/PluginContext.cs
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* This file is part of CounterStrikeSharp.
|
||||
* CounterStrikeSharp is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* CounterStrikeSharp is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
|
||||
*/
|
||||
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Core.Hosting;
|
||||
using CounterStrikeSharp.API.Core.Logging;
|
||||
using McMaster.NETCore.Plugins;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Serilog;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Plugin
|
||||
{
|
||||
public class PluginContext : IPluginContext
|
||||
{
|
||||
public PluginState State { get; set; } = PluginState.Unregistered;
|
||||
public IPlugin Plugin { get; private set; }
|
||||
|
||||
private PluginLoader Loader { get; set; }
|
||||
|
||||
private IServiceProvider ServiceProvider { get; set; }
|
||||
|
||||
public int PluginId { get; }
|
||||
|
||||
private readonly IScriptHostConfiguration _hostConfiguration;
|
||||
private readonly string _path;
|
||||
private readonly FileSystemWatcher _fileWatcher;
|
||||
private readonly IServiceProvider _applicationServiceProvider;
|
||||
|
||||
// TOOD: ServiceCollection
|
||||
private ILogger _logger = CoreLogging.Factory.CreateLogger<PluginContext>();
|
||||
|
||||
public PluginContext(IServiceProvider applicationServiceProvider, IScriptHostConfiguration hostConfiguration, string path, int id)
|
||||
{
|
||||
_applicationServiceProvider = applicationServiceProvider;
|
||||
_hostConfiguration = hostConfiguration;
|
||||
_path = path;
|
||||
PluginId = id;
|
||||
|
||||
Loader = PluginLoader.CreateFromAssemblyFile(path,
|
||||
new[]
|
||||
{
|
||||
typeof(IPlugin), typeof(ILogger), typeof(IServiceCollection), typeof(IPluginServiceCollection<>)
|
||||
}, config =>
|
||||
{
|
||||
config.EnableHotReload = true;
|
||||
config.IsUnloadable = true;
|
||||
});
|
||||
|
||||
_fileWatcher = new FileSystemWatcher
|
||||
{
|
||||
Path = Path.GetDirectoryName(path)
|
||||
};
|
||||
|
||||
_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)
|
||||
{
|
||||
_logger.LogInformation("Reloading plugin {Name}", Plugin.ModuleName);
|
||||
Loader = eventargs.Loader;
|
||||
Unload(hotReload: true);
|
||||
Load(hotReload: true);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void Load(bool hotReload = false)
|
||||
{
|
||||
if (State == PluginState.Loaded) return;
|
||||
|
||||
using (Loader.EnterContextualReflection())
|
||||
{
|
||||
var defaultAssembly = Loader.LoadDefaultAssembly();
|
||||
|
||||
Type pluginType = defaultAssembly.GetTypes()
|
||||
.FirstOrDefault(t => typeof(IPlugin).IsAssignableFrom(t));
|
||||
|
||||
if (pluginType == null) throw new Exception("Unable to find plugin in assembly");
|
||||
|
||||
var serviceCollection = new ServiceCollection();
|
||||
|
||||
serviceCollection.Scan(scan =>
|
||||
scan.FromAssemblies(defaultAssembly)
|
||||
.AddClasses(c => c.AssignableTo<IPlugin>())
|
||||
.AsSelf()
|
||||
.WithSingletonLifetime()
|
||||
);
|
||||
|
||||
serviceCollection.AddLogging(builder =>
|
||||
{
|
||||
builder.ClearProviders();
|
||||
builder.AddSerilog(new LoggerConfiguration()
|
||||
.Enrich.FromLogContext()
|
||||
.Enrich.With(new PluginNameEnricher(this))
|
||||
.WriteTo.Console(
|
||||
outputTemplate:
|
||||
"{Timestamp:HH:mm:ss} [{Level:u4}] (plugin:{PluginName}) {Message:lj}{NewLine}{Exception}")
|
||||
.WriteTo.File(
|
||||
Path.Join(new[]
|
||||
{
|
||||
_hostConfiguration.RootPath, "logs",
|
||||
$"log-{pluginType.Assembly.GetName().Name}.txt"
|
||||
}), rollingInterval: RollingInterval.Day,
|
||||
outputTemplate:
|
||||
"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u4}] plugin:{PluginName} {Message:lj}{NewLine}{Exception}")
|
||||
.WriteTo.File(Path.Join(new[] { _hostConfiguration.RootPath, "logs", $"log-all.txt" }),
|
||||
rollingInterval: RollingInterval.Day, shared: true,
|
||||
outputTemplate:
|
||||
"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u4}] plugin:{PluginName} {Message:lj}{NewLine}{Exception}")
|
||||
.CreateLogger());
|
||||
});
|
||||
|
||||
Type interfaceType = typeof(IPluginServiceCollection<>).MakeGenericType(pluginType);
|
||||
Type[] serviceCollectionConfiguratorTypes = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(assembly => assembly.GetTypes())
|
||||
.Where(type => interfaceType.IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract)
|
||||
.ToArray();
|
||||
|
||||
if (serviceCollectionConfiguratorTypes.Any())
|
||||
{
|
||||
foreach (var t in serviceCollectionConfiguratorTypes)
|
||||
{
|
||||
var pluginServiceCollection = Activator.CreateInstance(t);
|
||||
MethodInfo method = t.GetMethod("ConfigureServices");
|
||||
method?.Invoke(pluginServiceCollection, new object[] { serviceCollection });
|
||||
}
|
||||
}
|
||||
|
||||
serviceCollection.AddSingleton(this);
|
||||
ServiceProvider = serviceCollection.BuildServiceProvider();
|
||||
|
||||
var minimumApiVersion = pluginType.GetCustomAttribute<MinimumApiVersion>()?.Version;
|
||||
var currentVersion = Api.GetVersion();
|
||||
|
||||
// Ignore version 0 for local development
|
||||
if (currentVersion > 0 && minimumApiVersion != null && minimumApiVersion > currentVersion)
|
||||
throw new Exception(
|
||||
$"Plugin \"{Path.GetFileName(_path)}\" requires a newer version of CounterStrikeSharp. The plugin expects version [{minimumApiVersion}] but the current version is [{currentVersion}].");
|
||||
|
||||
_logger.LogInformation("Loading plugin {Name}", pluginType.Assembly.GetName().Name);
|
||||
|
||||
Plugin = ServiceProvider.GetRequiredService(pluginType) as IPlugin;
|
||||
|
||||
if (Plugin == null) throw new Exception("Unable to create plugin instance");
|
||||
|
||||
State = PluginState.Loading;
|
||||
|
||||
Plugin.ModulePath = _path;
|
||||
Plugin.RegisterAllAttributes(Plugin);
|
||||
Plugin.Logger = ServiceProvider.GetRequiredService<ILoggerFactory>().CreateLogger(pluginType);
|
||||
|
||||
Plugin.InitializeConfig(Plugin, pluginType);
|
||||
Plugin.Load(hotReload);
|
||||
|
||||
_logger.LogInformation("Finished loading plugin {Name}", Plugin.ModuleName);
|
||||
|
||||
State = PluginState.Loaded;
|
||||
}
|
||||
}
|
||||
|
||||
public void Unload(bool hotReload = false)
|
||||
{
|
||||
if (State == PluginState.Unloaded) return;
|
||||
|
||||
State = PluginState.Unloaded;
|
||||
var cachedName = Plugin.ModuleName;
|
||||
|
||||
_logger.LogInformation("Unloading plugin {Name}", Plugin.ModuleName);
|
||||
|
||||
Plugin.Unload(hotReload);
|
||||
|
||||
Plugin.Dispose();
|
||||
|
||||
_logger.LogInformation("Finished unloading plugin {Name}", cachedName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace CounterStrikeSharp.API.Core.Plugin;
|
||||
|
||||
public enum PluginState
|
||||
{
|
||||
Unregistered,
|
||||
Loading,
|
||||
Loaded,
|
||||
Unloaded,
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* This file is part of CounterStrikeSharp.
|
||||
* CounterStrikeSharp is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* CounterStrikeSharp is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Core.Logging;
|
||||
using McMaster.NETCore.Plugins;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
public class PluginContext
|
||||
{
|
||||
private BasePlugin _plugin;
|
||||
private PluginLoader _assemblyLoader;
|
||||
|
||||
public string Name => _plugin?.ModuleName;
|
||||
public string Version => _plugin?.ModuleVersion;
|
||||
public string Description => _plugin.ModuleDescription;
|
||||
public string Author => _plugin.ModuleAuthor;
|
||||
public Type PluginType => _plugin?.GetType();
|
||||
public string PluginPath => _plugin?.ModulePath;
|
||||
|
||||
public int PluginId { get; }
|
||||
|
||||
private readonly string _path;
|
||||
private readonly FileSystemWatcher _fileWatcher;
|
||||
|
||||
// TOOD: ServiceCollection
|
||||
private ILogger _logger = CoreLogging.Factory.CreateLogger<PluginContext>();
|
||||
|
||||
public PluginContext(string path, int id)
|
||||
{
|
||||
_path = path;
|
||||
PluginId = id;
|
||||
|
||||
_assemblyLoader = PluginLoader.CreateFromAssemblyFile(path, new[] { typeof(IPlugin) }, config =>
|
||||
{
|
||||
config.EnableHotReload = true;
|
||||
config.IsUnloadable = true;
|
||||
});
|
||||
|
||||
_fileWatcher = new FileSystemWatcher
|
||||
{
|
||||
Path = Path.GetDirectoryName(path)
|
||||
};
|
||||
|
||||
_fileWatcher.Deleted += async (s, e) =>
|
||||
{
|
||||
if (e.FullPath == path)
|
||||
{
|
||||
_logger.LogInformation("Plugin {Name} has been deleted, unloading...", Name);
|
||||
Unload(true);
|
||||
}
|
||||
};
|
||||
|
||||
_fileWatcher.Filter = "*.dll";
|
||||
_fileWatcher.EnableRaisingEvents = true;
|
||||
_assemblyLoader.Reloaded += async (s, e) => await OnReloadedAsync(s, e);
|
||||
}
|
||||
|
||||
private Task OnReloadedAsync(object sender, PluginReloadedEventArgs eventargs)
|
||||
{
|
||||
_logger.LogInformation("Reloading plugin {Name}", Name);
|
||||
_assemblyLoader = eventargs.Loader;
|
||||
Unload(hotReload: true);
|
||||
Load(hotReload: true);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void Load(bool hotReload = false)
|
||||
{
|
||||
using (_assemblyLoader.EnterContextualReflection())
|
||||
{
|
||||
Type pluginType = _assemblyLoader.LoadDefaultAssembly().GetTypes()
|
||||
.FirstOrDefault(t => typeof(IPlugin).IsAssignableFrom(t));
|
||||
|
||||
if (pluginType == null) throw new Exception("Unable to find plugin in DLL");
|
||||
|
||||
var minimumApiVersion = pluginType.GetCustomAttribute<MinimumApiVersion>()?.Version;
|
||||
var currentVersion = Api.GetVersion();
|
||||
|
||||
// Ignore version 0 for local development
|
||||
if (currentVersion > 0 && minimumApiVersion != null && minimumApiVersion > currentVersion)
|
||||
throw new Exception(
|
||||
$"Plugin \"{Path.GetFileName(_path)}\" requires a newer version of CounterStrikeSharp. The plugin expects version [{minimumApiVersion}] but the current version is [{currentVersion}].");
|
||||
|
||||
_logger.LogInformation("Loading plugin {Name}", pluginType.Name);
|
||||
_plugin = (BasePlugin)Activator.CreateInstance(pluginType)!;
|
||||
_plugin.ModulePath = _path;
|
||||
_plugin.RegisterAllAttributes(_plugin);
|
||||
_plugin.Logger = PluginLogging.CreatePluginLogger(this);
|
||||
_plugin.InitializeConfig(_plugin, pluginType);
|
||||
_plugin.Load(hotReload);
|
||||
|
||||
_logger.LogInformation("Finished loading plugin {Name}", Name);
|
||||
}
|
||||
}
|
||||
|
||||
public void Unload(bool hotReload = false)
|
||||
{
|
||||
var cachedName = Name;
|
||||
|
||||
_logger.LogInformation("Unloading plugin {Name}", Name);
|
||||
|
||||
_plugin.Unload(hotReload);
|
||||
|
||||
_plugin.Dispose();
|
||||
|
||||
if (!hotReload)
|
||||
{
|
||||
_assemblyLoader.Dispose();
|
||||
_fileWatcher.Dispose();
|
||||
}
|
||||
|
||||
_logger.LogInformation("Finished unloading plugin {Name}", Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
@@ -23,7 +23,10 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.4.0" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
|
||||
<PackageReference Include="Scrutor" Version="4.2.2" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
|
||||
5
managed/CounterStrikeSharp.API/GlobalUsings.cs
Normal file
5
managed/CounterStrikeSharp.API/GlobalUsings.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
// Global using directives
|
||||
|
||||
global using System;
|
||||
global using System.IO;
|
||||
global using CounterStrikeSharp.API.Core;
|
||||
@@ -0,0 +1,14 @@
|
||||
using CounterStrikeSharp.API.Modules.Commands.Targeting;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Commands;
|
||||
|
||||
public static class CommandExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Treats the argument at the specified index as a target string (@all, @me etc.) and returns the result.
|
||||
/// </summary>
|
||||
public static TargetResult GetArgTargetResult(this CommandInfo commandInfo, int index)
|
||||
{
|
||||
return new Target(commandInfo.GetArg(index)).GetTarget(commandInfo.CallingPlayer);
|
||||
}
|
||||
}
|
||||
@@ -25,13 +25,14 @@ namespace CounterStrikeSharp.API.Modules.Commands
|
||||
|
||||
public delegate HookResult CommandListenerCallback(CCSPlayerController? player, CommandInfo commandInfo);
|
||||
|
||||
private CCSPlayerController _player;
|
||||
public IntPtr Handle { get; private set; }
|
||||
public CCSPlayerController? CallingPlayer { get; }
|
||||
|
||||
public IntPtr Handle { get; }
|
||||
|
||||
internal CommandInfo(IntPtr pointer, CCSPlayerController player)
|
||||
{
|
||||
Handle = pointer;
|
||||
_player = player;
|
||||
CallingPlayer = player;
|
||||
}
|
||||
|
||||
public int ArgCount => NativeAPI.CommandGetArgCount(Handle);
|
||||
@@ -44,10 +45,10 @@ namespace CounterStrikeSharp.API.Modules.Commands
|
||||
public string GetArg(int index) => NativeAPI.CommandGetArgByIndex(Handle, index);
|
||||
|
||||
public void ReplyToCommand(string message, bool console = false) {
|
||||
if (_player != null)
|
||||
if (CallingPlayer != null)
|
||||
{
|
||||
if (console) { _player.PrintToConsole(message); }
|
||||
else _player.PrintToChat(message);
|
||||
if (console) { CallingPlayer.PrintToConsole(message); }
|
||||
else CallingPlayer.PrintToChat(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Commands.Targeting;
|
||||
|
||||
public class Target
|
||||
{
|
||||
private TargetType Type { get; }
|
||||
private string Raw { get; }
|
||||
private string Slug { get; }
|
||||
|
||||
private static readonly Dictionary<string, TargetType> TargetTypeMap = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ "@all", TargetType.GroupAll },
|
||||
{ "@bots", TargetType.GroupBots },
|
||||
{ "@human", TargetType.GroupHumans },
|
||||
{ "@alive", TargetType.GroupAlive },
|
||||
{ "@dead", TargetType.GroupDead },
|
||||
{ "@!me", TargetType.GroupNotMe },
|
||||
{ "@me", TargetType.PlayerMe },
|
||||
{ "@ct", TargetType.TeamCt },
|
||||
{ "@t", TargetType.TeamT },
|
||||
{ "@spec", TargetType.TeamSpec }
|
||||
};
|
||||
|
||||
|
||||
private static bool ConstTargetType(string target, out TargetType targetType)
|
||||
{
|
||||
targetType = TargetType.Invalid;
|
||||
if (!target.StartsWith("@"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return TargetTypeMap.TryGetValue(target, out targetType);
|
||||
}
|
||||
|
||||
private bool IdTargetType(string target,
|
||||
out TargetType targetType,
|
||||
[MaybeNullWhen(false)] out string slug)
|
||||
{
|
||||
targetType = TargetType.Invalid;
|
||||
slug = null!;
|
||||
if (!target.StartsWith("#"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
slug = target.TrimStart('#');
|
||||
if (slug.StartsWith("STEAM")) targetType = TargetType.IdSteamEscaped;
|
||||
else if (!ulong.TryParse(slug, out _)) targetType = TargetType.ExplicitName;
|
||||
else if (slug.Length == 17) targetType = TargetType.IdSteam64;
|
||||
else targetType = TargetType.IdUserid;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public Target(string target)
|
||||
{
|
||||
Raw = target.Trim();
|
||||
if (ConstTargetType(Raw, out var targetType))
|
||||
{
|
||||
Type = targetType;
|
||||
Slug = Raw;
|
||||
}
|
||||
else if (IdTargetType(Raw, out targetType, out var slug))
|
||||
{
|
||||
Type = targetType;
|
||||
Slug = slug;
|
||||
}
|
||||
else
|
||||
{
|
||||
Type = TargetType.ImplicitName;
|
||||
Slug = Raw;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TargetPredicate(CCSPlayerController player, CCSPlayerController? caller)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case TargetType.TeamCt:
|
||||
return player.TeamNum == (byte)CsTeam.CounterTerrorist;
|
||||
case TargetType.TeamT:
|
||||
return player.TeamNum == (byte)CsTeam.Terrorist;
|
||||
case TargetType.TeamSpec:
|
||||
return player.TeamNum == (byte)CsTeam.Spectator;
|
||||
case TargetType.GroupAll:
|
||||
return true;
|
||||
case TargetType.GroupBots:
|
||||
return player.IsBot;
|
||||
case TargetType.GroupHumans:
|
||||
return !player.IsBot;
|
||||
case TargetType.GroupAlive:
|
||||
return player.PlayerPawn is { IsValid: true, Value.LifeState: (byte)LifeState_t.LIFE_ALIVE };
|
||||
case TargetType.GroupDead:
|
||||
return player.PlayerPawn is { IsValid: true, Value.LifeState: (byte)LifeState_t.LIFE_DEAD or (byte)LifeState_t.LIFE_DYING };
|
||||
case TargetType.GroupNotMe:
|
||||
return player.SteamID != caller?.SteamID;
|
||||
case TargetType.PlayerMe:
|
||||
return player.SteamID == caller?.SteamID;
|
||||
case TargetType.IdUserid:
|
||||
return player.UserId.ToString() == Slug;
|
||||
case TargetType.IdSteamEscaped:
|
||||
return ((SteamID)player.SteamID).SteamId2 == Slug;
|
||||
case TargetType.IdSteam64:
|
||||
return ((SteamID)player.SteamID).SteamId64.ToString() == Slug;
|
||||
case TargetType.ExplicitName:
|
||||
case TargetType.ImplicitName:
|
||||
return player.PlayerName.Contains(Slug, StringComparison.OrdinalIgnoreCase);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public TargetResult GetTarget(CCSPlayerController? caller)
|
||||
{
|
||||
var players = Utilities.GetPlayers().Where(player => TargetPredicate(player, caller)).ToList();
|
||||
|
||||
return new TargetResult() { Players = players };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Commands.Targeting;
|
||||
|
||||
public class TargetResult : IEnumerable<CCSPlayerController>
|
||||
{
|
||||
public List<CCSPlayerController> Players { get; set; } = new();
|
||||
|
||||
public IEnumerator<CCSPlayerController> GetEnumerator()
|
||||
{
|
||||
return Players.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace CounterStrikeSharp.API.Modules.Commands.Targeting;
|
||||
|
||||
public enum TargetType
|
||||
{
|
||||
TeamCt, // @ct
|
||||
TeamT, // @t
|
||||
TeamSpec, // @spec
|
||||
|
||||
GroupAll, // @all
|
||||
GroupBots, // @bots
|
||||
GroupHumans, // @human
|
||||
GroupAlive, // @alive
|
||||
GroupDead, // @dead
|
||||
GroupNotMe, // @!me
|
||||
|
||||
PlayerMe, // @me
|
||||
|
||||
IdUserid, // #4
|
||||
IdSteamEscaped, // "#STEAM_0:1:8614"
|
||||
IdSteam64, // #76561198116940237
|
||||
|
||||
ExplicitName, // #name
|
||||
ImplicitName, // name
|
||||
Invalid
|
||||
}
|
||||
@@ -43,10 +43,11 @@ namespace CounterStrikeSharp.API.Modules.Config
|
||||
{
|
||||
string directoryPath = Path.Combine(_pluginConfigsFolderPath, pluginName);
|
||||
string configPath = Path.Combine(directoryPath, $"{pluginName}.json");
|
||||
string exampleConfigPath = Path.Combine(directoryPath, $"{pluginName}.example.json");
|
||||
|
||||
T config = (T)Activator.CreateInstance(typeof(T))!;
|
||||
|
||||
if (!File.Exists(configPath))
|
||||
if (!File.Exists(configPath) && !File.Exists(exampleConfigPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -56,8 +57,10 @@ namespace CounterStrikeSharp.API.Modules.Config
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.Append($"// This configuration was automatically generated by CounterStrikeSharp for plugin '{pluginName}', at {DateTimeOffset.Now:yyyy/MM/dd hh:mm:ss}\n");
|
||||
builder.Append(JsonSerializer.Serialize<T>(config, new JsonSerializerOptions { WriteIndented = true }));
|
||||
builder.Append(
|
||||
$"// This configuration was automatically generated by CounterStrikeSharp for plugin '{pluginName}', at {DateTimeOffset.Now:yyyy/MM/dd hh:mm:ss}\n");
|
||||
builder.Append(JsonSerializer.Serialize<T>(config,
|
||||
new JsonSerializerOptions { WriteIndented = true }));
|
||||
File.WriteAllText(configPath, builder.ToString());
|
||||
return config;
|
||||
}
|
||||
@@ -65,6 +68,17 @@ namespace CounterStrikeSharp.API.Modules.Config
|
||||
{
|
||||
_logger.LogError(ex, "Failed to generate configuration file for {PluginName}", pluginName);
|
||||
}
|
||||
} else if (File.Exists(exampleConfigPath) && !File.Exists(configPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Copying example configuration file for {PluginName}", pluginName);
|
||||
File.Copy(exampleConfigPath, configPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to copy example configuration file for {PluginName}", pluginName);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Entities.Constants
|
||||
{
|
||||
public enum CsItem
|
||||
{
|
||||
//-----------------------------------------
|
||||
//EQUIPMENT
|
||||
//-----------------------------------------
|
||||
[EnumMember(Value = "item_kevlar")]
|
||||
Kevlar = 000,
|
||||
|
||||
[EnumMember(Value = "item_assaultsuit")]
|
||||
AssaultSuit = 001,
|
||||
KevlarHelmet = AssaultSuit,
|
||||
|
||||
[EnumMember(Value = "weapon_taser")]
|
||||
Taser = 002,
|
||||
Zeus = Taser,
|
||||
|
||||
[EnumMember(Value = "weapon_snowball")]
|
||||
Snowball = 003,
|
||||
|
||||
[EnumMember(Value = "weapon_shield")]
|
||||
Shield = 004,
|
||||
|
||||
[EnumMember(Value = "weapon_c4")]
|
||||
C4 = 005,
|
||||
Bomb = C4,
|
||||
|
||||
[EnumMember(Value = "weapon_healthshot")]
|
||||
Healthshot = 006,
|
||||
|
||||
[EnumMember(Value = "weapon_breachcharge")]
|
||||
BreachCharge = 007,
|
||||
|
||||
[EnumMember(Value = "weapon_tablet")]
|
||||
Tablet = 008,
|
||||
|
||||
[EnumMember(Value = "weapon_bumpmine")]
|
||||
Bumpmine = 009,
|
||||
|
||||
//-----------------------------------------
|
||||
//GRENADES
|
||||
//-----------------------------------------
|
||||
[EnumMember(Value = "weapon_smokegrenade")]
|
||||
Smoke = 100,
|
||||
SmokeGrenade = Smoke,
|
||||
|
||||
[EnumMember(Value = "weapon_flashbang")]
|
||||
Flashbang = 101,
|
||||
FlashbangGrenade = Flashbang,
|
||||
|
||||
[EnumMember(Value = "weapon_hegrenade")]
|
||||
HighExplosive = 102,
|
||||
HE = HighExplosive,
|
||||
HighExplosiveGrenade = HighExplosive,
|
||||
HEGrenade = HighExplosive,
|
||||
|
||||
[EnumMember(Value = "weapon_molotov")]
|
||||
Molotov = 103,
|
||||
|
||||
[EnumMember(Value = "weapon_incgrenade")]
|
||||
Incendiary = 104,
|
||||
IncGrenade = Incendiary,
|
||||
IncendiaryGrenade = Incendiary,
|
||||
|
||||
[EnumMember(Value = "weapon_decoy")]
|
||||
Decoy = 105,
|
||||
DecoyGrenade = Decoy,
|
||||
|
||||
//XRay-Grenade
|
||||
[EnumMember(Value = "weapon_tagrenade")]
|
||||
TacticalAwareness = 106,
|
||||
TAGrenade = TacticalAwareness,
|
||||
XRayGrenade = TacticalAwareness,
|
||||
|
||||
//Dangerzone: Better HighExplosive
|
||||
[EnumMember(Value = "weapon_frag")]
|
||||
Frag = 107,
|
||||
FragGrenade = Frag,
|
||||
|
||||
//Dangerzone: Better Molotov
|
||||
[EnumMember(Value = "weapon_firebomb")]
|
||||
Firebomb = 108,
|
||||
|
||||
//Dangerzone: Decoy but Footsteps instead of gun sounds
|
||||
[EnumMember(Value = "weapon_diversion")]
|
||||
Diversion = 109,
|
||||
|
||||
//-----------------------------------------
|
||||
//PISTOLS
|
||||
//-----------------------------------------
|
||||
[EnumMember(Value = "weapon_deagle")]
|
||||
Deagle = 200,
|
||||
DesertEagle = Deagle,
|
||||
|
||||
[EnumMember(Value = "weapon_glock")]
|
||||
Glock = 201,
|
||||
Glock18 = Glock,
|
||||
|
||||
[EnumMember(Value = "weapon_usp_silencer")]
|
||||
USPS = 202,
|
||||
USP = USPS,
|
||||
|
||||
[EnumMember(Value = "weapon_hkp2000")]
|
||||
HKP2000 = 203,
|
||||
P2000 = HKP2000,
|
||||
P2K = HKP2000,
|
||||
|
||||
[EnumMember(Value = "weapon_elite")]
|
||||
Elite = 204,
|
||||
DualBerettas = Elite,
|
||||
Dualies = Elite,
|
||||
|
||||
[EnumMember(Value = "weapon_tec9")]
|
||||
Tec9 = 205,
|
||||
|
||||
[EnumMember(Value = "weapon_p250")]
|
||||
P250 = 206,
|
||||
|
||||
[EnumMember(Value = "weapon_cz75a")]
|
||||
CZ = 207,
|
||||
CZ75 = CZ,
|
||||
|
||||
[EnumMember(Value = "weapon_fiveseven")]
|
||||
FiveSeven = 208,
|
||||
|
||||
[EnumMember(Value = "weapon_revolver")]
|
||||
Revolver = 209,
|
||||
R8 = Revolver,
|
||||
|
||||
//-----------------------------------------
|
||||
//MID-TIER
|
||||
//-----------------------------------------
|
||||
[EnumMember(Value = "weapon_mac10")]
|
||||
Mac10 = 300,
|
||||
|
||||
[EnumMember(Value = "weapon_mp9")]
|
||||
MP9 = 301,
|
||||
|
||||
[EnumMember(Value = "weapon_mp7")]
|
||||
MP7 = 302,
|
||||
|
||||
[EnumMember(Value = "weapon_p90")]
|
||||
P90 = 303,
|
||||
|
||||
[EnumMember(Value = "weapon_mp5sd")]
|
||||
MP5SD = 304,
|
||||
MP5 = MP5SD,
|
||||
|
||||
[EnumMember(Value = "weapon_bizon")]
|
||||
Bizon = 305,
|
||||
PPBizon = Bizon,
|
||||
|
||||
[EnumMember(Value = "weapon_ump45")]
|
||||
UMP45 = 306,
|
||||
UMP = UMP45,
|
||||
|
||||
[EnumMember(Value = "weapon_xm1014")]
|
||||
XM1014 = 307,
|
||||
|
||||
[EnumMember(Value = "weapon_nova")]
|
||||
Nova = 308,
|
||||
|
||||
[EnumMember(Value = "weapon_mag7")]
|
||||
MAG7 = 309,
|
||||
|
||||
[EnumMember(Value = "weapon_sawedoff")]
|
||||
SawedOff = 310,
|
||||
|
||||
[EnumMember(Value = "weapon_m249")]
|
||||
M249 = 311,
|
||||
|
||||
[EnumMember(Value = "weapon_negev")]
|
||||
Negev = 312,
|
||||
|
||||
//-----------------------------------------
|
||||
//RIFLES
|
||||
//-----------------------------------------
|
||||
[EnumMember(Value = "weapon_ak47")]
|
||||
AK47 = 400,
|
||||
|
||||
[EnumMember(Value = "weapon_m4a1_silencer")]
|
||||
M4A1S = 401,
|
||||
SilencedM4 = M4A1S,
|
||||
|
||||
[EnumMember(Value = "weapon_m4a1")]
|
||||
M4A1 = 402,
|
||||
M4A4 = M4A1,
|
||||
|
||||
[EnumMember(Value = "weapon_galilar")]
|
||||
GalilAR = 403,
|
||||
Galil = GalilAR,
|
||||
|
||||
[EnumMember(Value = "weapon_famas")]
|
||||
Famas = 404,
|
||||
|
||||
[EnumMember(Value = "weapon_sg556")]
|
||||
SG556 = 405,
|
||||
SG553 = SG556,
|
||||
Krieg = SG556,
|
||||
|
||||
[EnumMember(Value = "weapon_awp")]
|
||||
AWP = 406,
|
||||
|
||||
[EnumMember(Value = "weapon_aug")]
|
||||
AUG = 407,
|
||||
|
||||
[EnumMember(Value = "weapon_ssg08")]
|
||||
SSG08 = 408,
|
||||
Scout = SSG08,
|
||||
|
||||
[EnumMember(Value = "weapon_scar20")]
|
||||
SCAR20 = 409,
|
||||
AutoSniperCT = SCAR20,
|
||||
|
||||
[EnumMember(Value = "weapon_g3sg1")]
|
||||
G3SG1 = 410,
|
||||
AutoSniperT = G3SG1,
|
||||
|
||||
//-----------------------------------------
|
||||
//KNIFE
|
||||
//-----------------------------------------
|
||||
[EnumMember(Value = "weapon_knife_t")]
|
||||
DefaultKnifeT = 500,
|
||||
KnifeT = DefaultKnifeT,
|
||||
|
||||
[EnumMember(Value = "weapon_knife")]
|
||||
DefaultKnifeCT = 501,
|
||||
KnifeCT = DefaultKnifeCT,
|
||||
Knife = DefaultKnifeCT,
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -73,9 +73,26 @@ namespace CounterStrikeSharp.API.Modules.Memory
|
||||
return types[Enum.GetUnderlyingType(type)];
|
||||
}
|
||||
|
||||
GlobalContext.Instance.Logger.LogWarning("Error retrieving data type for type {Type}", type.FullName);
|
||||
Core.Application.Instance.Logger.LogWarning("Error retrieving data type for type {Type}", type.FullName);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static DataType ToValidDataType(this Type type)
|
||||
{
|
||||
if (types.ContainsKey(type)) return types[type];
|
||||
|
||||
if (typeof(NativeObject).IsAssignableFrom(type))
|
||||
{
|
||||
return DataType.DATA_TYPE_POINTER;
|
||||
}
|
||||
|
||||
if (type.IsEnum && types.ContainsKey(Enum.GetUnderlyingType(type)))
|
||||
{
|
||||
return types[Enum.GetUnderlyingType(type)];
|
||||
}
|
||||
|
||||
throw new NotSupportedException("Data type not supported:" + type.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
|
||||
public abstract class BaseMemoryFunction : NativeObject
|
||||
{
|
||||
private static Dictionary<string, IntPtr> _createdFunctions = new();
|
||||
|
||||
private static IntPtr CreateValveFunctionBySignature(string signature, DataType returnType,
|
||||
DataType[] argumentTypes)
|
||||
{
|
||||
if (!_createdFunctions.TryGetValue(signature, out var function))
|
||||
{
|
||||
try
|
||||
{
|
||||
function = NativeAPI.CreateVirtualFunctionBySignature(IntPtr.Zero, Addresses.ServerPath, signature,
|
||||
argumentTypes.Length, (int)returnType, argumentTypes.Cast<object>().ToArray());
|
||||
_createdFunctions[signature] = function;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return function;
|
||||
}
|
||||
|
||||
public BaseMemoryFunction(string signature, DataType returnType, DataType[] parameters) : base(
|
||||
CreateValveFunctionBySignature(signature, returnType, parameters))
|
||||
{
|
||||
}
|
||||
|
||||
public void Hook(Func<DynamicHook, HookResult> handler, HookMode mode)
|
||||
{
|
||||
NativeAPI.HookFunction(Handle, handler, mode == HookMode.Post);
|
||||
}
|
||||
|
||||
public void Unhook(Func<DynamicHook, HookResult> handler, HookMode mode)
|
||||
{
|
||||
NativeAPI.UnhookFunction(Handle, handler, mode == HookMode.Post);
|
||||
}
|
||||
|
||||
protected T InvokeInternal<T>(params object[] args)
|
||||
{
|
||||
return NativeAPI.ExecuteVirtualFunction<T>(Handle, args);
|
||||
}
|
||||
|
||||
protected void InvokeInternalVoid(params object[] args)
|
||||
{
|
||||
NativeAPI.ExecuteVirtualFunction<object>(Handle, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
|
||||
public class DynamicHook : NativeObject
|
||||
{
|
||||
public DynamicHook(IntPtr pointer) : base(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
public T GetParam<T>(int index)
|
||||
{
|
||||
return NativeAPI.DynamicHookGetParam<T>(Handle, (int)typeof(T).ToValidDataType(), index);
|
||||
}
|
||||
|
||||
public T GetReturn<T>(int index)
|
||||
{
|
||||
return NativeAPI.DynamicHookGetReturn<T>(Handle, (int)typeof(T).ToValidDataType());
|
||||
}
|
||||
|
||||
public void SetParam<T>(int index, T value)
|
||||
{
|
||||
NativeAPI.DynamicHookSetParam(Handle, (int)typeof(T).ToValidDataType(), index, value);
|
||||
}
|
||||
|
||||
public void SetReturn<T>(T value)
|
||||
{
|
||||
NativeAPI.DynamicHookSetReturn(Handle, (int)typeof(T).ToValidDataType(), value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
using System;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
|
||||
public class MemoryFunctionVoid : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionVoid(string signature) : base(signature, DataType.DATA_TYPE_VOID, Array.Empty<DataType>())
|
||||
{
|
||||
}
|
||||
|
||||
public void Invoke()
|
||||
{
|
||||
InvokeInternalVoid();
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionVoid<T1> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionVoid(string signature) : base(signature, DataType.DATA_TYPE_VOID,
|
||||
new[] { typeof(T1).ToValidDataType() })
|
||||
{
|
||||
}
|
||||
|
||||
public void Invoke(T1 arg1)
|
||||
{
|
||||
InvokeInternalVoid(arg1);
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionVoid<T1, T2> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionVoid(string signature) : base(signature, DataType.DATA_TYPE_VOID,
|
||||
new[] { typeof(T1).ToValidDataType(), typeof(T2).ToValidDataType() })
|
||||
{
|
||||
}
|
||||
|
||||
public void Invoke(T1 arg1, T2 arg2)
|
||||
{
|
||||
InvokeInternalVoid(arg1, arg2);
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionVoid<T1, T2, T3> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionVoid(string signature) : base(signature, DataType.DATA_TYPE_VOID,
|
||||
new[] { typeof(T1).ToValidDataType(), typeof(T2).ToValidDataType(), typeof(T3).ToValidDataType() })
|
||||
{
|
||||
}
|
||||
|
||||
public void Invoke(T1 arg1, T2 arg2, T3 arg3)
|
||||
{
|
||||
InvokeInternalVoid(arg1, arg2, arg3);
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionVoid<T1, T2, T3, T4> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionVoid(string signature) : base(signature, DataType.DATA_TYPE_VOID,
|
||||
new[]
|
||||
{
|
||||
typeof(T1).ToValidDataType(), typeof(T2).ToValidDataType(),
|
||||
typeof(T3).ToValidDataType(), typeof(T4).ToValidDataType()
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public void Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
|
||||
{
|
||||
InvokeInternalVoid(arg1, arg2, arg3, arg4);
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionVoid<T1, T2, T3, T4, T5> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionVoid(string signature) : base(signature, DataType.DATA_TYPE_VOID,
|
||||
new[]
|
||||
{
|
||||
typeof(T1).ToValidDataType(), typeof(T2).ToValidDataType(),
|
||||
typeof(T3).ToValidDataType(), typeof(T4).ToValidDataType(),
|
||||
typeof(T5).ToValidDataType()
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public void Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
|
||||
{
|
||||
InvokeInternalVoid(arg1, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionVoid<T1, T2, T3, T4, T5, T6> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionVoid(string signature) : base(signature, DataType.DATA_TYPE_VOID,
|
||||
new[]
|
||||
{
|
||||
typeof(T1).ToValidDataType(), typeof(T2).ToValidDataType(),
|
||||
typeof(T3).ToValidDataType(), typeof(T4).ToValidDataType(),
|
||||
typeof(T5).ToValidDataType(), typeof(T6).ToValidDataType()
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public void Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6)
|
||||
{
|
||||
InvokeInternalVoid(arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionVoid<T1, T2, T3, T4, T5, T6, T7> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionVoid(string signature) : base(signature, DataType.DATA_TYPE_VOID,
|
||||
new[]
|
||||
{
|
||||
typeof(T1).ToValidDataType(), typeof(T2).ToValidDataType(),
|
||||
typeof(T3).ToValidDataType(), typeof(T4).ToValidDataType(),
|
||||
typeof(T5).ToValidDataType(), typeof(T6).ToValidDataType(),
|
||||
typeof(T7).ToValidDataType()
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public void Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7)
|
||||
{
|
||||
InvokeInternalVoid(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionVoid<T1, T2, T3, T4, T5, T6, T7, T8> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionVoid(string signature) : base(signature, DataType.DATA_TYPE_VOID,
|
||||
new[]
|
||||
{
|
||||
typeof(T1).ToValidDataType(), typeof(T2).ToValidDataType(),
|
||||
typeof(T3).ToValidDataType(), typeof(T4).ToValidDataType(),
|
||||
typeof(T5).ToValidDataType(), typeof(T6).ToValidDataType(),
|
||||
typeof(T7).ToValidDataType(), typeof(T8).ToValidDataType()
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public void Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8)
|
||||
{
|
||||
InvokeInternalVoid(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionVoid<T1, T2, T3, T4, T5, T6, T7, T8, T9> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionVoid(string signature) : base(signature, DataType.DATA_TYPE_VOID,
|
||||
new[]
|
||||
{
|
||||
typeof(T1).ToValidDataType(), typeof(T2).ToValidDataType(),
|
||||
typeof(T3).ToValidDataType(), typeof(T4).ToValidDataType(),
|
||||
typeof(T5).ToValidDataType(), typeof(T6).ToValidDataType(),
|
||||
typeof(T7).ToValidDataType(), typeof(T8).ToValidDataType(),
|
||||
typeof(T9).ToValidDataType()
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public void Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9)
|
||||
{
|
||||
InvokeInternalVoid(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionVoid<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionVoid(string signature) : base(signature, DataType.DATA_TYPE_VOID,
|
||||
new[]
|
||||
{
|
||||
typeof(T1).ToValidDataType(), typeof(T2).ToValidDataType(),
|
||||
typeof(T3).ToValidDataType(), typeof(T4).ToValidDataType(),
|
||||
typeof(T5).ToValidDataType(), typeof(T6).ToValidDataType(),
|
||||
typeof(T7).ToValidDataType(), typeof(T8).ToValidDataType(),
|
||||
typeof(T9).ToValidDataType(), typeof(T10).ToValidDataType()
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public void Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10)
|
||||
{
|
||||
InvokeInternalVoid(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
using System;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
|
||||
public class MemoryFunctionWithReturn<TResult> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionWithReturn(string signature) : base(signature, typeof(TResult).ToValidDataType(),
|
||||
Array.Empty<DataType>())
|
||||
{
|
||||
}
|
||||
|
||||
public TResult Invoke()
|
||||
{
|
||||
return InvokeInternal<TResult>();
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionWithReturn<T1, TResult> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionWithReturn(string signature) : base(signature, typeof(TResult).ToValidDataType(),
|
||||
new[] { typeof(T1).ToValidDataType() })
|
||||
{
|
||||
}
|
||||
|
||||
public TResult Invoke(T1 arg1)
|
||||
{
|
||||
return InvokeInternal<TResult>(arg1);
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionWithReturn<T1, T2, TResult> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionWithReturn(string signature) : base(signature, typeof(TResult).ToValidDataType(),
|
||||
new[] { typeof(T1).ToValidDataType(), typeof(T2).ToValidDataType() })
|
||||
{
|
||||
}
|
||||
|
||||
public TResult Invoke(T1 arg1, T2 arg2)
|
||||
{
|
||||
return InvokeInternal<TResult>(arg1, arg2);
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionWithReturn<T1, T2, T3, TResult> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionWithReturn(string signature) : base(signature, typeof(TResult).ToValidDataType(),
|
||||
new[] { typeof(T1).ToValidDataType(), typeof(T2).ToValidDataType(), typeof(T3).ToValidDataType() })
|
||||
{
|
||||
}
|
||||
|
||||
public TResult Invoke(T1 arg1, T2 arg2, T3 arg3)
|
||||
{
|
||||
return InvokeInternal<TResult>(arg1, arg2, arg3);
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionWithReturn<T1, T2, T3, T4, TResult> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionWithReturn(string signature) : base(signature, typeof(TResult).ToValidDataType(),
|
||||
new[]
|
||||
{
|
||||
typeof(T1).ToValidDataType(), typeof(T2).ToValidDataType(), typeof(T3).ToValidDataType(),
|
||||
typeof(T4).ToValidDataType()
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public TResult Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
|
||||
{
|
||||
return InvokeInternal<TResult>(arg1, arg2, arg3, arg4);
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionWithReturn<T1, T2, T3, T4, T5, TResult> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionWithReturn(string signature) : base(signature, typeof(TResult).ToValidDataType(),
|
||||
new[]
|
||||
{
|
||||
typeof(T1).ToValidDataType(), typeof(T2).ToValidDataType(), typeof(T3).ToValidDataType(),
|
||||
typeof(T4).ToValidDataType(), typeof(T5).ToValidDataType()
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public TResult Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
|
||||
{
|
||||
return InvokeInternal<TResult>(arg1, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionWithReturn<T1, T2, T3, T4, T5, T6, TResult> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionWithReturn(string signature) : base(signature, typeof(TResult).ToValidDataType(),
|
||||
new[]
|
||||
{
|
||||
typeof(T1).ToValidDataType(), typeof(T2).ToValidDataType(), typeof(T3).ToValidDataType(),
|
||||
typeof(T4).ToValidDataType(), typeof(T5).ToValidDataType(), typeof(T6).ToValidDataType()
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public TResult Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6)
|
||||
{
|
||||
return InvokeInternal<TResult>(arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionWithReturn<T1, T2, T3, T4, T5, T6, T7, TResult> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionWithReturn(string signature) : base(signature, typeof(TResult).ToValidDataType(),
|
||||
new[]
|
||||
{
|
||||
typeof(T1).ToValidDataType(), typeof(T2).ToValidDataType(), typeof(T3).ToValidDataType(),
|
||||
typeof(T4).ToValidDataType(), typeof(T5).ToValidDataType(), typeof(T6).ToValidDataType(),
|
||||
typeof(T7).ToValidDataType()
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public TResult Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7)
|
||||
{
|
||||
return InvokeInternal<TResult>(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionWithReturn<T1, T2, T3, T4, T5, T6, T7, T8, TResult> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionWithReturn(string signature) : base(signature, typeof(TResult).ToValidDataType(),
|
||||
new[]
|
||||
{
|
||||
typeof(T1).ToValidDataType(), typeof(T2).ToValidDataType(), typeof(T3).ToValidDataType(),
|
||||
typeof(T4).ToValidDataType(), typeof(T5).ToValidDataType(), typeof(T6).ToValidDataType(),
|
||||
typeof(T7).ToValidDataType(), typeof(T8).ToValidDataType()
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public TResult Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8)
|
||||
{
|
||||
return InvokeInternal<TResult>(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionWithReturn<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionWithReturn(string signature) : base(signature, typeof(TResult).ToValidDataType(),
|
||||
new[]
|
||||
{
|
||||
typeof(T1).ToValidDataType(), typeof(T2).ToValidDataType(), typeof(T3).ToValidDataType(),
|
||||
typeof(T4).ToValidDataType(), typeof(T5).ToValidDataType(), typeof(T6).ToValidDataType(),
|
||||
typeof(T7).ToValidDataType(), typeof(T8).ToValidDataType(), typeof(T9).ToValidDataType()
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public TResult Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9)
|
||||
{
|
||||
return InvokeInternal<TResult>(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
|
||||
}
|
||||
}
|
||||
|
||||
public class MemoryFunctionWithReturn<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult> : BaseMemoryFunction
|
||||
{
|
||||
public MemoryFunctionWithReturn(string signature) : base(signature, typeof(TResult).ToValidDataType(),
|
||||
new[]
|
||||
{
|
||||
typeof(T1).ToValidDataType(), typeof(T2).ToValidDataType(), typeof(T3).ToValidDataType(),
|
||||
typeof(T4).ToValidDataType(), typeof(T5).ToValidDataType(), typeof(T6).ToValidDataType(),
|
||||
typeof(T7).ToValidDataType(), typeof(T8).ToValidDataType(), typeof(T9).ToValidDataType(),
|
||||
typeof(T10).ToValidDataType()
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
public TResult Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10)
|
||||
{
|
||||
return InvokeInternal<TResult>(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -50,6 +51,8 @@ public class Schema
|
||||
"m_nMusicID",
|
||||
};
|
||||
|
||||
public static int GetClassSize(string className) => NativeAPI.GetSchemaClassSize(className);
|
||||
|
||||
public static short GetSchemaOffset(string className, string propertyName)
|
||||
{
|
||||
if (CoreConfig.FollowCS2ServerGuidelines && _cs2BadList.Contains(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,37 +1,79 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Memory;
|
||||
|
||||
public static class VirtualFunctions
|
||||
{
|
||||
public static Action<IntPtr, HudDestination, string, IntPtr, IntPtr, IntPtr, IntPtr> ClientPrint =
|
||||
VirtualFunction.CreateVoid<IntPtr, HudDestination, string, IntPtr, IntPtr, IntPtr, IntPtr>(
|
||||
public static MemoryFunctionVoid<IntPtr, HudDestination, string, IntPtr, IntPtr, IntPtr, IntPtr> ClientPrintFunc =
|
||||
new(
|
||||
GameData.GetSignature("ClientPrint"));
|
||||
|
||||
public static Action<IntPtr, HudDestination, string, IntPtr, IntPtr, IntPtr, IntPtr> ClientPrint =
|
||||
ClientPrintFunc.Invoke;
|
||||
|
||||
public static MemoryFunctionVoid<HudDestination, string, IntPtr, IntPtr, IntPtr, IntPtr> ClientPrintAllFunc =
|
||||
new(GameData.GetSignature("UTIL_ClientPrintAll"));
|
||||
|
||||
public static Action<HudDestination, string, IntPtr, IntPtr, IntPtr, IntPtr> ClientPrintAll =
|
||||
VirtualFunction.CreateVoid<HudDestination, string, IntPtr, IntPtr, IntPtr, IntPtr>(
|
||||
GameData.GetSignature("UTIL_ClientPrintAll"));
|
||||
ClientPrintAllFunc.Invoke;
|
||||
|
||||
// void (*FnGiveNamedItem)(void* itemService,const char* pchName, void* iSubType,void* pScriptItem, void* a5,void* a6) = nullptr;
|
||||
public static Func<IntPtr, string, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr> GiveNamedItem =
|
||||
VirtualFunction.Create<IntPtr, string, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr>(
|
||||
GameData.GetSignature("GiveNamedItem"));
|
||||
public static MemoryFunctionWithReturn<IntPtr, string, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr> GiveNamedItemFunc =
|
||||
new(GameData.GetSignature("GiveNamedItem"));
|
||||
|
||||
public static Action<IntPtr, byte> SwitchTeam =
|
||||
VirtualFunction.CreateVoid<IntPtr, byte>(GameData.GetSignature("CCSPlayerController_SwitchTeam"));
|
||||
public static Func<IntPtr, string, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr> GiveNamedItem = GiveNamedItemFunc.Invoke;
|
||||
|
||||
public static MemoryFunctionVoid<IntPtr, byte> SwitchTeamFunc =
|
||||
new(GameData.GetSignature("CCSPlayerController_SwitchTeam"));
|
||||
|
||||
public static Action<IntPtr, byte> SwitchTeam = SwitchTeamFunc.Invoke;
|
||||
|
||||
// void(*UTIL_Remove)(CEntityInstance*);
|
||||
public static Action<IntPtr> UTIL_Remove = VirtualFunction.CreateVoid<IntPtr>(GameData.GetSignature("UTIL_Remove"));
|
||||
public static MemoryFunctionVoid<IntPtr> UTIL_RemoveFunc =
|
||||
new(GameData.GetSignature("UTIL_Remove"));
|
||||
|
||||
public static Action<IntPtr> UTIL_Remove = UTIL_RemoveFunc.Invoke;
|
||||
|
||||
// void(*CBaseModelEntity_SetModel)(CBaseModelEntity*, const char*);
|
||||
public static Action<IntPtr, string> SetModel = VirtualFunction.CreateVoid<IntPtr, string>(GameData.GetSignature("CBaseModelEntity_SetModel"));
|
||||
public static MemoryFunctionVoid<IntPtr, string> SetModelFunc =
|
||||
new(GameData.GetSignature("CBaseModelEntity_SetModel"));
|
||||
|
||||
public static Action<IntPtr, RoundEndReason, float> TerminateRound = VirtualFunction.CreateVoid<nint, RoundEndReason, float>(GameData.GetSignature("CCSGameRules_TerminateRound"));
|
||||
public static Action<IntPtr, string> SetModel = SetModelFunc.Invoke;
|
||||
|
||||
public static Func<string, int, IntPtr> UTIL_CreateEntityByName = VirtualFunction.Create<string, int, IntPtr>(GameData.GetSignature("UTIL_CreateEntityByName"));
|
||||
public static MemoryFunctionVoid<nint, RoundEndReason, float> TerminateRoundFunc =
|
||||
new(GameData.GetSignature("CCSGameRules_TerminateRound"));
|
||||
|
||||
public static Action<IntPtr, IntPtr> CBaseEntity_DispatchSpawn = VirtualFunction.CreateVoid<IntPtr, IntPtr>(GameData.GetSignature("CBaseEntity_DispatchSpawn"));
|
||||
public static Action<IntPtr, RoundEndReason, float> TerminateRound = TerminateRoundFunc.Invoke;
|
||||
|
||||
public static MemoryFunctionWithReturn<string, int, IntPtr> UTIL_CreateEntityByNameFunc =
|
||||
new(GameData.GetSignature("UTIL_CreateEntityByName"));
|
||||
|
||||
public static Func<string, int, IntPtr> UTIL_CreateEntityByName = UTIL_CreateEntityByNameFunc.Invoke;
|
||||
|
||||
public static MemoryFunctionVoid<IntPtr, IntPtr> CBaseEntity_DispatchSpawnFunc =
|
||||
new(GameData.GetSignature("CBaseEntity_DispatchSpawn"));
|
||||
|
||||
public static Action<IntPtr, IntPtr> CBaseEntity_DispatchSpawn = CBaseEntity_DispatchSpawnFunc.Invoke;
|
||||
|
||||
public static MemoryFunctionVoid<IntPtr> CCSPlayerPawn_RespawnFunc = new(GameData.GetSignature("CCSPlayerPawn_Respawn"));
|
||||
|
||||
public static Action<IntPtr> CCSPlayerPawn_Respawn = CCSPlayerPawn_RespawnFunc.Invoke;
|
||||
|
||||
public static MemoryFunctionVoid<CEntityInstance, CTakeDamageInfo> CBaseEntity_TakeDamageOldFunc = new (GameData.GetSignature("CBaseEntity_TakeDamageOld"));
|
||||
public static Action<CEntityInstance, CTakeDamageInfo> CBaseEntity_TakeDamageOld = CBaseEntity_TakeDamageOldFunc.Invoke;
|
||||
|
||||
public static MemoryFunctionVoid<CCSPlayerPawnBase> CCSPlayerPawnBase_PostThinkFunc = new (GameData.GetSignature("CCSPlayerPawnBase_PostThink"));
|
||||
public static Action<CCSPlayerPawnBase> CCSPlayerPawnBase_PostThink = CCSPlayerPawnBase_PostThinkFunc.Invoke;
|
||||
|
||||
public static MemoryFunctionVoid<CBaseTrigger, CBaseEntity> CBaseTrigger_StartTouchFunc = new (GameData.GetSignature("CBaseTrigger_StartTouch"));
|
||||
public static Action<CBaseTrigger, CBaseEntity> CBaseTrigger_StartTouch = CBaseTrigger_StartTouchFunc.Invoke;
|
||||
|
||||
public static MemoryFunctionVoid<CBaseTrigger, CBaseEntity> CBaseTrigger_EndTouchFunc = new (GameData.GetSignature("CBaseTrigger_EndTouch"));
|
||||
public static Action<CBaseTrigger, CBaseEntity> CBaseTrigger_EndTouch = CBaseTrigger_EndTouchFunc.Invoke;
|
||||
}
|
||||
@@ -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 CHandle(uint raw)
|
||||
{
|
||||
Raw = raw;
|
||||
}
|
||||
|
||||
public CHandle(IntPtr raw)
|
||||
{
|
||||
_pointer = raw;
|
||||
}
|
||||
|
||||
public T? Value => (T)Activator.CreateInstance(typeof(T), EntitySystem.GetEntityByHandle(this));
|
||||
|
||||
public override string ToString() => IsValid ? $"Index = {Index}, Serial = {SerialNum}" : "<invalid>";
|
||||
|
||||
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 T Value => (T)Activator.CreateInstance(typeof(T), NativeAPI.GetEntityPointerFromHandle(Handle));
|
||||
|
||||
public unsafe ref ulong Raw => ref Unsafe.AsRef<ulong>((void*)Handle);
|
||||
|
||||
public override string ToString() => IsValid ? $"Index = {Index.Value}, Serial = {SerialNum}" : "<invalid>";
|
||||
|
||||
public bool IsValid => Index.Value != (Utilities.MaxEdicts - 1);
|
||||
|
||||
public CEntityIndex Index => new((uint)(Raw & (Utilities.MaxEdicts - 1)));
|
||||
public uint SerialNum => (uint)(Raw >> Utilities.MaxEdictBits);
|
||||
public CEntityHandle(IntPtr raw) : base (raw)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class PointerTo<T> : NativeObject where T : NativeObject
|
||||
|
||||
@@ -14,28 +14,29 @@
|
||||
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
|
||||
*/
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Utils
|
||||
{
|
||||
namespace CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
public class ChatColors
|
||||
{
|
||||
public static char Default = '\x01';
|
||||
public static char White = '\x01';
|
||||
public static char Darkred = '\x02';
|
||||
public static char Green = '\x04';
|
||||
public static char LightYellow = '\x03';
|
||||
public static char LightBlue = '\x03';
|
||||
public static char LightYellow = '\x09';
|
||||
public static char LightBlue = '\x0B';
|
||||
public static char Olive = '\x05';
|
||||
public static char Lime = '\x06';
|
||||
public static char Red = '\x07';
|
||||
public static char Purple = '\x03';
|
||||
public static char LightPurple = '\x03';
|
||||
public static char Purple = '\x0E';
|
||||
public static char Grey = '\x08';
|
||||
public static char Yellow = '\x09';
|
||||
public static char Gold = '\x10';
|
||||
public static char Silver = '\x0A';
|
||||
public static char Blue = '\x0B';
|
||||
public static char DarkBlue = '\x0C';
|
||||
public static char BlueGrey = '\x0D';
|
||||
public static char BlueGrey = '\x0A';
|
||||
public static char Magenta = '\x0E';
|
||||
public static char LightRed = '\x0F';
|
||||
}
|
||||
public static char Orange = '\x10';
|
||||
}
|
||||
64
managed/CounterStrikeSharp.API/Modules/Utils/EnumUtils.cs
Normal file
64
managed/CounterStrikeSharp.API/Modules/Utils/EnumUtils.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Utils
|
||||
{
|
||||
public static class EnumUtils
|
||||
{
|
||||
/// <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)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var enumString = enumValue.ToString();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(enumString))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var memberInfo = enumType.GetMember(enumString);
|
||||
var enumMemberAttribute = memberInfo.FirstOrDefault()?.GetCustomAttributes(false)
|
||||
.OfType<EnumMemberAttribute>().FirstOrDefault();
|
||||
if (enumMemberAttribute != null)
|
||||
{
|
||||
return enumMemberAttribute.Value;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,6 @@
|
||||
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace CounterStrikeSharp.API
|
||||
{
|
||||
[Flags]
|
||||
|
||||
@@ -14,24 +14,25 @@
|
||||
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
|
||||
*/
|
||||
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
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
|
||||
{
|
||||
@@ -64,13 +65,37 @@ namespace CounterStrikeSharp.API
|
||||
return Utilities.GetEntityFromIndex<CCSPlayerController>((userid & 0xFF) + 1);
|
||||
}
|
||||
|
||||
public static CCSPlayerController? GetPlayerFromSteamId(ulong steamId)
|
||||
{
|
||||
return Utilities.GetPlayers().FirstOrDefault(player => player.SteamID == steamId);
|
||||
}
|
||||
|
||||
public static TargetResult ProcessTargetString(string pattern, CCSPlayerController player)
|
||||
{
|
||||
return new Target(pattern).GetTarget(player);
|
||||
}
|
||||
|
||||
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.EntityHandle.IsValid; pEntity = pEntity.Next)
|
||||
{
|
||||
var value = pEntity.EntityInstance.EntityHandle.Value;
|
||||
if (value == null) continue;
|
||||
|
||||
yield return value.As<T>();
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<CEntityInstance> GetAllEntities()
|
||||
{
|
||||
var pEntity = new CEntityIdentity(EntitySystem.FirstActiveEntity);
|
||||
for (; pEntity != null && pEntity.Handle != IntPtr.Zero; pEntity = pEntity.Next)
|
||||
{
|
||||
if (!pEntity.DesignerName.Contains(designerName)) continue;
|
||||
yield return new PointerTo<T>(pEntity.Handle).Value;
|
||||
var value = pEntity.EntityInstance.EntityHandle.Value;
|
||||
if (value == null) continue;
|
||||
|
||||
yield return value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -157,6 +157,9 @@ internal static partial class Program
|
||||
WriteEnum(builder, enumName, schemaEnum);
|
||||
}
|
||||
|
||||
// Manually whitelist some classes
|
||||
visited.Add("CTakeDamageInfo");
|
||||
|
||||
var visitedClassNames = new HashSet<string>();
|
||||
foreach (var (className, schemaClass) in allClasses)
|
||||
{
|
||||
|
||||
@@ -26,6 +26,11 @@ public record SchemaFieldType
|
||||
this.Category = SchemaTypeCategory.Builtin;
|
||||
this.Name = "int32";
|
||||
}
|
||||
else if (this.Name == "AmmoIndex_t")
|
||||
{
|
||||
this.Category = SchemaTypeCategory.Builtin;
|
||||
this.Name = "uint8";
|
||||
}
|
||||
else if (this.Name == "CBitVec< 64 >")
|
||||
{
|
||||
this.Category = SchemaTypeCategory.FixedArray;
|
||||
@@ -68,6 +73,7 @@ public record SchemaFieldType
|
||||
"uint32" => "UInt32",
|
||||
"uint64" => "UInt64",
|
||||
"bool" => "bool",
|
||||
"char" => "char",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(name), name, $"Unknown built-in: {name}")
|
||||
};
|
||||
|
||||
@@ -85,6 +91,7 @@ public record SchemaFieldType
|
||||
SchemaAtomicCategory.T => $"{name.Split('<')[0]}<{inner!.CsTypeName}>",
|
||||
SchemaAtomicCategory.Collection => $"NetworkedVector<{inner!.CsTypeName}>",
|
||||
SchemaAtomicCategory.Unknown => "CBitVec",
|
||||
SchemaAtomicCategory.TT => "Unknown",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(atomic), atomic, $"Unsupported atomic: {atomic}")
|
||||
};
|
||||
|
||||
|
||||
@@ -26,10 +26,10 @@ using CounterStrikeSharp.API.Core.Attributes.Registration;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
using CounterStrikeSharp.API.Modules.Events;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Menu;
|
||||
using CounterStrikeSharp.API.Modules.Timers;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
@@ -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";
|
||||
@@ -61,6 +61,13 @@ namespace TestPlugin
|
||||
Config = config;
|
||||
}
|
||||
|
||||
private TestInjectedClass _testInjectedClass;
|
||||
|
||||
public SamplePlugin(TestInjectedClass testInjectedClass)
|
||||
{
|
||||
_testInjectedClass = testInjectedClass;
|
||||
}
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
// Basic usage of the configuration system
|
||||
@@ -75,6 +82,12 @@ namespace TestPlugin
|
||||
|
||||
Logger.LogWarning($"Max Players: {Server.MaxPlayers}");
|
||||
|
||||
VirtualFunctions.SwitchTeamFunc.Hook(hook =>
|
||||
{
|
||||
Logger.LogInformation("Switch team func called");
|
||||
return HookResult.Continue;
|
||||
}, HookMode.Pre);
|
||||
|
||||
SetupConvars();
|
||||
SetupGameEvents();
|
||||
SetupListeners();
|
||||
@@ -98,6 +111,59 @@ namespace TestPlugin
|
||||
var virtualFunc = VirtualFunction.Create<IntPtr>(server.Pointer, 91);
|
||||
var result = virtualFunc() - 8;
|
||||
Logger.LogInformation("Result of virtual func call is {Pointer:X}", result);
|
||||
|
||||
_testInjectedClass.Hello();
|
||||
|
||||
VirtualFunctions.CBaseTrigger_StartTouchFunc.Hook(h =>
|
||||
{
|
||||
var trigger = h.GetParam<CBaseTrigger>(0);
|
||||
var entity = h.GetParam<CBaseEntity>(1);
|
||||
|
||||
Logger.LogInformation("Trigger {Trigger} touched by {Entity}", trigger.DesignerName, entity.DesignerName);
|
||||
|
||||
return HookResult.Continue;
|
||||
}, HookMode.Post);
|
||||
|
||||
VirtualFunctions.CBaseTrigger_EndTouchFunc.Hook(h =>
|
||||
{
|
||||
var trigger = h.GetParam<CBaseTrigger>(0);
|
||||
var entity = h.GetParam<CBaseEntity>(1);
|
||||
|
||||
Logger.LogInformation("Trigger left {Trigger} by {Entity}", trigger.DesignerName, entity.DesignerName);
|
||||
|
||||
return HookResult.Continue;
|
||||
}, HookMode.Post);
|
||||
|
||||
VirtualFunctions.UTIL_RemoveFunc.Hook(hook =>
|
||||
{
|
||||
var entityInstance = hook.GetParam<CEntityInstance>(0);
|
||||
Logger.LogInformation("Removed entity {EntityIndex}", entityInstance.Index);
|
||||
|
||||
return HookResult.Continue;
|
||||
}, HookMode.Post);
|
||||
|
||||
VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Hook((h =>
|
||||
{
|
||||
var victim = h.GetParam<CEntityInstance>(0);
|
||||
var damageInfo = h.GetParam<CTakeDamageInfo>(1);
|
||||
|
||||
if (damageInfo.Inflictor.Value.DesignerName == "inferno")
|
||||
{
|
||||
var inferno = new CInferno(damageInfo.Inflictor.Value.Handle);
|
||||
Logger.LogInformation("Owner of inferno is {Owner}", inferno.OwnerEntity);
|
||||
|
||||
if (victim == inferno.OwnerEntity.Value)
|
||||
{
|
||||
damageInfo.Damage = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
damageInfo.Damage = 150;
|
||||
}
|
||||
}
|
||||
|
||||
return HookResult.Continue;
|
||||
}), HookMode.Pre);
|
||||
}
|
||||
|
||||
private void SetupConvars()
|
||||
@@ -151,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;
|
||||
});
|
||||
@@ -163,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) =>
|
||||
@@ -238,7 +298,7 @@ namespace TestPlugin
|
||||
switch (designerName)
|
||||
{
|
||||
case "smokegrenade_projectile":
|
||||
var projectile = new CSmokeGrenadeProjectile(entity.Handle);
|
||||
var projectile = entity.As<CSmokeGrenadeProjectile>();
|
||||
|
||||
Server.NextFrame(() =>
|
||||
{
|
||||
@@ -250,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;
|
||||
@@ -278,6 +338,24 @@ namespace TestPlugin
|
||||
giveItemMenu.AddMenuOption("weapon_ak47", handleGive);
|
||||
giveItemMenu.AddMenuOption("weapon_p250", handleGive);
|
||||
|
||||
AddCommand("css_target", "Target Test", (player, info) =>
|
||||
{
|
||||
if (player == null) return;
|
||||
|
||||
var targetResult = info.GetArgTargetResult(1);
|
||||
|
||||
if (!targetResult.Any())
|
||||
{
|
||||
player.PrintToChat("No players found.");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var result in targetResult.Players)
|
||||
{
|
||||
player.PrintToChat($"Target found: {result?.PlayerName}");
|
||||
}
|
||||
});
|
||||
|
||||
AddCommand("css_menu", "Opens example menu", (player, info) => { ChatMenus.OpenMenu(player, largeMenu); });
|
||||
AddCommand("css_gunmenu", "Gun Menu", (player, info) => { ChatMenus.OpenMenu(player, giveItemMenu); });
|
||||
|
||||
@@ -382,11 +460,45 @@ namespace TestPlugin
|
||||
|
||||
foreach (var weapon in player.PlayerPawn.Value.WeaponServices.MyWeapons)
|
||||
{
|
||||
// We don't currently have a `ReplyToCommand` equivalent so just print to chat for now.
|
||||
player.PrintToChat(weapon.Value.DesignerName);
|
||||
command.ReplyToCommand(weapon.Value.DesignerName);
|
||||
}
|
||||
}
|
||||
|
||||
[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)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.PlayerPawn.IsValid) return;
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -401,6 +513,30 @@ namespace TestPlugin
|
||||
player.GiveNamedItem(command.ArgByIndex(1));
|
||||
}
|
||||
|
||||
[ConsoleCommand("css_giveenum", "giveenum")]
|
||||
public void OnCommandGiveEnum(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid) return;
|
||||
|
||||
player.GiveNamedItem(CsItem.M4A1);
|
||||
player.GiveNamedItem(CsItem.HEGrenade);
|
||||
player.GiveNamedItem(CsItem.Kevlar);
|
||||
player.GiveNamedItem(CsItem.Tec9);
|
||||
}
|
||||
|
||||
[ConsoleCommand("css_give", "give")]
|
||||
public void OnCommandGiveItems(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid) return;
|
||||
|
||||
player.GiveNamedItem("weapon_m4a1");
|
||||
player.GiveNamedItem("weapon_hegrenade");
|
||||
player.GiveNamedItem("item_kevlar");
|
||||
player.GiveNamedItem("weapon_tec9");
|
||||
}
|
||||
|
||||
private HookResult GenericEventHandler<T>(T @event, GameEventInfo info) where T : GameEvent
|
||||
{
|
||||
Logger.LogInformation("Event found {Pointer:X}, event name: {EventName}, dont broadcast: {DontBroadcast}",
|
||||
|
||||
29
managed/TestPlugin/TestPluginServiceCollection.cs
Normal file
29
managed/TestPlugin/TestPluginServiceCollection.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace TestPlugin;
|
||||
|
||||
public class TestInjectedClass
|
||||
{
|
||||
private readonly ILogger<TestInjectedClass> _logger;
|
||||
|
||||
public TestInjectedClass(ILogger<TestInjectedClass> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Hello()
|
||||
{
|
||||
_logger.LogInformation("Hello World from Test Injected Class");
|
||||
}
|
||||
}
|
||||
|
||||
public class TestPluginServiceCollection : IPluginServiceCollection<SamplePlugin>
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection serviceCollection)
|
||||
{
|
||||
serviceCollection.AddScoped<TestInjectedClass>();
|
||||
}
|
||||
}
|
||||
@@ -26,13 +26,24 @@ CCoreConfig::~CCoreConfig() = default;
|
||||
|
||||
bool CCoreConfig::Init(char* conf_error, int conf_error_size)
|
||||
{
|
||||
std::ifstream ifs(m_sPath);
|
||||
std::ifstream ifs(std::string(m_sPath + ".json"));
|
||||
|
||||
if (!ifs) {
|
||||
std::ifstream exampleIfs(std::string(m_sPath + ".example.json"));
|
||||
|
||||
if (!exampleIfs) {
|
||||
V_snprintf(conf_error, conf_error_size, "CoreConfig file not found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
CSSHARP_CORE_INFO("CoreConfig file not found, creating one from example.");
|
||||
std::ofstream ofs(std::string(m_sPath + ".json"));
|
||||
ofs << exampleIfs.rdbuf();
|
||||
ofs.close();
|
||||
|
||||
return Init(conf_error, conf_error_size);
|
||||
}
|
||||
|
||||
m_json = json::parse(ifs);
|
||||
|
||||
try {
|
||||
|
||||
@@ -32,14 +32,21 @@
|
||||
#include "core/log.h"
|
||||
#include "dyncall/dyncall/dyncall.h"
|
||||
|
||||
#include "pch.h"
|
||||
#include "dynohook/core.h"
|
||||
#include "dynohook/manager.h"
|
||||
#include "dynohook/conventions/x64/x64SystemVcall.h"
|
||||
|
||||
namespace counterstrikesharp {
|
||||
|
||||
DCCallVM* g_pCallVM = dcNewCallVM(4096);
|
||||
std::map<dyno::Hook*, ValveFunction*> g_HookMap;
|
||||
|
||||
// ============================================================================
|
||||
// >> GetDynCallConvention
|
||||
// ============================================================================
|
||||
int GetDynCallConvention(Convention_t eConv) {
|
||||
int GetDynCallConvention(Convention_t eConv)
|
||||
{
|
||||
switch (eConv) {
|
||||
case CONV_CUSTOM:
|
||||
return -1;
|
||||
@@ -62,11 +69,10 @@ int GetDynCallConvention(Convention_t eConv) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ValveFunction::ValveFunction(void* ulAddr,
|
||||
Convention_t callingConvention,
|
||||
std::vector<DataType_t> args,
|
||||
DataType_t returnType)
|
||||
: m_ulAddr(ulAddr) {
|
||||
ValveFunction::ValveFunction(void* ulAddr, Convention_t callingConvention,
|
||||
std::vector<DataType_t> args, DataType_t returnType)
|
||||
: m_ulAddr(ulAddr)
|
||||
{
|
||||
m_Args = args;
|
||||
|
||||
m_eReturnType = returnType;
|
||||
@@ -76,11 +82,8 @@ ValveFunction::ValveFunction(void* ulAddr,
|
||||
m_iCallingConvention = GetDynCallConvention(m_eCallingConvention);
|
||||
}
|
||||
|
||||
ValveFunction::ValveFunction(void* ulAddr,
|
||||
Convention_t callingConvention,
|
||||
DataType_t* args,
|
||||
int argCount,
|
||||
DataType_t returnType)
|
||||
ValveFunction::ValveFunction(void* ulAddr, Convention_t callingConvention, DataType_t* args,
|
||||
int argCount, DataType_t returnType)
|
||||
: m_ulAddr(ulAddr)
|
||||
|
||||
{
|
||||
@@ -93,26 +96,14 @@ ValveFunction::ValveFunction(void* ulAddr,
|
||||
|
||||
ValveFunction::~ValveFunction() {}
|
||||
|
||||
bool ValveFunction::IsCallable() {
|
||||
bool ValveFunction::IsCallable()
|
||||
{
|
||||
return (m_eCallingConvention != CONV_CUSTOM) && (m_iCallingConvention != -1);
|
||||
}
|
||||
|
||||
// bool ValveFunction::IsHookable() { return m_pCallingConvention != NULL; }
|
||||
//
|
||||
// bool ValveFunction::IsHooked() { return GetHookManager()->FindHook((void*)m_ulAddr) != NULL; }
|
||||
//
|
||||
// CHook* ValveFunction::GetHook() { return GetHookManager()->FindHook((void*)m_ulAddr); }
|
||||
|
||||
// ValveFunction* ValveFunction::GetTrampoline() {
|
||||
// CHook* pHook = GetHookManager()->FindHook((void*)m_ulAddr);
|
||||
// if (!pHook) return nullptr;
|
||||
//
|
||||
// return new ValveFunction((unsigned long)pHook->m_pTrampoline, m_eCallingConvention, m_Args,
|
||||
// m_eReturnType);
|
||||
// }
|
||||
|
||||
template <class ReturnType, class Function>
|
||||
ReturnType CallHelper(Function func, DCCallVM* vm, void* addr) {
|
||||
ReturnType CallHelper(Function func, DCCallVM* vm, void* addr)
|
||||
{
|
||||
ReturnType result;
|
||||
result = (ReturnType)func(vm, (void*)addr);
|
||||
return result;
|
||||
@@ -120,8 +111,10 @@ ReturnType CallHelper(Function func, DCCallVM* vm, void* addr) {
|
||||
|
||||
void CallHelperVoid(DCCallVM* vm, void* addr) { dcCallVoid(vm, (void*)addr); }
|
||||
|
||||
void ValveFunction::Call(ScriptContext& script_context, int offset) {
|
||||
if (!IsCallable()) return;
|
||||
void ValveFunction::Call(ScriptContext& script_context, int offset)
|
||||
{
|
||||
if (!IsCallable())
|
||||
return;
|
||||
|
||||
dcReset(g_pCallVM);
|
||||
dcMode(g_pCallVM, m_iCallingConvention);
|
||||
@@ -160,8 +153,7 @@ void ValveFunction::Call(ScriptContext& script_context, int offset) {
|
||||
dcArgLongLong(g_pCallVM, script_context.GetArgument<long long>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_ULONG_LONG:
|
||||
dcArgLongLong(g_pCallVM,
|
||||
script_context.GetArgument<unsigned long long>(contextIndex));
|
||||
dcArgLongLong(g_pCallVM, script_context.GetArgument<unsigned long long>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_FLOAT:
|
||||
dcArgFloat(g_pCallVM, script_context.GetArgument<float>(contextIndex));
|
||||
@@ -173,8 +165,7 @@ void ValveFunction::Call(ScriptContext& script_context, int offset) {
|
||||
dcArgPointer(g_pCallVM, script_context.GetArgument<void*>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_STRING:
|
||||
dcArgPointer(g_pCallVM,
|
||||
(void*)script_context.GetArgument<const char*>(contextIndex));
|
||||
dcArgPointer(g_pCallVM, (void*)script_context.GetArgument<const char*>(contextIndex));
|
||||
break;
|
||||
default:
|
||||
assert(!"Unknown function parameter type!");
|
||||
@@ -237,40 +228,89 @@ void ValveFunction::Call(ScriptContext& script_context, int offset) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
//
|
||||
// CHook* HookFunctionHelper(void* addr, ICallingConvention* pConv) {
|
||||
// CHook* result;
|
||||
// result = GetHookManager()->HookFunction(addr, pConv);
|
||||
// return result;
|
||||
//}
|
||||
//
|
||||
// void ValveFunction::DeleteHook() {
|
||||
// CHook* pHook = GetHookManager()->FindHook((void*)m_ulAddr);
|
||||
// if (!pHook) return;
|
||||
//
|
||||
// // Set the calling convention to NULL, because DynamicHooks will delete it
|
||||
// // otherwise.
|
||||
// pHook->m_pCallingConvention = NULL;
|
||||
// GetHookManager()->UnhookFunction((void*)m_ulAddr);
|
||||
//}
|
||||
//
|
||||
// CHook* ValveFunction::AddHook(HookType_t eType, void* callable) {
|
||||
// if (!IsHookable()) return nullptr;
|
||||
//
|
||||
// CHook* pHook = GetHookManager()->FindHook((void*)m_ulAddr);
|
||||
//
|
||||
// if (!pHook) {
|
||||
// pHook = HookFunctionHelper((void*)m_ulAddr, m_pCallingConvention);
|
||||
//
|
||||
// // DynamicHooks will handle our convention from there, regardless if we
|
||||
// // allocated it or not.
|
||||
// m_bAllocatedCallingConvention = false;
|
||||
// }
|
||||
//
|
||||
// // Add the hook handler. If it's already added, it won't be added twice
|
||||
// pHook->AddCallback(eType, (HookHandlerFn*)(void*)callable);
|
||||
//
|
||||
// return pHook;
|
||||
//}
|
||||
|
||||
dyno::ReturnAction HookHandler(dyno::HookType hookType, dyno::Hook& hook)
|
||||
{
|
||||
auto vf = g_HookMap[&hook];
|
||||
|
||||
auto callback = hookType == dyno::HookType::Pre ? vf->m_precallback : vf->m_postcallback;
|
||||
|
||||
if (callback == nullptr) {
|
||||
return dyno::ReturnAction::Ignored;
|
||||
}
|
||||
|
||||
callback->Reset();
|
||||
callback->ScriptContext().Push(&hook);
|
||||
|
||||
for (auto fnMethodToCall : callback->GetFunctions()) {
|
||||
if (!fnMethodToCall)
|
||||
continue;
|
||||
fnMethodToCall(&callback->ScriptContextStruct());
|
||||
|
||||
auto result = callback->ScriptContext().GetResult<HookResult>();
|
||||
CSSHARP_CORE_TRACE("Received hook callback result of {}, hook mode {}", result,
|
||||
(int)hookType);
|
||||
|
||||
if (result >= HookResult::Handled) {
|
||||
return dyno::ReturnAction::Supercede;
|
||||
}
|
||||
}
|
||||
|
||||
return dyno::ReturnAction::Ignored;
|
||||
}
|
||||
|
||||
std::vector<dyno::DataObject> ConvertArgsToDynoHook(const std::vector<DataType_t>& dataTypes)
|
||||
{
|
||||
std::vector<dyno::DataObject> converted;
|
||||
converted.reserve(dataTypes.size());
|
||||
|
||||
for (DataType_t dt : dataTypes) {
|
||||
converted.push_back(dyno::DataObject(static_cast<dyno::DataType>(dt)));
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
void ValveFunction::AddHook(CallbackT callable, bool post)
|
||||
{
|
||||
dyno::HookManager& manager = dyno::HookManager::Get();
|
||||
dyno::Hook* hook = manager.hook((void*)m_ulAddr, [this] {
|
||||
return new dyno::x64SystemVcall(ConvertArgsToDynoHook(m_Args),
|
||||
static_cast<dyno::DataType>(this->m_eReturnType));
|
||||
});
|
||||
g_HookMap[hook] = this;
|
||||
hook->addCallback(dyno::HookType::Post, (dyno::HookHandler*)&HookHandler);
|
||||
hook->addCallback(dyno::HookType::Pre, (dyno::HookHandler*)&HookHandler);
|
||||
|
||||
if (post) {
|
||||
if (m_postcallback == nullptr) {
|
||||
m_postcallback = globals::callbackManager.CreateCallback("");
|
||||
}
|
||||
m_postcallback->AddListener(callable);
|
||||
} else {
|
||||
if (m_precallback == nullptr) {
|
||||
m_precallback = globals::callbackManager.CreateCallback("");
|
||||
}
|
||||
m_precallback->AddListener(callable);
|
||||
}
|
||||
}
|
||||
void ValveFunction::RemoveHook(CallbackT callable, bool post) {
|
||||
dyno::HookManager& manager = dyno::HookManager::Get();
|
||||
dyno::Hook* hook = manager.hook((void*)m_ulAddr, [this] {
|
||||
return new dyno::x64SystemVcall(ConvertArgsToDynoHook(m_Args),
|
||||
static_cast<dyno::DataType>(this->m_eReturnType));
|
||||
});
|
||||
g_HookMap[hook] = this;
|
||||
|
||||
if (post) {
|
||||
if (m_postcallback != nullptr) {
|
||||
m_postcallback->RemoveListener(callable);
|
||||
}
|
||||
} else {
|
||||
if (m_precallback != nullptr) {
|
||||
m_precallback->RemoveListener(callable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace counterstrikesharp
|
||||
@@ -32,6 +32,11 @@
|
||||
|
||||
#include "scripting/callback_manager.h"
|
||||
#include "scripting/script_engine.h"
|
||||
#include <map>
|
||||
|
||||
namespace dyno {
|
||||
class Hook;
|
||||
}
|
||||
|
||||
namespace counterstrikesharp {
|
||||
|
||||
@@ -81,19 +86,13 @@ public:
|
||||
~ValveFunction();
|
||||
|
||||
bool IsCallable();
|
||||
// bool IsHookable();
|
||||
|
||||
// bool IsHooked();
|
||||
// CHook* GetHook();
|
||||
|
||||
// ValveFunction* GetTrampoline();
|
||||
|
||||
void SetOffset(int offset) { m_offset = offset; }
|
||||
void SetSignature(const char* signature) { m_signature = signature; }
|
||||
|
||||
void Call(ScriptContext& args, int offset = 0);
|
||||
// CHook* AddHook(HookType_t eType, void* callable);
|
||||
// void DeleteHook();
|
||||
void AddHook(CallbackT callable, bool post);
|
||||
void RemoveHook(CallbackT callable, bool post);
|
||||
|
||||
void* m_ulAddr;
|
||||
std::vector<DataType_t> m_Args;
|
||||
@@ -107,6 +106,8 @@ public:
|
||||
|
||||
int m_offset;
|
||||
const char* m_signature;
|
||||
ScriptCallback* m_precallback = nullptr;
|
||||
ScriptCallback* m_postcallback = nullptr;
|
||||
};
|
||||
|
||||
} // namespace counterstrikesharp
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
SH_DECL_HOOK4_void(IServerGameClients, ClientActive, SH_NOATTRIB, 0, CPlayerSlot, bool, const char*,
|
||||
uint64);
|
||||
SH_DECL_HOOK5_void(IServerGameClients, ClientDisconnect, SH_NOATTRIB, 0, CPlayerSlot, int,
|
||||
SH_DECL_HOOK5_void(IServerGameClients, ClientDisconnect, SH_NOATTRIB, 0, CPlayerSlot, ENetworkDisconnectionReason,
|
||||
const char*, uint64, const char*);
|
||||
SH_DECL_HOOK4_void(IServerGameClients, ClientPutInServer, SH_NOATTRIB, 0, CPlayerSlot, char const*,
|
||||
int, uint64);
|
||||
@@ -119,8 +119,8 @@ bool PlayerManager::OnClientConnect(CPlayerSlot slot, const char* pszName, uint6
|
||||
CPlayer* pPlayer = &m_players[client];
|
||||
|
||||
if (pPlayer->IsConnected()) {
|
||||
OnClientDisconnect(slot, 0, pszName, xuid, pszNetworkID);
|
||||
OnClientDisconnect_Post(slot, 0, pszName, xuid, pszNetworkID);
|
||||
OnClientDisconnect(slot, ENetworkDisconnectionReason::NETWORK_DISCONNECT_INVALID, pszName, xuid, pszNetworkID);
|
||||
OnClientDisconnect_Post(slot, ENetworkDisconnectionReason::NETWORK_DISCONNECT_INVALID, pszName, xuid, pszNetworkID);
|
||||
}
|
||||
|
||||
pPlayer->Initialize(pszName, pszNetworkID, slot);
|
||||
@@ -222,7 +222,7 @@ void PlayerManager::OnClientPutInServer(CPlayerSlot slot, char const* pszName, i
|
||||
}
|
||||
|
||||
void PlayerManager::OnClientDisconnect(CPlayerSlot slot,
|
||||
/* ENetworkDisconnectionReason */ int reason,
|
||||
ENetworkDisconnectionReason reason,
|
||||
const char* pszName, uint64 xuid, const char* pszNetworkID)
|
||||
{
|
||||
CSSHARP_CORE_TRACE("[PlayerManager][OnClientDisconnect] - {}, {}, {}", slot.Get(), pszName,
|
||||
@@ -234,6 +234,7 @@ void PlayerManager::OnClientDisconnect(CPlayerSlot slot,
|
||||
if (pPlayer->IsConnected()) {
|
||||
m_on_client_disconnect_callback->ScriptContext().Reset();
|
||||
m_on_client_disconnect_callback->ScriptContext().Push(pPlayer->m_slot.Get());
|
||||
m_on_client_disconnect_callback->ScriptContext().Push(reason);
|
||||
m_on_client_disconnect_callback->Execute();
|
||||
}
|
||||
|
||||
@@ -245,7 +246,7 @@ void PlayerManager::OnClientDisconnect(CPlayerSlot slot,
|
||||
}
|
||||
|
||||
void PlayerManager::OnClientDisconnect_Post(CPlayerSlot slot,
|
||||
/* ENetworkDisconnectionReason */ int reason,
|
||||
ENetworkDisconnectionReason reason,
|
||||
const char* pszName, uint64 xuid,
|
||||
const char* pszNetworkID) const
|
||||
{
|
||||
@@ -263,6 +264,7 @@ void PlayerManager::OnClientDisconnect_Post(CPlayerSlot slot,
|
||||
|
||||
m_on_client_disconnect_post_callback->ScriptContext().Reset();
|
||||
m_on_client_disconnect_post_callback->ScriptContext().Push(pPlayer->m_slot.Get());
|
||||
m_on_client_disconnect_post_callback->ScriptContext().Push(reason);
|
||||
m_on_client_disconnect_post_callback->Execute();
|
||||
}
|
||||
|
||||
@@ -272,9 +274,9 @@ void PlayerManager::OnLevelEnd()
|
||||
|
||||
for (int i = 0; i <= m_max_clients; i++) {
|
||||
if (m_players[i].IsConnected()) {
|
||||
OnClientDisconnect(m_players[i].m_slot, 0, m_players[i].GetName(), 0,
|
||||
OnClientDisconnect(m_players[i].m_slot, ENetworkDisconnectionReason::NETWORK_DISCONNECT_INVALID, m_players[i].GetName(), 0,
|
||||
m_players[i].GetIpAddress());
|
||||
OnClientDisconnect_Post(m_players[i].m_slot, 0, m_players[i].GetName(), 0,
|
||||
OnClientDisconnect_Post(m_players[i].m_slot, ENetworkDisconnectionReason::NETWORK_DISCONNECT_INVALID, m_players[i].GetName(), 0,
|
||||
m_players[i].GetIpAddress());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,12 +130,12 @@ public:
|
||||
CBufferString *pRejectReason);
|
||||
void OnClientPutInServer(CPlayerSlot slot, char const *pszName, int type, uint64 xuid);
|
||||
void OnClientDisconnect(CPlayerSlot slot,
|
||||
/* ENetworkDisconnectionReason */ int reason,
|
||||
ENetworkDisconnectionReason reason,
|
||||
const char *pszName,
|
||||
uint64 xuid,
|
||||
const char *pszNetworkID);
|
||||
void OnClientDisconnect_Post(CPlayerSlot slot,
|
||||
/* ENetworkDisconnectionReason */ int reason,
|
||||
ENetworkDisconnectionReason reason,
|
||||
const char *pszName,
|
||||
uint64 xuid,
|
||||
const char *pszNetworkID) const;
|
||||
|
||||
@@ -84,7 +84,7 @@ bool CounterStrikeSharpMMPlugin::Load(PluginId id, ISmmAPI* ismm, char* error, s
|
||||
GET_V_IFACE_ANY(GetEngineFactory, globals::gameEventSystem, IGameEventSystem,
|
||||
GAMEEVENTSYSTEM_INTERFACE_VERSION);
|
||||
|
||||
auto coreconfig_path = std::string(utils::ConfigsDirectory() + "/core.json");
|
||||
auto coreconfig_path = std::string(utils::ConfigsDirectory() + "/core");
|
||||
globals::coreConfig = new CCoreConfig(coreconfig_path);
|
||||
char coreconfig_error[255] = "";
|
||||
|
||||
|
||||
@@ -199,16 +199,16 @@ bool CDotNetManager::Initialize()
|
||||
const std::string dotnetlib_path =
|
||||
std::string((base_dir + "/api/CounterStrikeSharp.API.dll").c_str());
|
||||
#endif
|
||||
const auto dotnet_type = STR("CounterStrikeSharp.API.Core.Helpers, CounterStrikeSharp.API");
|
||||
const auto dotnet_type = STR("CounterStrikeSharp.API.Bootstrap, CounterStrikeSharp.API");
|
||||
// Namespace, assembly name
|
||||
|
||||
typedef int(CORECLR_DELEGATE_CALLTYPE * custom_entry_point_fn)();
|
||||
custom_entry_point_fn entry_point = nullptr;
|
||||
const int rc = load_assembly_and_get_function_pointer(
|
||||
dotnetlib_path.c_str(), dotnet_type, STR("LoadAllPlugins"), UNMANAGEDCALLERSONLY_METHOD,
|
||||
dotnetlib_path.c_str(), dotnet_type, STR("Run"), UNMANAGEDCALLERSONLY_METHOD,
|
||||
nullptr, reinterpret_cast<void**>(&entry_point));
|
||||
if (entry_point == nullptr) {
|
||||
CSSHARP_CORE_ERROR("Trying to get entry point \"LoadAllPlugins\" but failed.");
|
||||
CSSHARP_CORE_ERROR("Trying to get entry point \"Bootstrap::Run\" but failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ bool CDotNetManager::Initialize()
|
||||
"Failure: load_assembly_and_get_function_pointer()");
|
||||
|
||||
if (const int invoke_result_code = entry_point(); invoke_result_code == 0) {
|
||||
CSSHARP_CORE_ERROR("LoadAllPlugins return failure.");
|
||||
CSSHARP_CORE_ERROR("Bootstrap::Run return failure.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
283
src/scripting/natives/natives_dynamichooks.cpp
Normal file
283
src/scripting/natives/natives_dynamichooks.cpp
Normal file
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* This file is part of CounterStrikeSharp.
|
||||
* CounterStrikeSharp is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* CounterStrikeSharp is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
|
||||
*/
|
||||
|
||||
// clang-format off
|
||||
#include "mm_plugin.h"
|
||||
#include "core/timer_system.h"
|
||||
#include "scripting/autonative.h"
|
||||
#include "scripting/script_engine.h"
|
||||
#include "core/function.h"
|
||||
#include "pch.h"
|
||||
#include "dynohook/core.h"
|
||||
#include "dynohook/manager.h"
|
||||
|
||||
// clang-format on
|
||||
|
||||
namespace counterstrikesharp {
|
||||
|
||||
void DHookGetReturn(ScriptContext& script_context)
|
||||
{
|
||||
auto hook = script_context.GetArgument<dyno::Hook*>(0);
|
||||
auto dataType = script_context.GetArgument<DataType_t>(1);
|
||||
if (hook == nullptr) {
|
||||
script_context.ThrowNativeError("Invalid hook");
|
||||
}
|
||||
|
||||
switch (dataType) {
|
||||
case DATA_TYPE_BOOL:
|
||||
script_context.SetResult(hook->getReturnValue<bool>());
|
||||
break;
|
||||
case DATA_TYPE_CHAR:
|
||||
script_context.SetResult(hook->getReturnValue<char>());
|
||||
break;
|
||||
case DATA_TYPE_UCHAR:
|
||||
script_context.SetResult(hook->getReturnValue<unsigned char>());
|
||||
break;
|
||||
case DATA_TYPE_SHORT:
|
||||
script_context.SetResult(hook->getReturnValue<short>());
|
||||
break;
|
||||
case DATA_TYPE_USHORT:
|
||||
script_context.SetResult(hook->getReturnValue<unsigned short>());
|
||||
break;
|
||||
case DATA_TYPE_INT:
|
||||
script_context.SetResult(hook->getReturnValue<int>());
|
||||
break;
|
||||
case DATA_TYPE_UINT:
|
||||
script_context.SetResult(hook->getReturnValue<unsigned int>());
|
||||
break;
|
||||
case DATA_TYPE_LONG:
|
||||
script_context.SetResult(hook->getReturnValue<long>());
|
||||
break;
|
||||
case DATA_TYPE_ULONG:
|
||||
script_context.SetResult(hook->getReturnValue<unsigned long>());
|
||||
break;
|
||||
case DATA_TYPE_LONG_LONG:
|
||||
script_context.SetResult(hook->getReturnValue<long long>());
|
||||
break;
|
||||
case DATA_TYPE_ULONG_LONG:
|
||||
script_context.SetResult(hook->getReturnValue<unsigned long long>());
|
||||
break;
|
||||
case DATA_TYPE_FLOAT:
|
||||
script_context.SetResult(hook->getReturnValue<float>());
|
||||
break;
|
||||
case DATA_TYPE_DOUBLE:
|
||||
script_context.SetResult(hook->getReturnValue<double>());
|
||||
break;
|
||||
case DATA_TYPE_POINTER:
|
||||
script_context.SetResult(hook->getReturnValue<void*>());
|
||||
break;
|
||||
case DATA_TYPE_STRING:
|
||||
script_context.SetResult(hook->getReturnValue<const char*>());
|
||||
break;
|
||||
default:
|
||||
assert(!"Unknown function parameter type!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DHookSetReturn(ScriptContext& script_context)
|
||||
{
|
||||
auto hook = script_context.GetArgument<dyno::Hook*>(0);
|
||||
auto dataType = script_context.GetArgument<DataType_t>(1);
|
||||
if (hook == nullptr) {
|
||||
script_context.ThrowNativeError("Invalid hook");
|
||||
}
|
||||
|
||||
auto valueIndex = 2;
|
||||
|
||||
switch (dataType) {
|
||||
case DATA_TYPE_BOOL:
|
||||
hook->setReturnValue(script_context.GetArgument<bool>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_CHAR:
|
||||
hook->setReturnValue(script_context.GetArgument<char>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_UCHAR:
|
||||
hook->setReturnValue(script_context.GetArgument<unsigned char>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_SHORT:
|
||||
hook->setReturnValue(script_context.GetArgument<short>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_USHORT:
|
||||
hook->setReturnValue(script_context.GetArgument<unsigned short>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_INT:
|
||||
hook->setReturnValue(script_context.GetArgument<int>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_UINT:
|
||||
hook->setReturnValue(script_context.GetArgument<unsigned int>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_LONG:
|
||||
hook->setReturnValue(script_context.GetArgument<long>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_ULONG:
|
||||
hook->setReturnValue(script_context.GetArgument<unsigned long>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_LONG_LONG:
|
||||
hook->setReturnValue(script_context.GetArgument<long long>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_ULONG_LONG:
|
||||
hook->setReturnValue(script_context.GetArgument<unsigned long long>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_FLOAT:
|
||||
hook->setReturnValue(script_context.GetArgument<float>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_DOUBLE:
|
||||
hook->setReturnValue(script_context.GetArgument<double>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_POINTER:
|
||||
hook->setReturnValue(script_context.GetArgument<void*>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_STRING:
|
||||
hook->setReturnValue(script_context.GetArgument<const char*>(valueIndex));
|
||||
break;
|
||||
default:
|
||||
assert(!"Unknown function parameter type!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DHookGetParam(ScriptContext& script_context)
|
||||
{
|
||||
auto hook = script_context.GetArgument<dyno::Hook*>(0);
|
||||
auto dataType = script_context.GetArgument<DataType_t>(1);
|
||||
auto paramIndex = script_context.GetArgument<int>(2);
|
||||
if (hook == nullptr) {
|
||||
script_context.ThrowNativeError("Invalid hook");
|
||||
}
|
||||
|
||||
switch (dataType) {
|
||||
case DATA_TYPE_BOOL:
|
||||
script_context.SetResult(hook->getArgument<bool>(paramIndex));
|
||||
break;
|
||||
case DATA_TYPE_CHAR:
|
||||
script_context.SetResult(hook->getArgument<char>(paramIndex));
|
||||
break;
|
||||
case DATA_TYPE_UCHAR:
|
||||
script_context.SetResult(hook->getArgument<unsigned char>(paramIndex));
|
||||
break;
|
||||
case DATA_TYPE_SHORT:
|
||||
script_context.SetResult(hook->getArgument<short>(paramIndex));
|
||||
break;
|
||||
case DATA_TYPE_USHORT:
|
||||
script_context.SetResult(hook->getArgument<unsigned short>(paramIndex));
|
||||
break;
|
||||
case DATA_TYPE_INT:
|
||||
script_context.SetResult(hook->getArgument<int>(paramIndex));
|
||||
break;
|
||||
case DATA_TYPE_UINT:
|
||||
script_context.SetResult(hook->getArgument<unsigned int>(paramIndex));
|
||||
break;
|
||||
case DATA_TYPE_LONG:
|
||||
script_context.SetResult(hook->getArgument<long>(paramIndex));
|
||||
break;
|
||||
case DATA_TYPE_ULONG:
|
||||
script_context.SetResult(hook->getArgument<unsigned long>(paramIndex));
|
||||
break;
|
||||
case DATA_TYPE_LONG_LONG:
|
||||
script_context.SetResult(hook->getArgument<long long>(paramIndex));
|
||||
break;
|
||||
case DATA_TYPE_ULONG_LONG:
|
||||
script_context.SetResult(hook->getArgument<unsigned long long>(paramIndex));
|
||||
break;
|
||||
case DATA_TYPE_FLOAT:
|
||||
script_context.SetResult(hook->getArgument<float>(paramIndex));
|
||||
break;
|
||||
case DATA_TYPE_DOUBLE:
|
||||
script_context.SetResult(hook->getArgument<double>(paramIndex));
|
||||
break;
|
||||
case DATA_TYPE_POINTER:
|
||||
script_context.SetResult(hook->getArgument<void*>(paramIndex));
|
||||
break;
|
||||
case DATA_TYPE_STRING:
|
||||
script_context.SetResult(hook->getArgument<const char*>(paramIndex));
|
||||
break;
|
||||
default:
|
||||
assert(!"Unknown function parameter type!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DHookSetParam(ScriptContext& script_context)
|
||||
{
|
||||
auto hook = script_context.GetArgument<dyno::Hook*>(0);
|
||||
auto dataType = script_context.GetArgument<DataType_t>(1);
|
||||
auto paramIndex = script_context.GetArgument<int>(2);
|
||||
if (hook == nullptr) {
|
||||
script_context.ThrowNativeError("Invalid hook");
|
||||
}
|
||||
|
||||
auto valueIndex = 3;
|
||||
|
||||
switch (dataType) {
|
||||
case DATA_TYPE_BOOL:
|
||||
hook->setArgument(paramIndex, script_context.GetArgument<bool>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_CHAR:
|
||||
hook->setArgument(paramIndex, script_context.GetArgument<char>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_UCHAR:
|
||||
hook->setArgument(paramIndex, script_context.GetArgument<unsigned char>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_SHORT:
|
||||
hook->setArgument(paramIndex, script_context.GetArgument<short>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_USHORT:
|
||||
hook->setArgument(paramIndex, script_context.GetArgument<unsigned short>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_INT:
|
||||
hook->setArgument(paramIndex, script_context.GetArgument<int>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_UINT:
|
||||
hook->setArgument(paramIndex, script_context.GetArgument<unsigned int>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_LONG:
|
||||
hook->setArgument(paramIndex, script_context.GetArgument<long>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_ULONG:
|
||||
hook->setArgument(paramIndex, script_context.GetArgument<unsigned long>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_LONG_LONG:
|
||||
hook->setArgument(paramIndex, script_context.GetArgument<long long>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_ULONG_LONG:
|
||||
hook->setArgument(paramIndex, script_context.GetArgument<unsigned long long>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_FLOAT:
|
||||
hook->setArgument(paramIndex, script_context.GetArgument<float>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_DOUBLE:
|
||||
hook->setArgument(paramIndex, script_context.GetArgument<double>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_POINTER:
|
||||
hook->setArgument(paramIndex, script_context.GetArgument<void*>(valueIndex));
|
||||
break;
|
||||
case DATA_TYPE_STRING:
|
||||
hook->setArgument(paramIndex, script_context.GetArgument<const char*>(valueIndex));
|
||||
break;
|
||||
default:
|
||||
assert(!"Unknown function parameter type!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_NATIVES(dynamichooks, {
|
||||
ScriptEngine::RegisterNativeHandler("DYNAMIC_HOOK_GET_RETURN", DHookGetReturn);
|
||||
ScriptEngine::RegisterNativeHandler("DYNAMIC_HOOK_SET_RETURN", DHookSetReturn);
|
||||
ScriptEngine::RegisterNativeHandler("DYNAMIC_HOOK_GET_PARAM", DHookGetParam);
|
||||
ScriptEngine::RegisterNativeHandler("DYNAMIC_HOOK_SET_PARAM", DHookSetParam);
|
||||
})
|
||||
} // namespace counterstrikesharp
|
||||
4
src/scripting/natives/natives_dynamichooks.yaml
Normal file
4
src/scripting/natives/natives_dynamichooks.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
DYNAMIC_HOOK_GET_RETURN: hook:pointer, datatype:int -> any
|
||||
DYNAMIC_HOOK_SET_RETURN: hook:pointer, datatype:int, value:any -> void
|
||||
DYNAMIC_HOOK_GET_PARAM: hook:pointer, datatype:int, paramIndex:int -> any
|
||||
DYNAMIC_HOOK_SET_PARAM: hook:pointer, datatype:int, paramIndex:int, value:any -> void
|
||||
@@ -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
|
||||
@@ -26,7 +26,8 @@
|
||||
namespace counterstrikesharp {
|
||||
std::vector<ValveFunction*> m_managed_ptrs;
|
||||
|
||||
byte *ConvertToByteArray(const char *str, size_t *outLength) {
|
||||
byte* ConvertToByteArray(const char* str, size_t* outLength)
|
||||
{
|
||||
size_t len = strlen(str) / 4; // Every byte is represented as \xHH
|
||||
byte* result = (byte*)malloc(len);
|
||||
|
||||
@@ -38,14 +39,16 @@ byte *ConvertToByteArray(const char *str, size_t *outLength) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void *FindSignatureNative(ScriptContext &scriptContext) {
|
||||
void* FindSignatureNative(ScriptContext& scriptContext)
|
||||
{
|
||||
auto moduleName = scriptContext.GetArgument<const char*>(0);
|
||||
auto bytesStr = scriptContext.GetArgument<const char*>(1);
|
||||
|
||||
return FindSignature(moduleName, bytesStr);
|
||||
}
|
||||
|
||||
ValveFunction *CreateVirtualFunctionBySignature(ScriptContext &script_context) {
|
||||
ValveFunction* CreateVirtualFunctionBySignature(ScriptContext& script_context)
|
||||
{
|
||||
auto ptr = script_context.GetArgument<unsigned long>(0);
|
||||
auto binary_name = script_context.GetArgument<const char*>(1);
|
||||
auto signature_hex_string = script_context.GetArgument<const char*>(2);
|
||||
@@ -55,7 +58,7 @@ ValveFunction *CreateVirtualFunctionBySignature(ScriptContext &script_context) {
|
||||
auto* function_addr = FindSignature(binary_name, signature_hex_string);
|
||||
|
||||
if (function_addr == nullptr) {
|
||||
script_context.ThrowNativeError("Could not find signature");
|
||||
script_context.ThrowNativeError("Could not find signature %s", signature_hex_string);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -67,13 +70,15 @@ ValveFunction *CreateVirtualFunctionBySignature(ScriptContext &script_context) {
|
||||
auto function = new ValveFunction(function_addr, CONV_CDECL, args, return_type);
|
||||
function->SetSignature(signature_hex_string);
|
||||
|
||||
CSSHARP_CORE_TRACE("Created virtual function, pointer found at {}, signature {}", function_addr, signature_hex_string);
|
||||
CSSHARP_CORE_TRACE("Created virtual function, pointer found at {}, signature {}", function_addr,
|
||||
signature_hex_string);
|
||||
|
||||
m_managed_ptrs.push_back(function);
|
||||
return function;
|
||||
}
|
||||
|
||||
ValveFunction *CreateVirtualFunction(ScriptContext &script_context) {
|
||||
ValveFunction* CreateVirtualFunction(ScriptContext& script_context)
|
||||
{
|
||||
auto ptr = script_context.GetArgument<void*>(0);
|
||||
auto vtable_offset = script_context.GetArgument<int>(1);
|
||||
auto num_arguments = script_context.GetArgument<int>(2);
|
||||
@@ -99,7 +104,36 @@ ValveFunction *CreateVirtualFunction(ScriptContext &script_context) {
|
||||
return function;
|
||||
}
|
||||
|
||||
void ExecuteVirtualFunction(ScriptContext &script_context) {
|
||||
void HookFunction(ScriptContext& script_context)
|
||||
{
|
||||
auto function = script_context.GetArgument<ValveFunction*>(0);
|
||||
auto callback = script_context.GetArgument<CallbackT>(1);
|
||||
auto post = script_context.GetArgument<bool>(2);
|
||||
|
||||
if (!function) {
|
||||
script_context.ThrowNativeError("Invalid function pointer");
|
||||
return;
|
||||
}
|
||||
|
||||
function->AddHook(callback, post);
|
||||
}
|
||||
|
||||
void UnhookFunction(ScriptContext& script_context)
|
||||
{
|
||||
auto function = script_context.GetArgument<ValveFunction*>(0);
|
||||
auto callback = script_context.GetArgument<CallbackT>(1);
|
||||
auto post = script_context.GetArgument<bool>(2);
|
||||
|
||||
if (!function) {
|
||||
script_context.ThrowNativeError("Invalid function pointer");
|
||||
return;
|
||||
}
|
||||
|
||||
function->RemoveHook(callback, post);
|
||||
}
|
||||
|
||||
void ExecuteVirtualFunction(ScriptContext& script_context)
|
||||
{
|
||||
auto function = script_context.GetArgument<ValveFunction*>(0);
|
||||
|
||||
if (!function) {
|
||||
@@ -110,41 +144,15 @@ void ExecuteVirtualFunction(ScriptContext &script_context) {
|
||||
function->Call(script_context, 1);
|
||||
}
|
||||
|
||||
// void HookFunction(ScriptContext& script_context) {
|
||||
// auto function = script_context.GetArgument<ValveFunction*>(0);
|
||||
// auto entity_index = script_context.GetArgument<int>(1);
|
||||
// auto post = script_context.GetArgument<bool>(2);
|
||||
// auto callback = script_context.GetArgument<CallbackT>(3);
|
||||
//
|
||||
// if (!function) {
|
||||
// script_context.ThrowNativeError("Invalid function pointer");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// globals::hook_manager.AddHook(function, entity_index, callback, post);
|
||||
// }
|
||||
//
|
||||
// void UnhookFunction(ScriptContext& script_context) {
|
||||
// auto function = script_context.GetArgument<ValveFunction*>(0);
|
||||
// auto entity_index = script_context.GetArgument<int>(1);
|
||||
// auto post = script_context.GetArgument<bool>(2);
|
||||
// auto callback = script_context.GetArgument<CallbackT>(3);
|
||||
//
|
||||
// if (!function) {
|
||||
// script_context.ThrowNativeError("Invalid function pointer");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// globals::hook_manager.Unhook(function, entity_index, callback, post);
|
||||
// }
|
||||
|
||||
int GetNetworkVectorSize(ScriptContext &script_context) {
|
||||
int GetNetworkVectorSize(ScriptContext& script_context)
|
||||
{
|
||||
auto vec = script_context.GetArgument<CUtlVector<void*>*>(0);
|
||||
|
||||
return vec->Count();
|
||||
}
|
||||
|
||||
void* GetNetworkVectorElementAt(ScriptContext &script_context) {
|
||||
void* GetNetworkVectorElementAt(ScriptContext& script_context)
|
||||
{
|
||||
auto vec = script_context.GetArgument<CUtlVector<CEntityHandle>*>(0);
|
||||
auto index = script_context.GetArgument<int>(1);
|
||||
|
||||
@@ -156,8 +164,8 @@ REGISTER_NATIVES(memory, {
|
||||
ScriptEngine::RegisterNativeHandler("CREATE_VIRTUAL_FUNCTION_BY_SIGNATURE",
|
||||
CreateVirtualFunctionBySignature);
|
||||
ScriptEngine::RegisterNativeHandler("EXECUTE_VIRTUAL_FUNCTION", ExecuteVirtualFunction);
|
||||
// ScriptEngine::RegisterNativeHandler("HOOK_FUNCTION", HookFunction);
|
||||
// ScriptEngine::RegisterNativeHandler("UNHOOK_FUNCTION", UnhookFunction);
|
||||
ScriptEngine::RegisterNativeHandler("HOOK_FUNCTION", HookFunction);
|
||||
ScriptEngine::RegisterNativeHandler("UNHOOK_FUNCTION", UnhookFunction);
|
||||
ScriptEngine::RegisterNativeHandler("FIND_SIGNATURE", FindSignatureNative);
|
||||
ScriptEngine::RegisterNativeHandler("GET_NETWORK_VECTOR_SIZE", GetNetworkVectorSize);
|
||||
ScriptEngine::RegisterNativeHandler("GET_NETWORK_VECTOR_ELEMENT_AT", GetNetworkVectorElementAt);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
CREATE_VIRTUAL_FUNCTION: pointer:pointer,vtableOffset:int,numArguments:int,returnType:int,arguments:object[] -> pointer
|
||||
CREATE_VIRTUAL_FUNCTION_BY_SIGNATURE: pointer:pointer,binaryName:string,signature:string,numArguments:int,returnType:int,arguments:object[] -> pointer
|
||||
HOOK_FUNCTION: function:pointer, hook:callback, post:bool -> void
|
||||
UNHOOK_FUNCTION: function:pointer, hook:callback, post:bool -> void
|
||||
EXECUTE_VIRTUAL_FUNCTION: function:pointer,arguments:object[] -> any
|
||||
FIND_SIGNATURE: modulePath:string, signature:string -> pointer
|
||||
GET_NETWORK_VECTOR_SIZE: vec:pointer -> int
|
||||
|
||||
@@ -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
|
||||
GET_SCHEMA_CLASS_SIZE: className:string -> int
|
||||
Reference in New Issue
Block a user