Compare commits

...

11 Commits
v1.0.31 ... v50

Author SHA1 Message Date
Roflmuffin
414710d05c hotfix: wrap vfunc creation in try catch to prevent all vfuncs from erroring 2023-11-13 21:40:33 +10:00
Michael Wilson
b09c2b62c8 Improved Command Handling (#76) 2023-11-13 20:59:46 +10:00
Roflmuffin
31760518ed ci: fighting with the machines 2023-11-13 20:27:52 +10:00
Roflmuffin
6a160bcc3d ci: remove build number from PR checks 2023-11-13 20:25:49 +10:00
Roflmuffin
9c8e9db56e ci: set fallback build number for PRs 2023-11-13 20:23:46 +10:00
Roflmuffin
e2e0eab87d ci: run main pipeline skipping publish on PR 2023-11-13 20:19:37 +10:00
Nexd
43292bb1d2 feat: CBaseModelEntity_SetModel (#72) 2023-11-13 09:10:49 +10:00
Nexd
12c54cd4fc hotfix: deserializer couldn't call setter (#70) 2023-11-12 22:39:15 +10:00
Michael Wilson
e155a70873 Cross platform builds (#69) 2023-11-12 15:43:51 +10:00
Nexd
69d9b5d2c8 feat: Provide configuration standard for plugins (#67) 2023-11-12 14:25:06 +10:00
laper32
933fdf9d81 [Windows] feat: Windows support (#52) 2023-11-12 13:39:13 +10:00
67 changed files with 1647 additions and 1014 deletions

View File

@@ -5,94 +5,193 @@ on:
paths-ignore:
- 'docs/**'
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
BUILD_TYPE: Release
jobs:
build:
permissions:
contents: write
packages: write
runs-on: ubuntu-latest
container:
image: registry.gitlab.steamos.cloud/steamrt/sniper/sdk:latest
build_windows:
runs-on: windows-latest
steps:
- name: Prepare env
shell: bash
run: echo "GITHUB_SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_ENV
- name: Setup protobuf
shell: bash
run: sudo apt-get update && sudo apt install -y protobuf-compiler
- name: Visual Studio environment
shell: cmd
run: |
:: See https://github.com/microsoft/vswhere/wiki/Find-VC
for /f "usebackq delims=*" %%i in (`vswhere -latest -property installationPath`) do (
call "%%i"\Common7\Tools\vsdevcmd.bat -arch=x64 -host_arch=x64
)
:: Loop over all environment variables and make them global.
for /f "delims== tokens=1,2" %%a in ('set') do (
echo>>"%GITHUB_ENV%" %%a=%%b
)
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Generate build number
id: buildnumber
uses: onyxmueller/build-tag-number@v1
with:
token: ${{secrets.github_token}}
- uses: actions/setup-dotnet@v3
with:
dotnet-version: '7.0.x'
- run: |
dotnet publish -c Release /p:Version=1.0.${{ env.BUILD_NUMBER }} managed/CounterStrikeSharp.API
dotnet pack -c Release /p:Version=1.0.${{ env.BUILD_NUMBER }} managed/CounterStrikeSharp.API
- name: Configure CMake
run: cmake -B build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
- name: Build
# Build your program with the given configuration
run: cmake --build build --config ${{env.BUILD_TYPE}}
run: |
mkdir -p build
cd build
cmake -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ..
cmake --build . --config ${{env.BUILD_TYPE}} -- /m:16
- name: Clean build directory
run: |
mkdir -p build/addons/counterstrikesharp/bin/win64
mv build/${{env.BUILD_TYPE}}/*.dll build/addons/counterstrikesharp/bin/win64
mkdir build/output/
mv build/addons build/output
- uses: actions/upload-artifact@v3
with:
name: counterstrikesharp-build-windows-${{ env.GITHUB_SHA_SHORT }}
path: build/output/
build_linux:
runs-on: ubuntu-latest
# Could not figure out how to run in a container only on some matrix paths, so I've split it out into its own build.
container:
image: registry.gitlab.steamos.cloud/steamrt/sniper/sdk:latest
steps:
- name: Prepare env
shell: bash
run: echo "GITHUB_SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_ENV
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Build
run: |
mkdir -p build
cd build
cmake -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ..
cmake --build . --config ${{env.BUILD_TYPE}} -- -j16
- name: Clean build directory
run: |
mkdir build/output/
mv build/addons build/output
- name: Add API to Artifacts
run: |
mkdir -p build/output/addons/counterstrikesharp/api
cp -r managed/CounterStrikeSharp.API/bin/Release/net7.0/publish/* build/output/addons/counterstrikesharp/api
- uses: actions/upload-artifact@v3
with:
name: counterstrikesharp-build-linux-${{ env.GITHUB_SHA_SHORT }}
path: build/output/
build_managed:
permissions:
contents: write
runs-on: ubuntu-latest
outputs:
buildnumber: ${{ steps.buildnumber.outputs.build_number }}
steps:
- name: Prepare env
shell: bash
run: echo "GITHUB_SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_ENV
- name: Fallback build number
if: github.event_name == 'pull_request'
shell: bash
run: echo "BUILD_NUMBER=0" >> $GITHUB_ENV
# We don't need expensive submodules for the managed side.
- uses: actions/checkout@v3
- name: Generate build number
if: github.event_name == 'push'
id: buildnumber
uses: onyxmueller/build-tag-number@v1
with:
token: ${{secrets.github_token}}
- name: Build runtime v${{ env.BUILD_NUMBER }}
uses: actions/setup-dotnet@v3
with:
dotnet-version: '7.0.x'
- run: |
dotnet publish -c Release /p:Version=1.0.${{ env.BUILD_NUMBER }} managed/CounterStrikeSharp.API
dotnet pack -c Release /p:Version=1.0.${{ env.BUILD_NUMBER }} managed/CounterStrikeSharp.API
- uses: actions/upload-artifact@v3
with:
name: counterstrikesharp-build-${{ env.BUILD_NUMBER }}-${{ env.GITHUB_SHA_SHORT }}
path: build/output/
name: counterstrikesharp-build-api-${{ env.GITHUB_SHA_SHORT }}
path: managed/CounterStrikeSharp.API/bin/Release
- name: Zip CounterStrikeSharp Build
run: (cd build/output && zip -qq -r ../../counterstrikesharp-build-${{ env.BUILD_NUMBER }}-${{ env.GITHUB_SHA_SHORT }}.zip *)
publish:
if: github.event_name == 'push'
permissions:
contents: write
needs: [ "build_linux", "build_windows", "build_managed" ]
runs-on: ubuntu-latest
steps:
- name: Prepare env
shell: bash
run: echo "GITHUB_SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_ENV
- uses: actions/download-artifact@v3
with:
name: counterstrikesharp-build-windows-${{ env.GITHUB_SHA_SHORT }}
path: build/windows
- uses: actions/download-artifact@v3
with:
name: counterstrikesharp-build-linux-${{ env.GITHUB_SHA_SHORT }}
path: build/linux
- uses: actions/download-artifact@v3
with:
name: counterstrikesharp-build-api-${{ env.GITHUB_SHA_SHORT }}
path: build/api
# TODO: This stuff should really be in a matrix
- name: Add API to Artifacts
run: |
mkdir -p build/linux/addons/counterstrikesharp/api
mkdir -p build/windows/addons/counterstrikesharp/api
cp -r build/api/net7.0/publish/* build/linux/addons/counterstrikesharp/api
cp -r build/api/net7.0/publish/* build/windows/addons/counterstrikesharp/api
- name: Zip Builds
run: |
(cd build/linux && zip -qq -r ../../counterstrikesharp-build-${{ needs.build_managed.outputs.buildnumber }}-linux-${{ env.GITHUB_SHA_SHORT }}.zip *)
(cd build/windows && zip -qq -r ../../counterstrikesharp-build-${{ needs.build_managed.outputs.buildnumber }}-windows-${{ env.GITHUB_SHA_SHORT }}.zip *)
- name: Add dotnet runtime
run: |
mkdir -p build/output/addons/counterstrikesharp/dotnet
mkdir -p build/linux/addons/counterstrikesharp/dotnet
curl -s -L https://download.visualstudio.microsoft.com/download/pr/dc2c0a53-85a8-4fda-a283-fa28adb5fbe2/8ccade5bc400a5bb40cd9240f003b45c/aspnetcore-runtime-7.0.11-linux-x64.tar.gz \
| tar xvz -C build/output/addons/counterstrikesharp/dotnet
mv build/output/addons/counterstrikesharp/dotnet/shared/Microsoft.NETCore.App/7.0.11/* build/output/addons/counterstrikesharp/dotnet/shared/Microsoft.NETCore.App/
| tar xvz -C build/linux/addons/counterstrikesharp/dotnet
mv build/linux/addons/counterstrikesharp/dotnet/shared/Microsoft.NETCore.App/7.0.11/* build/linux/addons/counterstrikesharp/dotnet/shared/Microsoft.NETCore.App/
mkdir -p build/windows/addons/counterstrikesharp/dotnet
curl -s -L https://download.visualstudio.microsoft.com/download/pr/a99861c8-2e00-4587-aaef-60366ca77307/a44ceec2c5d34165ae881600f52edc43/aspnetcore-runtime-7.0.11-win-x64.zip -o dotnet.zip
unzip -qq dotnet.zip -d build/windows/addons/counterstrikesharp/dotnet
- name: Zip CounterStrikeSharp Runtime Build
run: (cd build/output && zip -qq -r ../../counterstrikesharp-with-runtime-build-${{ env.BUILD_NUMBER }}-${{ env.GITHUB_SHA_SHORT }}.zip *)
- uses: actions/upload-artifact@v3
with:
name: counterstrikesharp-with-runtime-build-${{ env.BUILD_NUMBER }}-${{ env.GITHUB_SHA_SHORT }}
path: build/output/
- name: Zip Builds
run: |
(cd build/linux && zip -qq -r ../../counterstrikesharp-with-runtime-build-${{ needs.build_managed.outputs.buildnumber }}-linux-${{ env.GITHUB_SHA_SHORT }}.zip *)
(cd build/windows && zip -qq -r ../../counterstrikesharp-with-runtime-build-${{ needs.build_managed.outputs.buildnumber }}-windows-${{ env.GITHUB_SHA_SHORT }}.zip *)
- name: Release
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ env.BUILD_NUMBER }}
tag_name: v${{ needs.build_managed.outputs.buildnumber }}
files: |
counterstrikesharp-build-${{ env.BUILD_NUMBER }}-${{ env.GITHUB_SHA_SHORT }}.zip
counterstrikesharp-with-runtime-build-${{ env.BUILD_NUMBER }}-${{ env.GITHUB_SHA_SHORT }}.zip
counterstrikesharp-build-${{ needs.build_managed.outputs.buildnumber }}-windows-${{ env.GITHUB_SHA_SHORT }}.zip
counterstrikesharp-with-runtime-build-${{ needs.build_managed.outputs.buildnumber }}-windows-${{ env.GITHUB_SHA_SHORT }}.zip
counterstrikesharp-build-${{ needs.build_managed.outputs.buildnumber }}-linux-${{ env.GITHUB_SHA_SHORT }}.zip
counterstrikesharp-with-runtime-build-${{ needs.build_managed.outputs.buildnumber }}-linux-${{ env.GITHUB_SHA_SHORT }}.zip
- name: Publish NuGet package
run: |
dotnet nuget push managed/CounterStrikeSharp.API/bin/Release/CounterStrikeSharp.API.1.0.${{ env.BUILD_NUMBER }}.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
dotnet nuget push managed/CounterStrikeSharp.API/bin/Release/CounterStrikeSharp.API.1.0.${{ env.BUILD_NUMBER }}.snupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
dotnet nuget push build/api/CounterStrikeSharp.API.1.0.${{ needs.build_managed.outputs.buildnumber }}.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
dotnet nuget push build/api/CounterStrikeSharp.API.1.0.${{ needs.build_managed.outputs.buildnumber }}.snupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate

View File

@@ -1,53 +0,0 @@
name: Build
on:
pull_request:
branches: [ "main" ]
env:
BUILD_TYPE: Release
jobs:
build:
runs-on: ubuntu-latest
container:
image: registry.gitlab.steamos.cloud/steamrt/sniper/sdk:latest
permissions:
pull-requests: read
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- uses: dorny/paths-filter@v2
id: changes
with:
filters: |
csharp:
- managed/**/*
- src/scripting/natives/**/*
cpp:
- src/**/*
- uses: actions/setup-dotnet@v3
if: steps.changes.outputs.csharp == 'true'
with:
dotnet-version: '7.0.x'
- if: steps.changes.outputs.csharp == 'true'
run: dotnet build -c Release managed/CounterStrikeSharp.API
- name: Setup protobuf
shell: bash
if: steps.changes.outputs.cpp == 'true'
run: sudo apt-get update && sudo apt install -y protobuf-compiler
- name: Configure CMake
if: steps.changes.outputs.cpp == 'true'
run: cmake -B build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
- name: Build
# Build your program with the given configuration
if: steps.changes.outputs.cpp == 'true'
run: cmake --build build --config ${{env.BUILD_TYPE}}

5
.gitignore vendored
View File

@@ -5,8 +5,13 @@ cmake-build-debug/
.vscode/
generated/
# configure_file auto generated.
configs/addons/metamod/counterstrikesharp.vdf
libraries/mono/
CMakeSettings.json
build/
build_test/

View File

@@ -1,6 +1,7 @@
cmake_minimum_required(VERSION 3.18)
Project(counterstrikesharp C CXX)
# You must set ASM, otherwise CMAKE_ASM_COMPILE_OBJECT will not work on MSVC
project(counterstrikesharp C CXX ASM)
include("makefiles/shared.cmake")
@@ -11,75 +12,84 @@ add_subdirectory(libraries/funchook)
set_property(TARGET funchook-static PROPERTY POSITION_INDEPENDENT_CODE ON)
SET(SOURCE_FILES
src/mm_plugin.cpp
src/mm_plugin.h
libraries/hl2sdk-cs2/tier1/convar.cpp
libraries/hl2sdk-cs2/tier1/generichash.cpp
libraries/hl2sdk-cs2/entity2/entitysystem.cpp
libraries/hl2sdk-cs2/public/tier0/memoverride.cpp
libraries/dotnet/hostfxr.h
libraries/dotnet/coreclr_delegates.h
"libraries/metamod-source/core/sourcehook/sourcehook.cpp"
"libraries/metamod-source/core/sourcehook/sourcehook_impl_chookidman.cpp"
"libraries/metamod-source/core/sourcehook/sourcehook_impl_chookmaninfo.cpp"
"libraries/metamod-source/core/sourcehook/sourcehook_impl_cvfnptr.cpp"
"libraries/metamod-source/core/sourcehook/sourcehook_impl_cproto.cpp"
src/scripting/dotnet_host.h
src/scripting/dotnet_host.cpp
src/core/utils.h
src/core/globals.h
src/core/globals.cpp
src/core/log.h
src/core/log.cpp
src/scripting/script_engine.h
src/scripting/script_engine.cpp
src/core/global_listener.h
src/scripting/callback_manager.h
src/scripting/callback_manager.cpp
src/core/managers/event_manager.h
src/core/managers/event_manager.cpp
src/core/timer_system.h
src/core/timer_system.cpp
src/scripting/autonative.h
src/scripting/natives/natives_engine.cpp
src/core/engine_trace.h
src/core/engine_trace.cpp
src/scripting/natives/natives_callbacks.cpp
src/core/managers/player_manager.h
src/core/managers/player_manager.cpp
src/scripting/natives/natives_vector.cpp
src/scripting/natives/natives_timers.cpp
src/utils/virtual.h
src/scripting/natives/natives_events.cpp
src/core/memory.cpp
src/core/memory.h
src/core/managers/con_command_manager.cpp
src/core/managers/con_command_manager.h
src/scripting/natives/natives_commands.cpp
src/core/memory_module.h
src/core/cs2_sdk/interfaces/cgameresourceserviceserver.h
src/core/cs2_sdk/interfaces/cschemasystem.h
src/core/cs2_sdk/interfaces/cs2_interfaces.h
src/core/cs2_sdk/interfaces/cs2_interfaces.cpp
src/core/cs2_sdk/schema.h
src/core/cs2_sdk/schema.cpp
src/core/function.cpp
src/core/function.h
src/scripting/natives/natives_memory.cpp
src/scripting/natives/natives_schema.cpp
src/scripting/natives/natives_entities.cpp
src/core/managers/entity_manager.cpp
src/core/managers/entity_manager.h
src/core/managers/chat_manager.cpp
src/core/managers/chat_manager.h
src/core/managers/client_command_manager.cpp
src/core/managers/client_command_manager.h
src/core/managers/server_manager.cpp
src/core/managers/server_manager.h
src/scripting/natives/natives_server.cpp
libraries/nlohmann/json.hpp
src/mm_plugin.cpp
src/mm_plugin.h
libraries/hl2sdk-cs2/tier1/convar.cpp
libraries/hl2sdk-cs2/tier1/generichash.cpp
libraries/hl2sdk-cs2/entity2/entitysystem.cpp
libraries/dotnet/hostfxr.h
libraries/dotnet/coreclr_delegates.h
libraries/metamod-source/core/sourcehook/sourcehook.cpp
libraries/metamod-source/core/sourcehook/sourcehook_impl_chookidman.cpp
libraries/metamod-source/core/sourcehook/sourcehook_impl_chookmaninfo.cpp
libraries/metamod-source/core/sourcehook/sourcehook_impl_cvfnptr.cpp
libraries/metamod-source/core/sourcehook/sourcehook_impl_cproto.cpp
src/scripting/dotnet_host.h
src/scripting/dotnet_host.cpp
src/core/utils.h
src/core/globals.h
src/core/globals.cpp
src/core/gameconfig.h
src/core/gameconfig.cpp
src/core/log.h
src/core/log.cpp
src/scripting/script_engine.h
src/scripting/script_engine.cpp
src/core/global_listener.h
src/scripting/callback_manager.h
src/scripting/callback_manager.cpp
src/core/managers/event_manager.h
src/core/managers/event_manager.cpp
src/core/timer_system.h
src/core/timer_system.cpp
src/scripting/autonative.h
src/scripting/natives/natives_engine.cpp
src/core/engine_trace.h
src/core/engine_trace.cpp
src/scripting/natives/natives_callbacks.cpp
src/core/managers/player_manager.h
src/core/managers/player_manager.cpp
src/scripting/natives/natives_vector.cpp
src/scripting/natives/natives_timers.cpp
src/utils/virtual.h
src/scripting/natives/natives_events.cpp
src/core/memory.cpp
src/core/memory.h
src/core/managers/con_command_manager.cpp
src/core/managers/con_command_manager.h
src/scripting/natives/natives_commands.cpp
src/core/memory_module.h
src/core/memory_module.cpp
src/core/cs2_sdk/interfaces/cgameresourceserviceserver.h
src/core/cs2_sdk/interfaces/cschemasystem.h
src/core/cs2_sdk/interfaces/cs2_interfaces.h
src/core/cs2_sdk/interfaces/cs2_interfaces.cpp
src/core/cs2_sdk/schema.h
src/core/cs2_sdk/schema.cpp
src/core/function.cpp
src/core/function.h
src/scripting/natives/natives_memory.cpp
src/scripting/natives/natives_schema.cpp
src/scripting/natives/natives_entities.cpp
src/core/managers/entity_manager.cpp
src/core/managers/entity_manager.h
src/core/managers/chat_manager.cpp
src/core/managers/chat_manager.h
src/core/managers/server_manager.cpp
src/core/managers/server_manager.h
src/scripting/natives/natives_server.cpp
libraries/nlohmann/json.hpp
)
if (LINUX)
# memoverride.cpp is not usable on CMake Windows, cuz CMake default link libraries (seems) always link ucrt.lib
set(SOURCE_FILES
${SOURCE_FILES}
libraries/hl2sdk-cs2/public/tier0/memoverride.cpp
)
endif()
set(PROTO_DIRS -I${CMAKE_CURRENT_SOURCE_DIR}/libraries/GameTracking-CS2/Protobufs)
file(GLOB PROTOS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/GameTracking-CS2/Protobufs/*.proto")
@@ -105,15 +115,25 @@ target_include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/src/core/cs2_sdk
)
include("makefiles/linux.base.cmake")
if (LINUX)
include("makefiles/linux.base.cmake")
set_target_properties(${PROJECT_NAME} PROPERTIES
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/addons/counterstrikesharp/bin/linuxsteamrt64"
)
elseif(WIN32)
include("makefiles/windows.base.cmake")
set_target_properties(${PROJECT_NAME} PROPERTIES
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/addons/counterstrikesharp/bin/win64"
)
endif()
# Libraries
target_link_libraries(${PROJECT_NAME} ${COUNTER_STRIKE_SHARP_LINK_LIBRARIES})
set_target_properties(${PROJECT_NAME} PROPERTIES
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/addons/counterstrikesharp/bin/linuxsteamrt64"
)
add_custom_command(
TARGET ${PROJECT_NAME} PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory

View File

@@ -10,13 +10,6 @@ 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.
## Philosophy
As a result, there are a few key philosophies and trade-offs that drive the project.
- Only 64 bit is supported.
- .NET only supports x64 on Linux; CSGO previously only supported 32 bit servers, but CS2 supports 64 bit on Linux.
- Supporting both platforms is a lot of work for 1 person, so there are no real plans to support Windows.
## 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).
@@ -96,7 +89,7 @@ I've also used the scripting context & native system that is implemented in Five
## How to Build
Building requires CMake on Linux.
Building requires CMake.
Clone the repository

View File

@@ -0,0 +1,4 @@
Place your plugin configurations here.
TestPlugin/TestPlugin.json
AnotherPlugin/AnotherPlugin.json

View File

@@ -2,46 +2,80 @@
"UTIL_ClientPrintAll": {
"signatures": {
"library": "server",
"windows": "\\x48\\x89\\x5C\\x24\\x08\\x48\\x89\\x6C\\x24\\x10\\x48\\x89\\x74\\x24\\x18\\x57\\x48\\x81\\xEC\\x70\\x01\\x2A\\x2A\\x8B\\xE9",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x49\\x89\\xD7\\x41\\x56\\x49\\x89\\xF6\\x41\\x55\\x41\\x89\\xFD"
}
},
"ClientPrint": {
"signatures": {
"library": "server",
"windows": "\\x48\\x85\\xC9\\x0F\\x84\\x2A\\x2A\\x2A\\x2A\\x48\\x8B\\xC4\\x48\\x89\\x58\\x18",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x49\\x89\\xCF\\x41\\x56\\x49\\x89\\xD6\\x41\\x55\\x41\\x89\\xF5\\x41\\x54\\x4C\\x8D\\xA5\\xA0\\xFE\\xFF\\xFF"
}
},
"CCSPlayerController_SwitchTeam": {
"signatures": {
"library": "server",
"windows": "\\x40\\x56\\x57\\x48\\x81\\xEC\\x2A\\x2A\\x2A\\x2A\\x48\\x8B\\xF9\\x8B\\xF2\\x8B\\xCA",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x55\\x49\\x89\\xFD\\x89\\xF7"
}
},
"CCSPlayerController_ChangeTeam": {
"offsets": {
"windows": 90,
"linux": 89
}
},
"GiveNamedItem": {
"signatures": {
"library": "server",
"windows": "\\x48\\x89\\x5C\\x24\\x18\\x48\\x89\\x74\\x24\\x20\\x55\\x57\\x41\\x54\\x41\\x56\\x41\\x57\\x48\\x8D\\x6C\\x24\\xD9",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x49\\x89\\xCE\\x41\\x55\\x49\\x89\\xF5\\x41\\x54\\x49\\x89\\xD4"
}
},
"UTIL_Remove": {
"signatures": {
"library": "server",
"windows": "\\x48\\x85\\xC9\\x74\\x2A\\x48\\x8B\\xD1\\x48\\x8B\\x0D\\x2A\\x2A\\x2A\\x2A",
"linux": "\\x48\\x89\\xFE\\x48\\x85\\xFF\\x74\\x2A\\x48\\x8D\\x05\\x2A\\x2A\\x2A\\x2A\\x48"
}
},
"Host_Say": {
"signatures": {
"library": "server",
"windows": "\\x44\\x89\\x4C\\x24\\x20\\x44\\x88\\x44\\x24\\x18",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x49\\x89\\xFF\\x41\\x56\\x41\\x55\\x41\\x54\\x4D\\x89\\xC4"
}
},
"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"
}
},
"CBasePlayerPawn_CommitSuicide": {
"offsets": {
"windows": 355,
"linux": 355
}
},
"CBaseEntity_Teleport": {
"offsets": {
"windows": 148,
"linux": 147
}
},
"GameEntitySystem": {
"offsets": {
"windows": 88,
"linux": 80
}
},
"GameEventManager": {
"offsets": {
"windows": 91,
"linux": 91
}
}
}
}

View File

@@ -1,5 +0,0 @@
"Metamod Plugin"
{
"alias" "counterstrikesharp"
"file" "addons/counterstrikesharp/bin/linuxsteamrt64/counterstrikesharp"
}

View File

@@ -19,7 +19,9 @@
<PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\managed\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj" />
<ProjectReference Include="..\..\managed\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj">
<Private>false</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />

View File

@@ -1,34 +1,29 @@
include("makefiles/shared.cmake")
#SET(_ITERATOR_DEBUG_LEVEL 2)
#set(_ITERATOR_DEBUG_LEVEL 2)
add_definitions(-D_LINUX -DPOSIX -DLINUX -DGNUC -DCOMPILER_GCC -DPLATFORM_64BITS)
#Add_Definitions(-DCOMPILER_MSVC -DCOMPILER_MSVC32 -D_WIN32 -D_WINDOWS -D_ALLOW_KEYWORD_MACROS -D__STDC_LIMIT_MACROS)
# Set(CMAKE_CXX_FLAGS_RELEASE "/D_NDEBUG /MD /wd4005 /MP")
Set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp")
Set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dstrnicmp=strncasecmp -D_snprintf=snprintf")
Set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dstrnicmp=strncasecmp -D_snprintf=snprintf")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp")
# Warnings
Set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-uninitialized -Wno-switch -Wno-unused")
Set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-non-virtual-dtor -Wno-overloaded-virtual")
Set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-conversion-null -Wno-write-strings")
Set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-offsetof -Wno-reorder")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-uninitialized -Wno-switch -Wno-unused")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-non-virtual-dtor -Wno-overloaded-virtual")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-conversion-null -Wno-write-strings")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-offsetof -Wno-reorder")
# Others
Set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -msse -fno-strict-aliasing")
Set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -fno-threadsafe-statics -v -fvisibility=default")
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
)

View File

@@ -0,0 +1,10 @@
if (WIN32)
set(COUNTERSTRIKESHARP_VDF_PLATFORM "win64")
else()
set(COUNTERSTRIKESHARP_VDF_PLATFORM "linuxsteamrt64")
endif()
configure_file(
${CMAKE_CURRENT_LIST_DIR}/counterstrikesharp.vdf.in
${PROJECT_SOURCE_DIR}/configs/addons/metamod/counterstrikesharp.vdf
)

View File

@@ -0,0 +1,5 @@
"Metamod Plugin"
{
"alias" "counterstrikesharp"
"file" "addons/counterstrikesharp/bin/${COUNTERSTRIKESHARP_VDF_PLATFORM}/counterstrikesharp"
}

View File

@@ -1,40 +1,54 @@
Set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING
"Only do Release and Debug"
FORCE
if (UNIX AND NOT APPLE)
set(LINUX TRUE)
endif()
if (WIN32 AND NOT MSVC)
message(FATAL "MSVC restricted.")
endif()
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING
"Only do Release and Debug"
FORCE
)
Set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
# TODO: Use C++20 instead.
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_STATIC_LIBRARY_PREFIX "")
Set(SOURCESDK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libraries/hl2sdk-cs2)
Set(METAMOD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libraries/metamod-source)
set(SOURCESDK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libraries/hl2sdk-cs2)
set(METAMOD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libraries/metamod-source)
Set(SOURCESDK ${SOURCESDK_DIR}/${BRANCH})
Set(SOURCESDK_LIB ${SOURCESDK}/lib)
set(SOURCESDK ${SOURCESDK_DIR}/${BRANCH})
set(SOURCESDK_LIB ${SOURCESDK}/lib)
add_definitions(-DMETA_IS_SOURCE2)
include_directories(
${SOURCESDK}
${SOURCESDK}/common
${SOURCESDK}/game/shared
${SOURCESDK}/game/server
${SOURCESDK}/public
${SOURCESDK}/public/engine
${SOURCESDK}/public/mathlib
${SOURCESDK}/public/tier0
${SOURCESDK}/public/tier1
${SOURCESDK}/public/entity2
${SOURCESDK}/public/game/server
${SOURCESDK}/public/entity2
${METAMOD_DIR}/core
${METAMOD_DIR}/core/sourcehook
libraries/dyncall/dynload
libraries/dyncall/dyncall
libraries/spdlog/include
libraries/tl
libraries/funchook/include
libraries
${SOURCESDK}
${SOURCESDK}/common
${SOURCESDK}/game/shared
${SOURCESDK}/game/server
${SOURCESDK}/public
${SOURCESDK}/public/engine
${SOURCESDK}/public/mathlib
${SOURCESDK}/public/tier0
${SOURCESDK}/public/tier1
${SOURCESDK}/public/entity2
${SOURCESDK}/public/game/server
${SOURCESDK}/public/entity2
${METAMOD_DIR}/core
${METAMOD_DIR}/core/sourcehook
libraries/dyncall/dynload
libraries/dyncall/dyncall
libraries/spdlog/include
libraries/tl
libraries/funchook/include
libraries
)
Project(counterstrikesharp C CXX)
include(${CMAKE_CURRENT_LIST_DIR}/metamod/configure_metamod.cmake)
if (LINUX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
endif()

View File

@@ -0,0 +1,20 @@
add_definitions(
-DCOMPILER_MSVC -DCOMPILER_MSVC64 -D_WIN32 -D_WINDOWS -D_ALLOW_KEYWORD_MACROS -D__STDC_LIMIT_MACROS
-D_CRT_SECURE_NO_WARNINGS=1 -D_CRT_SECURE_NO_DEPRECATE=1 -D_CRT_NONSTDC_NO_DEPRECATE=1
)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4819 /wd4828 /wd5033 /permissive- /utf-8 /wd4005 /MP")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /OPT:REF /OPT:ICF")
set(COUNTER_STRIKE_SHARP_LINK_LIBRARIES
${SOURCESDK_LIB}/public/win64/tier0.lib
${SOURCESDK_LIB}/public/win64/tier1.lib
${SOURCESDK_LIB}/public/win64/interfaces.lib
${SOURCESDK_LIB}/public/win64/mathlib.lib
spdlog
dynload_s
dyncall_s
distorm
funchook-static
)

View File

@@ -30,7 +30,7 @@ namespace CounterStrikeSharp.API.Core
}
}
public static IntPtr AddCommand(string name, string description, bool serveronly, int flags, InputArgument callback){
public static void AddCommand(string name, string description, bool serveronly, int flags, InputArgument callback){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(name);
@@ -41,7 +41,6 @@ namespace CounterStrikeSharp.API.Core
ScriptContext.GlobalScriptContext.SetIdentifier(0x807C6B9C);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (IntPtr)ScriptContext.GlobalScriptContext.GetResult(typeof(IntPtr));
}
}

View File

@@ -30,6 +30,8 @@ using CounterStrikeSharp.API.Modules.Events;
using CounterStrikeSharp.API.Modules.Entities;
using CounterStrikeSharp.API.Modules.Listeners;
using CounterStrikeSharp.API.Modules.Timers;
using McMaster.NETCore.Plugins;
using CounterStrikeSharp.API.Modules.Config;
namespace CounterStrikeSharp.API.Core
{
@@ -207,14 +209,10 @@ namespace CounterStrikeSharp.API.Core
{
var wrappedHandler = new Func<int, IntPtr, HookResult>((i, ptr) =>
{
if (i == -1)
{
return HookResult.Continue;
}
var caller = (i != -1) ? new CCSPlayerController(NativeAPI.GetEntityFromIndex(i + 1)) : null;
var entity = new CCSPlayerController(NativeAPI.GetEntityFromIndex(i + 1));
var command = new CommandInfo(ptr, entity);
return handler.Invoke(entity.IsValid ? entity : null, command);
var command = new CommandInfo(ptr, caller);
return handler.Invoke(caller, command);
});
var subscriber = new CallbackSubscriber(handler, wrappedHandler, () => { RemoveCommandListener(name, handler, mode); });
@@ -333,6 +331,31 @@ namespace CounterStrikeSharp.API.Core
this.RegisterConsoleCommandAttributeHandlers(instance);
}
public void InitializeConfig(object instance, Type pluginType)
{
Type[] interfaces = pluginType.GetInterfaces();
Func<Type, bool> predicate = (i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IPluginConfig<>));
// if the plugin has set a configuration type (implements IPluginConfig<>)
if (interfaces.Any(predicate))
{
// IPluginConfig<>
Type @interface = interfaces.Where(predicate).FirstOrDefault()!;
// custom config type passed as generic
Type genericType = @interface!.GetGenericArguments().First();
var config = typeof(ConfigManager)
.GetMethod("Load")!
.MakeGenericMethod(genericType)
.Invoke(null, new object[] { Path.GetFileName(ModuleDirectory) }) as IBasePluginConfig;
// we KNOW that we can do this "safely"
pluginType.GetRuntimeMethod("OnConfigParsed", new Type[] { genericType })
.Invoke(instance, new object[] { config });
}
}
/// <summary>
/// Registers all game event handlers that are decorated with the `[GameEventHandler]` attribute.
/// </summary>

View File

@@ -0,0 +1,32 @@
/*
* This file is part of CounterStrikeSharp.
* CounterStrikeSharp is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CounterStrikeSharp is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
*/
using System.Text.Json.Serialization;
namespace CounterStrikeSharp.API.Core
{
public interface IBasePluginConfig
{
[JsonPropertyName("ConfigVersion")]
int Version { get; set; }
}
public class BasePluginConfig : IBasePluginConfig
{
[JsonPropertyName("ConfigVersion")]
public virtual int Version { get; set; } = 1;
}
}

View File

@@ -1,7 +1,37 @@
using System;
using System.Runtime.InteropServices;
namespace CounterStrikeSharp.API.Core;
public static class Constants
{
public const string ROOT_BINARY_PATH = "/bin/linuxsteamrt64/";
public const string GAME_BINARY_PATH = "/csgo/bin/linuxsteamrt64/";
public static string ModulePrefix { get; }
public static string ModuleSuffix { get; }
public static string RootBinaryPath { get; }
public static string GameBinaryPath { get; }
static Constants()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
ModulePrefix = "";
ModuleSuffix = ".dll";
GameBinaryPath = "/csgo/bin/win64/";
RootBinaryPath = "/bin/win64";
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
ModulePrefix = "lib";
ModuleSuffix = ".so";
GameBinaryPath = "/csgo/bin/linuxsteamrt64/";
RootBinaryPath = "/bin/linuxsteamrt64/";
}
else
{
throw new NotSupportedException($"""Current platform is "{RuntimeInformation.OSDescription}", but does not supported.""");
}
}
}

View File

@@ -31,11 +31,11 @@ namespace CounterStrikeSharp.API.Core
/// </summary>
internal sealed partial class CoreConfigData
{
[JsonPropertyName("PublicChatTrigger")] public string PublicChatTrigger { get; internal set; } = "!";
[JsonPropertyName("SilentChatTrigger")] public string SilentChatTrigger { get; internal set; } = "/";
[JsonPropertyName("PublicChatTrigger")] public string PublicChatTrigger { get; set; } = "!";
[JsonPropertyName("FollowCS2ServerGuidelines")] public bool FollowCS2ServerGuidelines { get; internal set; } = true;
[JsonPropertyName("SilentChatTrigger")] public string SilentChatTrigger { get; set; } = "/";
[JsonPropertyName("FollowCS2ServerGuidelines")] public bool FollowCS2ServerGuidelines { get; set; } = true;
}
/// <summary>
@@ -62,7 +62,9 @@ namespace CounterStrikeSharp.API.Core
///
/// <para>
/// Enabling this option will block plugins from using functionality that is known to cause this.
///
/// Note that this does NOT guarantee that you cannot
///
/// receive a ban.
/// </para>
///
@@ -101,12 +103,12 @@ namespace CounterStrikeSharp.API.Core
try
{
var data = JsonSerializer.Deserialize<CoreConfigData>(File.ReadAllText(coreConfigPath), new JsonSerializerOptions() { ReadCommentHandling = JsonCommentHandling.Skip });
if (data != null)
{
_coreConfig = data;
}
Console.WriteLine($"Loaded core configuration");
}
catch (Exception ex)

View File

@@ -37,41 +37,68 @@ public static class GameData
{
try
{
_methods = JsonSerializer.Deserialize<Dictionary<string, LoadedGameData>>(File.ReadAllText(gameDataPath));
_methods = JsonSerializer.Deserialize<Dictionary<string, LoadedGameData>>(File.ReadAllText(gameDataPath))!;
Console.WriteLine($"Loaded game data with {_methods.Count} methods.");
}
catch (Exception ex)
{
Console.WriteLine($"Failed to load game data: {ex.ToString()}");
Console.WriteLine($"Failed to load game data: {ex}");
}
}
public static string GetSignature(string key)
{
if (!_methods.ContainsKey(key)) throw new Exception($"Method {key} not found in gamedata.json");
if (_methods[key].Signatures == null) throw new Exception($"No signatures found for {key} in gamedata.json");
var methodMetadata = _methods[key];
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
Console.WriteLine($"Getting signature: {key}");
if (!_methods.ContainsKey(key))
{
return methodMetadata.Signatures!.Linux;
throw new ArgumentException($"Method {key} not found in gamedata.json");
}
return methodMetadata.Signatures!.Windows;
var methodMetadata = _methods[key];
if (methodMetadata.Signatures == null)
{
throw new InvalidOperationException($"No signatures found for {key} in gamedata.json");
}
string signature;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
signature = methodMetadata.Signatures?.Linux ?? throw new InvalidOperationException($"No Linux signature for {key} in gamedata.json");
}
else
{
signature = methodMetadata.Signatures?.Windows ?? throw new InvalidOperationException($"No Windows signature for {key} in gamedata.json");
}
return signature;
}
public static int GetOffset(string key)
{
if (!_methods.ContainsKey(key)) throw new Exception($"Method {key} not found in gamedata.json");
if (_methods[key].Offsets == null) throw new Exception($"No offsets found for {key} in gamedata.json");
var methodMetadata = _methods[key];
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
if (!_methods.ContainsKey(key))
{
return methodMetadata.Offsets!.Linux;
throw new Exception($"Method {key} not found in gamedata.json");
}
return methodMetadata.Offsets!.Windows;
var methodMetadata = _methods[key];
if (methodMetadata.Offsets == null)
{
throw new Exception($"No offsets found for {key} in gamedata.json");
}
int offset;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
offset = methodMetadata.Offsets?.Linux ?? throw new InvalidOperationException($"No Linux offset for {key} in gamedata.json");
}
else
{
offset = methodMetadata.Offsets?.Windows ?? throw new InvalidOperationException($"No Windows offset for {key} in gamedata.json");
}
return offset;
}
}

View File

@@ -68,7 +68,7 @@ namespace CounterStrikeSharp.API.Core
Console.WriteLine("Loading Admins from \"configs/admins.json\"");
AdminManager.Load(Path.Combine(rootDir.FullName, "configs", "admins.json"));
for (int i = 1; i <= 9; i++)
for (var i = 1; i <= 9; i++)
{
CommandUtils.AddStandaloneCommand("css_" + i, "Command Key Handler", (player, info) =>
{
@@ -79,18 +79,18 @@ namespace CounterStrikeSharp.API.Core
}
Console.WriteLine("Loading C# plugins...");
int pluginCount = LoadAllPlugins();
var pluginCount = LoadAllPlugins();
Console.WriteLine($"All managed modules were loaded. {pluginCount} plugins loaded.");
RegisterPluginCommands();
}
public void LoadPlugin(string path)
private void LoadPlugin(string path)
{
var existingPlugin = FindPluginByModulePath(path);
if (existingPlugin != null)
{
throw new Exception("Plugin is already loaded.");
throw new FileLoadException("Plugin is already loaded.");
}
var plugin = new PluginContext(path, _loadedPlugins.Select(x => x.PluginId).DefaultIfEmpty(0).Max() + 1);
@@ -98,37 +98,39 @@ namespace CounterStrikeSharp.API.Core
_loadedPlugins.Add(plugin);
}
public int LoadAllPlugins()
private int LoadAllPlugins()
{
DirectoryInfo modules_directory_info;
DirectoryInfo modulesDirectoryInfo;
try
{
modules_directory_info = new DirectoryInfo(Path.Combine(rootDir.FullName, "plugins"));
modulesDirectoryInfo = new DirectoryInfo(Path.Combine(rootDir.FullName, "plugins"));
}
catch (Exception e)
{
Console.WriteLine(
"Unable to access .NET modules directory: " + e.GetType().ToString() + " " + e.Message);
Console.WriteLine(e);
return 0;
}
DirectoryInfo[] proper_modules_directories;
DirectoryInfo[] properModulesDirectories;
try
{
proper_modules_directories = modules_directory_info.GetDirectories();
properModulesDirectories = modulesDirectoryInfo.GetDirectories();
}
catch
{
proper_modules_directories = new DirectoryInfo[0];
properModulesDirectories = Array.Empty<DirectoryInfo>();
}
var filePaths = proper_modules_directories
var filePaths = properModulesDirectories
.Where(d => d.GetFiles().Any((f) => f.Name == d.Name + ".dll"))
.Select(d => d.GetFiles().First((f) => f.Name == d.Name + ".dll").FullName)
.ToArray();
foreach (var path in filePaths)
{
Console.WriteLine($"Plugin path: {path}");
try
{
LoadPlugin(path);
@@ -320,7 +322,7 @@ namespace CounterStrikeSharp.API.Core
}
public void RegisterPluginCommands()
private void RegisterPluginCommands()
{
CommandUtils.AddStandaloneCommand("css", "Counter-Strike Sharp options.", OnCSSCommand);
CommandUtils.AddStandaloneCommand("css_plugins", "Counter-Strike Sharp plugin options.", OnCSSPluginCommand);

View File

@@ -64,7 +64,7 @@ namespace CounterStrikeSharp.API.Core
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
Console.WriteLine(e);
return 0;
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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.Core
{
/// <summary>
/// An interface that describes a plugin configuration.
/// </summary>
public interface IPluginConfig<T> where T: IBasePluginConfig, new()
{
T Config { get; set; }
public void OnConfigParsed(T config);
}
}

View File

@@ -0,0 +1,27 @@
/*
* 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 CBaseModelEntity
{
public void SetModel(string model)
{
VirtualFunctions.SetModel(Handle, model);
}
}

View File

@@ -26,7 +26,7 @@ public partial class CCSPlayerController
public void PrintToConsole(string message)
{
NativeAPI.PrintToConsole((int)EntityIndex.Value.Value, message);
NativeAPI.PrintToConsole((int)EntityIndex.Value.Value, $"{message}\n\0");
}
public void PrintToChat(string message)

View File

@@ -20,6 +20,7 @@ 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 McMaster.NETCore.Plugins;
@@ -103,6 +104,7 @@ namespace CounterStrikeSharp.API.Core
_plugin = (BasePlugin)Activator.CreateInstance(pluginType)!;
_plugin.ModulePath = _path;
_plugin.RegisterAllAttributes(_plugin);
_plugin.InitializeConfig(_plugin, pluginType);
_plugin.Load(hotReload);
Console.WriteLine($"Finished loading plugin: {Name}");

View 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/>. *
*/
using System;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.Json;
using CounterStrikeSharp.API.Core;
namespace CounterStrikeSharp.API.Modules.Config
{
public static class ConfigManager
{
private static readonly DirectoryInfo? _rootDir;
private static readonly string _pluginConfigsFolderPath;
static ConfigManager()
{
_rootDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.Parent;
_pluginConfigsFolderPath = Path.Combine(_rootDir.FullName, "configs", "plugins");
}
public static T Load<T>(string pluginName) where T : IBasePluginConfig, new()
{
string directoryPath = Path.Combine(_pluginConfigsFolderPath, pluginName);
string configPath = Path.Combine(directoryPath, $"{pluginName}.json");
T config = (T)Activator.CreateInstance(typeof(T))!;
if (!File.Exists(configPath))
{
try
{
if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
StringBuilder builder = new StringBuilder();
builder.Append($"// This configuration was automatically generated by CounterStrikeSharp for plugin '{pluginName}', at {DateTimeOffset.Now:yyyy/MM/dd hh:mm:ss}\n");
builder.Append(JsonSerializer.Serialize<T>(config, new JsonSerializerOptions { WriteIndented = true }));
File.WriteAllText(configPath, builder.ToString());
return config;
}
catch (Exception ex)
{
Console.WriteLine($"Failed to generate configuration file for {pluginName}: {ex}");
}
}
try
{
config = JsonSerializer.Deserialize<T>(File.ReadAllText(configPath), new JsonSerializerOptions() { ReadCommentHandling = JsonCommentHandling.Skip })!;
}
catch (Exception ex)
{
Console.WriteLine($"Failed to parse configuration '{pluginName}': {ex}");
}
return config;
}
}
}

View File

@@ -1,16 +1,17 @@
using System.IO;
using System.Runtime.InteropServices;
using CounterStrikeSharp.API.Core;
namespace CounterStrikeSharp.API.Modules.Memory;
public class Addresses
{
public static string EnginePath = Path.Join(Server.GameDirectory, Constants.ROOT_BINARY_PATH, "libengine2.so");
public static string Tier0Path = Path.Join(Server.GameDirectory, Constants.ROOT_BINARY_PATH, "libtier0.so");
public static string ServerPath = Path.Join(Server.GameDirectory, Constants.GAME_BINARY_PATH, "libserver.so");
public static string EnginePath => Path.Join(Server.GameDirectory, Constants.RootBinaryPath, $"{Constants.ModulePrefix}engine2{Constants.ModuleSuffix}");
public static string Tier0Path => Path.Join(Server.GameDirectory, Constants.RootBinaryPath, $"{Constants.ModulePrefix}tier0{Constants.ModuleSuffix}");
public static string SchemaSystemPath =
Path.Join(Server.GameDirectory, Constants.ROOT_BINARY_PATH, "libschemasystem.so");
public static string ServerPath => Path.Join(Server.GameDirectory, Constants.GameBinaryPath, $"{Constants.ModulePrefix}server{Constants.ModuleSuffix}");
public static string SchemaSystemPath => Path.Join(Server.GameDirectory, Constants.RootBinaryPath, $"{Constants.ModulePrefix}schemasystem{Constants.ModuleSuffix}");
public static string VScriptPath => Path.Join(Server.GameDirectory, Constants.RootBinaryPath, $"{Constants.ModulePrefix}vscript{Constants.ModuleSuffix}");
public static string VScriptPath = Path.Join(Server.GameDirectory, Constants.ROOT_BINARY_PATH, "libvscript.so");
}

View File

@@ -31,9 +31,15 @@ public partial class VirtualFunction
{
if (!_createdFunctions.TryGetValue(signature, out var function))
{
function = NativeAPI.CreateVirtualFunctionBySignature(IntPtr.Zero, Addresses.ServerPath, signature,
argumentTypes.Count(), (int)returnType, arguments);
_createdFunctions[signature] = function;
try
{
function = NativeAPI.CreateVirtualFunctionBySignature(IntPtr.Zero, Addresses.ServerPath, signature,
argumentTypes.Count(), (int)returnType, arguments);
_createdFunctions[signature] = function;
}
catch (Exception)
{
}
}
return function;

View File

@@ -24,4 +24,7 @@ public static class VirtualFunctions
// void(*UTIL_Remove)(CEntityInstance*);
public static Action<IntPtr> UTIL_Remove = VirtualFunction.CreateVoid<IntPtr>(GameData.GetSignature("UTIL_Remove"));
// void(*CBaseModelEntity_SetModel)(CBaseModelEntity*, const char*);
public static Action<IntPtr, string> SetModel = VirtualFunction.CreateVoid<IntPtr, string>(GameData.GetSignature("CBaseModelEntity_SetModel"));
}

View File

@@ -1,8 +0,0 @@
{
"profiles": {
"CounterStrikeSharp.API": {
"commandName": "Project",
"nativeDebugging": true
}
}
}

View File

@@ -18,6 +18,8 @@ using System;
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;
@@ -32,8 +34,17 @@ using CounterStrikeSharp.API.Modules.Utils;
namespace TestPlugin
{
[MinimumApiVersion(1)]
public class SamplePlugin : BasePlugin
public class SampleConfig : BasePluginConfig
{
[JsonPropertyName("IsPluginEnabled")]
public bool IsPluginEnabled { get; set; } = true;
[JsonPropertyName("LogPrefix")]
public string LogPrefix { get; set; } = "CSSharp";
}
[MinimumApiVersion(33)]
public class SamplePlugin : BasePlugin, IPluginConfig<SampleConfig>
{
public override string ModuleName => "Sample Plugin";
public override string ModuleVersion => "v1.0.0";
@@ -42,8 +53,24 @@ namespace TestPlugin
public override string ModuleDescription => "A playground of features used for testing";
public SampleConfig Config { get; set; }
// This method is called right before `Load` is called
public void OnConfigParsed(SampleConfig config)
{
// Save config instance
Config = config;
}
public override void Load(bool hotReload)
{
// Basic usage of the configuration system
if (!Config.IsPluginEnabled)
{
Console.WriteLine($"{Config.LogPrefix} {ModuleName} is disabled");
return;
}
Console.WriteLine(
$"Test Plugin has been loaded, and the hot reload flag was {hotReload}, path is {ModulePath}");
@@ -327,7 +354,7 @@ namespace TestPlugin
[ConsoleCommand("cssharp_attribute", "This is a custom attribute event")]
public void OnCommand(CCSPlayerController? player, CommandInfo command)
{
Log("cssharp_attribute called!");
command.ReplyToCommand("cssharp_attribute called", true);
}
[ConsoleCommand("css_changelevel", "Changes map")]

View File

@@ -9,6 +9,8 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj" />
<ProjectReference Include="..\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj">
<Private>false</Private>
</ProjectReference>
</ItemGroup>
</Project>

View File

@@ -18,17 +18,18 @@
*/
#pragma once
#include <platform.h>
#include "interfaces/interfaces.h"
#include <cstdint>
#include "core/gameconfig.h"
class CGameEntitySystem;
class CGameResourceService
{
public:
CGameEntitySystem *GetGameEntitySystem()
{
return *reinterpret_cast<CGameEntitySystem **>((uintptr_t)(this) + 0x50);
}
public:
CGameEntitySystem* GetGameEntitySystem()
{
return *reinterpret_cast<CGameEntitySystem**>(
(uintptr_t)(this) +
counterstrikesharp::globals::gameConfig->GetOffset("GameEntitySystem"));
}
};

View File

@@ -18,11 +18,12 @@
*/
#include "cs2_interfaces.h"
#include "interfaces/interfaces.h"
#include "tier0/memdbgon.h"
#include "core/memory_module.h"
#include "core/globals.h"
#include "interfaces/interfaces.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
namespace counterstrikesharp {
void interfaces::Initialize() {

View File

@@ -5,7 +5,7 @@
#include <string_view>
#include <array>
#include "core/cs2_sdk/interfaces/CUtlTSHash.h"
#define CSGO2
#define CS2
#define CSCHEMATYPE_GETSIZES_INDEX 3
#define SCHEMASYSTEM_TYPE_SCOPES_OFFSET 0x190
@@ -181,7 +181,8 @@ class CSchemaType
CSchemaType* element_type_;
};
struct generic_type_t {
struct generic_type_t
{
uint64_t unknown;
const char* m_name_; // 0x0008
};
@@ -316,12 +317,24 @@ class CSchemaSystemTypeScope
public:
CSchemaClassInfo* FindDeclaredClass(const char* class_name)
{
#ifdef _WIN32
CSchemaClassInfo* rv = nullptr;
CALL_VIRTUAL(void, 2, this, &rv, class_name);
return rv;
#else
return CALL_VIRTUAL(CSchemaClassInfo*, 2, this, class_name);
#endif
}
CSchemaEnumBinding* FindDeclaredEnum(const char* name)
{
#ifdef _WIN32
CSchemaEnumBinding* rv = nullptr;
CALL_VIRTUAL(void, 3, this, &rv, name);
return rv;
#else
return CALL_VIRTUAL(CSchemaEnumBinding*, 3, this, name);
#endif
}
CSchemaType* FindSchemaTypeByName(const char* name, std::uintptr_t* pSchema)
@@ -349,24 +362,19 @@ class CSchemaSystemTypeScope
return CALL_VIRTUAL(CSchemaEnumBinding*, 8, this, name);
}
std::string_view GetScopeName() {
return {m_name_.data()};
}
std::string_view GetScopeName() { return {m_name_.data()}; }
[[nodiscard]] CUtlTSHash<CSchemaClassBinding*> GetClasses() const {
return m_classes_;
}
[[nodiscard]] CUtlTSHash<CSchemaClassBinding*> GetClasses() const { return m_classes_; }
[[nodiscard]] CUtlTSHash<CSchemaEnumBinding*> GetEnums() const { return m_enumes_; }
[[nodiscard]] CUtlTSHash<CSchemaEnumBinding*> GetEnums() const {
return m_enumes_;
}
private:
char pad_0x0000[0x8]; // 0x0000
std::array<char, 256> m_name_ = {};
char pad_0x0108[SCHEMASYSTEMTYPESCOPE_OFF1]; // 0x0108
CUtlTSHash<CSchemaClassBinding*> m_classes_; // 0x0588
char pad_0x0594[SCHEMASYSTEMTYPESCOPE_OFF2]; // 0x05C8
CUtlTSHash<CSchemaEnumBinding*> m_enumes_; // 0x2DD0
CUtlTSHash<CSchemaEnumBinding*> m_enumes_; // 0x2DD0
private:
static constexpr unsigned int s_class_list = 0x580;
};

View File

@@ -20,13 +20,15 @@
#include "schema.h"
#include "interfaces/cs2_interfaces.h"
#include "../globals.h"
// #include <unordered_map>
#include "tier1/utlmap.h"
#include "tier0/memdbgon.h"
#include "../memory.h"
#include "core/globals.h"
#include "core/memory.h"
#include "core/log.h"
#include "tier1/utlmap.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
using SchemaKeyValueMap_t = CUtlMap<uint32_t, SchemaKey>;
using SchemaTableMap_t = CUtlMap<uint32_t, SchemaKeyValueMap_t*>;

View File

@@ -19,23 +19,13 @@
#pragma once
#include "stdint.h"
#ifdef _WIN32
#pragma warning(push)
#pragma warning(disable : 4005)
#endif
#include <type_traits>
#ifdef _WIN32
#pragma warning(pop)
#endif
#include "tier0/dbg.h"
#include "const.h"
#include "../../utils/virtual.h"
#include "stdint.h"
#include "utils/virtual.h"
#include <stdint.h>
#include <type_traits>
#undef schema
struct SchemaKey {

236
src/core/gameconfig.cpp Normal file
View File

@@ -0,0 +1,236 @@
#include "core/gameconfig.h"
#include <fstream>
#include "log.h"
#include "metamod_oslink.h"
namespace counterstrikesharp {
CGameConfig::CGameConfig(const std::string& path) { m_sPath = path; }
CGameConfig::~CGameConfig() = default;
bool CGameConfig::Init(char* conf_error, int conf_error_size)
{
std::ifstream ifs(m_sPath);
if (!ifs) {
V_snprintf(conf_error, conf_error_size, "Gamedata file not found.");
return false;
}
m_json = json::parse(ifs);
#if _WIN32
constexpr auto platform = "windows";
#else
constexpr auto platform = "linux";
#endif
try {
for (auto& [k, v] : m_json.items()) {
if (v.contains("signatures")) {
if (auto library = v["signatures"]["library"]; library.is_string()) {
m_umLibraries[k] = library.get<std::string>();
}
if (auto signature = v["signatures"][platform]; signature.is_string()) {
m_umSignatures[k] = signature.get<std::string>();
}
}
if (v.contains("offsets")) {
if (auto offset = v["offsets"][platform]; offset.is_number_integer()) {
m_umOffsets[k] = offset.get<std::int64_t>();
}
}
if (v.contains("patches")) {
if (auto patch = v["patches"][platform]; patch.is_string()) {
m_umPatches[k] = patch.get<std::string>();
}
}
}
} catch (const std::exception& ex) {
V_snprintf(conf_error, conf_error_size, "Failed to parse gamedata file: %s", ex.what());
return false;
}
return true;
}
const std::string CGameConfig::GetPath()
{
return m_sPath;
}
const char* CGameConfig::GetLibrary(const std::string& name)
{
// My recommendation is switch to C++20.
auto it = m_umLibraries.find(name);
if (it == m_umLibraries.end()) {
return nullptr;
}
return it->second.c_str();
}
const char* CGameConfig::GetSignature(const std::string& name)
{
auto it = m_umSignatures.find(name);
if (it == m_umSignatures.end()) {
return nullptr;
}
return it->second.c_str();
}
const char* CGameConfig::GetSymbol(const char* name)
{
const char* symbol = this->GetSignature(name);
if (!symbol || strlen(symbol) <= 1) {
CSSHARP_CORE_ERROR("Missing symbol: {}\n", name);
return nullptr;
}
return symbol + 1;
}
const char* CGameConfig::GetPatch(const std::string& name)
{
auto it = m_umPatches.find(name);
if (it == m_umPatches.end()) {
return nullptr;
}
return it->second.c_str();
}
int CGameConfig::GetOffset(const std::string& name)
{
auto it = m_umOffsets.find(name);
if (it == m_umOffsets.end()) {
return -1;
}
return it->second;
}
void* CGameConfig::GetAddress(const std::string& name, void* engine, void* server, char* error,
int maxlen)
{
CSSHARP_CORE_ERROR("Not implemented.");
return nullptr;
}
modules::CModule** CGameConfig::GetModule(const char* name)
{
const char* library = this->GetLibrary(name);
if (!library)
return nullptr;
if (strcmp(library, "engine") == 0)
return &modules::engine;
else if (strcmp(library, "server") == 0)
return &modules::server;
else if (strcmp(library, "vscript") == 0)
return &modules::vscript;
else if (strcmp(library, "tier0") == 0)
return &modules::tier0;
return nullptr;
}
bool CGameConfig::IsSymbol(const char* name)
{
const char* sigOrSymbol = this->GetSignature(name);
if (!sigOrSymbol || strlen(sigOrSymbol) <= 0) {
CSSHARP_CORE_ERROR("Missing signature or symbol: {}\n", name);
return false;
}
return sigOrSymbol[0] == '@';
}
void* CGameConfig::ResolveSignature(const char* name)
{
modules::CModule** module = this->GetModule(name);
if (!module || !(*module)) {
CSSHARP_CORE_ERROR("Invalid Module {}\n", name);
return nullptr;
}
void* address = nullptr;
if (this->IsSymbol(name)) {
const char* symbol = this->GetSymbol(name);
if (!symbol) {
CSSHARP_CORE_ERROR("Invalid symbol for {}\n", name);
return nullptr;
}
address = dlsym((*module)->m_hModule, symbol);
} else {
const char* signature = this->GetSignature(name);
if (!signature) {
CSSHARP_CORE_ERROR("Failed to find signature for {}\n", name);
return nullptr;
}
size_t iLength = 0;
byte* pSignature = HexToByte(signature, iLength);
if (!pSignature) {
return nullptr;
}
address = (*module)->FindSignature(pSignature, iLength);
}
if (!address) {
CSSHARP_CORE_ERROR("Failed to find address for {}\n", name);
return nullptr;
}
return address;
}
std::string CGameConfig::GetDirectoryName(const std::string& directoryPathInput)
{
std::string directoryPath = std::string(directoryPathInput);
size_t found = std::string(directoryPath).find_last_of("/\\");
if (found != std::string::npos) {
return std::string(directoryPath, found + 1);
}
return "";
}
int CGameConfig::HexStringToUint8Array(const char* hexString, uint8_t* byteArray, size_t maxBytes)
{
if (!hexString) {
printf("Invalid hex string.\n");
return -1;
}
size_t hexStringLength = strlen(hexString);
size_t byteCount = hexStringLength / 4; // Each "\\x" represents one byte.
if (hexStringLength % 4 != 0 || byteCount == 0 || byteCount > maxBytes) {
printf("Invalid hex string format or byte count.\n");
return -1; // Return an error code.
}
for (size_t i = 0; i < hexStringLength; i += 4) {
if (sscanf(hexString + i, "\\x%2hhX", &byteArray[i / 4]) != 1) {
printf("Failed to parse hex string at position %zu.\n", i);
return -1; // Return an error code.
}
}
byteArray[byteCount] = '\0'; // Add a null-terminating character.
return byteCount; // Return the number of bytes successfully converted.
}
byte* CGameConfig::HexToByte(const char* src, size_t& length)
{
if (!src || strlen(src) <= 0) {
CSSHARP_CORE_INFO("Invalid hex string\n");
return nullptr;
}
length = strlen(src) / 4;
uint8_t* dest = new uint8_t[length];
int byteCount = HexStringToUint8Array(src, dest, length);
if (byteCount <= 0) {
CSSHARP_CORE_INFO("Invalid hex format %s\n", src);
return nullptr;
}
return dest;
}
} // namespace counterstrikesharp

51
src/core/gameconfig.h Normal file
View File

@@ -0,0 +1,51 @@
#pragma once
#include "core/globals.h"
#include "core/memory_module.h"
#include <KeyValues.h>
#include <string>
#include <unordered_map>
#undef snprintf
#include <nlohmann/json.hpp>
namespace counterstrikesharp {
class CGameConfig
{
public:
using json = nlohmann::json;
CGameConfig(const std::string& path);
~CGameConfig();
bool Init(char* conf_error, int conf_error_size);
const std::string GetPath();
const char* GetLibrary(const std::string& name);
const char* GetSignature(const std::string& name);
const char* GetSymbol(const char* name);
const char* GetPatch(const std::string& name);
int GetOffset(const std::string& name);
void* GetAddress(const std::string& name, void* engine, void* server, char* error, int maxlen);
modules::CModule** GetModule(const char* name);
bool IsSymbol(const char* name);
void* ResolveSignature(const char* name);
static std::string GetDirectoryName(const std::string& directoryPathInput);
static int HexStringToUint8Array(const char* hexString, uint8_t* byteArray, size_t maxBytes);
static byte* HexToByte(const char* src, size_t& length);
private:
std::string m_sPath;
// use Valve KeyValues in the future.
// since we'd better make '\' easier.
json m_json;
std::unordered_map<std::string, int> m_umOffsets;
std::unordered_map<std::string, std::string> m_umSignatures;
std::unordered_map<std::string, void*> m_umAddresses;
std::unordered_map<std::string, std::string> m_umLibraries;
std::unordered_map<std::string, std::string> m_umPatches;
};
} // namespace counterstrikesharp

View File

@@ -1,3 +1,4 @@
#include "mm_plugin.h"
#include "core/globals.h"
#include "core/managers/player_manager.h"
#include "iserver.h"
@@ -12,16 +13,17 @@
#include "log.h"
#include "utils/virtual.h"
#include "core/memory.h"
#include "core/managers/con_command_manager.h"
#include "core/managers/chat_manager.h"
#include "memory_module.h"
#include "interfaces/cs2_interfaces.h"
#include "core/managers/entity_manager.h"
#include "core/managers/client_command_manager.h"
#include "core/managers/server_manager.h"
#include <public/game/server/iplayerinfo.h>
#include <public/entity2/entitysystem.h>
namespace counterstrikesharp {
namespace modules {
@@ -63,6 +65,7 @@ SourceHook::Impl::CSourceHookImpl source_hook_impl;
SourceHook::ISourceHook *source_hook = &source_hook_impl;
ISmmAPI *ismm = nullptr;
CGameEntitySystem* entitySystem = nullptr;
CGameConfig* gameConfig = nullptr;
// Custom Managers
CallbackManager callbackManager;
@@ -72,7 +75,6 @@ TimerSystem timerSystem;
ConCommandManager conCommandManager;
EntityManager entityManager;
ChatManager chatManager;
ClientCommandManager clientCommandManager;
ServerManager serverManager;
void Initialize() {
@@ -84,16 +86,20 @@ void Initialize() {
interfaces::Initialize();
globals::entitySystem = interfaces::pGameResourceServiceServer->GetGameEntitySystem();
entitySystem = interfaces::pGameResourceServiceServer->GetGameEntitySystem();
gameEventManager = (IGameEventManager2 *)(CALL_VIRTUAL(uintptr_t, 91, server) - 8);
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();
if (!server) return nullptr;
if (!server) {
return nullptr;
}
return networkServerService->GetIGameServer()->GetGlobals();
}

View File

@@ -48,8 +48,8 @@ class ChatCommands;
class HookManager;
class EntityManager;
class ChatManager;
class ClientCommandManager;
class ServerManager;
class CGameConfig;
namespace globals {
@@ -91,7 +91,6 @@ extern EntityManager entityManager;
extern TimerSystem timerSystem;
extern ChatCommands chatCommands;
extern ChatManager chatManager;
extern ClientCommandManager clientCommandManager;
extern ServerManager serverManager;
extern HookManager hookManager;
@@ -100,6 +99,7 @@ extern int source_hook_pluginid;
extern IGameEventSystem *gameEventSystem;
extern CounterStrikeSharpMMPlugin *mmPlugin;
extern ISmmAPI *ismm;
extern CGameConfig* gameConfig;
void Initialize();
// Should only be called within the active game loop (i e map should be loaded

View File

@@ -8,17 +8,22 @@ namespace counterstrikesharp {
std::shared_ptr<spdlog::logger> Log::m_core_logger;
void Log::Init() {
std::vector<spdlog::sink_ptr> logSinks;
auto ansiColorSink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_mt>();
ansiColorSink->set_color(spdlog::level::trace, ansiColorSink->yellow);
logSinks.emplace_back(ansiColorSink);
logSinks.emplace_back(
std::vector<spdlog::sink_ptr> log_sinks;
auto color_sink = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();
#if _WIN32
color_sink->set_color(spdlog::level::trace, 6);
#else
color_sink->set_color(spdlog::level::trace, color_sink->yellow);
#endif
log_sinks.emplace_back(color_sink);
log_sinks.emplace_back(
std::make_shared<spdlog::sinks::basic_file_sink_mt>("counterstrikesharp.log", true));
logSinks[0]->set_pattern("%^[%T.%e] %n: %v%$");
logSinks[1]->set_pattern("[%T.%e] [%l] %n: %v");
log_sinks[0]->set_pattern("%^[%T.%e] %n: %v%$");
log_sinks[1]->set_pattern("[%T.%e] [%l] %n: %v");
m_core_logger = std::make_shared<spdlog::logger>("CSSharp", begin(logSinks), end(logSinks));
m_core_logger = std::make_shared<spdlog::logger>("CSSharp", begin(log_sinks), end(log_sinks));
spdlog::register_logger(m_core_logger);
m_core_logger->set_level(spdlog::level::info);
m_core_logger->flush_on(spdlog::level::info);

View File

@@ -24,9 +24,12 @@
#include <public/eiface.h>
#include "core/memory.h"
#include "core/log.h"
#include "core/gameconfig.h"
#include <funchook.h>
#include "core/memory_module.h"
namespace counterstrikesharp {
ChatManager::ChatManager() {}
@@ -35,11 +38,13 @@ ChatManager::~ChatManager() {}
void ChatManager::OnAllInitialized()
{
// TODO: Allow reading of the shared game data json from the C++ side too so this isn't
// being hardcoded.
m_pHostSay = (HostSay)FindSignature(
MODULE_PREFIX "server" MODULE_EXT,
R"(\x55\x48\x89\xE5\x41\x57\x49\x89\xFF\x41\x56\x41\x55\x41\x54\x4D\x89\xC4)");
m_pHostSay = reinterpret_cast<HostSay>(
modules::server->FindSignature(globals::gameConfig->GetSignature("Host_Say")));
if (m_pHostSay == nullptr) {
CSSHARP_CORE_ERROR("Failed to find signature for \'Host_Say\'");
return;
}
auto m_hook = funchook_create();
funchook_prepare(m_hook, (void**)&m_pHostSay, (void*)&DetourHostSay);
@@ -54,30 +59,28 @@ void DetourHostSay(CBaseEntity* pController, CCommand& args, bool teamonly, int
CCommand newArgs;
newArgs.Tokenize(args.Arg(1));
if (pController) {
auto pEvent = globals::gameEventManager->CreateEvent("player_chat", true);
if (pEvent) {
pEvent->SetBool("teamonly", teamonly);
pEvent->SetInt("userid", pController->GetEntityIndex().Get() - 1);
pEvent->SetString("text", args[1]);
globals::gameEventManager->FireEvent(pEvent, true);
}
}
if (*args[1] == '/' || *args[1] == '!') {
globals::chatManager.OnSayCommandPost(pController, newArgs);
return;
}
m_pHostSay(pController, args, teamonly, unk1, unk2);
if (pController) {
auto pEvent = globals::gameEventManager->CreateEvent("player_chat", true);
if (pEvent) {
pEvent->SetBool("teamonly", teamonly);
pEvent->SetInt("userid", pController->GetEntityIndex().Get());
pEvent->SetString("text", args[1]);
globals::gameEventManager->FireEvent(pEvent, true);
}
}
}
bool ChatManager::OnSayCommandPre(CBaseEntity* pController, CCommand& command) {
return false;
}
bool ChatManager::OnSayCommandPre(CBaseEntity* pController, CCommand& command) { return false; }
bool ChatManager::OnSayCommandPost(CBaseEntity* pController, CCommand& command)
void ChatManager::OnSayCommandPost(CBaseEntity* pController, CCommand& command)
{
const char* args = command.ArgS();
auto commandStr = command.Arg(0);
@@ -85,7 +88,7 @@ bool ChatManager::OnSayCommandPost(CBaseEntity* pController, CCommand& command)
return InternalDispatch(pController, commandStr + 1, command);
}
bool ChatManager::InternalDispatch(CBaseEntity* pPlayerController, const char* szTriggerPhase,
void ChatManager::InternalDispatch(CBaseEntity* pPlayerController, const char* szTriggerPhase,
CCommand& fullCommand)
{
auto ppArgV = new const char*[fullCommand.ArgC()];
@@ -96,26 +99,28 @@ bool ChatManager::InternalDispatch(CBaseEntity* pPlayerController, const char* s
auto prefixedPhrase = std::string("css_") + szTriggerPhase;
auto command = globals::conCommandManager.FindCommand(prefixedPhrase.c_str());
auto bValidWithPrefix = globals::conCommandManager.IsValidValveCommand(prefixedPhrase.c_str());
if (command) {
if (bValidWithPrefix) {
ppArgV[0] = prefixedPhrase.c_str();
}
CCommand commandCopy(fullCommand.ArgC(), ppArgV);
if (pPlayerController == nullptr) {
auto result = globals::conCommandManager.InternalDispatch(CPlayerSlot(-1), &commandCopy);
globals::conCommandManager.ExecuteCommandCallbacks(
commandCopy.Arg(0), CCommandContext(CommandTarget_t::CT_NO_TARGET, CPlayerSlot(-1)),
commandCopy, HookMode::Pre);
delete[] ppArgV;
return result;
return;
}
auto index = pPlayerController->GetEntityIndex().Get();
auto slot = CPlayerSlot(index - 1);
auto result = globals::conCommandManager.InternalDispatch(slot, &commandCopy);
globals::conCommandManager.ExecuteCommandCallbacks(
commandCopy.Arg(0), CCommandContext(CommandTarget_t::CT_NO_TARGET, slot), commandCopy,
HookMode::Pre);
delete[] ppArgV;
return result;
}
} // namespace counterstrikesharp

View File

@@ -53,10 +53,10 @@ class ChatManager : public GlobalClass
void OnShutdown() override;
bool OnSayCommandPre(CBaseEntity* pController, CCommand& args);
bool OnSayCommandPost(CBaseEntity* pController, CCommand& args);
void OnSayCommandPost(CBaseEntity* pController, CCommand& args);
private:
bool InternalDispatch(CBaseEntity* pPlayerController, const char* szTriggerPhrase,
void InternalDispatch(CBaseEntity* pPlayerController, const char* szTriggerPhrase,
CCommand& pFullCommand);
std::vector<ChatCommandInfo*> m_cmd_list;

View File

@@ -1,147 +0,0 @@
#include "core/managers/client_command_manager.h"
#include <public/eiface.h>
#include <algorithm>
#include "scripting/callback_manager.h"
#include "core/log.h"
namespace counterstrikesharp {
ClientCommandManager::ClientCommandManager() {}
ClientCommandManager::~ClientCommandManager() {}
void ClientCommandManager::OnAllInitialized()
{
m_global_cmd.callback_pre = globals::callbackManager.CreateCallback("OnClientCommandGlobalPre");
m_global_cmd.callback_post =
globals::callbackManager.CreateCallback("OnClientCommandGlobalPost");
}
void ClientCommandManager::OnShutdown() {}
bool ClientCommandManager::DispatchClientCommand(CPlayerSlot slot, const char* cmd,
const CCommand* args)
{
CSSHARP_CORE_TRACE("Dispatch client command {}", cmd);
auto* p_info = m_cmd_lookup[cmd];
bool result = false;
if (m_global_cmd.callback_pre->GetFunctionCount() > 0) {
m_global_cmd.callback_pre->ScriptContext().Reset();
m_global_cmd.callback_pre->ScriptContext().Push(slot.Get());
m_global_cmd.callback_pre->ScriptContext().Push(args);
for (auto fnMethodToCall : m_global_cmd.callback_pre->GetFunctions()) {
if (!fnMethodToCall)
continue;
fnMethodToCall(&m_global_cmd.callback_pre->ScriptContextStruct());
auto hookResult = m_global_cmd.callback_pre->ScriptContext().GetResult<HookResult>();
CSSHARP_CORE_TRACE("Received hook result from command callback {}:{}", cmd, hookResult);
if (hookResult >= HookResult::Stop) {
return true;
} else if (hookResult >= HookResult::Handled) {
result = true;
}
}
}
if (p_info && p_info->callback_pre) {
p_info->callback_pre->ScriptContext().Reset();
p_info->callback_pre->ScriptContext().Push(slot.Get());
p_info->callback_pre->ScriptContext().Push(args);
for (auto fnMethodToCall : p_info->callback_pre->GetFunctions()) {
if (!fnMethodToCall)
continue;
fnMethodToCall(&p_info->callback_pre->ScriptContextStruct());
auto hookResult = p_info->callback_pre->ScriptContext().GetResult<HookResult>();
CSSHARP_CORE_TRACE("Received hook result from command callback {}:{}", cmd, hookResult);
if (hookResult >= HookResult::Stop) {
return true;
} else if (hookResult >= HookResult::Handled) {
result = true;
}
}
}
if (m_global_cmd.callback_post->GetFunctionCount() > 0) {
m_global_cmd.callback_post->ScriptContext().Reset();
m_global_cmd.callback_post->ScriptContext().Push(slot.Get());
m_global_cmd.callback_post->ScriptContext().Push(args);
m_global_cmd.callback_post->Execute();
}
if (result && p_info && p_info->callback_post) {
p_info->callback_post->ScriptContext().Reset();
p_info->callback_post->ScriptContext().Push(slot.Get());
p_info->callback_post->ScriptContext().Push(args);
p_info->callback_post->Execute();
}
return result;
}
void ClientCommandManager::AddCommandListener(const char* cmd, CallbackT callback, bool bPost)
{
// Handle global command listeners that listen for every ClientCommand.
if (cmd == nullptr) {
if (bPost) {
m_global_cmd.callback_post->AddListener(callback);
return;
}
m_global_cmd.callback_pre->AddListener(callback);
return;
}
auto* p_info = m_cmd_lookup[std::string(cmd)];
if (!p_info) {
p_info = new ClientCommandInfo();
p_info->command = cmd;
p_info->callback_pre = globals::callbackManager.CreateCallback(cmd);
p_info->callback_post = globals::callbackManager.CreateCallback(cmd);
m_cmd_list.push_back(p_info);
m_cmd_lookup[cmd] = p_info;
}
if (bPost) {
p_info->callback_post->AddListener(callback);
} else {
p_info->callback_pre->AddListener(callback);
}
}
void ClientCommandManager::RemoveCommandListener(const char* cmd, CallbackT callback, bool bPost)
{
if (cmd == nullptr) {
if (bPost) {
m_global_cmd.callback_post->RemoveListener(callback);
return;
}
m_global_cmd.callback_pre->RemoveListener(callback);
return;
}
auto* p_info = m_cmd_lookup[std::string(cmd)];
if (!p_info) {
return;
}
if (bPost) {
p_info->callback_post->RemoveListener(callback);
} else {
p_info->callback_pre->RemoveListener(callback);
}
}
} // namespace counterstrikesharp

View File

@@ -1,45 +0,0 @@
#pragma once
#include <map>
#include <vector>
#include "core/globals.h"
#include "core/global_listener.h"
#include "scripting/script_engine.h"
#include <string>
#include "playerslot.h"
namespace counterstrikesharp {
class ScriptCallback;
class ClientCommandInfo {
friend class ClientCommandManager;
public:
ClientCommandInfo() {}
private:
std::string command;
ScriptCallback* callback_pre;
ScriptCallback* callback_post;
};
class ClientCommandManager : public GlobalClass {
public:
ClientCommandManager();
~ClientCommandManager();
void OnAllInitialized() override;
void OnShutdown() override;
bool DispatchClientCommand(CPlayerSlot slot, const char* cmd, const CCommand* args);
void AddCommandListener(const char* cmd, CallbackT callback, bool bPost);
void RemoveCommandListener(const char* cmd, CallbackT callback, bool bPost);
private:
std::vector<ClientCommandInfo*> m_cmd_list;
std::map<std::string, ClientCommandInfo*> m_cmd_lookup;
ClientCommandInfo m_global_cmd;
};
} // namespace counterstrikesharp

View File

@@ -43,6 +43,8 @@
#include "core/memory.h"
#include "interfaces/cs2_interfaces.h"
#include <nlohmann/json.hpp>
#include "metamod_oslink.h"
using json = nlohmann::json;
namespace counterstrikesharp {
@@ -155,307 +157,187 @@ CON_COMMAND(dump_schema, "dump schema symbols")
output << std::setw(2) << j << std::endl;
}
SH_DECL_HOOK2_void(ConCommandHandle, Dispatch, SH_NOATTRIB, false, const CCommandContext&,
const CCommand&);
SH_DECL_HOOK3_void(ICvar, DispatchConCommand, SH_NOATTRIB, 0, ConCommandHandle,
const CCommandContext&, const CCommand&);
void ConCommandInfo::HookChange(CallbackT cb, bool post)
ConCommandInfo::ConCommandInfo()
{
if (post) {
this->callback_post->AddListener(cb);
} else {
this->callback_pre->AddListener(cb);
}
callback_pre = globals::callbackManager.CreateCallback("");
callback_post = globals::callbackManager.CreateCallback("");
}
ConCommandInfo::~ConCommandInfo()
{
globals::callbackManager.ReleaseCallback(callback_pre);
globals::callbackManager.ReleaseCallback(callback_post);
}
void ConCommandInfo::UnhookChange(CallbackT cb, bool post)
{
if (post) {
if (this->callback_post && this->callback_post->GetFunctionCount()) {
callback_post->RemoveListener(cb);
}
} else {
if (this->callback_pre && this->callback_pre->GetFunctionCount()) {
callback_pre->RemoveListener(cb);
}
}
}
ConCommandManager::ConCommandManager() : last_command_client(-1) {}
ConCommandManager::ConCommandManager() {}
ConCommandManager::~ConCommandManager() {}
void ConCommandManager::OnAllInitialized() {}
void ConCommandManager::OnAllInitialized()
{
SH_ADD_HOOK_MEMFUNC(ICvar, DispatchConCommand, globals::cvars, this,
&ConCommandManager::Hook_DispatchConCommand, false);
SH_ADD_HOOK_MEMFUNC(ICvar, DispatchConCommand, globals::cvars, this,
&ConCommandManager::Hook_DispatchConCommand_Post, true);
}
void ConCommandManager::OnShutdown() {}
void ConCommandManager::OnShutdown()
{
SH_REMOVE_HOOK_MEMFUNC(ICvar, DispatchConCommand, globals::cvars, this,
&ConCommandManager::Hook_DispatchConCommand, false);
SH_REMOVE_HOOK_MEMFUNC(ICvar, DispatchConCommand, globals::cvars, this,
&ConCommandManager::Hook_DispatchConCommand_Post, true);
}
void CommandCallback(const CCommandContext& context, const CCommand& command)
{
bool rval = globals::conCommandManager.InternalDispatch(context.GetPlayerSlot(), &command);
if (rval) {
RETURN_META(MRES_SUPERCEDE);
}
// This is handled by the global hook
RETURN_META(MRES_SUPERCEDE);
}
void CommandCallback_Post(const CCommandContext& context, const CCommand& command)
void ConCommandManager::AddCommandListener(const char* name, CallbackT callback, HookMode mode)
{
bool rval = globals::conCommandManager.InternalDispatch_Post(context.GetPlayerSlot(), &command);
auto strName = std::string(name);
ConCommandInfo* pInfo = m_cmd_lookup[strName];
if (rval) {
RETURN_META(MRES_SUPERCEDE);
}
}
if (!pInfo) {
pInfo = new ConCommandInfo();
m_cmd_lookup[strName] = pInfo;
ConCommandInfo* ConCommandManager::AddOrFindCommand(const char* name, const char* description,
bool server_only, int flags)
{
ConCommandInfo* p_info = m_cmd_lookup[std::string(name)];
if (!p_info) {
CSSHARP_CORE_TRACE("[ConCommandManager] Could not find command in existing lookup {}",
name);
// auto found = std::find_if(m_cmd_list.begin(), m_cmd_list.end(),
// [&](ConCommandInfo* info) {
// return V_strcasecmp(info->command->GetName(), name) == 0;
// });
// if (found != m_cmd_list.end()) {
// return *found;
// }
p_info = new ConCommandInfo();
ConCommandHandle existingCommand = globals::cvars->FindCommand(name);
ConCommandRefAbstract pointerConCommand;
p_info->p_cmd = pointerConCommand;
if (!existingCommand.IsValid()) {
if (!description) {
description = "";
}
CSSHARP_CORE_TRACE("[ConCommandManager] Creating new command {}", name);
char* new_name = strdup(name);
char* new_desc = strdup(description);
CSSHARP_CORE_TRACE("[ConCommandManager] Creating new command {}, {}, {}, {}, {}",
(void*)&pointerConCommand, new_name, (void*)CommandCallback,
new_desc, flags);
auto conCommand =
new ConCommand(&pointerConCommand, new_name, CommandCallback, new_desc, flags);
CSSHARP_CORE_TRACE("[ConCommandManager] Creating callbacks for command {}", name);
p_info->command = conCommand;
p_info->callback_pre = globals::callbackManager.CreateCallback(name);
p_info->callback_post = globals::callbackManager.CreateCallback(name);
p_info->server_only = server_only;
CSSHARP_CORE_TRACE(
"[ConCommandManager] Adding hooks for command callback for command {}", name);
SH_ADD_HOOK(ConCommandHandle, Dispatch, &pointerConCommand.handle,
SH_STATIC(CommandCallback), false);
SH_ADD_HOOK(ConCommandHandle, Dispatch, &pointerConCommand.handle,
SH_STATIC(CommandCallback_Post), true);
CSSHARP_CORE_TRACE("[ConCommandManager] Adding command to internal lookup {}", name);
m_cmd_list.push_back(p_info);
m_cmd_lookup[name] = p_info;
} else {
p_info->callback_pre = globals::callbackManager.CreateCallback(name);
p_info->callback_post = globals::callbackManager.CreateCallback(name);
p_info->server_only = server_only;
ConCommandHandle hExistingCommand = globals::cvars->FindCommand(name);
if (hExistingCommand.IsValid()) {
pInfo->command = globals::cvars->GetCommand(hExistingCommand);
}
return p_info;
}
return p_info;
}
ConCommandInfo* ConCommandManager::AddCommand(const char* name, const char* description,
bool server_only, int flags, CallbackT callback)
{
ConCommandInfo* p_info = AddOrFindCommand(name, description, server_only, flags);
if (!p_info || !p_info->callback_pre) {
return nullptr;
if (mode == HookMode::Pre) {
pInfo->callback_pre->AddListener(callback);
} else {
pInfo->callback_post->AddListener(callback);
}
p_info->callback_pre->AddListener(callback);
return p_info;
}
bool ConCommandManager::RemoveCommand(const char* name, CallbackT callback)
void ConCommandManager::RemoveCommandListener(const char* name, CallbackT callback, HookMode mode)
{
auto strName = std::string(strdup(name));
ConCommandInfo* p_info = m_cmd_lookup[strName];
if (!p_info)
auto strName = std::string(name);
ConCommandInfo* pInfo = m_cmd_lookup[strName];
if (!pInfo) {
return;
}
if (mode == HookMode::Pre) {
pInfo->callback_pre->RemoveListener(callback);
} else {
pInfo->callback_post->RemoveListener(callback);
}
}
bool ConCommandManager::AddValveCommand(const char* name, const char* description, bool server_only,
int flags)
{
ConCommandHandle hExistingCommand = globals::cvars->FindCommand(name);
if (hExistingCommand.IsValid())
return false;
if (p_info->callback_pre && p_info->callback_pre->GetFunctionCount()) {
p_info->callback_pre->RemoveListener(callback);
ConCommandRefAbstract conCommandRefAbstract;
auto conCommand =
new ConCommand(&conCommandRefAbstract, strdup(name), CommandCallback, strdup(description), flags);
ConCommandInfo* pInfo = m_cmd_lookup[std::string(name)];
if (!pInfo) {
pInfo = new ConCommandInfo();
m_cmd_lookup[std::string(name)] = pInfo;
}
if (p_info->callback_post && p_info->callback_post->GetFunctionCount()) {
p_info->callback_post->RemoveListener(callback);
}
if (!p_info->callback_pre || p_info->callback_pre->GetFunctionCount() == 0) {
// It does not look like this actually removes the con command.
// You can still find with `find` command.
globals::cvars->UnregisterConCommand(p_info->p_cmd.handle);
}
pInfo->p_cmd = conCommandRefAbstract;
pInfo->command = conCommand;
pInfo->server_only = server_only;
return true;
}
ConCommandInfo* ConCommandManager::FindCommand(const char* name)
bool ConCommandManager::RemoveValveCommand(const char* name)
{
ConCommandInfo* p_info = m_cmd_lookup[std::string(name)];
auto hFoundCommand = globals::cvars->FindCommand(name);
if (p_info == nullptr) {
auto found = std::find_if(m_cmd_list.begin(), m_cmd_list.end(), [&](ConCommandInfo* info) {
return V_strcasecmp(info->command->GetName(), name) == 0;
});
if (found != m_cmd_list.end()) {
return *found;
}
ConCommandHandle p_cmd = globals::cvars->FindCommand(name);
if (!p_cmd.IsValid())
return nullptr;
p_info = new ConCommandInfo();
p_info->command = globals::cvars->GetCommand(p_cmd);
p_info->p_cmd = *p_info->command->GetRef();
p_info->callback_pre = globals::callbackManager.CreateCallback(name);
p_info->callback_post = globals::callbackManager.CreateCallback(name);
p_info->server_only = false;
m_cmd_list.push_back(p_info);
m_cmd_lookup[name] = p_info;
return p_info;
}
return p_info;
}
int ConCommandManager::GetCommandClient() { return last_command_client; }
void ConCommandManager::SetCommandClient(int client) { last_command_client = client + 1; }
bool ConCommandManager::InternalDispatch(CPlayerSlot slot, const CCommand* args)
{
const char* cmd = args->Arg(0);
ConCommandInfo* p_info = m_cmd_lookup[cmd];
if (p_info == nullptr) {
if (slot.Get() == 0 && !globals::engine->IsDedicatedServer())
return false;
for (ConCommandInfo* cmdInfo : m_cmd_list) {
if ((cmdInfo != nullptr) && strcasecmp(cmdInfo->command->GetName(), cmd) == 0) {
p_info = cmdInfo;
continue;
}
}
}
if (!p_info) {
if (!hFoundCommand.IsValid()) {
return false;
}
int realClient = slot.Get();
globals::cvars->UnregisterConCommand(hFoundCommand);
bool result = false;
if (p_info->callback_pre) {
p_info->callback_pre->ScriptContext().Reset();
p_info->callback_pre->ScriptContext().SetArgument(0, realClient);
p_info->callback_pre->ScriptContext().SetArgument(1, args);
p_info->callback_pre->Execute(false);
result = p_info->callback_pre->ScriptContext().GetResult<bool>();
auto pInfo = m_cmd_lookup[std::string(name)];
if (!pInfo) {
return true;
}
return result;
pInfo->command = nullptr;
return true;
}
bool ConCommandManager::InternalDispatch_Post(CPlayerSlot slot, const CCommand* args)
HookResult ConCommandManager::ExecuteCommandCallbacks(const char* name, const CCommandContext& ctx,
const CCommand& args, HookMode mode)
{
const char* cmd = args->Arg(0);
CSSHARP_CORE_TRACE("[ConCommandManager::ExecuteCommandCallbacks][{}]: {}", mode == Pre ? "Pre" : "Post", name);
ConCommandInfo* pInfo = m_cmd_lookup[std::string(name)];
if (!pInfo) {
return HookResult::Continue;
}
ConCommandInfo* p_info = m_cmd_lookup[cmd];
if (p_info == nullptr) {
if (slot.Get() == 0 && !globals::engine->IsDedicatedServer())
return false;
auto pCallback = mode == HookMode::Pre ? pInfo->callback_pre : pInfo->callback_post;
for (ConCommandInfo* cmdInfo : m_cmd_list) {
if ((cmdInfo != nullptr) && strcasecmp(cmdInfo->command->GetName(), cmd) == 0) {
p_info = cmdInfo;
continue;
}
pCallback->Reset();
pCallback->ScriptContext().Push(ctx.GetPlayerSlot().Get());
pCallback->ScriptContext().Push(&args);
for (auto fnMethodToCall : pCallback->GetFunctions()) {
if (!fnMethodToCall)
continue;
fnMethodToCall(&pCallback->ScriptContextStruct());
auto result = pCallback->ScriptContext().GetResult<HookResult>();
if (result >= HookResult::Handled) {
return result;
}
}
int realClient = slot.Get();
bool result = false;
if (p_info->callback_post) {
p_info->callback_post->ScriptContext().Reset();
p_info->callback_post->ScriptContext().SetArgument(0, realClient);
p_info->callback_post->ScriptContext().SetArgument(1, args);
p_info->callback_post->Execute(false);
result = p_info->callback_post->ScriptContext().GetResult<bool>();
}
return result;
return HookResult::Continue;
}
bool ConCommandManager::DispatchClientCommand(CPlayerSlot slot, const char* cmd,
const CCommand* args)
void ConCommandManager::Hook_DispatchConCommand(ConCommandHandle cmd, const CCommandContext& ctx,
const CCommand& args)
{
ConCommandInfo* p_info = m_cmd_lookup[cmd];
if (p_info == nullptr) {
auto found =
std::find_if(m_cmd_list.begin(), m_cmd_list.end(), [&](const ConCommandInfo* info) {
return V_strcasecmp(info->command->GetName(), cmd) == 0;
});
if (found == m_cmd_list.end()) {
return false;
}
const char* name = args.Arg(0);
p_info = *found;
CSSHARP_CORE_TRACE("[ConCommandManager::Hook_DispatchConCommand]: {}", name);
auto result = ExecuteCommandCallbacks(name, ctx, args, HookMode::Pre);
if (result >= HookResult::Handled) {
RETURN_META(MRES_SUPERCEDE);
}
if (p_info->server_only)
return false;
bool result = false;
if (p_info->callback_pre) {
p_info->callback_pre->ScriptContext().Reset();
p_info->callback_pre->ScriptContext().Push(slot.Get());
p_info->callback_pre->ScriptContext().Push(args);
p_info->callback_pre->Execute();
result = true;
}
if (result) {
if (p_info->callback_post) {
p_info->callback_post->ScriptContext().Reset();
p_info->callback_post->ScriptContext().Push(slot.Get());
p_info->callback_post->ScriptContext().Push(args);
p_info->callback_post->Execute();
result = true;
}
}
return result;
}
void ConCommandManager::Hook_DispatchConCommand_Post(ConCommandHandle cmd,
const CCommandContext& ctx,
const CCommand& args)
{
const char* name = args.Arg(0);
auto result = ExecuteCommandCallbacks(name, ctx, args, HookMode::Post);
if (result >= HookResult::Handled) {
RETURN_META(MRES_SUPERCEDE);
}
}
bool ConCommandManager::IsValidValveCommand(const char* name) {
ConCommandHandle pCmd = globals::cvars->FindCommand(name);
return pCmd.IsValid();
}
} // namespace counterstrikesharp

View File

@@ -40,6 +40,16 @@
#include <string>
#include "playerslot.h"
struct CaseInsensitiveComparator {
bool operator()(const std::string& lhs, const std::string& rhs) const {
return std::lexicographical_compare(
lhs.begin(), lhs.end(),
rhs.begin(), rhs.end(),
[](char a, char b) { return std::tolower(a) < std::tolower(b); }
);
}
};
namespace counterstrikesharp {
class ScriptCallback;
@@ -47,7 +57,8 @@ class ConCommandInfo {
friend class ConCommandManager;
public:
ConCommandInfo() {}
ConCommandInfo();
~ConCommandInfo();
public:
void HookChange(CallbackT callback, bool post);
@@ -64,39 +75,26 @@ private:
class ConCommandManager : public GlobalClass {
friend class ConCommandInfo;
friend void CommandCallback(const CCommand& command);
friend void CommandCallback_Post(const CCommand& command);
public:
ConCommandManager();
~ConCommandManager();
void OnAllInitialized() override;
void OnShutdown() override;
ConCommandInfo* AddOrFindCommand(const char* name,
const char* description,
bool server_only,
int flags);
bool DispatchClientCommand(CPlayerSlot slot, const char* cmd, const CCommand* args);
bool InternalDispatch(CPlayerSlot slot, const CCommand* args);
int GetCommandClient();
bool InternalDispatch_Post(CPlayerSlot slot, const CCommand* args);
public:
ConCommandInfo* AddCommand(
const char* name, const char* description, bool server_only, int flags, CallbackT callback);
bool RemoveCommand(const char* name, CallbackT callback);
ConCommandInfo* FindCommand(const char* name);
void AddCommandListener(const char* name, CallbackT callback, HookMode mode);
void RemoveCommandListener(const char* name, CallbackT callback, HookMode mode);
bool IsValidValveCommand(const char* name);
bool AddValveCommand(const char* name, const char* description, bool server_only, int flags);
bool RemoveValveCommand(const char* name);
void Hook_DispatchConCommand(ConCommandHandle cmd, const CCommandContext& ctx, const CCommand& args);
void Hook_DispatchConCommand_Post(ConCommandHandle cmd, const CCommandContext& ctx, const CCommand& args);
HookResult ExecuteCommandCallbacks(const char* name, const CCommandContext& ctx,
const CCommand& args, HookMode mode);
private:
void SetCommandClient(int client);
private:
int last_command_client;
std::vector<ConCommandInfo*> m_cmd_list;
std::map<std::string, ConCommandInfo*> m_cmd_lookup;
std::map<std::string, ConCommandInfo*, CaseInsensitiveComparator> m_cmd_lookup;
};
} // namespace counterstrikesharp

View File

@@ -44,6 +44,8 @@ class CUtlString;
#include "core/globals.h"
#include "scripting/script_engine.h"
namespace counterstrikesharp {
class ScriptCallback;
class PluginFunction;

View File

@@ -30,7 +30,7 @@
*/
#include "core/managers/player_manager.h"
#include "core/managers/client_command_manager.h"
#include "core/managers/con_command_manager.h"
#include <public/eiface.h>
#include <public/inetchannelinfo.h>
@@ -287,8 +287,10 @@ void PlayerManager::OnClientCommand(CPlayerSlot slot, const CCommand& args) cons
const char* cmd = args.Arg(0);
bool response = globals::clientCommandManager.DispatchClientCommand(slot, cmd, &args);
if (response) {
auto result = globals::conCommandManager.ExecuteCommandCallbacks(
cmd, CCommandContext(CommandTarget_t::CT_NO_TARGET, slot), args, HookMode::Pre);
if (result >= HookResult::Handled) {
RETURN_META(MRES_SUPERCEDE);
}
}
@@ -381,6 +383,7 @@ bool CPlayer::IsAuthStringValidated() const
if (!IsFakeClient()) {
return globals::engine->IsClientFullyAuthenticated(m_slot);
}
return false;
}
void CPlayer::Authorize() { m_is_authorized = true; }

View File

@@ -21,12 +21,22 @@
#include <cstdlib>
#include <cstring>
#if __linux__
#include <dlfcn.h>
#include <elf.h>
#include <link.h>
#else
#include <Windows.h>
#include <Psapi.h>
#endif
#include <cstdio>
#include "memory_module.h"
#include "metamod_oslink.h"
#include "wchartypes.h"
#if __linux__
struct ModuleInfo {
const char *path; // in
uint8_t *base; // out
@@ -106,6 +116,7 @@ int GetModuleInformation(void *hModule, void **base, size_t *length) {
return 0;
}
#endif
byte *ConvertToByteArray(const char *str, size_t *outLength) {
size_t len = strlen(str) / 4; // Every byte is represented as \xHH
@@ -124,17 +135,24 @@ void* FindSignature(const char* moduleName, const char* bytesStr) {
size_t iSigLength;
auto sigBytes = ConvertToByteArray(bytesStr, &iSigLength);
auto module = dlopen(moduleName, RTLD_NOW);
auto module = dlmount(moduleName);
if (module == nullptr) {
return nullptr;
}
void *moduleBase;
size_t moduleSize;
#if __linux__
if (GetModuleInformation(module, &moduleBase, &moduleSize) != 0) {
return nullptr;
}
#else
MODULEINFO m_hModuleInfo;
GetModuleInformation(GetCurrentProcess(), module, &m_hModuleInfo, sizeof(m_hModuleInfo));
moduleBase = (void*)m_hModuleInfo.lpBaseOfDll;
moduleSize = m_hModuleInfo.SizeOfImage;
#endif
unsigned char *pMemory;
void *returnAddr = nullptr;

View File

@@ -1,16 +1,18 @@
#include <cstring>
#pragma once
#ifdef _WIN32
#define ROOTBIN "/bin/win64/"
#define GAMEBIN "/csgo/bin/win64/"
#define MODULE_PREFIX ""
#define MODULE_EXT ".dll"
#else
#define ROOTBIN "/bin/linuxsteamrt64/"
#define GAMEBIN "/csgo/bin/linuxsteamrt64/"
#endif
#define MODULE_PREFIX "lib"
#define MODULE_EXT ".so"
#endif
#if __linux__
int GetModuleInformation(void *hModule, void **base, size_t *length);
#endif
void* FindSignature(const char* moduleName, const char* bytesStr);

View File

@@ -0,0 +1,83 @@
#include "core/memory_module.h"
#if _WIN32
#include <Psapi.h>
#endif
#include "dbg.h"
#include "core/gameconfig.h"
#include "core/memory.h"
#include "metamod_oslink.h"
namespace counterstrikesharp::modules {
CModule::CModule(const char* path, const char* module) : m_pszModule(module), m_pszPath(path)
{
char szModule[MAX_PATH];
V_snprintf(szModule, MAX_PATH, "%s%s%s%s%s", Plat_GetGameDirectory(), path, MODULE_PREFIX,
m_pszModule, MODULE_EXT);
m_hModule = dlmount(szModule);
if (!m_hModule)
Error("Could not find %s\n", szModule);
#ifdef _WIN32
MODULEINFO m_hModuleInfo;
GetModuleInformation(GetCurrentProcess(), m_hModule, &m_hModuleInfo, sizeof(m_hModuleInfo));
m_base = (void*)m_hModuleInfo.lpBaseOfDll;
m_size = m_hModuleInfo.SizeOfImage;
#else
if (int e = GetModuleInformation(m_hModule, &m_base, &m_size))
Error("Failed to get module info for %s, error %d\n", szModule, e);
#endif
}
void* CModule::FindSignature(const char* signature)
{
if (strlen(signature) == 0) {
return nullptr;
}
size_t iSigLength = 0;
byte* pData = CGameConfig::HexToByte(signature, iSigLength);
return this->FindSignature(pData, iSigLength);
}
void* CModule::FindSignature(const byte* pData, size_t iSigLength)
{
unsigned char* pMemory;
void* return_addr = nullptr;
pMemory = (byte*)m_base;
for (size_t i = 0; i < m_size; i++) {
size_t Matches = 0;
while (*(pMemory + i + Matches) == pData[Matches] || pData[Matches] == '\x2A') {
Matches++;
if (Matches == iSigLength)
return_addr = (void*)(pMemory + i);
}
}
return return_addr;
}
void* CModule::FindInterface(const char* name)
{
CreateInterfaceFn fn = (CreateInterfaceFn)dlsym(m_hModule, "CreateInterface");
if (!fn)
Error("Could not find CreateInterface in %s\n", m_pszModule);
void* pInterface = fn(name, nullptr);
if (!pInterface)
Error("Could not find %s in %s\n", name, m_pszModule);
return pInterface;
}
}

View File

@@ -18,80 +18,31 @@
*/
#pragma once
#include "dbg.h"
#include <cstdio>
#include "interface.h"
#include "strtools.h"
#include "metamod_oslink.h"
#include "memory.h"
#undef snprintf
#ifdef _WIN32
#include <Psapi.h>
#endif
namespace counterstrikesharp {
namespace modules {
namespace counterstrikesharp::modules {
class CModule {
public:
CModule(const char *path, const char *module)
: m_pszModule(module),
m_pszPath(path) {
char szModule[MAX_PATH];
class CModule
{
public:
CModule(const char* path, const char* module);
V_snprintf(szModule, MAX_PATH, "%s%s%s%s%s", Plat_GetGameDirectory(), path, MODULE_PREFIX,
m_pszModule, MODULE_EXT);
void* FindSignature(const char* signature);
m_hModule = dlmount(szModule);
void* FindSignature(const byte* pData, size_t iSigLength);
if (!m_hModule) Error("Could not find %s\n", szModule);
void* FindInterface(const char* name);
#ifdef _WIN32
MODULEINFO m_hModuleInfo;
GetModuleInformation(GetCurrentProcess(), m_hModule, &m_hModuleInfo, sizeof(m_hModuleInfo));
m_base = (void *)m_hModuleInfo.lpBaseOfDll;
m_size = m_hModuleInfo.SizeOfImage;
#else
if (int e = GetModuleInformation(m_hModule, &m_base, &m_size))
Error("Failed to get module info for %s, error %d\n", szModule, e);
#endif
}
void *FindSignature(const byte *pData) {
unsigned char *pMemory;
void *return_addr = nullptr;
size_t iSigLength = V_strlen((const char *)pData);
pMemory = (byte *)m_base;
for (size_t i = 0; i < m_size; i++) {
size_t Matches = 0;
while (*(pMemory + i + Matches) == pData[Matches] || pData[Matches] == '\x2A') {
Matches++;
if (Matches == iSigLength) return_addr = (void *)(pMemory + i);
}
}
return return_addr;
}
void *FindInterface(const char *name) {
CreateInterfaceFn fn = (CreateInterfaceFn)dlsym(m_hModule, "CreateInterface");
if (!fn) Error("Could not find CreateInterface in %s\n", m_pszModule);
void *pInterface = fn(name, nullptr);
if (!pInterface) Error("Could not find %s in %s\n", name, m_pszModule);
return pInterface;
}
const char *m_pszModule;
const char *m_pszPath;
const char* m_pszModule;
const char* m_pszPath;
HINSTANCE m_hModule;
void *m_base;
void* m_base;
size_t m_size;
};
} // namespace modules
} // namespace counterstrikesharp
} // namespace counterstrikesharp::modules

View File

@@ -18,6 +18,7 @@
#include "core/global_listener.h"
#include "core/log.h"
#include "core/gameconfig.h"
#include "core/timer_system.h"
#include "core/utils.h"
#include "core/managers/entity_manager.h"
@@ -29,9 +30,12 @@
#include "entity2/entitysystem.h"
#include "interfaces/cs2_interfaces.h"
counterstrikesharp::GlobalClass* counterstrikesharp::GlobalClass::head = nullptr;
extern "C" void InvokeNative(counterstrikesharp::fxNativeContext& context)
// TODO: Workaround for windows, we __MUST__ have COUNTERSTRIKESHARP_API to handle it.
// like on windows it should be `extern "C" __declspec(dllexport)`, on linux it should be anything else.
DLL_EXPORT void InvokeNative(counterstrikesharp::fxNativeContext& context)
{
if (context.nativeIdentifier == 0)
return;
@@ -79,6 +83,15 @@ bool CounterStrikeSharpMMPlugin::Load(PluginId id, ISmmAPI* ismm, char* error, s
GET_V_IFACE_ANY(GetEngineFactory, globals::gameEventSystem, IGameEventSystem,
GAMEEVENTSYSTEM_INTERFACE_VERSION);
auto gamedata_path = std::string(utils::GamedataDirectory() + "/gamedata.json");
globals::gameConfig = new CGameConfig(gamedata_path);
char conf_error[255] = "";
if (!globals::gameConfig->Init(conf_error, sizeof(conf_error))) {
CSSHARP_CORE_ERROR("Could not read \'{}\'. Error: {}", gamedata_path, conf_error);
return false;
}
globals::Initialize();
CSSHARP_CORE_INFO("Globals loaded.");

View File

@@ -35,7 +35,7 @@ void ScriptCallback::AddListener(CallbackT fnPluginFunction)
bool ScriptCallback::RemoveListener(CallbackT fnPluginFunction)
{
bool bSuccess;
bool bSuccess = true;
m_functions.erase(std::remove(m_functions.begin(), m_functions.end(), fnPluginFunction),
m_functions.end());

View File

@@ -1,3 +1,19 @@
/*
* 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 "scripting/dotnet_host.h"
#include <dotnet/coreclr_delegates.h>
@@ -7,15 +23,19 @@
#include <locale>
#ifdef WIN32
#include <Windows.h>
#include <direct.h>
#include <Windows.h>
#include <direct.h>
#define STR(s) L##s
#define CH(c) L##c
#define DIR_SEPARATOR L'\\'
#define STR(s) L##s
#define CH(c) L##c
#define DIR_SEPARATOR L'\\'
#else
#include <dlfcn.h>
#define STR(s) s
#define CH(c) c
#define DIR_SEPARATOR '/'
#include <dlfcn.h>
#endif
#include <cassert>
@@ -24,6 +44,8 @@
#include "core/log.h"
#include "core/utils.h"
#include "utils/string.h"
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
namespace {
@@ -33,34 +55,38 @@ hostfxr_close_fn close_fptr;
hostfxr_handle cxt;
bool load_hostfxr();
load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t *assembly);
} // namespace
load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t* assembly);
} // namespace
namespace {
// Forward declarations
void *load_library(const char_t *);
void *get_export(void *, const char *);
void* load_library(const char_t*);
void* get_export(void*, const char*);
#ifdef _WINDOWS
void *load_library(const char_t *path) {
void* load_library(const char_t* path)
{
HMODULE h = ::LoadLibraryW(path);
assert(h != nullptr);
return (void *)h;
return (void*)h;
}
void *get_export(void *h, const char *name) {
void *f = ::GetProcAddress((HMODULE)h, name);
void* get_export(void* h, const char* name)
{
void* f = ::GetProcAddress((HMODULE)h, name);
assert(f != nullptr);
return f;
}
#else
void *load_library(const char_t *path) {
void *h = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
void* load_library(const char_t* path)
{
void* h = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
assert(h != nullptr);
return h;
}
void *get_export(void *h, const char *name) {
void *f = dlsym(h, name);
void* get_export(void* h, const char* name)
{
void* f = dlsym(h, name);
assert(f != nullptr);
return f;
}
@@ -68,20 +94,39 @@ void *get_export(void *h, const char *name) {
// <SnippetLoadHostFxr>
// Using the nethost library, discover the location of hostfxr and get exports
bool load_hostfxr() {
std::string baseDir = counterstrikesharp::utils::PluginDirectory();
std::string buffer = std::string(baseDir + "/dotnet/host/fxr/7.0.11/libhostfxr.so");
CSSHARP_CORE_TRACE("Loading hostfxr from {0}", buffer.c_str());
bool load_hostfxr()
{
std::string base_dir = counterstrikesharp::utils::PluginDirectory();
namespace css = counterstrikesharp;
#if _WIN32
std::wstring buffer =
std::wstring(css::widen(base_dir) + L"\\dotnet\\host\\fxr\\7.0.11\\hostfxr.dll");
CSSHARP_CORE_INFO("Loading hostfxr from {0}", css::narrow(buffer).c_str());
#else
std::string buffer = std::string(base_dir + "/dotnet/host/fxr/7.0.11/libhostfxr.so");
CSSHARP_CORE_INFO("Loading hostfxr from {0}", buffer.c_str());
#endif
// Load hostfxr and get desired exports
void *lib = load_library(buffer.c_str());
void* lib = load_library(buffer.c_str());
init_fptr = (hostfxr_initialize_for_runtime_config_fn)get_export(
lib, "hostfxr_initialize_for_runtime_config");
if (init_fptr == nullptr) {
CSSHARP_CORE_CRITICAL(
"unable to get export function: \"hostfxr_initialize_for_runtime_config\"");
return false;
}
get_delegate_fptr =
(hostfxr_get_runtime_delegate_fn)get_export(lib, "hostfxr_get_runtime_delegate");
if (!get_delegate_fptr) {
CSSHARP_CORE_CRITICAL("unable to get export function: \"hostfxr_get_runtime_delegate\"");
return false;
}
close_fptr = (hostfxr_close_fn)get_export(lib, "hostfxr_close");
if (!close_fptr) {
CSSHARP_CORE_CRITICAL("unable to get export function: \"hostfxr_close\"");
return false;
}
return (init_fptr && get_delegate_fptr && close_fptr);
}
@@ -89,12 +134,13 @@ bool load_hostfxr() {
// <SnippetInitialize>
// Load and initialize .NET Core and get desired function pointer for scenario
load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t *config_path) {
load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t* config_path)
{
// Load .NET Core
void *load_assembly_and_get_function_pointer = nullptr;
void* load_assembly_and_get_function_pointer = nullptr;
int rc = init_fptr(config_path, nullptr, &cxt);
if (rc != 0 || cxt == nullptr) {
std::cerr << "Init failed: " << std::hex << std::showbase << rc << std::endl;
CSSHARP_CORE_CRITICAL("Init failed: {0:x}", rc);
close_fptr(cxt);
return nullptr;
}
@@ -102,53 +148,75 @@ load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t
// Get the load assembly function pointer
rc = get_delegate_fptr(cxt, hdt_load_assembly_and_get_function_pointer,
&load_assembly_and_get_function_pointer);
if (rc != 0 || load_assembly_and_get_function_pointer == nullptr)
std::cerr << "Get delegate failed: " << std::hex << std::showbase << rc << std::endl;
if (rc != 0 || load_assembly_and_get_function_pointer == nullptr) {
CSSHARP_CORE_ERROR("Get delegate failed: {0:x}", rc);
}
// close_fptr(cxt);
return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer;
}
} // namespace
} // namespace
CDotNetManager::CDotNetManager() {}
CDotNetManager::~CDotNetManager() {}
bool CDotNetManager::Initialize() {
std::string baseDir = counterstrikesharp::utils::PluginDirectory();
bool CDotNetManager::Initialize()
{
const std::string base_dir = counterstrikesharp::utils::PluginDirectory();
CSSHARP_CORE_INFO("Loading .NET runtime...");
if (!load_hostfxr()) {
CSSHARP_CORE_ERROR("Failed to initialize .NET");
CSSHARP_CORE_ERROR("Failed to initialize .NET runtime.");
return false;
}
CSSHARP_CORE_INFO(".NET Runtime Initialised.");
namespace css = counterstrikesharp;
#if _WIN32
const auto wide_str =
std::wstring(css::widen(base_dir) + L"\\api\\CounterStrikeSharp.API.runtimeconfig.json");
CSSHARP_CORE_INFO("Loading CSS API, Runtime config: {}",
counterstrikesharp::narrow(wide_str).c_str());
#else
std::string wide_str =
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.");
return false;
}
CSSHARP_CORE_INFO(".NET Runtime Initialised.");
std::string wideStr =
std::string((baseDir + "/api/CounterStrikeSharp.API.runtimeconfig.json").c_str());
load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer = nullptr;
load_assembly_and_get_function_pointer = get_dotnet_load_assembly(wideStr.c_str());
assert(load_assembly_and_get_function_pointer != nullptr &&
"Failure: get_dotnet_load_assembly()");
#if _WIN32
const auto dotnetlib_path =
std::wstring(css::widen(base_dir) + L"\\api\\CounterStrikeSharp.API.dll");
CSSHARP_CORE_INFO("CSS API DLL: {}", counterstrikesharp::narrow(dotnetlib_path));
#else
const std::string dotnetlib_path =
std::string((baseDir + "/api/CounterStrikeSharp.API.dll").c_str());
const char_t *dotnet_type = "CounterStrikeSharp.API.Core.Helpers, CounterStrikeSharp.API";
std::string((base_dir + "/api/CounterStrikeSharp.API.dll").c_str());
#endif
const auto dotnet_type = STR("CounterStrikeSharp.API.Core.Helpers, CounterStrikeSharp.API");
// Namespace, assembly name
typedef int(CORECLR_DELEGATE_CALLTYPE * custom_entry_point_fn)();
custom_entry_point_fn entry_point = nullptr;
int rc = load_assembly_and_get_function_pointer(dotnetlib_path.c_str(), dotnet_type,
"LoadAllPlugins", UNMANAGEDCALLERSONLY_METHOD,
nullptr, (void **)&entry_point);
const int rc = load_assembly_and_get_function_pointer(
dotnetlib_path.c_str(), dotnet_type, STR("LoadAllPlugins"), UNMANAGEDCALLERSONLY_METHOD,
nullptr, reinterpret_cast<void**>(&entry_point));
if (entry_point == nullptr) {
CSSHARP_CORE_ERROR("Trying to get entry point \"LoadAllPlugins\" but failed.");
return false;
}
assert(rc == 0 && entry_point != nullptr &&
"Failure: load_assembly_and_get_function_pointer()");
const bool success = entry_point();
if (!success) {
CSSHARP_CORE_ERROR("Failed to initialize .NET");
if (const int invoke_result_code = entry_point(); invoke_result_code == 0) {
CSSHARP_CORE_ERROR("LoadAllPlugins return failure.");
return false;
}
@@ -156,10 +224,13 @@ bool CDotNetManager::Initialize() {
return true;
}
void CDotNetManager::UnloadPlugin(PluginContext *context) {}
void CDotNetManager::UnloadPlugin(PluginContext* context) {}
void CDotNetManager::Shutdown() {
void CDotNetManager::Shutdown()
{
// CoreCLR does not currently supporting unloading... :(
// I think this is intentionally, you should handle Init/Shutdown manually.
// Better rework in the future, but not now.
}
PluginContext *CDotNetManager::FindContext(std::string path) { return nullptr; }
PluginContext* CDotNetManager::FindContext(std::string path) { return nullptr; }

View File

@@ -1,3 +1,19 @@
/*
* 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 <memory>

View File

@@ -16,7 +16,6 @@
#include <eiface.h>
#include "core/managers/client_command_manager.h"
#include "scripting/autonative.h"
#include "scripting/callback_manager.h"
#include "core/managers/con_command_manager.h"
@@ -26,7 +25,7 @@
namespace counterstrikesharp {
static ConCommandInfo* AddCommand(ScriptContext& script_context)
static void AddCommand(ScriptContext& script_context)
{
auto name = script_context.GetArgument<const char*>(0);
auto description = script_context.GetArgument<const char*>(1);
@@ -37,7 +36,8 @@ static ConCommandInfo* AddCommand(ScriptContext& script_context)
CSSHARP_CORE_TRACE("Adding command {}, {}, {}, {}, {}", name, description, server_only, flags,
(void*)callback);
return globals::conCommandManager.AddCommand(name, description, server_only, flags, callback);
globals::conCommandManager.AddValveCommand(name, description, server_only, flags);
globals::conCommandManager.AddCommandListener(name, callback, HookMode::Pre);
}
static void RemoveCommand(ScriptContext& script_context)
@@ -45,7 +45,8 @@ static void RemoveCommand(ScriptContext& script_context)
auto name = script_context.GetArgument<const char*>(0);
auto callback = script_context.GetArgument<CallbackT>(1);
globals::conCommandManager.RemoveCommand(name, callback);
globals::conCommandManager.RemoveCommandListener(name, callback, HookMode::Pre);
globals::conCommandManager.RemoveValveCommand(name);
}
static void AddCommandListener(ScriptContext& script_context)
@@ -54,7 +55,7 @@ static void AddCommandListener(ScriptContext& script_context)
auto callback = script_context.GetArgument<CallbackT>(1);
auto post = script_context.GetArgument<bool>(2);
globals::clientCommandManager.AddCommandListener(name, callback, post);
globals::conCommandManager.AddCommandListener(name, callback, post ? HookMode::Post : HookMode::Pre);
}
static void RemoveCommandListener(ScriptContext& script_context)
@@ -63,7 +64,7 @@ static void RemoveCommandListener(ScriptContext& script_context)
auto callback = script_context.GetArgument<CallbackT>(1);
auto post = script_context.GetArgument<bool>(2);
globals::clientCommandManager.RemoveCommandListener(name, callback, post);
globals::conCommandManager.RemoveCommandListener(name, callback, post ? HookMode::Post : HookMode::Pre);
}
static int CommandGetArgCount(ScriptContext& script_context)

View File

@@ -1,4 +1,4 @@
ADD_COMMAND: name:string,description:string,serverOnly:bool,flags:int,callback:callback -> pointer
ADD_COMMAND: name:string,description:string,serverOnly:bool,flags:int,callback:callback -> void
REMOVE_COMMAND: name:string,callback:callback -> void
ADD_COMMAND_LISTENER: cmd:string, callback:callback, post:bool -> void
REMOVE_COMMAND_LISTENER: cmd:string, callback:callback, post:bool -> void

View File

@@ -15,7 +15,6 @@
*/
#include <IEngineSound.h>
#include <dlfcn.h>
#include <edict.h>
#include <eiface.h>
#include <filesystem.h>
@@ -33,6 +32,10 @@
#include "core/function.h"
// clang-format on
#if _WIN32
#undef GetCurrentTime
#endif
namespace counterstrikesharp {
const char* GetMapName(ScriptContext& script_context)

View File

@@ -15,9 +15,11 @@
*/
#include "core/globals.h"
#include "core/log.h"
#include "core/managers/event_manager.h"
#include "scripting/autonative.h"
namespace counterstrikesharp {
std::vector<IGameEvent *> managed_game_events;
@@ -189,7 +191,7 @@ static void *GetPlayerController(ScriptContext &scriptContext) {
return gameEvent->GetPlayerController(keyName);
}
static void *SetPlayerController(ScriptContext &scriptContext) {
static void SetPlayerController(ScriptContext &scriptContext) {
IGameEvent *gameEvent = scriptContext.GetArgument<IGameEvent *>(0);
const char *keyName = scriptContext.GetArgument<const char *>(1);
auto *value = scriptContext.GetArgument<CEntityInstance *>(2);
@@ -199,7 +201,7 @@ static void *SetPlayerController(ScriptContext &scriptContext) {
}
}
static void *SetEntity(ScriptContext &scriptContext) {
static void SetEntity(ScriptContext &scriptContext) {
IGameEvent *gameEvent = scriptContext.GetArgument<IGameEvent *>(0);
const char *keyName = scriptContext.GetArgument<const char *>(1);
auto *value = scriptContext.GetArgument<CEntityInstance *>(2);
@@ -209,7 +211,7 @@ static void *SetEntity(ScriptContext &scriptContext) {
}
}
static void *SetEntityIndex(ScriptContext &scriptContext) {
static void SetEntityIndex(ScriptContext &scriptContext) {
IGameEvent *gameEvent = scriptContext.GetArgument<IGameEvent *>(0);
const char *keyName = scriptContext.GetArgument<const char *>(1);
auto index = scriptContext.GetArgument<int>(2);
@@ -243,7 +245,7 @@ static uint64 GetUint64(ScriptContext &scriptContext) {
return gameEvent->GetUint64(keyName);
}
static void *SetUint64(ScriptContext &scriptContext) {
static void SetUint64(ScriptContext &scriptContext) {
IGameEvent *gameEvent = scriptContext.GetArgument<IGameEvent *>(0);
const char *keyName = scriptContext.GetArgument<const char *>(1);
auto value = scriptContext.GetArgument<uint64>(2);

View File

@@ -16,7 +16,6 @@
#include <ios>
#include <sstream>
#include <dlfcn.h>
#include "scripting/autonative.h"
#include "core/function.h"

View File

@@ -16,7 +16,6 @@
#include <ios>
#include <sstream>
#include <dlfcn.h>
#include "scripting/autonative.h"
#include "scripting/script_engine.h"

View File

@@ -40,6 +40,11 @@ enum HookResult {
Stop = 4,
};
enum HookMode {
Pre = 0,
Post = 1,
};
inline uint32_t hash_string(const char *string) {
unsigned long result = 5381;

47
src/utils/string.h Normal file
View File

@@ -0,0 +1,47 @@
/**
* 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 <string>
#include <sstream>
namespace counterstrikesharp {
std::wstring widen(const std::string& str)
{
std::wostringstream wstm;
const auto& ctfacet = std::use_facet<std::ctype<wchar_t>>(wstm.getloc());
for (size_t i = 0; i < str.size(); ++i) {
wstm << ctfacet.widen(str[i]);
}
return wstm.str();
}
std::string narrow(const std::wstring& str)
{
std::ostringstream stm;
// Incorrect code from the link
// const ctype<char>& ctfacet = use_facet<ctype<char>>(stm.getloc());
// Correct code.
const auto& ctfacet = std::use_facet<std::ctype<wchar_t>>(stm.getloc());
for (size_t i = 0; i < str.size(); ++i) {
stm << ctfacet.narrow(str[i], 0);
}
return stm.str();
}
}