mirror of
https://github.com/roflmuffin/CounterStrikeSharp.git
synced 2025-12-06 08:03:12 -08:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a018f295b | ||
|
|
8b725d435f | ||
|
|
ea3596417a | ||
|
|
123f41914e | ||
|
|
8f69076405 | ||
|
|
44a85d1201 | ||
|
|
20f50289ee | ||
|
|
bb5fb5de72 | ||
|
|
6147739cfa | ||
|
|
3ab5893f22 | ||
|
|
50ce09a7b3 | ||
|
|
9c7944e6f1 | ||
|
|
bc71aa7739 | ||
|
|
16a1efc0cb | ||
|
|
8ae85cedf4 | ||
|
|
8e2234ff25 | ||
|
|
04e7ed682a | ||
|
|
15e1260146 | ||
|
|
517607d962 | ||
|
|
0f72631eb0 | ||
|
|
75fcf21fb7 | ||
|
|
0ddf6bcdfa | ||
|
|
98661cd069 |
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
|
||||
@@ -29,6 +31,8 @@ SET(SOURCE_FILES
|
||||
src/core/utils.h
|
||||
src/core/globals.h
|
||||
src/core/globals.cpp
|
||||
src/core/coreconfig.h
|
||||
src/core/coreconfig.cpp
|
||||
src/core/gameconfig.h
|
||||
src/core/gameconfig.cpp
|
||||
src/core/log.h
|
||||
@@ -79,12 +83,13 @@ 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
|
||||
)
|
||||
|
||||
|
||||
if (LINUX)
|
||||
# memoverride.cpp is not usable on CMake Windows, cuz CMake default link libraries (seems) always link ucrt.lib
|
||||
set(SOURCE_FILES
|
||||
set(SOURCE_FILES
|
||||
${SOURCE_FILES}
|
||||
libraries/hl2sdk-cs2/public/tier0/memoverride.cpp
|
||||
)
|
||||
@@ -94,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})
|
||||
|
||||
17
README.md
17
README.md
@@ -11,18 +11,20 @@ This project is an ongoing migration of a previous project (titled [VSP.NET](htt
|
||||
Due to the architectural changes of CS2, the plugin is being rebuilt on the ground up, to support Linux 64-bit, something which was previously impossible.
|
||||
|
||||
## Install
|
||||
|
||||
Download the latest build from [here](https://github.com/roflmuffin/CounterStrikeSharp/releases). (Download the with runtime version if this is your first time installing).
|
||||
|
||||
Detailed installation instructions can be found in the [docs](https://docs.cssharp.dev/guides/getting-started/).
|
||||
|
||||
## What works?
|
||||
|
||||
_(Note, these were features in the previous VSP.NET project, but have not been implemented yet in this project)_
|
||||
|
||||
These features are the core of the platform and work pretty well/have a low risk of causing issues.
|
||||
|
||||
- [x] Console Commands, Server Commands (e.g. css_mycommand)
|
||||
- [x] Chat Commands with `!` and `/` prefixes (e.g. !mycommand)
|
||||
- [ ] **(In Progress)** Console Variables
|
||||
- [x] Console Commands, Server Commands (e.g. css_mycommand)
|
||||
- [x] Chat Commands with `!` and `/` prefixes (e.g. !mycommand)
|
||||
- [ ] **(In Progress)** Console Variables
|
||||
- [x] Game Event Handlers & Custom Events (e.g. player_death)
|
||||
- [x] Basic event value get/set (string, bool, int32, float)
|
||||
- [x] Complex event values get/set (ehandle, pawn, player controller)
|
||||
@@ -33,9 +35,10 @@ These features are the core of the platform and work pretty well/have a low risk
|
||||
- [x] OnMapStart
|
||||
- [x] OnTick
|
||||
- [x] Server Information (current map, game time, tick rate, model precaching)
|
||||
- [x] Schema System Access (access player values like current weapon, money, location etc.)
|
||||
- [x] Schema System Access (access player values like current weapon, money, location etc.)
|
||||
|
||||
## Links
|
||||
|
||||
- [Join the Discord](https://discord.gg/X7r3PmuYKq): Ask questions, provide suggestions
|
||||
- [Read the docs](https://docs.cssharp.dev/): Getting started guide, hello world plugin example
|
||||
- [Issue tracker](https://github.com/roflmuffin/CounterStrikeSharp/issues): Raise any issues here
|
||||
@@ -62,14 +65,14 @@ public class HelloWorldPlugin : BasePlugin
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
Console.WriteLine("Hello World!");
|
||||
Logger.LogInformation("Plugin loaded successfully!");
|
||||
}
|
||||
|
||||
[GameEventHandler]
|
||||
public HookResult OnPlayerConnect(EventPlayerConnect @event, GameEventInfo info)
|
||||
{
|
||||
// Userid will give you a reference to a CCSPlayerController class
|
||||
Log($"Player {@event.Userid.PlayerName} has connected!");
|
||||
Logger.LogInformation("Player {Name} has connected!", @event.Userid.PlayerName);
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
@@ -77,7 +80,7 @@ public class HelloWorldPlugin : BasePlugin
|
||||
[ConsoleCommand("issue_warning", "Issue warning to player")]
|
||||
public void OnCommand(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
Log("You shouldn't be doing that!");
|
||||
Logger.LogWarning("Player shouldn't be doing that");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
23
configs/addons/counterstrikesharp/configs/admin_groups.json
Normal file
23
configs/addons/counterstrikesharp/configs/admin_groups.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"#css/admin": {
|
||||
"flags": [
|
||||
"@css/reservation",
|
||||
"@css/generic",
|
||||
"@css/kick",
|
||||
"@css/ban",
|
||||
"@css/unban",
|
||||
"@css/vip",
|
||||
"@css/slay",
|
||||
"@css/changemap",
|
||||
"@css/cvar",
|
||||
"@css/config",
|
||||
"@css/chat",
|
||||
"@css/vote",
|
||||
"@css/password",
|
||||
"@css/rcon",
|
||||
"@css/cheats",
|
||||
"@css/root"
|
||||
],
|
||||
"immunity": 100
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"example_command": {
|
||||
"flags": [
|
||||
"@css/custom-permission"
|
||||
],
|
||||
"check_type": "all",
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,18 @@
|
||||
{
|
||||
"Erikj": {
|
||||
"identity": "76561197960265731",
|
||||
"immunity": 100,
|
||||
"flags": [
|
||||
"@css/reservation",
|
||||
"@css/generic",
|
||||
"@css/kick",
|
||||
"@css/ban",
|
||||
"@css/unban",
|
||||
"@css/vip",
|
||||
"@css/slay",
|
||||
"@css/changemap",
|
||||
"@css/cvar",
|
||||
"@css/config",
|
||||
"@css/chat",
|
||||
"@css/vote",
|
||||
"@css/password",
|
||||
"@css/rcon",
|
||||
"@css/cheats",
|
||||
"@css/root"
|
||||
]
|
||||
"@css/custom-flag-1",
|
||||
"@css/custom-flag-2"
|
||||
],
|
||||
"groups": [
|
||||
"#css/admin"
|
||||
],
|
||||
"command_overrides": {
|
||||
"css_plugins": true,
|
||||
"css": false
|
||||
}
|
||||
},
|
||||
"Another erikj": {
|
||||
"identity": "STEAM_0:1:1",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"PublicChatTrigger": "!",
|
||||
"SilentChatTrigger": "/",
|
||||
"PublicChatTrigger": [ "!" ],
|
||||
"SilentChatTrigger": [ "/" ],
|
||||
"FollowCS2ServerGuidelines": true
|
||||
}
|
||||
@@ -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",
|
||||
@@ -50,14 +70,60 @@
|
||||
"CBaseModelEntity_SetModel": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x48\\x89\\x5C\\x24\\x2A\\x48\\x89\\x7C\\x24\\x2A\\x55\\x48\\x8B\\xEC\\x48\\x83\\xEC\\x50\\x48\\x8B\\xF9",
|
||||
"linux": "\\x55\\x48\\x89\\xF2\\x48\\x89\\xE5\\x41\\x54\\x49\\x89\\xFC\\x48\\x8D\\x7D\\xE0\\x48\\x83\\xEC\\x18\\x48\\x8D\\x05\\xE5\\xD1\\xBF\\x00"
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"CCSPlayer_ItemServices_DropActivePlayerWeapon": {
|
||||
"offsets": {
|
||||
"windows": 20,
|
||||
"linux": 19
|
||||
}
|
||||
},
|
||||
"CCSPlayer_ItemServices_RemoveWeapons": {
|
||||
"offsets": {
|
||||
"windows": 21,
|
||||
"linux": 20
|
||||
}
|
||||
},
|
||||
"CGameSceneNode_GetSkeletonInstance": {
|
||||
"offsets": {
|
||||
"windows": 8,
|
||||
"linux": 8
|
||||
}
|
||||
},
|
||||
"CCSGameRules_TerminateRound": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x48\\x8B\\xC4\\x4C\\x89\\x48\\x20\\x55\\x56",
|
||||
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x41\\x55\\x49\\x89\\xFD\\x41\\x54\\x53\\x48\\x81\\xEC\\xE8"
|
||||
}
|
||||
},
|
||||
"UTIL_CreateEntityByName": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x48\\x83\\xEC\\x48\\xC6\\x44\\x24\\x30\\x00\\x4C\\x8B\\xC1",
|
||||
"linux": "\\x48\\x8D\\x05\\xC9\\xC2\\xBC"
|
||||
}
|
||||
},
|
||||
"CBaseEntity_DispatchSpawn": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x48\\x89\\x5C\\x24\\x10\\x57\\x48\\x83\\xEC\\x30\\x48\\x8B\\xDA\\x48\\x8B\\xF9\\x48\\x85\\xC9",
|
||||
"linux": "\\x48\\x85\\xFF\\x74\\x4B\\x55\\x48\\x89\\xE5\\x41\\x56"
|
||||
}
|
||||
},
|
||||
"LegacyGameEventListener": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x48\\x8B\\x15\\x2A\\x2A\\x2A\\x2A\\x48\\x85\\xD2\\x74\\x2A\\x85\\xC9\\x74",
|
||||
"linux": "\\x48\\x8B\\x05\\x2A\\x2A\\x2A\\x2A\\x48\\x85\\xC0\\x74\\x2A\\x83\\xFF\\x3F\\x76\\x2A\\x31\\xC0"
|
||||
}
|
||||
},
|
||||
"CBasePlayerPawn_CommitSuicide": {
|
||||
"offsets": {
|
||||
"windows": 355,
|
||||
"linux": 355
|
||||
"windows": 356,
|
||||
"linux": 356
|
||||
}
|
||||
},
|
||||
"CBaseEntity_Teleport": {
|
||||
@@ -66,6 +132,13 @@
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"GameEntitySystem": {
|
||||
"offsets": {
|
||||
"windows": 88,
|
||||
|
||||
@@ -36,6 +36,10 @@ export default defineConfig({
|
||||
label: 'Features',
|
||||
autogenerate: { directory: 'features' },
|
||||
},
|
||||
{
|
||||
label: 'Admin Framework',
|
||||
autogenerate: { directory: 'admin-framework' },
|
||||
},
|
||||
{
|
||||
label: 'Reference',
|
||||
autogenerate: { directory: 'reference' },
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: Admin Command Attributes
|
||||
description: A guide on using the Admin Command Attributes in plugins.
|
||||
---
|
||||
|
||||
## Assigning permissions to a Command
|
||||
|
||||
Assigning permissions to a Command is as easy as tagging the Command method (function callback) with either a `RequiresPermissions` or `RequiresPermissionsOr` attribute. The difference between the two attributes is that `RequiresPermissionsOr` needs only one permission to be present on the caller to be passed, while `RequiresPermissions` needs the caller to have all permissions listed. CounterStrikeSharp handles all of the permission checks behind the scenes for you.
|
||||
|
||||
```csharp
|
||||
[RequiresPermissions("@css/slay", "@custom/permission")]
|
||||
public void OnMyCommand(CCSPlayerController? caller, CommandInfo info)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
```csharp
|
||||
[RequiresPermissionsOr("@css/ban", "@custom/permission-2")]
|
||||
public void OnMyOtherCommand(CCSPlayerController? caller, CommandInfo info)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
You can even stack the attributes on top of each other, and all of them will be checked.
|
||||
|
||||
```csharp
|
||||
// Requires (@css/cvar AND @custom/permission-1) AND either (@custom/permission-1 OR @custom/permission-2).
|
||||
[RequiresPermissions("@css/cvar", "@custom/permission-1")]
|
||||
[RequiresPermissionsOr("@css/ban", "@custom/permission-2")]
|
||||
public void OnMyComplexCommand(CCSPlayerController? caller, CommandInfo info)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
You can also check for groups using the same attributes.
|
||||
|
||||
```csharp
|
||||
[RequiresPermissions("#css/simple-admin")]
|
||||
public void OnMyGroupCommand(CCSPlayerController? caller, CommandInfo info)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Admin Framework
|
||||
description: A guide on using the Admin Framework in plugins.
|
||||
title: Defining Admins
|
||||
description: A guide on how to define admins for CounterStrikeSharp.
|
||||
---
|
||||
|
||||
## Admin Framework
|
||||
@@ -9,38 +9,32 @@ CounterStrikeSharp has a basic framework which allows plugin developers to assig
|
||||
|
||||
## Adding Admins
|
||||
|
||||
Adding an Admin is as simple as creating a new entry in the `configs/admins.json` file. The important things you need to declare are the SteamID identifier and the permissions they have. CounterStrikeSharp will do all the heavy-lifting to decipher your SteamID. If you're familar with SourceMod, permission definitions are slightly different as they're defined by an array of strings instead of a string of characters.
|
||||
Adding an Admin is as simple as creating a new entry in the `configs/admins.json` file. The important things you need to declare are the SteamID identifier and the permissions they have. CounterStrikeSharp will do all the heavy-lifting to decipher your SteamID. If you're familiar with SourceMod, permission definitions are slightly different as they're defined by an array of strings instead of a string of characters.
|
||||
|
||||
```json
|
||||
{
|
||||
"ZoNiCaL": {
|
||||
"identity": "76561198808392634",
|
||||
"flags": ["@css/changemap", "@css/generic"]
|
||||
},
|
||||
"another ZoNiCaL": {
|
||||
"identity": "STEAM_0:1:1",
|
||||
"flags": ["@css/generic"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also manually assign permissions to players in code with `AddPlayerPermissions` and `RemovePlayerPermissions`. These changes are not saved to `configs/admins.json`.
|
||||
You can also manually assign permissions to players in code with `AdminManager.AddPlayerPermissions` and `AdminManager.RemovePlayerPermissions`. These changes are not saved to `configs/admins.json`.
|
||||
|
||||
## Assigning permissions to a Command
|
||||
|
||||
Assigning permissions to a Command is as easy as tagging the Command method (function callback) with a `RequiresPermissions` attribute.
|
||||
|
||||
```csharp
|
||||
[RequiresPermissions("@css/slay", "@custom/permission")]
|
||||
public void OnMyCommand(CCSPlayerController? caller, CommandInfo info)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
CounterStrikeSharp handles all of the permission checks behind the scenes for you.
|
||||
:::note
|
||||
All user permissions MUST start with an at-symbol @ character, otherwise CounterStrikeSharp will not recognize the permission.
|
||||
:::
|
||||
|
||||
### Standard Permissions
|
||||
|
||||
Because the flag system is just a list of strings associated with a user, there is no real list of letter based flags like there was previously in something like SourceMod. This means as a plugin author you can declare your own flags, scoped with an `@` symbol, like `@roflmuffin/guns`, which might be the permission to allow spawning of guns in a given command.
|
||||
|
||||
However there is a somewhat standardised list of flags that it is advised you use if you are adding functionality that aligns with their purpose, and these are based on the original SourceMod flags:
|
||||
However there is a somewhat standardized list of flags that it is advised you use if you are adding functionality that aligns with their purpose, and these are based on the original SourceMod flags:
|
||||
|
||||
```shell
|
||||
@css/reservation # Reserved slot access.
|
||||
@@ -60,3 +54,8 @@ However there is a somewhat standardised list of flags that it is advised you us
|
||||
@css/cheats # Change sv_cheats or use cheating commands.
|
||||
@css/root # Magically enables all flags and ignores immunity values.
|
||||
```
|
||||
|
||||
:::note
|
||||
CounterStrikeSharp does not implement traditional admin command such as `!slay`, `!kick`, and `!ban`. It is up to individual plugins to implement these commands.
|
||||
:::
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
title: Defining Command Overrides
|
||||
description: A guide on how to define command overrides for CounterStrikeSharp.
|
||||
---
|
||||
|
||||
## Defining Admin and Group specific overrides
|
||||
|
||||
Command permissions can be overriden so specific admins or groups can execute the command, regardless of any permissions they may or may not have. You can define command overrides by adding the `command_overrides` key to each admin in `configs/admins.json` or group in `configs/admin_groups.json`. Command overrides can either be set to `true`, meaning the admin/group can execute the command, or `false`, meaning the admin/group cannot execute the command at all.
|
||||
|
||||
```json
|
||||
{
|
||||
"ZoNiCaL": {
|
||||
"identity": "76561198808392634",
|
||||
"flags": ["@css/changemap", "@css/generic"],
|
||||
"immunity": 100,
|
||||
"command_overrides": {
|
||||
"example_command": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
"#css/simple-admin": {
|
||||
"flags": [
|
||||
"@css/generic",
|
||||
"@css/reservation",
|
||||
"@css/ban",
|
||||
"@css/slay",
|
||||
],
|
||||
"command_overrides": {
|
||||
"example_command_2": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can set a command override for a player in code using `AdminManager.SetPlayerCommandOverride`.
|
||||
|
||||
## Replacing Command permissions
|
||||
|
||||
Command permissions can be entirely replaced. These are defined in `configs/admin_overrides.json`. The important things you need to declare are what commands are being changed and what their new flags are. Command overrides can be set to be enabled or disabled, and you can toggle them in code with `AdminManager.SetCommandOverrideState`. You can also specify whether the command override requires the caller to have all of the new permissions (similar to a `RequiresPermissions` attribute check) or only one or more permissions (similar to a `RequirePermissionsOr` attribute check). You cannot stack permission checks.
|
||||
|
||||
```json
|
||||
"css": {
|
||||
"flags": [
|
||||
"@css/custom-permission"
|
||||
],
|
||||
"check_type": "all",
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
You can check if a command has been overriden in code using `AdminManager.CommandIsOverriden`, and you can manipulate the command override permissions using `AdminManager.AddPermissionOverride` and `AdminManager.RemovePermissionOverride`.
|
||||
42
docs/src/content/docs/admin-framework/defining-groups.md
Normal file
42
docs/src/content/docs/admin-framework/defining-groups.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
title: Defining Admin Groups
|
||||
description: A guide on how to define admin groups for CounterStrikeSharp.
|
||||
---
|
||||
|
||||
## Adding Groups
|
||||
|
||||
Groups can be created to group a series of permissions together under one tag. They are defined in `configs/admin_groups.json`. The important things you need to declare is the name of the group and the permissions they have.
|
||||
|
||||
```json
|
||||
"#css/simple-admin": {
|
||||
"flags": [
|
||||
"@css/generic",
|
||||
"@css/reservation",
|
||||
"@css/ban",
|
||||
"@css/slay",
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
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 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`.
|
||||
|
||||
36
docs/src/content/docs/admin-framework/defining-immunity.md
Normal file
36
docs/src/content/docs/admin-framework/defining-immunity.md
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: Defining Admin Immunity
|
||||
description: A guide on how to define immunity for admins for CounterStrikeSharp.
|
||||
---
|
||||
|
||||
## Player Immunity
|
||||
|
||||
Admins can be assigned an immunity value, similar to SourceMod. If an admin or player with a lower immunity value targets another admin or player with a larger immunity value, then the command will fail. You can define an immunity value by adding the `immunity` key to each admin in `configs/admins.json`.
|
||||
|
||||
```json
|
||||
{
|
||||
"ZoNiCaL": {
|
||||
"identity": "76561198808392634",
|
||||
"flags": ["@css/changemap", "@css/generic"],
|
||||
"immunity": 100
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can even assign an immunity value to groups. If an admin has a lower immunity value, the group's value will be used instead.
|
||||
|
||||
```json
|
||||
"#css/simple-admin": {
|
||||
"flags": [
|
||||
"@css/generic",
|
||||
"@css/reservation",
|
||||
"@css/ban",
|
||||
"@css/slay",
|
||||
],
|
||||
"immunity": 100
|
||||
}
|
||||
```
|
||||
|
||||
:::note
|
||||
CounterStrikeSharp does not automatically handle immunity checking. It is up to individual plugins to handle immunity checks as they can implement different ways of targeting players. This can be done in code with `AdminManager.CanPlayerTarget`. You can also set a player's immunity in code with `AdminManager.SetPlayerImmunity`.
|
||||
:::
|
||||
@@ -18,7 +18,7 @@ The first parameter type must be a subclass of the `GameEvent` class. The names
|
||||
public HookResult OnPlayerConnect(EventPlayerConnect @event, GameEventInfo info)
|
||||
{
|
||||
// Userid will give you a reference to a CCSPlayerController class
|
||||
Log($"Player {@event.Userid.PlayerName} has connected!");
|
||||
Logger.LogInformation("Player {Name} has connected!", @event.Userid.PlayerName);
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
@@ -33,7 +33,7 @@ public override void Load(bool hotReload)
|
||||
{
|
||||
RegisterEventHandler<EventRoundStart>((@event, info) =>
|
||||
{
|
||||
Console.WriteLine($"Round has started with time limit of {@event.Timelimit}");
|
||||
Logger.LogInformation("Round has started with time limit of {Timelimit}", @event.Timelimit);
|
||||
|
||||
return HookResult.Continue;
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ public override void Load(bool hotReload)
|
||||
projectile.SmokeColor.X = Random.Shared.NextSingle() * 255.0f;
|
||||
projectile.SmokeColor.Y = Random.Shared.NextSingle() * 255.0f;
|
||||
projectile.SmokeColor.Z = Random.Shared.NextSingle() * 255.0f;
|
||||
Log($"Smoke grenade spawned with color {projectile.SmokeColor}");
|
||||
Logger.LogInformation("Smoke grenade spawned with color {SmokeColor}", projectile.SmokeColor);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -34,6 +34,13 @@ Use your IDE (Visual Studio/Rider) to add a reference to the `CounterStrikeSharp
|
||||
</Project>
|
||||
```
|
||||
|
||||
:::tip
|
||||
Instead of manually adding a reference to `CounterStrikeSharp.Api.dll`, you can install the NuGet package `CounterStrikeSharp.Api` using the following:
|
||||
```shell
|
||||
dotnet add package CounterStrikeSharp.API
|
||||
```
|
||||
:::
|
||||
|
||||
### Creating a Plugin File
|
||||
|
||||
Rename the default class file that came with your new project (by default it should be `Class1.cs`) to something more accurate, like `HelloWorldPlugin.cs`. Inside this file, we will insert the stub hello world plugin. Be sure to change the name and namespace so it matches your project name.
|
||||
|
||||
@@ -27,14 +27,14 @@ public class HelloWorldPlugin : BasePlugin
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
Console.WriteLine("Hello World!");
|
||||
Logger.LogInformation("Plugin loaded successfully!");
|
||||
}
|
||||
|
||||
[GameEventHandler]
|
||||
public HookResult OnPlayerConnect(EventPlayerConnect @event, GameEventInfo info)
|
||||
{
|
||||
// Userid will give you a reference to a CCSPlayerController class
|
||||
Log($"Player {@event.Userid.PlayerName} has connected!");
|
||||
Logger.LogInformation("Player {Name} has connected!", @event.Userid.PlayerName);
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
@@ -42,7 +42,7 @@ public class HelloWorldPlugin : BasePlugin
|
||||
[ConsoleCommand("issue_warning", "Issue warning to player")]
|
||||
public void OnCommand(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
Log("You shouldn't be doing that!");
|
||||
Logger.LogWarning("Player shouldn't be doing that");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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,14 +16,16 @@ 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
|
||||
${SOURCESDK_LIB}/linux64/libtier0.so
|
||||
${SOURCESDK_LIB}/linux64/tier1.a
|
||||
${SOURCESDK_LIB}/linux64/interfaces.a
|
||||
${SOURCESDK_LIB}/linux64/mathlib.a
|
||||
spdlog
|
||||
dynload_s
|
||||
dyncall_s
|
||||
distorm
|
||||
funchook-static
|
||||
SET(
|
||||
COUNTER_STRIKE_SHARP_LINK_LIBRARIES
|
||||
${SOURCESDK_LIB}/linux64/libtier0.so
|
||||
${SOURCESDK_LIB}/linux64/tier1.a
|
||||
${SOURCESDK_LIB}/linux64/interfaces.a
|
||||
${SOURCESDK_LIB}/linux64/mathlib.a
|
||||
spdlog
|
||||
dynload_s
|
||||
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
|
||||
)
|
||||
@@ -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();
|
||||
@@ -552,6 +602,17 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
public static void FireEventToClient(IntPtr gameevent, int clientindex){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(gameevent);
|
||||
ScriptContext.GlobalScriptContext.Push(clientindex);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0x40B7C06C);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetEventName(IntPtr gameevent){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
@@ -792,6 +853,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();
|
||||
|
||||
@@ -32,6 +32,7 @@ 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
|
||||
{
|
||||
@@ -53,6 +54,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
public string ModulePath { get; internal set; }
|
||||
|
||||
public string ModuleDirectory => Path.GetDirectoryName(ModulePath);
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public virtual void Load(bool hotReload)
|
||||
{
|
||||
@@ -162,6 +164,42 @@ namespace CounterStrikeSharp.API.Core
|
||||
var command = new CommandInfo(ptr, caller);
|
||||
|
||||
var methodInfo = handler?.GetMethodInfo();
|
||||
|
||||
if (!AdminManager.CommandIsOverriden(name))
|
||||
{
|
||||
// Do not execute command if we do not have the correct permissions.
|
||||
var permissions = methodInfo?.GetCustomAttributes<BaseRequiresPermissions>();
|
||||
if (permissions != null)
|
||||
{
|
||||
foreach (var attr in permissions)
|
||||
{
|
||||
attr.Command = name;
|
||||
if (!attr.CanExecuteCommand(caller))
|
||||
{
|
||||
command.ReplyToCommand("[CSS] You do not have the correct permissions to execute this command.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If this command has it's permissions overriden, we will do an AND check for all permissions.
|
||||
else
|
||||
{
|
||||
// I don't know if this is the most sane implementation of this, can be edited in code review.
|
||||
var data = AdminManager.GetCommandOverrideData(name);
|
||||
if (data != null)
|
||||
{
|
||||
var attrType = (data.CheckType == "all") ? typeof(RequiresPermissions) : typeof(RequiresPermissionsOr);
|
||||
var attr = (BaseRequiresPermissions)Activator.CreateInstance(attrType, args: AdminManager.GetPermissionOverrides(name));
|
||||
attr.Command = name;
|
||||
if (!attr.CanExecuteCommand(caller))
|
||||
{
|
||||
command.ReplyToCommand("[CSS] You do not have the correct permissions to execute this command.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not execute if we shouldn't be calling this command.
|
||||
var helperAttribute = methodInfo?.GetCustomAttribute<CommandHelperAttribute>();
|
||||
if (helperAttribute != null)
|
||||
@@ -185,14 +223,6 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
// Do not execute command if we do not have the correct permissions.
|
||||
var permissions = methodInfo?.GetCustomAttribute<RequiresPermissions>()?.RequiredPermissions;
|
||||
if (permissions != null && !AdminManager.PlayerHasPermissions(caller, permissions))
|
||||
{
|
||||
command.ReplyToCommand("[CSS] You do not have the correct permissions to execute this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
handler?.Invoke(caller, command);
|
||||
});
|
||||
|
||||
@@ -286,7 +316,8 @@ namespace CounterStrikeSharp.API.Core
|
||||
.Select(p => p.GetCustomAttribute<CastFromAttribute>()?.Type)
|
||||
.ToArray();
|
||||
|
||||
Console.WriteLine($"Registering listener for {listenerName} with {parameterTypes.Length}");
|
||||
GlobalContext.Instance.Logger.LogDebug("Registering listener for {ListenerName} with {ParameterCount} parameters",
|
||||
listenerName, parameterTypes.Length);
|
||||
|
||||
var wrappedHandler = new Action<ScriptContext>(context =>
|
||||
{
|
||||
|
||||
@@ -23,6 +23,9 @@ 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.Logging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
@@ -31,9 +34,9 @@ namespace CounterStrikeSharp.API.Core
|
||||
/// </summary>
|
||||
internal sealed partial class CoreConfigData
|
||||
{
|
||||
[JsonPropertyName("PublicChatTrigger")] public string PublicChatTrigger { get; set; } = "!";
|
||||
[JsonPropertyName("PublicChatTrigger")] public IEnumerable<string> PublicChatTrigger { get; set; } = new HashSet<string>() { "!" };
|
||||
|
||||
[JsonPropertyName("SilentChatTrigger")] public string SilentChatTrigger { get; set; } = "/";
|
||||
[JsonPropertyName("SilentChatTrigger")] public IEnumerable<string> SilentChatTrigger { get; set; } = new HashSet<string>() { "/" };
|
||||
|
||||
[JsonPropertyName("FollowCS2ServerGuidelines")] public bool FollowCS2ServerGuidelines { get; set; } = true;
|
||||
}
|
||||
@@ -46,12 +49,12 @@ namespace CounterStrikeSharp.API.Core
|
||||
/// <summary>
|
||||
/// List of characters to use for public chat triggers.
|
||||
/// </summary>
|
||||
public static string PublicChatTrigger => _coreConfig.PublicChatTrigger;
|
||||
public static IEnumerable<string> PublicChatTrigger => _coreConfig.PublicChatTrigger;
|
||||
|
||||
/// <summary>
|
||||
/// List of characters to use for silent chat triggers.
|
||||
/// </summary>
|
||||
public static string SilentChatTrigger => _coreConfig.SilentChatTrigger;
|
||||
public static IEnumerable<string> SilentChatTrigger => _coreConfig.SilentChatTrigger;
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
@@ -78,6 +81,9 @@ namespace CounterStrikeSharp.API.Core
|
||||
public static partial class CoreConfig
|
||||
{
|
||||
private static CoreConfigData _coreConfig = new CoreConfigData();
|
||||
|
||||
// TODO: ServiceCollection
|
||||
private static ILogger _logger = CoreLogging.Factory.CreateLogger("CoreConfig");
|
||||
|
||||
static CoreConfig()
|
||||
{
|
||||
@@ -96,7 +102,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
if (!File.Exists(coreConfigPath))
|
||||
{
|
||||
Console.WriteLine($"Core configuration could not be found at path '{coreConfigPath}', fallback values will be used.");
|
||||
_logger.LogWarning("Core configuration could not be found at path \"{CoreConfigPath}\", fallback values will be used.", coreConfigPath);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -108,12 +114,12 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
_coreConfig = data;
|
||||
}
|
||||
|
||||
Console.WriteLine($"Loaded core configuration");
|
||||
|
||||
_logger.LogInformation("Successfully loaded core configuration.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Failed to load core configuration: {ex}, fallback values will be used.");
|
||||
_logger.LogWarning(ex, "Failed to load core configuration, fallback values will be used");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
@@ -76,7 +77,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
GlobalContext.Instance.Logger.LogError(e, "Error invoking callback");
|
||||
}
|
||||
});
|
||||
s_callback = dg;
|
||||
@@ -140,10 +141,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
ms_references.Remove(reference);
|
||||
|
||||
Console.BackgroundColor = ConsoleColor.Blue;
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.WriteLine($"Removing function/callback reference: {reference}");
|
||||
Console.ResetColor();
|
||||
GlobalContext.Instance.Logger.LogDebug("Removing function/callback reference: {Reference}", reference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
@@ -39,17 +41,17 @@ public static class GameData
|
||||
{
|
||||
_methods = JsonSerializer.Deserialize<Dictionary<string, LoadedGameData>>(File.ReadAllText(gameDataPath))!;
|
||||
|
||||
Console.WriteLine($"Loaded game data with {_methods.Count} methods.");
|
||||
GlobalContext.Instance.Logger.LogInformation("Loaded game data with {Count} methods.", _methods.Count);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Failed to load game data: {ex}");
|
||||
GlobalContext.Instance.Logger.LogError(ex, "Failed to load game data");
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetSignature(string key)
|
||||
{
|
||||
Console.WriteLine($"Getting signature: {key}");
|
||||
GlobalContext.Instance.Logger.LogDebug("Getting signature: {Key}", key);
|
||||
if (!_methods.ContainsKey(key))
|
||||
{
|
||||
throw new ArgumentException($"Method {key} not found in gamedata.json");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* 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
|
||||
@@ -20,18 +20,25 @@ 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();
|
||||
@@ -40,6 +47,9 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
rootDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.Parent;
|
||||
_instance = this;
|
||||
Logger = CoreLogging.Factory.CreateLogger("Core");
|
||||
|
||||
Logger.LogInformation("CounterStrikeSharp is starting up...");
|
||||
}
|
||||
|
||||
~GlobalContext()
|
||||
@@ -59,14 +69,28 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
public void InitGlobalContext()
|
||||
{
|
||||
Console.WriteLine("Loading CoreConfig from \"configs/core.json\"");
|
||||
CoreConfig.Load(Path.Combine(rootDir.FullName, "configs", "core.json"));
|
||||
var coreConfigPath = Path.Combine(rootDir.FullName, "configs", "core.json");
|
||||
Logger.LogInformation("Loading CoreConfig from {Path}", coreConfigPath);
|
||||
CoreConfig.Load(coreConfigPath);
|
||||
|
||||
Console.WriteLine("Loading GameData from \"gamedata/gamedata.json\"");
|
||||
GameData.Load(Path.Combine(rootDir.FullName, "gamedata", "gamedata.json"));
|
||||
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);
|
||||
|
||||
Console.WriteLine("Loading Admins from \"configs/admins.json\"");
|
||||
AdminManager.Load(Path.Combine(rootDir.FullName, "configs", "admins.json"));
|
||||
AdminManager.MergeGroupPermsIntoAdmins();
|
||||
|
||||
for (var i = 1; i <= 9; i++)
|
||||
{
|
||||
@@ -77,10 +101,10 @@ namespace CounterStrikeSharp.API.Core
|
||||
ChatMenus.OnKeyPress(player, key);
|
||||
});
|
||||
}
|
||||
|
||||
Console.WriteLine("Loading C# plugins...");
|
||||
|
||||
Logger.LogInformation("Loading C# plugins...");
|
||||
var pluginCount = LoadAllPlugins();
|
||||
Console.WriteLine($"All managed modules were loaded. {pluginCount} plugins loaded.");
|
||||
Logger.LogInformation("All managed modules were loaded. {PluginCount} plugins loaded.", pluginCount);
|
||||
|
||||
RegisterPluginCommands();
|
||||
}
|
||||
@@ -107,7 +131,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
Logger.LogError(e, "Error finding plugin path");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -130,14 +154,13 @@ namespace CounterStrikeSharp.API.Core
|
||||
|
||||
foreach (var path in filePaths)
|
||||
{
|
||||
Console.WriteLine($"Plugin path: {path}");
|
||||
try
|
||||
{
|
||||
LoadPlugin(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine($"Failed to load plugin {path} with error {e}");
|
||||
Logger.LogError(e, "Failed to load plugin from {Path}", path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +211,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
return plugin;
|
||||
}
|
||||
|
||||
[RequiresPermissions("can_execute_css_commands")]
|
||||
[RequiresPermissions("@css/generic")]
|
||||
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
private void OnCSSCommand(CCSPlayerController? caller, CommandInfo info)
|
||||
{
|
||||
@@ -201,7 +224,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
return;
|
||||
}
|
||||
|
||||
[RequiresPermissions("can_execute_css_commands")]
|
||||
[RequiresPermissions("@css/generic")]
|
||||
[CommandHelper(minArgs: 1,
|
||||
usage: "[option]\n" +
|
||||
" list - List all plugins currently loaded.\n" +
|
||||
@@ -261,7 +284,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine($"Failed to load plugin {path} with error {e}");
|
||||
Logger.LogError(e, "Failed to load plugin from {Path}", path);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
29
managed/CounterStrikeSharp.API/Core/Logging/CoreLogging.cs
Normal file
29
managed/CounterStrikeSharp.API/Core/Logging/CoreLogging.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Serilog;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Logging;
|
||||
|
||||
public static class CoreLogging
|
||||
{
|
||||
public static ILoggerFactory Factory { get; }
|
||||
|
||||
static CoreLogging()
|
||||
{
|
||||
var logger = 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}")
|
||||
.CreateLogger();
|
||||
|
||||
Factory =
|
||||
LoggerFactory.Create(builder =>
|
||||
{
|
||||
builder.AddSerilog(logger);
|
||||
});
|
||||
}
|
||||
}
|
||||
32
managed/CounterStrikeSharp.API/Core/Logging/PluginLogging.cs
Normal file
32
managed/CounterStrikeSharp.API/Core/Logging/PluginLogging.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Logging;
|
||||
|
||||
public class PluginNameEnricher : ILogEventEnricher
|
||||
{
|
||||
public const string PropertyName = "PluginName";
|
||||
|
||||
public PluginNameEnricher(PluginContext pluginContext)
|
||||
{
|
||||
Context = pluginContext;
|
||||
}
|
||||
|
||||
public PluginContext Context { get; }
|
||||
|
||||
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
|
||||
{
|
||||
var property = propertyFactory.CreateProperty(PropertyName, Context.PluginType.Name);
|
||||
logEvent.AddPropertyIfAbsent(property);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Linq;
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Logging;
|
||||
|
||||
public class SourceContextEnricher : ILogEventEnricher
|
||||
{
|
||||
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
|
||||
{
|
||||
if (logEvent.Properties.TryGetValue("SourceContext", out var property))
|
||||
{
|
||||
var scalarValue = property as ScalarValue;
|
||||
var value = scalarValue?.Value as string;
|
||||
|
||||
if (value?.StartsWith("CounterStrikeSharp") ?? false)
|
||||
{
|
||||
var lastElement = value.Split(".").LastOrDefault();
|
||||
if (!string.IsNullOrWhiteSpace(lastElement))
|
||||
{
|
||||
logEvent.AddOrUpdateProperty(new LogEventProperty("SourceContext", new ScalarValue(lastElement)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,11 @@ public partial class CBaseEntity
|
||||
Handle, position.Handle, angles.Handle, velocity.Handle);
|
||||
}
|
||||
|
||||
public void DispatchSpawn()
|
||||
{
|
||||
VirtualFunctions.CBaseEntity_DispatchSpawn(Handle, IntPtr.Zero);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shorthand for accessing an entity's CBodyComponent?.SceneNode?.AbsOrigin;
|
||||
/// </summary>
|
||||
|
||||
@@ -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);
|
||||
|
||||
31
managed/CounterStrikeSharp.API/Core/Model/CCSGameRules.cs
Normal file
31
managed/CounterStrikeSharp.API/Core/Model/CCSGameRules.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CCSGameRules
|
||||
{
|
||||
/// <summary>
|
||||
/// Terminate the round with the given delay and reason.
|
||||
/// </summary>
|
||||
public void TerminateRound(float delay, RoundEndReason roundEndReason)
|
||||
{
|
||||
VirtualFunctions.TerminateRound(Handle, roundEndReason, delay);
|
||||
}
|
||||
}
|
||||
@@ -47,9 +47,62 @@ public partial class CCSPlayerController
|
||||
Duration = 5,
|
||||
Userid = this
|
||||
};
|
||||
@event.FireEvent(false);
|
||||
@event.FireEventToClient(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drops the active player weapon on the ground.
|
||||
/// </summary>
|
||||
public void DropActiveWeapon()
|
||||
{
|
||||
if (!PlayerPawn.IsValid) return;
|
||||
if (!PlayerPawn.Value.IsValid) return;
|
||||
if (PlayerPawn.Value.ItemServices == null) return;
|
||||
if (PlayerPawn.Value.WeaponServices == null) return;
|
||||
if (!PlayerPawn.Value.WeaponServices.ActiveWeapon.IsValid) return;
|
||||
|
||||
CCSPlayer_ItemServices itemServices = new CCSPlayer_ItemServices(PlayerPawn.Value.ItemServices.Handle);
|
||||
CCSPlayer_WeaponServices weponServices = new CCSPlayer_WeaponServices(PlayerPawn.Value.WeaponServices.Handle);
|
||||
itemServices.DropActivePlayerWeapon(weponServices.ActiveWeapon.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes every weapon from the player.
|
||||
/// </summary>
|
||||
public void RemoveWeapons()
|
||||
{
|
||||
if (!PlayerPawn.IsValid) return;
|
||||
if (!PlayerPawn.Value.IsValid) return;
|
||||
if (PlayerPawn.Value.ItemServices == null) return;
|
||||
|
||||
CCSPlayer_ItemServices itemServices = new CCSPlayer_ItemServices(PlayerPawn.Value.ItemServices.Handle);
|
||||
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);
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 CounterStrikeSharp.API.Modules.Memory;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CCSPlayer_ItemServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Drops the active player weapon on the ground.
|
||||
/// </summary>
|
||||
public void DropActivePlayerWeapon(CBasePlayerWeapon activeWeapon)
|
||||
{
|
||||
VirtualFunction.CreateVoid<nint, nint>(Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon"))(Handle, activeWeapon.Handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes every weapon from the player.
|
||||
/// </summary>
|
||||
public void RemoveWeapons()
|
||||
{
|
||||
VirtualFunction.CreateVoid<nint>(Handle, GameData.GetOffset("CCSPlayer_ItemServices_RemoveWeapons"))(Handle);
|
||||
}
|
||||
}
|
||||
30
managed/CounterStrikeSharp.API/Core/Model/CGameSceneNode.cs
Normal file
30
managed/CounterStrikeSharp.API/Core/Model/CGameSceneNode.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 CounterStrikeSharp.API.Modules.Memory;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CGameSceneNode
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the <see cref="CSkeletonInstance"/> instance from the node.
|
||||
/// </summary>
|
||||
public CSkeletonInstance GetSkeletonInstance()
|
||||
{
|
||||
return new CSkeletonInstance(VirtualFunction.Create<nint, nint>(Handle, GameData.GetOffset("CGameSceneNode_GetSkeletonInstance"))(Handle));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -355,6 +355,13 @@ public enum BrushSolidities_e : uint
|
||||
BRUSHSOLID_ALWAYS = 0x2,
|
||||
}
|
||||
|
||||
public enum C4LightEffect_t : uint
|
||||
{
|
||||
eLightEffectNone = 0x0,
|
||||
eLightEffectDropped = 0x1,
|
||||
eLightEffectThirdPersonHeld = 0x2,
|
||||
}
|
||||
|
||||
public enum CAnimationGraphVisualizerPrimitiveType : uint
|
||||
{
|
||||
ANIMATIONGRAPHVISUALIZERPRIMITIVETYPE_Text = 0x0,
|
||||
@@ -2773,6 +2780,7 @@ public enum TakeDamageFlags_t : uint
|
||||
DFLAG_RADIUS_DMG = 0x400,
|
||||
DMG_LASTDFLAG = 0x400,
|
||||
DFLAG_IGNORE_ARMOR = 0x800,
|
||||
DFLAG_SUPPRESS_UTILREMOVE = 0x1000,
|
||||
}
|
||||
|
||||
public enum TextureRepetitionMode_t : uint
|
||||
@@ -3927,6 +3935,9 @@ public partial class CBaseCSGrenadeProjectile : CBaseGrenade
|
||||
{
|
||||
public CBaseCSGrenadeProjectile (IntPtr pointer) : base(pointer) {}
|
||||
|
||||
// m_vInitialPosition
|
||||
public Vector InitialPosition => Schema.GetDeclaredClass<Vector>(this.Handle, "CBaseCSGrenadeProjectile", "m_vInitialPosition");
|
||||
|
||||
// m_vInitialVelocity
|
||||
public Vector InitialVelocity => Schema.GetDeclaredClass<Vector>(this.Handle, "CBaseCSGrenadeProjectile", "m_vInitialVelocity");
|
||||
|
||||
@@ -3972,6 +3983,12 @@ public partial class CBaseCSGrenadeProjectile : CBaseGrenade
|
||||
// m_nTicksAtZeroVelocity
|
||||
public ref Int32 TicksAtZeroVelocity => ref Schema.GetRef<Int32>(this.Handle, "CBaseCSGrenadeProjectile", "m_nTicksAtZeroVelocity");
|
||||
|
||||
// m_bHasEverHitPlayer
|
||||
public ref bool HasEverHitPlayer => ref Schema.GetRef<bool>(this.Handle, "CBaseCSGrenadeProjectile", "m_bHasEverHitPlayer");
|
||||
|
||||
// m_bClearFromPlayers
|
||||
public ref bool ClearFromPlayers => ref Schema.GetRef<bool>(this.Handle, "CBaseCSGrenadeProjectile", "m_bClearFromPlayers");
|
||||
|
||||
}
|
||||
|
||||
public partial class CBaseDMStart : CPointEntity
|
||||
@@ -7099,8 +7116,8 @@ public partial class CCSGameRules : CTeamplayRules
|
||||
// m_nShorthandedBonusLastEvalRound
|
||||
public ref Int32 ShorthandedBonusLastEvalRound => ref Schema.GetRef<Int32>(this.Handle, "CCSGameRules", "m_nShorthandedBonusLastEvalRound");
|
||||
|
||||
// m_bMatchAbortedDueToPlayerBan
|
||||
public ref bool MatchAbortedDueToPlayerBan => ref Schema.GetRef<bool>(this.Handle, "CCSGameRules", "m_bMatchAbortedDueToPlayerBan");
|
||||
// m_nMatchAbortedEarlyReason
|
||||
public ref Int32 MatchAbortedEarlyReason => ref Schema.GetRef<Int32>(this.Handle, "CCSGameRules", "m_nMatchAbortedEarlyReason");
|
||||
|
||||
// m_bHasTriggeredRoundStartMusic
|
||||
public ref bool HasTriggeredRoundStartMusic => ref Schema.GetRef<bool>(this.Handle, "CCSGameRules", "m_bHasTriggeredRoundStartMusic");
|
||||
@@ -7515,6 +7532,12 @@ public partial class CCSPlayer_MovementServices : CPlayer_MovementServices_Human
|
||||
// m_flStamina
|
||||
public ref float Stamina => ref Schema.GetRef<float>(this.Handle, "CCSPlayer_MovementServices", "m_flStamina");
|
||||
|
||||
// m_flHeightAtJumpStart
|
||||
public ref float HeightAtJumpStart => ref Schema.GetRef<float>(this.Handle, "CCSPlayer_MovementServices", "m_flHeightAtJumpStart");
|
||||
|
||||
// m_flMaxJumpHeightThisJump
|
||||
public ref float MaxJumpHeightThisJump => ref Schema.GetRef<float>(this.Handle, "CCSPlayer_MovementServices", "m_flMaxJumpHeightThisJump");
|
||||
|
||||
}
|
||||
|
||||
public partial class CCSPlayer_PingServices : CPlayerPawnComponent
|
||||
@@ -7792,6 +7815,9 @@ public partial class CCSPlayerController : CBasePlayerController
|
||||
// m_uiAbandonRecordedReason
|
||||
public ref UInt32 UiAbandonRecordedReason => ref Schema.GetRef<UInt32>(this.Handle, "CCSPlayerController", "m_uiAbandonRecordedReason");
|
||||
|
||||
// m_bCannotBeKicked
|
||||
public ref bool CannotBeKicked => ref Schema.GetRef<bool>(this.Handle, "CCSPlayerController", "m_bCannotBeKicked");
|
||||
|
||||
// m_bEverFullyConnected
|
||||
public ref bool EverFullyConnected => ref Schema.GetRef<bool>(this.Handle, "CCSPlayerController", "m_bEverFullyConnected");
|
||||
|
||||
@@ -7909,9 +7935,15 @@ public partial class CCSPlayerController : CBasePlayerController
|
||||
// m_bGaveTeamDamageWarningThisRound
|
||||
public ref bool GaveTeamDamageWarningThisRound => ref Schema.GetRef<bool>(this.Handle, "CCSPlayerController", "m_bGaveTeamDamageWarningThisRound");
|
||||
|
||||
// m_dblLastReceivedPacketPlatFloatTime
|
||||
public ref double DblLastReceivedPacketPlatFloatTime => ref Schema.GetRef<double>(this.Handle, "CCSPlayerController", "m_dblLastReceivedPacketPlatFloatTime");
|
||||
|
||||
// m_LastTeamDamageWarningTime
|
||||
public ref float LastTeamDamageWarningTime => ref Schema.GetRef<float>(this.Handle, "CCSPlayerController", "m_LastTeamDamageWarningTime");
|
||||
|
||||
// m_LastTimePlayerWasDisconnectedForPawnsRemove
|
||||
public ref float LastTimePlayerWasDisconnectedForPawnsRemove => ref Schema.GetRef<float>(this.Handle, "CCSPlayerController", "m_LastTimePlayerWasDisconnectedForPawnsRemove");
|
||||
|
||||
}
|
||||
|
||||
public partial class CCSPlayerController_ActionTrackingServices : CPlayerControllerComponent
|
||||
@@ -8046,6 +8078,9 @@ public partial class CCSPlayerPawn : CCSPlayerPawnBase
|
||||
set { Schema.SetString(this.Handle, "CCSPlayerPawn", "m_szLastPlaceName", value); }
|
||||
}
|
||||
|
||||
// m_bInHostageResetZone
|
||||
public ref bool InHostageResetZone => ref Schema.GetRef<bool>(this.Handle, "CCSPlayerPawn", "m_bInHostageResetZone");
|
||||
|
||||
// m_bInBuyZone
|
||||
public ref bool InBuyZone => ref Schema.GetRef<bool>(this.Handle, "CCSPlayerPawn", "m_bInBuyZone");
|
||||
|
||||
@@ -12825,6 +12860,9 @@ public partial class CHostage : CHostageExpresserShim
|
||||
// m_vecSpawnGroundPos
|
||||
public Vector SpawnGroundPos => Schema.GetDeclaredClass<Vector>(this.Handle, "CHostage", "m_vecSpawnGroundPos");
|
||||
|
||||
// m_vecHostageResetPosition
|
||||
public Vector HostageResetPosition => Schema.GetDeclaredClass<Vector>(this.Handle, "CHostage", "m_vecHostageResetPosition");
|
||||
|
||||
}
|
||||
|
||||
public partial class CHostageAlias_info_hostage_spawn : CHostage
|
||||
@@ -14153,6 +14191,28 @@ public partial class CLogicDistanceCheck : CLogicalEntity
|
||||
|
||||
}
|
||||
|
||||
public partial class CLogicEventListener : CLogicalEntity
|
||||
{
|
||||
public CLogicEventListener (IntPtr pointer) : base(pointer) {}
|
||||
|
||||
// m_strEventName
|
||||
public string StrEventName
|
||||
{
|
||||
get { return Schema.GetUtf8String(this.Handle, "CLogicEventListener", "m_strEventName"); }
|
||||
set { Schema.SetString(this.Handle, "CLogicEventListener", "m_strEventName", value); }
|
||||
}
|
||||
|
||||
// m_bIsEnabled
|
||||
public ref bool IsEnabled => ref Schema.GetRef<bool>(this.Handle, "CLogicEventListener", "m_bIsEnabled");
|
||||
|
||||
// m_nTeam
|
||||
public ref Int32 Team => ref Schema.GetRef<Int32>(this.Handle, "CLogicEventListener", "m_nTeam");
|
||||
|
||||
// m_OnEventFired
|
||||
public CEntityIOOutput OnEventFired => Schema.GetDeclaredClass<CEntityIOOutput>(this.Handle, "CLogicEventListener", "m_OnEventFired");
|
||||
|
||||
}
|
||||
|
||||
public partial class CLogicGameEvent : CLogicalEntity
|
||||
{
|
||||
public CLogicGameEvent (IntPtr pointer) : base(pointer) {}
|
||||
@@ -19746,6 +19806,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) {}
|
||||
@@ -20138,6 +20255,12 @@ public partial class CTriggerGravity : CBaseTrigger
|
||||
|
||||
}
|
||||
|
||||
public partial class CTriggerHostageReset : CBaseTrigger
|
||||
{
|
||||
public CTriggerHostageReset (IntPtr pointer) : base(pointer) {}
|
||||
|
||||
}
|
||||
|
||||
public partial class CTriggerHurt : CBaseTrigger
|
||||
{
|
||||
public CTriggerHurt (IntPtr pointer) : base(pointer) {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* 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
|
||||
@@ -20,9 +20,9 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Modules.Config;
|
||||
using CounterStrikeSharp.API.Modules.Events;
|
||||
using CounterStrikeSharp.API.Core.Logging;
|
||||
using McMaster.NETCore.Plugins;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
@@ -43,6 +43,9 @@ namespace CounterStrikeSharp.API.Core
|
||||
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;
|
||||
@@ -63,7 +66,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
if (e.FullPath == path)
|
||||
{
|
||||
Console.WriteLine($"Plugin {Name} has been deleted, unloading...");
|
||||
_logger.LogInformation("Plugin {Name} has been deleted, unloading...", Name);
|
||||
Unload(true);
|
||||
}
|
||||
};
|
||||
@@ -75,7 +78,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
|
||||
private Task OnReloadedAsync(object sender, PluginReloadedEventArgs eventargs)
|
||||
{
|
||||
Console.WriteLine($"Reloading plugin {Name}");
|
||||
_logger.LogInformation("Reloading plugin {Name}", Name);
|
||||
_assemblyLoader = eventargs.Loader;
|
||||
Unload(hotReload: true);
|
||||
Load(hotReload: true);
|
||||
@@ -100,14 +103,15 @@ namespace CounterStrikeSharp.API.Core
|
||||
throw new Exception(
|
||||
$"Plugin \"{Path.GetFileName(_path)}\" requires a newer version of CounterStrikeSharp. The plugin expects version [{minimumApiVersion}] but the current version is [{currentVersion}].");
|
||||
|
||||
Console.WriteLine($"Loading plugin: {pluginType.Name}");
|
||||
_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);
|
||||
|
||||
Console.WriteLine($"Finished loading plugin: {Name}");
|
||||
_logger.LogInformation("Finished loading plugin {Name}", Name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +119,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
var cachedName = Name;
|
||||
|
||||
Console.WriteLine($"Unloading plugin {Name}");
|
||||
_logger.LogInformation("Unloading plugin {Name}", Name);
|
||||
|
||||
_plugin.Unload(hotReload);
|
||||
|
||||
@@ -127,7 +131,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
_fileWatcher.Dispose();
|
||||
}
|
||||
|
||||
Console.WriteLine($"Finished unloading plugin {cachedName}");
|
||||
_logger.LogInformation("Finished unloading plugin {Name}", Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,13 +67,11 @@ namespace CounterStrikeSharp.API.Core
|
||||
|
||||
public unsafe ScriptContext()
|
||||
{
|
||||
//Console.WriteLine("Global context address: " + (IntPtr)m_extContext);
|
||||
}
|
||||
|
||||
public unsafe ScriptContext(fxScriptContext* context)
|
||||
{
|
||||
m_extContext = *context;
|
||||
//Console.WriteLine("Global context address: " + (IntPtr)m_extContext);
|
||||
}
|
||||
|
||||
private readonly ConcurrentQueue<Action> ms_finalizers = new ConcurrentQueue<Action>();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
@@ -23,6 +23,10 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.4.0" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
|
||||
<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" />
|
||||
<PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json;
|
||||
using System.Linq;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
|
||||
public partial class CommandData
|
||||
{
|
||||
[JsonPropertyName("flags")] public required HashSet<string> Flags { get; init; }
|
||||
[JsonPropertyName("enabled")] public bool Enabled { get; set; } = true;
|
||||
[JsonPropertyName("check_type")] public required string CheckType { get; init; }
|
||||
}
|
||||
|
||||
public static partial class AdminManager
|
||||
{
|
||||
private static Dictionary<string, CommandData> CommandOverrides = new();
|
||||
public static void LoadCommandOverrides(string overridePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(overridePath))
|
||||
{
|
||||
Console.WriteLine("Admin command overrides file not found. Skipping admin command overrides load.");
|
||||
return;
|
||||
}
|
||||
|
||||
var overridesFromFile = JsonSerializer.Deserialize<Dictionary<string, CommandData>>
|
||||
(File.ReadAllText(overridePath), new JsonSerializerOptions() { ReadCommentHandling = JsonCommentHandling.Skip });
|
||||
if (overridesFromFile == null) { throw new FileNotFoundException(); }
|
||||
foreach (var (command, overrideDef) in overridesFromFile)
|
||||
{
|
||||
if (CommandOverrides.ContainsKey(command))
|
||||
{
|
||||
CommandOverrides[command].Flags.UnionWith(overrideDef.Flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
CommandOverrides.Add(command, overrideDef);
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"Loaded {CommandOverrides.Count} admin command overrides.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Failed to load admin command overrides: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if a command has overriden permissions.
|
||||
/// </summary>
|
||||
/// <param name="commandName">Name of the command.</param>
|
||||
/// <returns>True if the command has overriden permissions, false if not.</returns>
|
||||
public static bool CommandIsOverriden(string commandName)
|
||||
{
|
||||
CommandOverrides.TryGetValue(commandName, out var overrideDef);
|
||||
if (overrideDef == null) return false;
|
||||
return overrideDef.Enabled && overrideDef?.Flags.Count() > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Grabs the data for a command override that was loaded from "configs/admin_overrides.json".
|
||||
/// </summary>
|
||||
/// <param name="commandName">Name of the command.</param>
|
||||
/// <returns>CommandData class if found, null if not.</returns>
|
||||
public static CommandData? GetCommandOverrideData(string commandName)
|
||||
{
|
||||
return CommandOverrides.GetValueOrDefault(commandName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Grabs the new, overriden flags for a command.
|
||||
/// </summary>
|
||||
/// <param name="commandName">Name of the command.</param>
|
||||
/// <returns>If the command is valid, a valid array of flags.</returns>
|
||||
public static string[] GetPermissionOverrides(string commandName)
|
||||
{
|
||||
CommandOverrides.TryGetValue(commandName, out var overrideDef);
|
||||
return overrideDef?.Flags.ToArray() ?? new string[] { };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new permission to a command override.
|
||||
/// This is not saved to "configs/admin_overrides.json".
|
||||
/// </summary>
|
||||
/// <param name="commandName">Name of the command.</param>
|
||||
/// <param name="permissions">Permissions to add to the command override.</param>
|
||||
public static void AddPermissionOverride(string commandName, params string[] permissions)
|
||||
{
|
||||
CommandOverrides.TryGetValue(commandName, out var overrideDef);
|
||||
if (overrideDef == null)
|
||||
{
|
||||
overrideDef = new CommandData()
|
||||
{
|
||||
Flags = new(permissions),
|
||||
Enabled = true,
|
||||
CheckType = "all"
|
||||
};
|
||||
|
||||
CommandOverrides[commandName] = overrideDef;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var flag in permissions)
|
||||
{
|
||||
overrideDef.Flags.Add(flag);
|
||||
}
|
||||
CommandOverrides[commandName] = overrideDef;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a permission from a command override.
|
||||
/// This is not saved to "configs/admin_overrides.json".
|
||||
/// </summary>
|
||||
/// <param name="commandName">Name of the command.</param>
|
||||
/// <param name="permissions">Permissions to remove from the command override.</param>
|
||||
public static void RemovePermissionOverride(string commandName, params string[] permissions)
|
||||
{
|
||||
CommandOverrides.TryGetValue(commandName, out var overrideDef);
|
||||
if (overrideDef == null) return;
|
||||
|
||||
overrideDef.Flags.ExceptWith(permissions);
|
||||
CommandOverrides[commandName] = overrideDef;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all permissions from a command override.
|
||||
/// This is not saved to "configs/admin_overrides.json".
|
||||
/// </summary>
|
||||
/// <param name="commandName">Name of the command.</param>
|
||||
/// <param name="disable">Whether to disable the command override after clearing.</param>
|
||||
public static void ClearPermissionOverride(string commandName, bool disable = true)
|
||||
{
|
||||
CommandOverrides.TryGetValue(commandName, out var overrideDef);
|
||||
if (overrideDef == null) return;
|
||||
|
||||
overrideDef.Flags?.Clear();
|
||||
overrideDef.Enabled = !disable;
|
||||
CommandOverrides[commandName] = overrideDef;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a command override.
|
||||
/// This is not saved to "configs/admin_overrides.json".
|
||||
/// </summary>
|
||||
/// <param name="commandName">Name of the command.</param>
|
||||
public static void DeleteCommandOverride(string commandName)
|
||||
{
|
||||
if (!CommandOverrides.ContainsKey(commandName)) return;
|
||||
CommandOverrides.Remove(commandName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a command override to be enabled or disabled.
|
||||
/// This is not saved to "configs/admin_overrides.json".
|
||||
/// </summary>
|
||||
/// <param name="commandName">Name of the command.</param>
|
||||
/// <param name="state">New state of the command override.</param>
|
||||
public static void SetCommandOverideState(string commandName, bool state)
|
||||
{
|
||||
CommandOverrides.TryGetValue(commandName, out var overrideDef);
|
||||
if (overrideDef == null) return;
|
||||
|
||||
overrideDef.Flags?.Clear();
|
||||
overrideDef.Enabled = state;
|
||||
CommandOverrides[commandName] = overrideDef;
|
||||
}
|
||||
}
|
||||
}
|
||||
193
managed/CounterStrikeSharp.API/Modules/Admin/AdminGroup.cs
Normal file
193
managed/CounterStrikeSharp.API/Modules/Admin/AdminGroup.cs
Normal file
@@ -0,0 +1,193 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
public partial class AdminData
|
||||
{
|
||||
[JsonPropertyName("groups")] public HashSet<string> Groups { get; init; } = new();
|
||||
}
|
||||
public partial class AdminGroupData
|
||||
{
|
||||
[JsonPropertyName("flags")] public required HashSet<string> Flags { get; init; }
|
||||
[JsonPropertyName("immunity")] public uint Immunity { get; set; } = 0;
|
||||
[JsonPropertyName("command_overrides")] public Dictionary<string, bool> CommandOverrides { get; init; } = new();
|
||||
}
|
||||
|
||||
public static partial class AdminManager
|
||||
{
|
||||
private static Dictionary<string, AdminGroupData> Groups = new();
|
||||
|
||||
public static void LoadAdminGroups(string adminGroupsPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(adminGroupsPath))
|
||||
{
|
||||
Console.WriteLine("Admin groups file not found. Skipping admin groups load.");
|
||||
return;
|
||||
}
|
||||
|
||||
var groupsFromFile = JsonSerializer.Deserialize<Dictionary<string, AdminGroupData>>(File.ReadAllText(adminGroupsPath), new JsonSerializerOptions() { ReadCommentHandling = JsonCommentHandling.Skip });
|
||||
if (groupsFromFile == null) { throw new FileNotFoundException(); }
|
||||
foreach (var (key, groupDef) in groupsFromFile)
|
||||
{
|
||||
if (Groups.ContainsKey(key))
|
||||
{
|
||||
Groups[key].Flags.UnionWith(groupDef.Flags);
|
||||
if (groupDef.Immunity > Groups[key].Immunity)
|
||||
{
|
||||
Groups[key].Immunity = groupDef.Immunity;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Groups.Add(key, groupDef);
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"Loaded {Groups.Count} admin groups.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Failed to load admin groups: {ex}");
|
||||
}
|
||||
|
||||
// Loop over each of the admins. If one of our admins is in a group,
|
||||
// add the flags from the group to their admin definition and change
|
||||
// the admin's immunity if it's higher.
|
||||
foreach (var adminData in Admins.Values)
|
||||
{
|
||||
var groups = adminData.Groups;
|
||||
foreach (var group in groups)
|
||||
{
|
||||
// roflmuffin is probably smart enough to condense this function down ;)
|
||||
if (Groups.TryGetValue(group, out var groupData))
|
||||
{
|
||||
adminData.Flags.UnionWith(groupData.Flags);
|
||||
if (groupData.Immunity > adminData.Immunity)
|
||||
{
|
||||
adminData.Immunity = groupData.Immunity;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if the player is part of an admin group.
|
||||
/// </summary>
|
||||
/// <param name="player">Player controller.</param>
|
||||
/// <param name="groups">Groups to check for.</param>
|
||||
/// <returns>True if a player is part of all of the groups provided, false if not.</returns>
|
||||
public static bool PlayerInGroup(CCSPlayerController? player, params string[] groups)
|
||||
{
|
||||
// This is here for cases where the server console is attempting to call commands.
|
||||
// The server console should have access to all commands, regardless of groups.
|
||||
if (player == null) return true;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return false; }
|
||||
var playerData = GetPlayerAdminData((SteamID)player.SteamID);
|
||||
return playerData?.Groups.IsSupersetOf(groups) ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if the player is part of an admin group.
|
||||
/// </summary>
|
||||
/// <param name="steamId">SteamID of the player.</param>
|
||||
/// <param name="groups">Groups to check for.</param>
|
||||
/// <returns>True if a player is part of all of the groups provided, false if not.</returns>
|
||||
public static bool PlayerInGroup(SteamID steamId, params string[] groups)
|
||||
{
|
||||
var playerData = GetPlayerAdminData(steamId);
|
||||
return playerData?.Groups.IsSupersetOf(groups) ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a player to a group.
|
||||
/// </summary>
|
||||
/// <param name="player">Player controller.</param>
|
||||
/// <param name="groups">Groups to add the player to.</param>
|
||||
public static void AddPlayerToGroup(CCSPlayerController? player, params string[] groups)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return; }
|
||||
AddPlayerToGroup((SteamID)player.SteamID, groups);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a player to a group.
|
||||
/// </summary>
|
||||
/// <param name="steamId">SteamID of the player.</param>
|
||||
/// <param name="groups">Groups to add the player to.</param>
|
||||
public static void AddPlayerToGroup(SteamID steamId, params string[] groups)
|
||||
{
|
||||
var data = GetPlayerAdminData(steamId);
|
||||
if (data == null)
|
||||
{
|
||||
data = new AdminData()
|
||||
{
|
||||
Identity = steamId.SteamId64.ToString(),
|
||||
Flags = new(),
|
||||
Groups = new(groups)
|
||||
};
|
||||
}
|
||||
|
||||
foreach (var group in groups)
|
||||
{
|
||||
if (Groups.TryGetValue(group, out var groupDef))
|
||||
{
|
||||
data.Flags.UnionWith(groupDef.Flags);
|
||||
groupDef.CommandOverrides.ToList().ForEach(x => data.CommandOverrides[x.Key] = x.Value);
|
||||
}
|
||||
}
|
||||
Admins[steamId] = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a player from a group.
|
||||
/// </summary>
|
||||
/// <param name="player">Player controller.</param>
|
||||
/// <param name="removeInheritedFlags">If true, all of the flags that the player inherited from being in the group will be removed.</param>
|
||||
/// <param name="groups"></param>
|
||||
public static void RemovePlayerFromGroup(CCSPlayerController? player, bool removeInheritedFlags = true, params string[] groups)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return; }
|
||||
RemovePlayerFromGroup((SteamID)player.SteamID, true, groups);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a player from a group.
|
||||
/// </summary>
|
||||
/// <param name="steamId">SteamID of the player.</param>
|
||||
/// <param name="removeInheritedFlags">If true, all of the flags that the player inherited from being in the group will be removed.</param>
|
||||
/// <param name="groups"></param>
|
||||
public static void RemovePlayerFromGroup(SteamID steamId, bool removeInheritedFlags = true, params string[] groups)
|
||||
{
|
||||
var data = GetPlayerAdminData(steamId);
|
||||
if (data == null) return;
|
||||
|
||||
data.Groups.ExceptWith(groups);
|
||||
|
||||
if (removeInheritedFlags)
|
||||
{
|
||||
foreach (var group in groups)
|
||||
{
|
||||
if (Groups.TryGetValue(group, out var groupDef))
|
||||
{
|
||||
data.Flags.ExceptWith(groupDef.Flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
Admins[steamId] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +1,45 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using CounterStrikeSharp.API.Core.Logging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
public partial class AdminData
|
||||
{
|
||||
[JsonPropertyName("identity")] public required string Identity { get; init; }
|
||||
[JsonPropertyName("flags")] public required HashSet<string> Flags { get; init; }
|
||||
}
|
||||
|
||||
public static class AdminManager
|
||||
public static partial class AdminManager
|
||||
{
|
||||
private static readonly Dictionary<SteamID, AdminData> Admins = new();
|
||||
|
||||
static AdminManager()
|
||||
{
|
||||
CommandUtils.AddStandaloneCommand("css_admins_reload", "Reloads the admin file.", ReloadAdminsCommand);
|
||||
CommandUtils.AddStandaloneCommand("css_admins_list", "List admins and their flags.", ListAdminsCommand);
|
||||
CommandUtils.AddStandaloneCommand("css_groups_reload", "Reloads the admin groups file.", ReloadAdminGroupsCommand);
|
||||
CommandUtils.AddStandaloneCommand("css_groups_list", "List admin groups and their flags.", ListAdminGroupsCommand);
|
||||
CommandUtils.AddStandaloneCommand("css_overrides_reload", "Reloads the admin command overrides file.", ReloadAdminOverridesCommand);
|
||||
CommandUtils.AddStandaloneCommand("css_overrides_list", "List admin command overrides and their flags.", ListAdminOverridesCommand);
|
||||
}
|
||||
|
||||
[RequiresPermissions("can_reload_admins")]
|
||||
public static void MergeGroupPermsIntoAdmins()
|
||||
{
|
||||
foreach (var (steamID, adminDef) in Admins)
|
||||
{
|
||||
AddPlayerToGroup(steamID, adminDef.Groups.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
[RequiresPermissions(permissions:"@css/generic")]
|
||||
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
private static void ReloadAdminsCommand(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
Admins.Clear();
|
||||
var rootDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.Parent;
|
||||
Load(Path.Combine(rootDir.FullName, "configs", "admins.json"));
|
||||
LoadAdminData(Path.Combine(rootDir.FullName, "configs", "admins.json"));
|
||||
}
|
||||
|
||||
[RequiresPermissions("can_reload_admins")]
|
||||
|
||||
[RequiresPermissions(permissions:"@css/generic")]
|
||||
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
private static void ListAdminsCommand(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
@@ -48,166 +49,42 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
}
|
||||
}
|
||||
|
||||
public static void Load(string adminDataPath)
|
||||
[RequiresPermissions(permissions:"@css/generic")]
|
||||
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
private static void ReloadAdminGroupsCommand(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(adminDataPath))
|
||||
{
|
||||
Console.WriteLine("Admin data file not found. Skipping admin data load.");
|
||||
return;
|
||||
}
|
||||
|
||||
var adminsFromFile = JsonSerializer.Deserialize<Dictionary<string, AdminData>>(File.ReadAllText(adminDataPath), new JsonSerializerOptions() { ReadCommentHandling = JsonCommentHandling.Skip });
|
||||
if (adminsFromFile == null) { throw new FileNotFoundException(); }
|
||||
foreach (var adminDef in adminsFromFile.Values)
|
||||
{
|
||||
if (SteamID.TryParse(adminDef.Identity, out var steamId))
|
||||
{
|
||||
if (Admins.ContainsKey(steamId!))
|
||||
{
|
||||
Admins[steamId!].Flags.UnionWith(adminDef.Flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
Admins.Add(steamId!, adminDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
Groups.Clear();
|
||||
var rootDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.Parent;
|
||||
LoadAdminGroups(Path.Combine(rootDir.FullName, "configs", "admin_groups.json"));
|
||||
}
|
||||
|
||||
Console.WriteLine($"Loaded admin data with {Admins.Count} admins.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
[RequiresPermissions(permissions: "@css/generic")]
|
||||
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
private static void ListAdminGroupsCommand(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
foreach (var (groupName, groupDef) in Groups)
|
||||
{
|
||||
Console.WriteLine($"Failed to load admin data: {ex}");
|
||||
command.ReplyToCommand($"{groupName} - {string.Join(", ", groupDef.Flags)}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Grabs the admin data for a player that was loaded from "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="steamId">SteamID object of the player.</param>
|
||||
/// <returns>AdminData class if data found, null if not.</returns>
|
||||
public static AdminData? GetPlayerAdminData(SteamID steamId)
|
||||
[RequiresPermissions(permissions: "@css/generic")]
|
||||
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
private static void ReloadAdminOverridesCommand(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
return Admins.GetValueOrDefault(steamId);
|
||||
CommandOverrides.Clear();
|
||||
var rootDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.Parent;
|
||||
LoadCommandOverrides(Path.Combine(rootDir.FullName, "configs", "admin_overrides.json"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if a player has access to a certain set of permission flags.
|
||||
/// </summary>
|
||||
/// <param name="player">Player or server console.</param>
|
||||
/// <param name="flags">Flags to look for in the players permission flags.</param>
|
||||
/// <returns>True if flags are present, false if not.</returns>
|
||||
public static bool PlayerHasPermissions(CCSPlayerController? player, params string[] flags)
|
||||
[RequiresPermissions(permissions: "@css/generic")]
|
||||
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
private static void ListAdminOverridesCommand(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
// This is here for cases where the server console is attempting to call commands.
|
||||
// The server console should have access to all commands, regardless of permissions.
|
||||
if (player == null) return true;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return false; }
|
||||
var playerData = GetPlayerAdminData((SteamID)player.SteamID);
|
||||
return playerData?.Flags.IsSupersetOf(flags) ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if a player has access to a certain set of permission flags.
|
||||
/// </summary>
|
||||
/// <param name="steamId">Steam ID object.</param>
|
||||
/// <param name="flags">Flags to look for in the players permission flags.</param>
|
||||
/// <returns>True if flags are present, false if not.</returns>
|
||||
public static bool PlayerHasPermissions(SteamID steamId, params string[] flags)
|
||||
{
|
||||
var playerData = GetPlayerAdminData(steamId);
|
||||
return playerData?.Flags.IsSupersetOf(flags) ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily adds a permission flag to the player. These flags are not saved to
|
||||
/// "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="player">Player controller to add a flag to.</param>
|
||||
/// <param name="flags">Flags to add for the player.</param>
|
||||
public static void AddPlayerPermissions(CCSPlayerController? player, params string[] flags)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
|
||||
AddPlayerPermissions((SteamID)player.SteamID, flags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily adds a permission flag to the player. These flags are not saved to
|
||||
/// "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="steamId">SteamID to add a flag to.</param>
|
||||
/// <param name="flags">Flags to add for the player.</param>
|
||||
public static void AddPlayerPermissions(SteamID steamId, params string[] flags)
|
||||
{
|
||||
var data = GetPlayerAdminData(steamId);
|
||||
if (data == null)
|
||||
foreach (var (commandName, commandDef) in CommandOverrides)
|
||||
{
|
||||
data = new AdminData()
|
||||
{
|
||||
Identity = steamId.SteamId64.ToString(),
|
||||
Flags = new(flags)
|
||||
};
|
||||
|
||||
Admins[steamId] = data;
|
||||
return;
|
||||
command.ReplyToCommand($"{commandName} (enabled: {commandDef.Enabled.ToString()}) - {string.Join(", ", commandDef.Flags)}");
|
||||
}
|
||||
|
||||
foreach (var flag in flags)
|
||||
{
|
||||
data.Flags.Add(flag);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily removes a permission flag to the player. These flags are not saved to
|
||||
/// "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="player">Player controller to remove flags from.</param>
|
||||
/// <param name="flags">Flags to remove from the player.</param>
|
||||
public static void RemovePlayerPermissions(CCSPlayerController? player, params string[] flags)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
|
||||
|
||||
RemovePlayerPermissions((SteamID)player.SteamID, flags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily removes a permission flag to the player. These flags are not saved to
|
||||
/// "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="steamId">Steam ID to remove flags from.</param>
|
||||
/// <param name="flags">Flags to remove from the player.</param>
|
||||
public static void RemovePlayerPermissions(SteamID steamId, params string[] flags)
|
||||
{
|
||||
var data = GetPlayerAdminData(steamId);
|
||||
if (data == null) return;
|
||||
|
||||
data.Flags.ExceptWith(flags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a players admin data. This is not saved to "configs/admins.json"
|
||||
/// </summary>
|
||||
/// <param name="player">Player controller to remove admin data from.</param>
|
||||
public static void RemovePlayerAdminData(CCSPlayerController? player)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
|
||||
|
||||
RemovePlayerAdminData((SteamID)player.SteamID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a players admin data. This is not saved to "configs/admins.json"
|
||||
/// </summary>
|
||||
/// <param name="steamId">Steam ID remove admin data from.</param>
|
||||
public static void RemovePlayerAdminData(SteamID steamId)
|
||||
{
|
||||
Admins.Remove(steamId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
396
managed/CounterStrikeSharp.API/Modules/Admin/AdminPermissions.cs
Normal file
396
managed/CounterStrikeSharp.API/Modules/Admin/AdminPermissions.cs
Normal file
@@ -0,0 +1,396 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using System.Reflection;
|
||||
using System.Numerics;
|
||||
using System.Linq;
|
||||
using CounterStrikeSharp.API.Core.Logging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
public partial class AdminData
|
||||
{
|
||||
[JsonPropertyName("identity")] public required string Identity { get; init; }
|
||||
[JsonPropertyName("flags")] public HashSet<string> Flags { get; init; } = new();
|
||||
[JsonPropertyName("immunity")] public uint Immunity { get; set; } = 0;
|
||||
[JsonPropertyName("command_overrides")] public Dictionary<string, bool> CommandOverrides { get; init; } = new();
|
||||
}
|
||||
|
||||
public static partial class AdminManager
|
||||
{
|
||||
private static Dictionary<SteamID, AdminData> Admins = new();
|
||||
|
||||
// TODO: ServiceCollection
|
||||
private static ILogger _logger = CoreLogging.Factory.CreateLogger("AdminManager");
|
||||
|
||||
public static void LoadAdminData(string adminDataPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(adminDataPath))
|
||||
{
|
||||
_logger.LogWarning("Admin data file not found. Skipping admin data load.");
|
||||
return;
|
||||
}
|
||||
|
||||
var adminsFromFile = JsonSerializer.Deserialize<Dictionary<string, AdminData>>(File.ReadAllText(adminDataPath), new JsonSerializerOptions() { ReadCommentHandling = JsonCommentHandling.Skip });
|
||||
if (adminsFromFile == null) { throw new FileNotFoundException(); }
|
||||
foreach (var adminDef in adminsFromFile.Values)
|
||||
{
|
||||
if (SteamID.TryParse(adminDef.Identity, out var steamId))
|
||||
{
|
||||
if (Admins.ContainsKey(steamId!))
|
||||
{
|
||||
Admins[steamId!].Flags.UnionWith(adminDef.Flags);
|
||||
if (adminDef.Immunity > Admins[steamId!].Immunity)
|
||||
{
|
||||
Admins[steamId!].Immunity = adminDef.Immunity;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Admins.Add(steamId!, adminDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogInformation("Loaded admin data with {Count} admins.", Admins.Count);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to load admin data");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Grabs the admin data for a player that was loaded from "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="steamId">SteamID object of the player.</param>
|
||||
/// <returns>AdminData class if data found, null if not.</returns>
|
||||
public static AdminData? GetPlayerAdminData(SteamID steamId)
|
||||
{
|
||||
return Admins.GetValueOrDefault(steamId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a players admin data. This is not saved to "configs/admins.json"
|
||||
/// </summary>
|
||||
/// <param name="steamId">Steam ID remove admin data from.</param>
|
||||
public static void RemovePlayerAdminData(SteamID steamId)
|
||||
{
|
||||
Admins.Remove(steamId);
|
||||
}
|
||||
|
||||
#region Command Permission Checks
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if a player has access to a certain set of permission flags.
|
||||
/// </summary>
|
||||
/// <param name="player">Player or server console.</param>
|
||||
/// <param name="flags">Flags to look for in the players permission flags.</param>
|
||||
/// <returns>True if flags are present, false if not.</returns>
|
||||
public static bool PlayerHasPermissions(CCSPlayerController? player, params string[] flags)
|
||||
{
|
||||
// This is here for cases where the server console is attempting to call commands.
|
||||
// The server console should have access to all commands, regardless of permissions.
|
||||
if (player == null) return true;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return false; }
|
||||
var playerData = GetPlayerAdminData((SteamID)player.SteamID);
|
||||
return playerData?.Flags.IsSupersetOf(flags) ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if a player has access to a certain set of permission flags.
|
||||
/// </summary>
|
||||
/// <param name="steamId">Steam ID object.</param>
|
||||
/// <param name="flags">Flags to look for in the players permission flags.</param>
|
||||
/// <returns>True if flags are present, false if not.</returns>
|
||||
public static bool PlayerHasPermissions(SteamID steamId, params string[] flags)
|
||||
{
|
||||
var playerData = GetPlayerAdminData(steamId);
|
||||
return playerData?.Flags.IsSupersetOf(flags) ?? false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// This is placed here instead of in AdminCommandOverrides.cs as this all relates to admins that are
|
||||
// defined within the "configs/admins.json" file.
|
||||
#region Admin Specific Command Overrides
|
||||
/// <summary>
|
||||
/// Checks to see if a player has a command override. This does NOT return the actual
|
||||
/// state of the override.
|
||||
/// </summary>
|
||||
/// <param name="player">Player or server console.</param>
|
||||
/// <param name="command">Name of the command to check for.</param>
|
||||
/// <returns>True if override exists, false if not.</returns>
|
||||
public static bool PlayerHasCommandOverride(CCSPlayerController? player, string command)
|
||||
{
|
||||
// This is here for cases where the server console is attempting to call commands.
|
||||
// The server console should have access to all commands, regardless of permissions.
|
||||
if (player == null) return true;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return false; }
|
||||
var playerData = GetPlayerAdminData((SteamID)player.SteamID);
|
||||
return playerData?.CommandOverrides.ContainsKey(command) ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if a player has a command override. This does NOT return the actual
|
||||
/// state of the override.
|
||||
/// </summary>
|
||||
/// <param name="steamId">Steam ID object.</param>
|
||||
/// <param name="command">Name of the command to check for.</param>
|
||||
/// <returns>True if override exists, false if not.</returns>
|
||||
public static bool PlayerHasCommandOverride(SteamID steamId, string command)
|
||||
{
|
||||
var playerData = GetPlayerAdminData(steamId);
|
||||
return playerData?.CommandOverrides.ContainsKey(command) ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of a command override state.
|
||||
/// </summary>
|
||||
/// <param name="player">Player or server console.</param>
|
||||
/// <param name="command">Name of the command to check for.</param>
|
||||
/// <returns>True if override is active, false if not.</returns>
|
||||
public static bool GetPlayerCommandOverrideState(CCSPlayerController? player, string command)
|
||||
{
|
||||
// This is here for cases where the server console is attempting to call commands.
|
||||
// The server console should have access to all commands, regardless of permissions.
|
||||
if (player == null) return true;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return false; }
|
||||
var playerData = GetPlayerAdminData((SteamID)player.SteamID);
|
||||
return playerData?.CommandOverrides.GetValueOrDefault(command) ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of a command override state.
|
||||
/// </summary>
|
||||
/// <param name="steamId">Steam ID object.</param>
|
||||
/// <param name="command">Name of the command to check for.</param>
|
||||
/// <returns>True if override is active, false if not.</returns>
|
||||
public static bool GetPlayerCommandOverrideState(SteamID steamId, string command)
|
||||
{
|
||||
var playerData = GetPlayerAdminData(steamId);
|
||||
return playerData?.CommandOverrides.GetValueOrDefault(command) ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a player command override. This is not saved to "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="player">Player or server console.</param>
|
||||
/// <param name="command">Name of the command to check for.</param>
|
||||
/// <param name="state">New state of the command override.</param>
|
||||
public static void SetPlayerCommandOverride(CCSPlayerController? player, string command, bool state)
|
||||
{
|
||||
// This is here for cases where the server console is attempting to call commands.
|
||||
// The server console should have access to all commands, regardless of permissions.
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return; }
|
||||
SetPlayerCommandOverride((SteamID)player.SteamID, command, state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a player command override. This is not saved to "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="steamId">SteamID to add a flag to.</param>
|
||||
/// <param name="command">Name of the command to check for.</param>
|
||||
/// <param name="state">New state of the command override.</param>
|
||||
public static void SetPlayerCommandOverride(SteamID steamId, string command, bool state)
|
||||
{
|
||||
var data = GetPlayerAdminData(steamId);
|
||||
if (data == null)
|
||||
{
|
||||
data = new AdminData()
|
||||
{
|
||||
Identity = steamId.SteamId64.ToString(),
|
||||
Flags = new(),
|
||||
Groups = new(),
|
||||
CommandOverrides = new() { { command, state } }
|
||||
};
|
||||
|
||||
Admins[steamId] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
data.CommandOverrides[command] = state;
|
||||
Admins[steamId] = data;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Manipulating Permissions
|
||||
/// <summary>
|
||||
/// Temporarily adds a permission flag to the player. These flags are not saved to
|
||||
/// "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="player">Player controller to add a flag to.</param>
|
||||
/// <param name="flags">Flags to add for the player.</param>
|
||||
public static void AddPlayerPermissions(CCSPlayerController? player, params string[] flags)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
|
||||
AddPlayerPermissions((SteamID)player.SteamID, flags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily adds a permission flag to the player. These flags are not saved to
|
||||
/// "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="steamId">SteamID to add a flag to.</param>
|
||||
/// <param name="flags">Flags to add for the player.</param>
|
||||
public static void AddPlayerPermissions(SteamID steamId, params string[] flags)
|
||||
{
|
||||
var data = GetPlayerAdminData(steamId);
|
||||
if (data == null)
|
||||
{
|
||||
data = new AdminData()
|
||||
{
|
||||
Identity = steamId.SteamId64.ToString(),
|
||||
Flags = new(flags),
|
||||
Groups = new()
|
||||
};
|
||||
|
||||
Admins[steamId] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var flag in flags)
|
||||
{
|
||||
data.Flags.Add(flag);
|
||||
}
|
||||
Admins[steamId] = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily removes a permission flag to the player. These flags are not saved to
|
||||
/// "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="player">Player controller to remove flags from.</param>
|
||||
/// <param name="flags">Flags to remove from the player.</param>
|
||||
public static void RemovePlayerPermissions(CCSPlayerController? player, params string[] flags)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
|
||||
|
||||
RemovePlayerPermissions((SteamID)player.SteamID, flags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily removes a permission flag to the player. These flags are not saved to
|
||||
/// "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="steamId">Steam ID to remove flags from.</param>
|
||||
/// <param name="flags">Flags to remove from the player.</param>
|
||||
public static void RemovePlayerPermissions(SteamID steamId, params string[] flags)
|
||||
{
|
||||
var data = GetPlayerAdminData(steamId);
|
||||
if (data == null) return;
|
||||
|
||||
data.Flags.ExceptWith(flags);
|
||||
Admins[steamId] = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily removes all permission flags from a player. These flags are not saved to
|
||||
/// "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="player">Player controller to remove flags from.</param>
|
||||
public static void ClearPlayerPermissions(CCSPlayerController? player)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
|
||||
|
||||
ClearPlayerPermissions((SteamID)player.SteamID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily removes all permission flags from a player. These flags are not saved to
|
||||
/// "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="steamId">Steam ID to remove flags from.</param>
|
||||
public static void ClearPlayerPermissions(SteamID steamId)
|
||||
{
|
||||
var data = GetPlayerAdminData(steamId);
|
||||
if (data == null) return;
|
||||
|
||||
data.Flags.Clear();
|
||||
Admins[steamId] = data;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Player Immunity
|
||||
/// <summary>
|
||||
/// Sets the immunity value for a player.
|
||||
/// </summary>
|
||||
/// <param name="player">Player controller.</param>
|
||||
/// <param name="value">New immunity value.</param>
|
||||
public static void SetPlayerImmunity(CCSPlayerController? player, uint value)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
|
||||
|
||||
SetPlayerImmunity((SteamID)player.SteamID, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the immunity value for a player.
|
||||
/// </summary>
|
||||
/// <param name="steamId">Steam ID of the player.</param>
|
||||
/// <param name="value">New immunity value.</param>
|
||||
public static void SetPlayerImmunity(SteamID steamId, uint value)
|
||||
{
|
||||
var data = GetPlayerAdminData(steamId);
|
||||
if (data == null) return;
|
||||
|
||||
data.Immunity = value;
|
||||
Admins[steamId] = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if a player can target another player based on their immunity value.
|
||||
/// </summary>
|
||||
/// <param name="caller">Caller of the command.</param>
|
||||
/// <param name="target">Target of the command.</param>
|
||||
/// <returns></returns>
|
||||
public static bool CanPlayerTarget(CCSPlayerController? caller, CCSPlayerController? target)
|
||||
{
|
||||
// The server console should be able to target everyone.
|
||||
if (caller == null) return true;
|
||||
|
||||
if (target == null) return false;
|
||||
if (!target.IsValid || target.Connected != PlayerConnectedState.PlayerConnected) return false;
|
||||
|
||||
var callerData = GetPlayerAdminData((SteamID)caller.SteamID);
|
||||
if (callerData == null) return false;
|
||||
|
||||
var targetData = GetPlayerAdminData((SteamID)target.SteamID);
|
||||
if (targetData == null) return true;
|
||||
|
||||
return callerData.Immunity >= targetData.Immunity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if a player can target another player based on their immunity value.
|
||||
/// </summary>
|
||||
/// <param name="caller">Caller of the command.</param>
|
||||
/// <param name="target">Target of the command.</param>
|
||||
/// <returns></returns>
|
||||
public static bool CanPlayerTarget(SteamID caller, SteamID target)
|
||||
{
|
||||
var callerData = GetPlayerAdminData(caller);
|
||||
if (callerData == null) return false;
|
||||
|
||||
var targetData = GetPlayerAdminData(caller);
|
||||
if (targetData == null) return true;
|
||||
|
||||
return callerData.Immunity >= targetData.Immunity;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
public class BaseRequiresPermissions : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The permissions for the command.
|
||||
/// </summary>
|
||||
public string[] Permissions { get; }
|
||||
/// <summary>
|
||||
/// The name of the command that is attached to this attribute.
|
||||
/// </summary>
|
||||
public string Command { get; set; }
|
||||
/// <summary>
|
||||
/// Whether this attribute should be used for permission checks.
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public BaseRequiresPermissions(params string[] permissions)
|
||||
{
|
||||
Permissions = permissions;
|
||||
Command = "";
|
||||
}
|
||||
|
||||
public virtual bool CanExecuteCommand(CCSPlayerController? caller)
|
||||
{
|
||||
// If we have a command in the "command_overrides" section in "configs/admins.json",
|
||||
// we skip the checks below and just return the defined value.
|
||||
var adminData = AdminManager.GetPlayerAdminData((SteamID)caller.SteamID);
|
||||
if (adminData == null) return false;
|
||||
if (adminData.CommandOverrides.ContainsKey(Command))
|
||||
{
|
||||
return adminData.CommandOverrides[Command];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
public class PermissionCharacters
|
||||
{
|
||||
// Example: "#css/admin"
|
||||
public const char GroupPermissionChar = '#';
|
||||
// Example: "@css/manipulate_players"
|
||||
public const char UserPermissionChar = '@';
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,33 @@
|
||||
using System;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class RequiresPermissions : Attribute
|
||||
{
|
||||
public string[] RequiredPermissions { get; }
|
||||
|
||||
public RequiresPermissions(params string[] permissions)
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||
public class RequiresPermissions : BaseRequiresPermissions
|
||||
{
|
||||
public RequiresPermissions(params string[] permissions) : base(permissions) { }
|
||||
|
||||
public override bool CanExecuteCommand(CCSPlayerController? caller)
|
||||
{
|
||||
RequiredPermissions = permissions;
|
||||
if (caller == null) return true;
|
||||
if (AdminManager.PlayerHasCommandOverride(caller, Command))
|
||||
{
|
||||
return AdminManager.GetPlayerCommandOverrideState(caller, Command);
|
||||
}
|
||||
if (!base.CanExecuteCommand(caller)) return false;
|
||||
|
||||
var groupPermissions = Permissions.Where(perm => perm.StartsWith(PermissionCharacters.GroupPermissionChar));
|
||||
var userPermissions = Permissions.Where(perm => perm.StartsWith(PermissionCharacters.UserPermissionChar));
|
||||
|
||||
if (!AdminManager.PlayerInGroup(caller, groupPermissions.ToArray())) return false;
|
||||
if (!AdminManager.PlayerHasPermissions(caller, userPermissions.ToArray())) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||
public class RequiresPermissionsOr : BaseRequiresPermissions
|
||||
{
|
||||
public RequiresPermissionsOr(params string[] permissions) : base(permissions) { }
|
||||
|
||||
public override bool CanExecuteCommand(CCSPlayerController? caller)
|
||||
{
|
||||
if (caller == null) return true;
|
||||
if (AdminManager.PlayerHasCommandOverride(caller, Command))
|
||||
{
|
||||
return AdminManager.GetPlayerCommandOverrideState(caller, Command);
|
||||
}
|
||||
if (!base.CanExecuteCommand(caller)) return false;
|
||||
|
||||
var groupPermissions = Permissions.Where(perm => perm.StartsWith(PermissionCharacters.GroupPermissionChar));
|
||||
var userPermissions = Permissions.Where(perm => perm.StartsWith(PermissionCharacters.UserPermissionChar));
|
||||
|
||||
var adminData = AdminManager.GetPlayerAdminData((SteamID)caller.SteamID);
|
||||
if (adminData == null) return false;
|
||||
return (groupPermissions.Intersect(adminData.Groups).Count() + userPermissions.Intersect(adminData.Flags).Count()) > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Logging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Config
|
||||
{
|
||||
@@ -29,6 +31,7 @@ namespace CounterStrikeSharp.API.Modules.Config
|
||||
private static readonly DirectoryInfo? _rootDir;
|
||||
|
||||
private static readonly string _pluginConfigsFolderPath;
|
||||
private static ILogger _logger = CoreLogging.Factory.CreateLogger("ConfigManager");
|
||||
|
||||
static ConfigManager()
|
||||
{
|
||||
@@ -60,7 +63,7 @@ namespace CounterStrikeSharp.API.Modules.Config
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Failed to generate configuration file for {pluginName}: {ex}");
|
||||
_logger.LogError(ex, "Failed to generate configuration file for {PluginName}", pluginName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +73,7 @@ namespace CounterStrikeSharp.API.Modules.Config
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Failed to parse configuration '{pluginName}': {ex}");
|
||||
_logger.LogError(ex, "Failed to parse configuration file for {PluginName}", pluginName);
|
||||
}
|
||||
|
||||
return config;
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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/>. *
|
||||
*/
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Entities.Constants
|
||||
{
|
||||
public enum RoundEndReason : uint
|
||||
{
|
||||
Unknown = 0x0u,
|
||||
TargetBombed = 0x1u,
|
||||
TerroristsEscaped = 0x4u,
|
||||
CTsPreventEscape = 0x5u,
|
||||
EscapingTerroristsNeutralized = 0x6u,
|
||||
BombDefused = 0x7u,
|
||||
CTsWin = 0x8u,
|
||||
TerroristsWin = 0x9u,
|
||||
RoundDraw = 0xAu,
|
||||
AllHostageRescued = 0xBu,
|
||||
TargetSaved = 0xCu,
|
||||
HostagesNotRescued = 0xDu,
|
||||
TerroristsNotEscaped = 0xEu,
|
||||
GameCommencing = 0x10u,
|
||||
|
||||
TerroristsSurrender = 0x11u, // this also triggers match cancelled
|
||||
CTsSurrender = 0x12u, // this also triggers match cancelled
|
||||
|
||||
TerroristsPlanned = 0x13u,
|
||||
CTsReachedHostage = 0x14u,
|
||||
SurvivalWin = 0x15u,
|
||||
SurvivalDraw = 0x16u
|
||||
}
|
||||
}
|
||||
@@ -123,6 +123,7 @@ namespace CounterStrikeSharp.API.Modules.Events
|
||||
protected void SetEntityIndex(string name, int value) => NativeAPI.SetEventEntityIndex(Handle, name, value);
|
||||
|
||||
public void FireEvent(bool dontBroadcast) => NativeAPI.FireEvent(Handle, dontBroadcast);
|
||||
// public void FireEventToClient(int clientId, bool dontBroadcast) => NativeAPI.FireEventToClient(Handle, clientId);
|
||||
|
||||
public void FireEventToClient(CCSPlayerController player) => NativeAPI.FireEventToClient(Handle, (int)player.EntityIndex.Value.Value);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Memory
|
||||
{
|
||||
@@ -71,9 +73,26 @@ namespace CounterStrikeSharp.API.Modules.Memory
|
||||
return types[Enum.GetUnderlyingType(type)];
|
||||
}
|
||||
|
||||
Console.WriteLine("Error retrieving data type for type" + type.FullName);
|
||||
GlobalContext.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);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,73 @@
|
||||
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, string> SetModel = SetModelFunc.Invoke;
|
||||
|
||||
public static MemoryFunctionVoid<nint, RoundEndReason, float> TerminateRoundFunc =
|
||||
new(GameData.GetSignature("CCSGameRules_TerminateRound"));
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -16,6 +16,42 @@ public class CommandUtils
|
||||
var command = new CommandInfo(ptr, caller);
|
||||
|
||||
var methodInfo = handler?.GetMethodInfo();
|
||||
|
||||
if (!AdminManager.CommandIsOverriden(name))
|
||||
{
|
||||
// Do not execute command if we do not have the correct permissions.
|
||||
var permissions = methodInfo?.GetCustomAttributes<BaseRequiresPermissions>();
|
||||
if (permissions != null)
|
||||
{
|
||||
foreach (var attr in permissions)
|
||||
{
|
||||
attr.Command = name;
|
||||
if (!attr.CanExecuteCommand(caller))
|
||||
{
|
||||
command.ReplyToCommand("[CSS] You do not have the correct permissions to execute this command.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If this command has it's permissions overriden, we will do an AND check for all permissions.
|
||||
else
|
||||
{
|
||||
// I don't know if this is the most sane implementation of this, can be edited in code review.
|
||||
var data = AdminManager.GetCommandOverrideData(name);
|
||||
if (data != null)
|
||||
{
|
||||
var attrType = (data.CheckType == "all") ? typeof(RequiresPermissions) : typeof(RequiresPermissionsOr);
|
||||
var attr = (BaseRequiresPermissions)Activator.CreateInstance(attrType, args: AdminManager.GetPermissionOverrides(name));
|
||||
attr.Command = name;
|
||||
if (!attr.CanExecuteCommand(caller))
|
||||
{
|
||||
command.ReplyToCommand("[CSS] You do not have the correct permissions to execute this command.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not execute if we shouldn't be calling this command.
|
||||
var helperAttribute = methodInfo?.GetCustomAttribute<CommandHelperAttribute>();
|
||||
if (helperAttribute != null)
|
||||
@@ -41,14 +77,6 @@ public class CommandUtils
|
||||
}
|
||||
}
|
||||
|
||||
// Do not execute command if we do not have the correct permissions.
|
||||
var permissions = methodInfo?.GetCustomAttribute<RequiresPermissions>()?.RequiredPermissions;
|
||||
if (permissions != null && !AdminManager.PlayerHasPermissions(caller, permissions))
|
||||
{
|
||||
command.ReplyToCommand("[CSS] You do not have the correct permissions to execute this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
handler?.Invoke(caller, command);
|
||||
});
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -43,6 +44,11 @@ namespace CounterStrikeSharp.API
|
||||
return (T)Activator.CreateInstance(typeof(T), NativeAPI.GetEntityFromIndex(index))!;
|
||||
}
|
||||
|
||||
public static T? CreateEntityByName<T>(string name) where T : CBaseEntity
|
||||
{
|
||||
return (T?)Activator.CreateInstance(typeof(T), VirtualFunctions.UTIL_CreateEntityByName(name, -1));
|
||||
}
|
||||
|
||||
public static CCSPlayerController GetPlayerFromIndex(int index)
|
||||
{
|
||||
return Utilities.GetEntityFromIndex<CCSPlayerController>(index);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -4855,6 +4855,14 @@
|
||||
},
|
||||
"CBaseCSGrenadeProjectile": {
|
||||
"fields": [
|
||||
{
|
||||
"name": "m_vInitialPosition",
|
||||
"type": {
|
||||
"atomic": 0,
|
||||
"category": 4,
|
||||
"name": "Vector"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "m_vInitialVelocity",
|
||||
"type": {
|
||||
@@ -4970,6 +4978,20 @@
|
||||
"category": 0,
|
||||
"name": "int32"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "m_bHasEverHitPlayer",
|
||||
"type": {
|
||||
"category": 0,
|
||||
"name": "bool"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "m_bClearFromPlayers",
|
||||
"type": {
|
||||
"category": 0,
|
||||
"name": "bool"
|
||||
}
|
||||
}
|
||||
],
|
||||
"parent": "CBaseGrenade"
|
||||
@@ -13245,10 +13267,10 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "m_bMatchAbortedDueToPlayerBan",
|
||||
"name": "m_nMatchAbortedEarlyReason",
|
||||
"type": {
|
||||
"category": 0,
|
||||
"name": "bool"
|
||||
"name": "int32"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -13828,6 +13850,13 @@
|
||||
"name": "uint32"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "m_bCannotBeKicked",
|
||||
"type": {
|
||||
"category": 0,
|
||||
"name": "bool"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "m_bEverFullyConnected",
|
||||
"type": {
|
||||
@@ -14125,12 +14154,26 @@
|
||||
"name": "bool"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "m_dblLastReceivedPacketPlatFloatTime",
|
||||
"type": {
|
||||
"category": 0,
|
||||
"name": "float64"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "m_LastTeamDamageWarningTime",
|
||||
"type": {
|
||||
"category": 5,
|
||||
"name": "GameTime_t"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "m_LastTimePlayerWasDisconnectedForPawnsRemove",
|
||||
"type": {
|
||||
"category": 5,
|
||||
"name": "GameTime_t"
|
||||
}
|
||||
}
|
||||
],
|
||||
"parent": "CBasePlayerController"
|
||||
@@ -14439,6 +14482,13 @@
|
||||
"name": "char[18]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "m_bInHostageResetZone",
|
||||
"type": {
|
||||
"category": 0,
|
||||
"name": "bool"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "m_bInBuyZone",
|
||||
"type": {
|
||||
@@ -16244,6 +16294,20 @@
|
||||
"category": 0,
|
||||
"name": "float32"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "m_flHeightAtJumpStart",
|
||||
"type": {
|
||||
"category": 0,
|
||||
"name": "float32"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "m_flMaxJumpHeightThisJump",
|
||||
"type": {
|
||||
"category": 0,
|
||||
"name": "float32"
|
||||
}
|
||||
}
|
||||
],
|
||||
"parent": "CPlayer_MovementServices_Humanoid"
|
||||
@@ -30081,6 +30145,14 @@
|
||||
"category": 4,
|
||||
"name": "Vector"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "m_vecHostageResetPosition",
|
||||
"type": {
|
||||
"atomic": 0,
|
||||
"category": 4,
|
||||
"name": "Vector"
|
||||
}
|
||||
}
|
||||
],
|
||||
"parent": "CHostageExpresserShim"
|
||||
@@ -32719,6 +32791,40 @@
|
||||
],
|
||||
"parent": "CLogicalEntity"
|
||||
},
|
||||
"CLogicEventListener": {
|
||||
"fields": [
|
||||
{
|
||||
"name": "m_strEventName",
|
||||
"type": {
|
||||
"atomic": 0,
|
||||
"category": 4,
|
||||
"name": "CUtlString"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "m_bIsEnabled",
|
||||
"type": {
|
||||
"category": 0,
|
||||
"name": "bool"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "m_nTeam",
|
||||
"type": {
|
||||
"category": 0,
|
||||
"name": "int32"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "m_OnEventFired",
|
||||
"type": {
|
||||
"category": 5,
|
||||
"name": "CEntityIOOutput"
|
||||
}
|
||||
}
|
||||
],
|
||||
"parent": "CLogicalEntity"
|
||||
},
|
||||
"CLogicGameEvent": {
|
||||
"fields": [
|
||||
{
|
||||
@@ -54460,6 +54566,10 @@
|
||||
"fields": [],
|
||||
"parent": "CBaseTrigger"
|
||||
},
|
||||
"CTriggerHostageReset": {
|
||||
"fields": [],
|
||||
"parent": "CBaseTrigger"
|
||||
},
|
||||
"CTriggerHurt": {
|
||||
"fields": [
|
||||
{
|
||||
@@ -90276,6 +90386,23 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"C4LightEffect_t": {
|
||||
"align": 4,
|
||||
"items": [
|
||||
{
|
||||
"name": "eLightEffectNone",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"name": "eLightEffectDropped",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"name": "eLightEffectThirdPersonHeld",
|
||||
"value": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
"CAnimationGraphVisualizerPrimitiveType": {
|
||||
"align": 4,
|
||||
"items": [
|
||||
@@ -96794,6 +96921,10 @@
|
||||
{
|
||||
"name": "DFLAG_IGNORE_ARMOR",
|
||||
"value": 2048
|
||||
},
|
||||
{
|
||||
"name": "DFLAG_SUPPRESS_UTILREMOVE",
|
||||
"value": 4096
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -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}")
|
||||
};
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
@@ -31,16 +30,15 @@ using CounterStrikeSharp.API.Modules.Events;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Menu;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace TestPlugin
|
||||
{
|
||||
public class SampleConfig : BasePluginConfig
|
||||
{
|
||||
[JsonPropertyName("IsPluginEnabled")]
|
||||
public bool IsPluginEnabled { get; set; } = true;
|
||||
[JsonPropertyName("IsPluginEnabled")] public bool IsPluginEnabled { get; set; } = true;
|
||||
|
||||
[JsonPropertyName("LogPrefix")]
|
||||
public string LogPrefix { get; set; } = "CSSharp";
|
||||
[JsonPropertyName("LogPrefix")] public string LogPrefix { get; set; } = "CSSharp";
|
||||
}
|
||||
|
||||
[MinimumApiVersion(33)]
|
||||
@@ -67,14 +65,20 @@ namespace TestPlugin
|
||||
// Basic usage of the configuration system
|
||||
if (!Config.IsPluginEnabled)
|
||||
{
|
||||
Console.WriteLine($"{Config.LogPrefix} {ModuleName} is disabled");
|
||||
Logger.LogWarning($"{Config.LogPrefix} {ModuleName} is disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine(
|
||||
Logger.LogInformation(
|
||||
$"Test Plugin has been loaded, and the hot reload flag was {hotReload}, path is {ModulePath}");
|
||||
|
||||
Console.WriteLine($"Max Players: {Server.MaxPlayers}");
|
||||
Logger.LogWarning($"Max Players: {Server.MaxPlayers}");
|
||||
|
||||
VirtualFunctions.SwitchTeamFunc.Hook(hook =>
|
||||
{
|
||||
Logger.LogInformation("Switch team func called");
|
||||
return HookResult.Continue;
|
||||
}, HookMode.Pre);
|
||||
|
||||
SetupConvars();
|
||||
SetupGameEvents();
|
||||
@@ -84,7 +88,7 @@ namespace TestPlugin
|
||||
|
||||
// ValveInterface provides pointers to loaded modules via Interface Name exposed from the engine (e.g. Source2Server001)
|
||||
var server = ValveInterface.Server;
|
||||
Log($"Server pointer found @ {server.Pointer:X}");
|
||||
Logger.LogInformation("Server pointer found @ {Pointer:X}", server.Pointer);
|
||||
|
||||
// You can use `ModuleDirectory` to get the directory of the plugin (for storing config files, saving database files etc.)
|
||||
File.WriteAllText(Path.Join(ModuleDirectory, "example.txt"),
|
||||
@@ -98,7 +102,38 @@ namespace TestPlugin
|
||||
// This value is asserted against the native code that points to the same function.
|
||||
var virtualFunc = VirtualFunction.Create<IntPtr>(server.Pointer, 91);
|
||||
var result = virtualFunc() - 8;
|
||||
Log($"Result of virtual func call is {result:X}");
|
||||
Logger.LogInformation("Result of virtual func call is {Pointer:X}", result);
|
||||
|
||||
VirtualFunctions.UTIL_RemoveFunc.Hook(hook =>
|
||||
{
|
||||
var entityInstance = hook.GetParam<CEntityInstance>(0);
|
||||
Logger.LogInformation("Removed entity {EntityIndex}", entityInstance.EntityIndex.Value.Value);
|
||||
|
||||
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()
|
||||
@@ -109,13 +144,13 @@ namespace TestPlugin
|
||||
cheatsCvar.SetValue(true);
|
||||
|
||||
var numericCvar = ConVar.Find("mp_warmuptime");
|
||||
Console.WriteLine($"mp_warmuptime = {numericCvar?.GetPrimitiveValue<float>()}");
|
||||
Logger.LogInformation("mp_warmuptime = {Value}", numericCvar?.GetPrimitiveValue<float>());
|
||||
|
||||
var stringCvar = ConVar.Find("sv_skyname");
|
||||
Console.WriteLine($"sv_skyname = {stringCvar?.StringValue}");
|
||||
Logger.LogInformation("sv_skyname = {Value}", stringCvar?.StringValue);
|
||||
|
||||
var fogCvar = ConVar.Find("fog_color");
|
||||
Console.WriteLine($"fog_color = {fogCvar?.GetNativeValue<Vector>()}");
|
||||
Logger.LogInformation("fog_color = {Value}", fogCvar?.GetNativeValue<Vector>());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -128,7 +163,7 @@ namespace TestPlugin
|
||||
var entity = new CCSPlayerController(NativeAPI.GetEntityFromIndex(@event.Userid));
|
||||
if (!entity.IsValid)
|
||||
{
|
||||
Log("invalid entity");
|
||||
Logger.LogInformation("invalid entity");
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
@@ -151,7 +186,8 @@ namespace TestPlugin
|
||||
if (!@event.Userid.IsValid) return 0;
|
||||
if (!@event.Userid.PlayerPawn.IsValid) return 0;
|
||||
|
||||
Log($"Player spawned with entity index: {@event.Userid.EntityIndex} & User ID: {@event.Userid.UserId}");
|
||||
Logger.LogInformation("Player spawned with entity index: {EntityIndex} & User ID: {UserId}",
|
||||
@event.Userid.EntityIndex, @event.Userid.UserId);
|
||||
|
||||
return HookResult.Continue;
|
||||
});
|
||||
@@ -166,7 +202,7 @@ namespace TestPlugin
|
||||
// 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)));
|
||||
@@ -175,17 +211,14 @@ namespace TestPlugin
|
||||
activeWeapon.ReserveAmmo[0] = 250;
|
||||
activeWeapon.Clip1 = 250;
|
||||
|
||||
Log(
|
||||
$"Pawn Position: {pawn.CBodyComponent?.SceneNode?.AbsOrigin} @{pawn.CBodyComponent?.SceneNode.Rotation}");
|
||||
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;
|
||||
|
||||
Log(
|
||||
$"Found steamID {new SteamID(player.SteamID)} for player {player.PlayerName}:{pawn.Health}|{pawn.InBuyZone}");
|
||||
Log($"{@event.Userid}, {@event.X},{@event.Y},{@event.Z}");
|
||||
Logger.LogInformation("Bullet Impact: {X},{Y},{Z}", @event.X, @event.Y, @event.Z);
|
||||
|
||||
return HookResult.Continue;
|
||||
});
|
||||
@@ -193,7 +226,7 @@ namespace TestPlugin
|
||||
{
|
||||
// Grab all cs_player_controller entities and set their cash value to $1337.
|
||||
var playerEntities = Utilities.GetPlayers();
|
||||
Log($"cs_player_controller count: {playerEntities.Count()}");
|
||||
Logger.LogInformation($"cs_player_controller count: {playerEntities.Count()}");
|
||||
|
||||
foreach (var player in playerEntities)
|
||||
{
|
||||
@@ -204,7 +237,7 @@ namespace TestPlugin
|
||||
// Grab everything starting with cs_, but we'll only mainpulate cs_gamerules.
|
||||
// Note: this iterates through all entities, so is an expensive operation.
|
||||
var csEntities = Utilities.FindAllEntitiesByDesignerName<CBaseEntity>("cs_");
|
||||
Log($"Amount of cs_* entities: {csEntities.Count()}");
|
||||
Logger.LogInformation("Amount of cs_* entities: {Count}", csEntities.Count());
|
||||
|
||||
foreach (var entity in csEntities)
|
||||
{
|
||||
@@ -220,15 +253,18 @@ namespace TestPlugin
|
||||
private void SetupListeners()
|
||||
{
|
||||
// Hook global listeners defined by CounterStrikeSharp
|
||||
RegisterListener<Listeners.OnMapStart>(mapName => { Log($"Map {mapName} has started!"); });
|
||||
RegisterListener<Listeners.OnMapEnd>(() => { Log($"Map has ended."); });
|
||||
RegisterListener<Listeners.OnMapStart>(mapName =>
|
||||
{
|
||||
Logger.LogInformation("Map {Map} has started!", mapName);
|
||||
});
|
||||
RegisterListener<Listeners.OnMapEnd>(() => { Logger.LogInformation($"Map has ended."); });
|
||||
RegisterListener<Listeners.OnClientConnect>((index, name, ip) =>
|
||||
{
|
||||
Log($"Client {name} from {ip} has connected!");
|
||||
Logger.LogInformation("Client {Name} from {Ip} has connected!", name, ip);
|
||||
});
|
||||
RegisterListener<Listeners.OnClientAuthorized>((index, id) =>
|
||||
{
|
||||
Log($"Client {index} with address {id}");
|
||||
Logger.LogInformation("Client {Index} with address {Id}", index, id);
|
||||
});
|
||||
|
||||
RegisterListener<Listeners.OnEntitySpawned>(entity =>
|
||||
@@ -245,7 +281,8 @@ namespace TestPlugin
|
||||
projectile.SmokeColor.X = Random.Shared.NextSingle() * 255.0f;
|
||||
projectile.SmokeColor.X = Random.Shared.NextSingle() * 255.0f;
|
||||
projectile.SmokeColor.X = Random.Shared.NextSingle() * 255.0f;
|
||||
Log($"Smoke grenade spawned with color {projectile.SmokeColor}");
|
||||
Logger.LogInformation("Smoke grenade spawned with color {SmokeColor}",
|
||||
projectile.SmokeColor);
|
||||
});
|
||||
return;
|
||||
case "flashbang_projectile":
|
||||
@@ -298,8 +335,9 @@ namespace TestPlugin
|
||||
(player, info) =>
|
||||
{
|
||||
if (player == null) return;
|
||||
Log(
|
||||
$"CounterStrikeSharp - a test command was called by {new SteamID(player.SteamID).SteamId2} with {info.ArgString}");
|
||||
Logger.LogInformation(
|
||||
"CounterStrikeSharp - a test command was called by {SteamID2} with {Arguments}",
|
||||
((SteamID)player.SteamID).SteamId2, info.ArgString);
|
||||
});
|
||||
|
||||
AddCommand("css_changeteam", "change team", (player, info) =>
|
||||
@@ -319,7 +357,8 @@ namespace TestPlugin
|
||||
// Listens for any client use of the command `jointeam`.
|
||||
AddCommandListener("jointeam", (player, info) =>
|
||||
{
|
||||
Log($"{player.PlayerName} just did a jointeam (pre) [{info.ArgString}]");
|
||||
Logger.LogInformation("{PlayerName} just did a jointeam (pre) [{ArgString}]", player.PlayerName,
|
||||
info.ArgString);
|
||||
|
||||
return HookResult.Continue;
|
||||
});
|
||||
@@ -328,7 +367,7 @@ namespace TestPlugin
|
||||
[GameEventHandler]
|
||||
public HookResult OnPlayerConnect(EventPlayerConnect @event, GameEventInfo info)
|
||||
{
|
||||
Log($"Player {@event.Name} has connected! (post)");
|
||||
Logger.LogInformation("Player {Name} has connected! (post)", @event.Name);
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
@@ -336,7 +375,7 @@ namespace TestPlugin
|
||||
[GameEventHandler(HookMode.Pre)]
|
||||
public HookResult OnPlayerConnectPre(EventPlayerConnect @event, GameEventInfo info)
|
||||
{
|
||||
Log($"Player {@event.Name} has connected! (pre)");
|
||||
Logger.LogInformation("Player {Name} has connected! (pre)", @event.Name);
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
@@ -379,15 +418,14 @@ 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_pause", "Pause Game")]
|
||||
public void OnCommandPause(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
Log("Pause");
|
||||
Logger.LogInformation("Pause");
|
||||
}
|
||||
|
||||
[ConsoleCommand("css_give", "Give named item")]
|
||||
@@ -400,17 +438,10 @@ namespace TestPlugin
|
||||
|
||||
private HookResult GenericEventHandler<T>(T @event, GameEventInfo info) where T : GameEvent
|
||||
{
|
||||
Log($"Event found {@event.Handle:X}, event name: {@event.EventName} dont broadcast: {info.DontBroadcast}");
|
||||
Logger.LogInformation("Event found {Pointer:X}, event name: {EventName}, dont broadcast: {DontBroadcast}",
|
||||
@event.Handle, @event.EventName, info.DontBroadcast);
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
private void Log(string message)
|
||||
{
|
||||
Console.BackgroundColor = ConsoleColor.DarkGray;
|
||||
Console.ForegroundColor = ConsoleColor.DarkMagenta;
|
||||
Console.WriteLine(message);
|
||||
Console.ResetColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
79
src/core/coreconfig.cpp
Normal file
79
src/core/coreconfig.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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/>. *
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include "core/log.h"
|
||||
#include "core/coreconfig.h"
|
||||
|
||||
namespace counterstrikesharp {
|
||||
|
||||
CCoreConfig::CCoreConfig(const std::string& path) { m_sPath = path; }
|
||||
|
||||
CCoreConfig::~CCoreConfig() = default;
|
||||
|
||||
bool CCoreConfig::Init(char* conf_error, int conf_error_size)
|
||||
{
|
||||
std::ifstream ifs(m_sPath);
|
||||
|
||||
if (!ifs) {
|
||||
V_snprintf(conf_error, conf_error_size, "CoreConfig file not found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_json = json::parse(ifs);
|
||||
|
||||
try {
|
||||
PublicChatTrigger = m_json["PublicChatTrigger"];
|
||||
SilentChatTrigger = m_json["SilentChatTrigger"];
|
||||
FollowCS2ServerGuidelines = m_json["FollowCS2ServerGuidelines"];
|
||||
} catch (const std::exception& ex) {
|
||||
V_snprintf(conf_error, conf_error_size, "Failed to parse CoreConfig file: %s", ex.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string CCoreConfig::GetPath() const
|
||||
{
|
||||
return m_sPath;
|
||||
}
|
||||
|
||||
bool CCoreConfig::IsTriggerInternal(std::vector<std::string> triggers, const std::string& message, std::string& prefix) const
|
||||
{
|
||||
for (std::string& trigger : triggers)
|
||||
{
|
||||
if (message.rfind(trigger, 0) == 0)
|
||||
{
|
||||
prefix = trigger;
|
||||
CSSHARP_CORE_TRACE("Trigger found, prefix is {}", prefix);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CCoreConfig::IsSilentChatTrigger(const std::string& message, std::string& prefix) const
|
||||
{
|
||||
return IsTriggerInternal(SilentChatTrigger, message, prefix);
|
||||
}
|
||||
|
||||
bool CCoreConfig::IsPublicChatTrigger(const std::string& message, std::string& prefix) const
|
||||
{
|
||||
return IsTriggerInternal(PublicChatTrigger, message, prefix);
|
||||
}
|
||||
} // namespace counterstrikesharp
|
||||
50
src/core/coreconfig.h
Normal file
50
src/core/coreconfig.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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/>. *
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/globals.h"
|
||||
#include <string>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace counterstrikesharp {
|
||||
|
||||
class CCoreConfig
|
||||
{
|
||||
public:
|
||||
std::vector<std::string> PublicChatTrigger = { std::string("!") };
|
||||
std::vector<std::string> SilentChatTrigger = { std::string("/") };
|
||||
bool FollowCS2ServerGuidelines = true;
|
||||
|
||||
using json = nlohmann::json;
|
||||
CCoreConfig(const std::string& path);
|
||||
~CCoreConfig();
|
||||
|
||||
bool Init(char* conf_error, int conf_error_size);
|
||||
const std::string GetPath() const;
|
||||
|
||||
bool IsSilentChatTrigger(const std::string& message, std::string& prefix) const;
|
||||
bool IsPublicChatTrigger(const std::string& message, std::string& prefix) const;
|
||||
|
||||
private:
|
||||
bool IsTriggerInternal(std::vector<std::string> triggers, const std::string& message, std::string& prefix) const;
|
||||
|
||||
private:
|
||||
std::string m_sPath;
|
||||
json m_json;
|
||||
};
|
||||
|
||||
} // namespace counterstrikesharp
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "const.h"
|
||||
#include "utils/virtual.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
#include <type_traits>
|
||||
|
||||
@@ -54,6 +56,45 @@ inline uint64_t hash_64_fnv1a_const(const char *str, const uint64_t value = val_
|
||||
}
|
||||
|
||||
namespace schema {
|
||||
static std::vector<std::string> CS2BadList = {
|
||||
"m_bIsValveDS",
|
||||
"m_bIsQuestEligible",
|
||||
"m_iItemDefinitionIndex", // in unmanaged this cannot be set.
|
||||
"m_iEntityLevel",
|
||||
"m_iItemIDHigh",
|
||||
"m_iItemIDLow",
|
||||
"m_iAccountID",
|
||||
"m_iEntityQuality",
|
||||
|
||||
"m_bInitialized",
|
||||
"m_szCustomName",
|
||||
"m_iAttributeDefinitionIndex",
|
||||
"m_iRawValue32",
|
||||
"m_iRawInitialValue32",
|
||||
"m_flValue", // MNetworkAlias "m_iRawValue32"
|
||||
"m_flInitialValue", // MNetworkAlias "m_iRawInitialValue32"
|
||||
"m_bSetBonus",
|
||||
"m_nRefundableCurrency",
|
||||
|
||||
"m_OriginalOwnerXuidLow",
|
||||
"m_OriginalOwnerXuidHigh",
|
||||
|
||||
"m_nFallbackPaintKit",
|
||||
"m_nFallbackSeed",
|
||||
"m_flFallbackWear",
|
||||
"m_nFallbackStatTrak",
|
||||
|
||||
"m_iCompetitiveWins",
|
||||
"m_iCompetitiveRanking",
|
||||
"m_iCompetitiveRankType",
|
||||
"m_iCompetitiveRankingPredicted_Win",
|
||||
"m_iCompetitiveRankingPredicted_Loss",
|
||||
"m_iCompetitiveRankingPredicted_Tie",
|
||||
|
||||
"m_nActiveCoinRank",
|
||||
"m_nMusicID",
|
||||
};
|
||||
|
||||
int16_t FindChainOffset(const char *className);
|
||||
SchemaKey GetOffset(const char *className, uint32_t classKey, const char *memberName, uint32_t memberKey);
|
||||
} // namespace schema
|
||||
|
||||
@@ -32,41 +32,47 @@
|
||||
#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;
|
||||
case CONV_CDECL:
|
||||
return DC_CALL_C_DEFAULT;
|
||||
case CONV_THISCALL:
|
||||
case CONV_CUSTOM:
|
||||
return -1;
|
||||
case CONV_CDECL:
|
||||
return DC_CALL_C_DEFAULT;
|
||||
case CONV_THISCALL:
|
||||
#ifdef _WIN32
|
||||
return DC_CALL_C_X86_WIN32_THIS_MS;
|
||||
return DC_CALL_C_X86_WIN32_THIS_MS;
|
||||
#else
|
||||
return DC_CALL_C_X86_WIN32_THIS_GNU;
|
||||
return DC_CALL_C_X86_WIN32_THIS_GNU;
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
case CONV_STDCALL:
|
||||
return DC_CALL_C_X86_WIN32_STD;
|
||||
case CONV_FASTCALL:
|
||||
return DC_CALL_C_X86_WIN32_FAST_MS;
|
||||
case CONV_STDCALL:
|
||||
return DC_CALL_C_X86_WIN32_STD;
|
||||
case CONV_FASTCALL:
|
||||
return DC_CALL_C_X86_WIN32_FAST_MS;
|
||||
#endif
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -129,148 +122,195 @@ void ValveFunction::Call(ScriptContext& script_context, int offset) {
|
||||
for (size_t i = 0; i < m_Args.size(); i++) {
|
||||
int contextIndex = i + offset;
|
||||
switch (m_Args[i]) {
|
||||
case DATA_TYPE_BOOL:
|
||||
dcArgBool(g_pCallVM, script_context.GetArgument<bool>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_CHAR:
|
||||
dcArgChar(g_pCallVM, script_context.GetArgument<char>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_UCHAR:
|
||||
dcArgChar(g_pCallVM, script_context.GetArgument<unsigned char>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_SHORT:
|
||||
dcArgShort(g_pCallVM, script_context.GetArgument<short>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_USHORT:
|
||||
dcArgShort(g_pCallVM, script_context.GetArgument<unsigned short>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_INT:
|
||||
dcArgInt(g_pCallVM, script_context.GetArgument<int>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_UINT:
|
||||
dcArgInt(g_pCallVM, script_context.GetArgument<unsigned int>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_LONG:
|
||||
dcArgLong(g_pCallVM, script_context.GetArgument<long>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_ULONG:
|
||||
dcArgLong(g_pCallVM, script_context.GetArgument<unsigned long>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_LONG_LONG:
|
||||
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));
|
||||
break;
|
||||
case DATA_TYPE_FLOAT:
|
||||
dcArgFloat(g_pCallVM, script_context.GetArgument<float>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_DOUBLE:
|
||||
dcArgDouble(g_pCallVM, script_context.GetArgument<double>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_POINTER:
|
||||
dcArgPointer(g_pCallVM, script_context.GetArgument<void*>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_STRING:
|
||||
dcArgPointer(g_pCallVM,
|
||||
(void*)script_context.GetArgument<const char*>(contextIndex));
|
||||
break;
|
||||
default:
|
||||
assert(!"Unknown function parameter type!");
|
||||
break;
|
||||
case DATA_TYPE_BOOL:
|
||||
dcArgBool(g_pCallVM, script_context.GetArgument<bool>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_CHAR:
|
||||
dcArgChar(g_pCallVM, script_context.GetArgument<char>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_UCHAR:
|
||||
dcArgChar(g_pCallVM, script_context.GetArgument<unsigned char>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_SHORT:
|
||||
dcArgShort(g_pCallVM, script_context.GetArgument<short>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_USHORT:
|
||||
dcArgShort(g_pCallVM, script_context.GetArgument<unsigned short>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_INT:
|
||||
dcArgInt(g_pCallVM, script_context.GetArgument<int>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_UINT:
|
||||
dcArgInt(g_pCallVM, script_context.GetArgument<unsigned int>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_LONG:
|
||||
dcArgLong(g_pCallVM, script_context.GetArgument<long>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_ULONG:
|
||||
dcArgLong(g_pCallVM, script_context.GetArgument<unsigned long>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_LONG_LONG:
|
||||
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));
|
||||
break;
|
||||
case DATA_TYPE_FLOAT:
|
||||
dcArgFloat(g_pCallVM, script_context.GetArgument<float>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_DOUBLE:
|
||||
dcArgDouble(g_pCallVM, script_context.GetArgument<double>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_POINTER:
|
||||
dcArgPointer(g_pCallVM, script_context.GetArgument<void*>(contextIndex));
|
||||
break;
|
||||
case DATA_TYPE_STRING:
|
||||
dcArgPointer(g_pCallVM, (void*)script_context.GetArgument<const char*>(contextIndex));
|
||||
break;
|
||||
default:
|
||||
assert(!"Unknown function parameter type!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (m_eReturnType) {
|
||||
case DATA_TYPE_VOID:
|
||||
CallHelperVoid(g_pCallVM, m_ulAddr);
|
||||
break;
|
||||
case DATA_TYPE_BOOL:
|
||||
script_context.SetResult(CallHelper<bool>(dcCallBool, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_CHAR:
|
||||
script_context.SetResult(CallHelper<char>(dcCallChar, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_UCHAR:
|
||||
script_context.SetResult(CallHelper<unsigned char>(dcCallChar, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_SHORT:
|
||||
script_context.SetResult(CallHelper<short>(dcCallShort, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_USHORT:
|
||||
script_context.SetResult(CallHelper<unsigned short>(dcCallShort, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_INT:
|
||||
script_context.SetResult(CallHelper<int>(dcCallInt, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_UINT:
|
||||
script_context.SetResult(CallHelper<unsigned int>(dcCallInt, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_LONG:
|
||||
script_context.SetResult(CallHelper<long>(dcCallLong, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_ULONG:
|
||||
script_context.SetResult(CallHelper<unsigned long>(dcCallLong, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_LONG_LONG:
|
||||
script_context.SetResult(CallHelper<long long>(dcCallLongLong, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_ULONG_LONG:
|
||||
script_context.SetResult(
|
||||
CallHelper<unsigned long long>(dcCallLongLong, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_FLOAT:
|
||||
script_context.SetResult(CallHelper<float>(dcCallFloat, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_DOUBLE:
|
||||
script_context.SetResult(CallHelper<double>(dcCallDouble, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_POINTER:
|
||||
script_context.SetResult(CallHelper<void*>(dcCallPointer, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_STRING:
|
||||
script_context.SetResult(CallHelper<const char*>(dcCallPointer, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
default:
|
||||
assert(!"Unknown function return type!");
|
||||
break;
|
||||
case DATA_TYPE_VOID:
|
||||
CallHelperVoid(g_pCallVM, m_ulAddr);
|
||||
break;
|
||||
case DATA_TYPE_BOOL:
|
||||
script_context.SetResult(CallHelper<bool>(dcCallBool, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_CHAR:
|
||||
script_context.SetResult(CallHelper<char>(dcCallChar, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_UCHAR:
|
||||
script_context.SetResult(CallHelper<unsigned char>(dcCallChar, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_SHORT:
|
||||
script_context.SetResult(CallHelper<short>(dcCallShort, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_USHORT:
|
||||
script_context.SetResult(CallHelper<unsigned short>(dcCallShort, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_INT:
|
||||
script_context.SetResult(CallHelper<int>(dcCallInt, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_UINT:
|
||||
script_context.SetResult(CallHelper<unsigned int>(dcCallInt, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_LONG:
|
||||
script_context.SetResult(CallHelper<long>(dcCallLong, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_ULONG:
|
||||
script_context.SetResult(CallHelper<unsigned long>(dcCallLong, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_LONG_LONG:
|
||||
script_context.SetResult(CallHelper<long long>(dcCallLongLong, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_ULONG_LONG:
|
||||
script_context.SetResult(
|
||||
CallHelper<unsigned long long>(dcCallLongLong, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_FLOAT:
|
||||
script_context.SetResult(CallHelper<float>(dcCallFloat, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_DOUBLE:
|
||||
script_context.SetResult(CallHelper<double>(dcCallDouble, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_POINTER:
|
||||
script_context.SetResult(CallHelper<void*>(dcCallPointer, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
case DATA_TYPE_STRING:
|
||||
script_context.SetResult(CallHelper<const char*>(dcCallPointer, g_pCallVM, m_ulAddr));
|
||||
break;
|
||||
default:
|
||||
assert(!"Unknown function return type!");
|
||||
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;
|
||||
//}
|
||||
|
||||
} // namespace counterstrikesharp
|
||||
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
|
||||
@@ -23,48 +23,48 @@
|
||||
#include <public/game/server/iplayerinfo.h>
|
||||
#include <public/entity2/entitysystem.h>
|
||||
|
||||
|
||||
namespace counterstrikesharp {
|
||||
|
||||
namespace modules {
|
||||
CModule *engine = nullptr;
|
||||
CModule *tier0 = nullptr;
|
||||
CModule *server = nullptr;
|
||||
CModule *schemasystem = nullptr;
|
||||
CModule *vscript = nullptr;
|
||||
} // namespace modules
|
||||
CModule* engine = nullptr;
|
||||
CModule* tier0 = nullptr;
|
||||
CModule* server = nullptr;
|
||||
CModule* schemasystem = nullptr;
|
||||
CModule* vscript = nullptr;
|
||||
} // namespace modules
|
||||
|
||||
namespace globals {
|
||||
IVEngineServer *engine = nullptr;
|
||||
IGameEventManager2 *gameEventManager = nullptr;
|
||||
IGameEventSystem *gameEventSystem = nullptr;
|
||||
IPlayerInfoManager *playerinfoManager = nullptr;
|
||||
IBotManager *botManager = nullptr;
|
||||
IServerPluginHelpers *helpers = nullptr;
|
||||
IUniformRandomStream *randomStream = nullptr;
|
||||
IEngineTrace *engineTrace = nullptr;
|
||||
IEngineSound *engineSound = nullptr;
|
||||
INetworkStringTableContainer *netStringTables = nullptr;
|
||||
CGlobalVars *globalVars = nullptr;
|
||||
IFileSystem *fileSystem = nullptr;
|
||||
IServerGameDLL *serverGameDll = nullptr;
|
||||
IServerGameClients *serverGameClients = nullptr;
|
||||
INetworkServerService *networkServerService = nullptr;
|
||||
IServerTools *serverTools = nullptr;
|
||||
IPhysics *physics = nullptr;
|
||||
IPhysicsCollision *physicsCollision = nullptr;
|
||||
IPhysicsSurfaceProps *physicsSurfaceProps = nullptr;
|
||||
IMDLCache *modelCache = nullptr;
|
||||
IVoiceServer *voiceServer = nullptr;
|
||||
IVEngineServer* engine = nullptr;
|
||||
IGameEventManager2* gameEventManager = nullptr;
|
||||
IGameEventSystem* gameEventSystem = nullptr;
|
||||
IPlayerInfoManager* playerinfoManager = nullptr;
|
||||
IBotManager* botManager = nullptr;
|
||||
IServerPluginHelpers* helpers = nullptr;
|
||||
IUniformRandomStream* randomStream = nullptr;
|
||||
IEngineTrace* engineTrace = nullptr;
|
||||
IEngineSound* engineSound = nullptr;
|
||||
INetworkStringTableContainer* netStringTables = nullptr;
|
||||
CGlobalVars* globalVars = nullptr;
|
||||
IFileSystem* fileSystem = nullptr;
|
||||
IServerGameDLL* serverGameDll = nullptr;
|
||||
IServerGameClients* serverGameClients = nullptr;
|
||||
INetworkServerService* networkServerService = nullptr;
|
||||
IServerTools* serverTools = nullptr;
|
||||
IPhysics* physics = nullptr;
|
||||
IPhysicsCollision* physicsCollision = nullptr;
|
||||
IPhysicsSurfaceProps* physicsSurfaceProps = nullptr;
|
||||
IMDLCache* modelCache = nullptr;
|
||||
IVoiceServer* voiceServer = nullptr;
|
||||
CDotNetManager dotnetManager;
|
||||
ICvar *cvars = nullptr;
|
||||
ISource2Server *server = nullptr;
|
||||
CGlobalEntityList *globalEntityList = nullptr;
|
||||
CounterStrikeSharpMMPlugin *mmPlugin = nullptr;
|
||||
ICvar* cvars = nullptr;
|
||||
ISource2Server* server = nullptr;
|
||||
CGlobalEntityList* globalEntityList = nullptr;
|
||||
CounterStrikeSharpMMPlugin* mmPlugin = nullptr;
|
||||
SourceHook::Impl::CSourceHookImpl source_hook_impl;
|
||||
SourceHook::ISourceHook *source_hook = &source_hook_impl;
|
||||
ISmmAPI *ismm = nullptr;
|
||||
SourceHook::ISourceHook* source_hook = &source_hook_impl;
|
||||
ISmmAPI* ismm = nullptr;
|
||||
CGameEntitySystem* entitySystem = nullptr;
|
||||
CCoreConfig* coreConfig = nullptr;
|
||||
CGameConfig* gameConfig = nullptr;
|
||||
|
||||
// Custom Managers
|
||||
@@ -77,7 +77,10 @@ EntityManager entityManager;
|
||||
ChatManager chatManager;
|
||||
ServerManager serverManager;
|
||||
|
||||
void Initialize() {
|
||||
GetLegacyGameEventListener_t* GetLegacyGameEventListener = nullptr;
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
modules::engine = new modules::CModule(ROOTBIN, "engine2");
|
||||
modules::tier0 = new modules::CModule(ROOTBIN, "tier0");
|
||||
modules::server = new modules::CModule(GAMEBIN, "server");
|
||||
@@ -88,14 +91,18 @@ void Initialize() {
|
||||
|
||||
entitySystem = interfaces::pGameResourceServiceServer->GetGameEntitySystem();
|
||||
|
||||
GetLegacyGameEventListener = reinterpret_cast<GetLegacyGameEventListener_t*>(modules::server->FindSignature(
|
||||
globals::gameConfig->GetSignature("LegacyGameEventListener")));
|
||||
|
||||
if (int offset = -1; (offset = gameConfig->GetOffset("GameEventManager")) != -1) {
|
||||
gameEventManager = (IGameEventManager2*)(CALL_VIRTUAL(uintptr_t, offset, server) - 8);
|
||||
}
|
||||
}
|
||||
|
||||
int source_hook_pluginid = 0;
|
||||
CGlobalVars *getGlobalVars() {
|
||||
INetworkGameServer *server = networkServerService->GetIGameServer();
|
||||
CGlobalVars* getGlobalVars()
|
||||
{
|
||||
INetworkGameServer* server = networkServerService->GetIGameServer();
|
||||
|
||||
if (!server) {
|
||||
return nullptr;
|
||||
@@ -104,5 +111,5 @@ CGlobalVars *getGlobalVars() {
|
||||
return networkServerService->GetIGameServer()->GetGlobals();
|
||||
}
|
||||
|
||||
} // namespace globals
|
||||
} // namespace counterstrikesharp
|
||||
} // namespace globals
|
||||
} // namespace counterstrikesharp
|
||||
|
||||
@@ -33,6 +33,7 @@ class ICvar;
|
||||
class IGameEventSystem;
|
||||
class CounterStrikeSharpMMPlugin;
|
||||
class CGameEntitySystem;
|
||||
class IGameEventListener2;
|
||||
|
||||
namespace counterstrikesharp {
|
||||
class EntityListener;
|
||||
@@ -49,6 +50,7 @@ class HookManager;
|
||||
class EntityManager;
|
||||
class ChatManager;
|
||||
class ServerManager;
|
||||
class CCoreConfig;
|
||||
class CGameConfig;
|
||||
|
||||
namespace globals {
|
||||
@@ -99,8 +101,13 @@ extern int source_hook_pluginid;
|
||||
extern IGameEventSystem *gameEventSystem;
|
||||
extern CounterStrikeSharpMMPlugin *mmPlugin;
|
||||
extern ISmmAPI *ismm;
|
||||
extern CCoreConfig* coreConfig;
|
||||
extern CGameConfig* gameConfig;
|
||||
|
||||
typedef IGameEventListener2 *GetLegacyGameEventListener_t(CPlayerSlot slot);
|
||||
|
||||
extern GetLegacyGameEventListener_t* GetLegacyGameEventListener;
|
||||
|
||||
void Initialize();
|
||||
// Should only be called within the active game loop (i e map should be loaded
|
||||
// and active) otherwise that'll be nullptr!
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <public/eiface.h>
|
||||
#include "core/memory.h"
|
||||
#include "core/log.h"
|
||||
#include "core/coreconfig.h"
|
||||
#include "core/gameconfig.h"
|
||||
|
||||
#include <funchook.h>
|
||||
@@ -56,9 +57,6 @@ void ChatManager::OnShutdown() {}
|
||||
void DetourHostSay(CBaseEntity* pController, CCommand& args, bool teamonly, int unk1,
|
||||
const char* unk2)
|
||||
{
|
||||
CCommand newArgs;
|
||||
newArgs.Tokenize(args.Arg(1));
|
||||
|
||||
if (pController) {
|
||||
auto pEvent = globals::gameEventManager->CreateEvent("player_chat", true);
|
||||
if (pEvent) {
|
||||
@@ -70,48 +68,53 @@ void DetourHostSay(CBaseEntity* pController, CCommand& args, bool teamonly, int
|
||||
}
|
||||
}
|
||||
|
||||
if (*args[1] == '/' || *args[1] == '!') {
|
||||
globals::chatManager.OnSayCommandPost(pController, newArgs);
|
||||
return;
|
||||
std::string prefix;
|
||||
bool bSilent = globals::coreConfig->IsSilentChatTrigger(args[1], prefix);
|
||||
bool bCommand = globals::coreConfig->IsPublicChatTrigger(args[1], prefix) || bSilent;
|
||||
|
||||
if (!bSilent) {
|
||||
m_pHostSay(pController, args, teamonly, unk1, unk2);
|
||||
}
|
||||
|
||||
m_pHostSay(pController, args, teamonly, unk1, unk2);
|
||||
if (bCommand)
|
||||
{
|
||||
char *pszMessage = (char *)(args.ArgS() + prefix.length() + 1);
|
||||
|
||||
// Trailing slashes are only removed if Host_Say has been called.
|
||||
if (bSilent)
|
||||
pszMessage[V_strlen(pszMessage) - 1] = 0;
|
||||
|
||||
CCommand args;
|
||||
args.Tokenize(pszMessage);
|
||||
|
||||
auto prefixedPhrase = std::string("css_") + args.Arg(0);
|
||||
auto bValidWithPrefix = globals::conCommandManager.IsValidValveCommand(prefixedPhrase.c_str());
|
||||
|
||||
if (bValidWithPrefix) {
|
||||
// Re-tokenize with a `css_` prefix if we have found that its a valid command.
|
||||
args.Tokenize(("css_" + std::string(pszMessage)).c_str());
|
||||
}
|
||||
|
||||
globals::chatManager.OnSayCommandPost(pController, args);
|
||||
}
|
||||
}
|
||||
|
||||
bool ChatManager::OnSayCommandPre(CBaseEntity* pController, CCommand& command) { return false; }
|
||||
|
||||
void ChatManager::OnSayCommandPost(CBaseEntity* pController, CCommand& command)
|
||||
{
|
||||
const char* args = command.ArgS();
|
||||
auto commandStr = command.Arg(0);
|
||||
|
||||
return InternalDispatch(pController, commandStr + 1, command);
|
||||
return InternalDispatch(pController, commandStr, command);
|
||||
}
|
||||
|
||||
void ChatManager::InternalDispatch(CBaseEntity* pPlayerController, const char* szTriggerPhase,
|
||||
CCommand& fullCommand)
|
||||
{
|
||||
auto ppArgV = new const char*[fullCommand.ArgC()];
|
||||
ppArgV[0] = strdup(szTriggerPhase);
|
||||
for (int i = 1; i < fullCommand.ArgC(); i++) {
|
||||
ppArgV[i] = fullCommand.Arg(i);
|
||||
}
|
||||
|
||||
auto prefixedPhrase = std::string("css_") + szTriggerPhase;
|
||||
|
||||
auto bValidWithPrefix = globals::conCommandManager.IsValidValveCommand(prefixedPhrase.c_str());
|
||||
|
||||
if (bValidWithPrefix) {
|
||||
ppArgV[0] = prefixedPhrase.c_str();
|
||||
}
|
||||
|
||||
CCommand commandCopy(fullCommand.ArgC(), ppArgV);
|
||||
|
||||
if (pPlayerController == nullptr) {
|
||||
globals::conCommandManager.ExecuteCommandCallbacks(
|
||||
commandCopy.Arg(0), CCommandContext(CommandTarget_t::CT_NO_TARGET, CPlayerSlot(-1)),
|
||||
commandCopy, HookMode::Pre);
|
||||
delete[] ppArgV;
|
||||
fullCommand.Arg(0), CCommandContext(CommandTarget_t::CT_NO_TARGET, CPlayerSlot(-1)),
|
||||
fullCommand, HookMode::Pre);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -119,8 +122,7 @@ void ChatManager::InternalDispatch(CBaseEntity* pPlayerController, const char* s
|
||||
auto slot = CPlayerSlot(index - 1);
|
||||
|
||||
globals::conCommandManager.ExecuteCommandCallbacks(
|
||||
commandCopy.Arg(0), CCommandContext(CommandTarget_t::CT_NO_TARGET, slot), commandCopy,
|
||||
fullCommand.Arg(0), CCommandContext(CommandTarget_t::CT_NO_TARGET, slot), fullCommand,
|
||||
HookMode::Pre);
|
||||
delete[] ppArgV;
|
||||
}
|
||||
} // namespace counterstrikesharp
|
||||
@@ -54,7 +54,6 @@ class ChatManager : public GlobalClass
|
||||
|
||||
bool OnSayCommandPre(CBaseEntity* pController, CCommand& args);
|
||||
void OnSayCommandPost(CBaseEntity* pController, CCommand& args);
|
||||
|
||||
private:
|
||||
void InternalDispatch(CBaseEntity* pPlayerController, const char* szTriggerPhrase,
|
||||
CCommand& pFullCommand);
|
||||
|
||||
@@ -364,7 +364,7 @@ HookResult ConCommandManager::ExecuteCommandCallbacks(const char* name, const CC
|
||||
auto thisResult = pCallback->ScriptContext().GetResult<HookResult>();
|
||||
|
||||
if (thisResult >= HookResult::Handled) {
|
||||
return result;
|
||||
return thisResult;
|
||||
} else if (thisResult > result) {
|
||||
result = thisResult;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -20,7 +20,9 @@ inline std::string GameDirectory() {
|
||||
return gameDirectory;
|
||||
}
|
||||
|
||||
inline std::string PluginDirectory() { return GameDirectory() + "/addons/counterstrikesharp"; }
|
||||
inline std::string GetRootDirectory() { return GameDirectory() + "/addons/counterstrikesharp"; }
|
||||
inline std::string PluginsDirectory() { return GameDirectory() + "/addons/counterstrikesharp/plugins"; }
|
||||
inline std::string ConfigsDirectory() { return GameDirectory() + "/addons/counterstrikesharp/configs"; }
|
||||
inline std::string GamedataDirectory() { return GameDirectory() + "/addons/counterstrikesharp/gamedata"; }
|
||||
|
||||
} // namespace utils
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "core/global_listener.h"
|
||||
#include "core/log.h"
|
||||
#include "core/coreconfig.h"
|
||||
#include "core/gameconfig.h"
|
||||
#include "core/timer_system.h"
|
||||
#include "core/utils.h"
|
||||
@@ -83,6 +84,17 @@ 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");
|
||||
globals::coreConfig = new CCoreConfig(coreconfig_path);
|
||||
char coreconfig_error[255] = "";
|
||||
|
||||
if (!globals::coreConfig->Init(coreconfig_error, sizeof(coreconfig_error))) {
|
||||
CSSHARP_CORE_ERROR("Could not read \'{}\'. Error: {}", coreconfig_path, coreconfig_error);
|
||||
return false;
|
||||
}
|
||||
|
||||
CSSHARP_CORE_INFO("CoreConfig loaded.");
|
||||
|
||||
auto gamedata_path = std::string(utils::GamedataDirectory() + "/gamedata.json");
|
||||
globals::gameConfig = new CGameConfig(gamedata_path);
|
||||
char conf_error[255] = "";
|
||||
|
||||
@@ -96,7 +96,7 @@ void* get_export(void* h, const char* name)
|
||||
// Using the nethost library, discover the location of hostfxr and get exports
|
||||
bool load_hostfxr()
|
||||
{
|
||||
std::string base_dir = counterstrikesharp::utils::PluginDirectory();
|
||||
std::string base_dir = counterstrikesharp::utils::GetRootDirectory();
|
||||
namespace css = counterstrikesharp;
|
||||
#if _WIN32
|
||||
std::wstring buffer =
|
||||
@@ -164,7 +164,7 @@ CDotNetManager::~CDotNetManager() {}
|
||||
|
||||
bool CDotNetManager::Initialize()
|
||||
{
|
||||
const std::string base_dir = counterstrikesharp::utils::PluginDirectory();
|
||||
const std::string base_dir = counterstrikesharp::utils::GetRootDirectory();
|
||||
|
||||
CSSHARP_CORE_INFO("Loading .NET runtime...");
|
||||
|
||||
@@ -184,7 +184,7 @@ bool CDotNetManager::Initialize()
|
||||
std::string((base_dir + "/api/CounterStrikeSharp.API.runtimeconfig.json").c_str());
|
||||
CSSHARP_CORE_INFO("Loading CSS API, Runtime Config: {}", wide_str);
|
||||
#endif
|
||||
|
||||
|
||||
const auto load_assembly_and_get_function_pointer = get_dotnet_load_assembly(wide_str.c_str());
|
||||
if (load_assembly_and_get_function_pointer == nullptr) {
|
||||
CSSHARP_CORE_ERROR("Failed to load CSS API.");
|
||||
|
||||
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
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "core/managers/event_manager.h"
|
||||
#include "scripting/autonative.h"
|
||||
#include "igameevents.h"
|
||||
|
||||
|
||||
namespace counterstrikesharp {
|
||||
@@ -61,24 +62,21 @@ static void FireEvent(ScriptContext &script_context) {
|
||||
managed_game_events.erase(std::remove(managed_game_events.begin(), managed_game_events.end(), game_event), managed_game_events.end());
|
||||
}
|
||||
|
||||
// static void FireEventToClient(ScriptContext& script_context) {
|
||||
// auto game_event = script_context.GetArgument<IGameEvent*>(0);
|
||||
// int client = script_context.GetArgument<int>(1);
|
||||
// if (!game_event) {
|
||||
// script_context.ThrowNativeError("Invalid game event");
|
||||
// }
|
||||
//
|
||||
// auto base_entity = reinterpret_cast<CBaseEntityWrapper*>(ExcBaseEntityFromIndex(client));
|
||||
// if (!base_entity)
|
||||
// {
|
||||
// script_context.ThrowNativeError("Invalid client index");
|
||||
// }
|
||||
//
|
||||
// auto iclient = base_entity->GetIClient();
|
||||
// IGameEventListener2* game_client = (IGameEventListener2*)((intptr_t)iclient - sizeof(void*));
|
||||
//
|
||||
// game_client->FireGameEvent(game_event);
|
||||
// }
|
||||
|
||||
static void FireEventToClient(ScriptContext& script_context) {
|
||||
auto game_event = script_context.GetArgument<IGameEvent*>(0);
|
||||
int entityIndex = script_context.GetArgument<int>(1);
|
||||
if (!game_event) {
|
||||
script_context.ThrowNativeError("Invalid game event");
|
||||
}
|
||||
|
||||
IGameEventListener2* pListener = globals::GetLegacyGameEventListener(CPlayerSlot(entityIndex - 1));
|
||||
if (!pListener) {
|
||||
script_context.ThrowNativeError("Could not get player event listener");
|
||||
}
|
||||
|
||||
pListener->FireGameEvent(game_event);
|
||||
}
|
||||
|
||||
static const char *GetEventName(ScriptContext &script_context) {
|
||||
IGameEvent *game_event = script_context.GetArgument<IGameEvent *>(0);
|
||||
@@ -266,7 +264,7 @@ REGISTER_NATIVES(events, {
|
||||
ScriptEngine::RegisterNativeHandler("UNHOOK_EVENT", UnhookEvent);
|
||||
ScriptEngine::RegisterNativeHandler("CREATE_EVENT", CreateEvent);
|
||||
ScriptEngine::RegisterNativeHandler("FIRE_EVENT", FireEvent);
|
||||
// ScriptEngine::RegisterNativeHandler("FIRE_EVENT_TO_CLIENT", FireEventToClient);
|
||||
ScriptEngine::RegisterNativeHandler("FIRE_EVENT_TO_CLIENT", FireEventToClient);
|
||||
|
||||
ScriptEngine::RegisterNativeHandler("GET_EVENT_NAME", GetEventName);
|
||||
ScriptEngine::RegisterNativeHandler("GET_EVENT_BOOL", GetEventBool);
|
||||
|
||||
@@ -2,7 +2,7 @@ HOOK_EVENT: name:string, callback:func, isPost:bool -> void
|
||||
UNHOOK_EVENT: name:string, callback:func, isPost:bool -> void
|
||||
CREATE_EVENT: name:string, force:bool -> pointer
|
||||
FIRE_EVENT: gameEvent:pointer, dontBroadcast:bool -> void
|
||||
# FIRE_EVENT_TO_CLIENT: gameEvent:pointer, clientIndex:int -> void
|
||||
FIRE_EVENT_TO_CLIENT: gameEvent:pointer, clientIndex:int -> void
|
||||
GET_EVENT_NAME: gameEvent:pointer -> string
|
||||
GET_EVENT_BOOL: gameEvent:pointer, name:string -> bool
|
||||
GET_EVENT_INT: gameEvent:pointer, name:string -> int
|
||||
|
||||
@@ -24,11 +24,12 @@
|
||||
#include "core/log.h"
|
||||
|
||||
namespace counterstrikesharp {
|
||||
std::vector<ValveFunction *> m_managed_ptrs;
|
||||
std::vector<ValveFunction*> m_managed_ptrs;
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
for (size_t i = 0, j = 0; i < len; ++i, j += 4) {
|
||||
sscanf(str + j, "\\x%2hhx", &result[i]);
|
||||
@@ -38,24 +39,26 @@ byte *ConvertToByteArray(const char *str, size_t *outLength) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void *FindSignatureNative(ScriptContext &scriptContext) {
|
||||
auto moduleName = scriptContext.GetArgument<const char *>(0);
|
||||
auto bytesStr = scriptContext.GetArgument<const char *>(1);
|
||||
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);
|
||||
auto binary_name = script_context.GetArgument<const char*>(1);
|
||||
auto signature_hex_string = script_context.GetArgument<const char*>(2);
|
||||
auto num_arguments = script_context.GetArgument<int>(3);
|
||||
auto return_type = script_context.GetArgument<DataType_t>(4);
|
||||
|
||||
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,19 +70,21 @@ 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) {
|
||||
auto ptr = script_context.GetArgument<void *>(0);
|
||||
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);
|
||||
auto return_type = script_context.GetArgument<DataType_t>(3);
|
||||
|
||||
void **vtable = *(void ***)ptr;
|
||||
void** vtable = *(void***)ptr;
|
||||
if (!vtable) {
|
||||
script_context.ThrowNativeError("Failed to get the virtual function table.");
|
||||
return nullptr;
|
||||
@@ -99,8 +104,37 @@ ValveFunction *CreateVirtualFunction(ScriptContext &script_context) {
|
||||
return function;
|
||||
}
|
||||
|
||||
void ExecuteVirtualFunction(ScriptContext &script_context) {
|
||||
auto function = script_context.GetArgument<ValveFunction *>(0);
|
||||
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) {
|
||||
script_context.ThrowNativeError("Invalid function pointer");
|
||||
@@ -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,10 +164,10 @@ 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);
|
||||
})
|
||||
} // namespace counterstrikesharp
|
||||
} // namespace counterstrikesharp
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "core/log.h"
|
||||
#include "schema.h"
|
||||
#include "core/function.h"
|
||||
#include "core/coreconfig.h"
|
||||
|
||||
namespace counterstrikesharp {
|
||||
|
||||
@@ -122,6 +123,12 @@ void SetSchemaValueByName(ScriptContext& script_context)
|
||||
auto dataType = script_context.GetArgument<DataType_t>(1);
|
||||
auto className = script_context.GetArgument<const char*>(2);
|
||||
auto memberName = script_context.GetArgument<const char*>(3);
|
||||
|
||||
if (globals::coreConfig->FollowCS2ServerGuidelines && std::find(schema::CS2BadList.begin(), schema::CS2BadList.end(), memberName) != schema::CS2BadList.end()) {
|
||||
CSSHARP_CORE_ERROR("Cannot set '{}::{}' with \"FollowCS2ServerGuidelines\" option enabled.", className, memberName);
|
||||
return;
|
||||
}
|
||||
|
||||
auto classKey = hash_32_fnv1a_const(className);
|
||||
auto memberKey = hash_32_fnv1a_const(memberName);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user