mirror of
https://github.com/roflmuffin/CounterStrikeSharp.git
synced 2025-12-07 08:26:34 -08:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e155a70873 | ||
|
|
69d9b5d2c8 | ||
|
|
933fdf9d81 | ||
|
|
18e9e37a98 | ||
|
|
fe236806e1 | ||
|
|
2c4e9bca42 | ||
|
|
8f3e0c226b | ||
|
|
5f6ccf2839 | ||
|
|
6c2f56793b | ||
|
|
cc7dd5ca96 | ||
|
|
ebc361b2f8 | ||
|
|
c72eff2546 | ||
|
|
4b432e9efc | ||
|
|
22bbf835c7 | ||
|
|
092a6077c3 | ||
|
|
4430060efd | ||
|
|
77ea6fd80d | ||
|
|
f18df3df2b | ||
|
|
4ce1ec2cf5 | ||
|
|
9005f3c29c | ||
|
|
b7ace4256a | ||
|
|
b725f7f79a | ||
|
|
cb6d86a54d | ||
|
|
d4a2ae68e1 | ||
|
|
82c92f555b | ||
|
|
19a0923559 | ||
|
|
cef9758c12 | ||
|
|
0dc35818dd | ||
|
|
a0f9d30753 | ||
|
|
d6fe9e10e1 | ||
|
|
e1246af66a | ||
|
|
6b4205a0d2 | ||
|
|
f1efc6103d | ||
|
|
f6935cc9d2 |
197
.github/workflows/cmake-single-platform.yml
vendored
197
.github/workflows/cmake-single-platform.yml
vendored
@@ -4,88 +4,185 @@ on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
branches: [ "main" ]
|
||||
|
||||
branches: [ "main", "feature/cross-platform-builds" ]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
permissions:
|
||||
contents: 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
|
||||
|
||||
- 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
|
||||
mkdir -p build/output/addons/counterstrikesharp/plugins
|
||||
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
|
||||
|
||||
# We don't need expensive submodules for the managed side.
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Generate build number
|
||||
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: zip -qq -r counterstrikesharp-build-${{ env.BUILD_NUMBER }}-${{ env.GITHUB_SHA_SHORT }}.zip build/output/
|
||||
publish:
|
||||
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: zip -qq -r counterstrikesharp-with-runtime-build-${{ env.BUILD_NUMBER }}-${{ env.GITHUB_SHA_SHORT }}.zip build/output/
|
||||
|
||||
- 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 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
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -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/
|
||||
|
||||
|
||||
176
CMakeLists.txt
176
CMakeLists.txt
@@ -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,74 +12,86 @@ 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
|
||||
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/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
|
||||
)
|
||||
|
||||
|
||||
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")
|
||||
|
||||
@@ -104,16 +117,27 @@ 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_target(build-time-make-directory ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/addons/metamod")
|
||||
configure_file(configs/counterstrikesharp.vdf "${CMAKE_BINARY_DIR}/addons/metamod/counterstrikesharp.vdf" COPYONLY)
|
||||
configure_file(configs/gamedata.json "${CMAKE_BINARY_DIR}/addons/counterstrikesharp/gamedata/gamedata.json" COPYONLY)
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${PROJECT_NAME} PRE_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/configs ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
13
README.md
13
README.md
@@ -2,21 +2,16 @@
|
||||
|
||||
CounterStrikeSharp is a server side modding framework for Counter-Strike: Global Offensive. This project attempts to implement a .NET Core scripting layer on top of a Metamod Source Plugin, allowing developers to create plugins that interact with the game server in a modern language (C#) to facilitate the creation of maintainable and testable code.
|
||||
|
||||
[Come and join our Discord](https://discord.gg/X7r3PmuYKq)
|
||||
|
||||
## History
|
||||
|
||||
This project is an ongoing migration of a previous project (titled [VSP.NET](https://github.com/roflmuffin/vspdotnet)) whereby a scripting layer was added to a Valve Server Plugin for CSGO.
|
||||
|
||||
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
|
||||
Development builds are currently available through GitHub actions, you can download the latest build from [there](https://github.com/roflmuffin/CounterStrikeSharp/actions/workflows/cmake-single-platform.yml).
|
||||
Download the latest build from [here](https://github.com/roflmuffin/CounterStrikeSharp/releases). (Download the with runtime version if this is your first time installing).
|
||||
|
||||
Detailed installation instructions can be found in the [docs](https://docs.cssharp.dev/guides/getting-started/).
|
||||
|
||||
@@ -94,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
|
||||
|
||||
|
||||
27
configs/addons/counterstrikesharp/configs/admins.json
Normal file
27
configs/addons/counterstrikesharp/configs/admins.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"Erikj": {
|
||||
"identity": "76561197960265731",
|
||||
"flags": [
|
||||
"@css/reservation",
|
||||
"@css/generic",
|
||||
"@css/kick",
|
||||
"@css/ban",
|
||||
"@css/unban",
|
||||
"@css/vip",
|
||||
"@css/slay",
|
||||
"@css/changemap",
|
||||
"@css/cvar",
|
||||
"@css/config",
|
||||
"@css/chat",
|
||||
"@css/vote",
|
||||
"@css/password",
|
||||
"@css/rcon",
|
||||
"@css/cheats",
|
||||
"@css/root"
|
||||
]
|
||||
},
|
||||
"Another erikj": {
|
||||
"identity": "STEAM_0:1:1",
|
||||
"flags": ["@mycustomplugin/admin"]
|
||||
}
|
||||
}
|
||||
5
configs/addons/counterstrikesharp/configs/core.json
Normal file
5
configs/addons/counterstrikesharp/configs/core.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"PublicChatTrigger": "!",
|
||||
"SilentChatTrigger": "/",
|
||||
"FollowCS2ServerGuidelines": true
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
Place your plugin configurations here.
|
||||
|
||||
TestPlugin/TestPlugin.json
|
||||
AnotherPlugin/AnotherPlugin.json
|
||||
@@ -2,46 +2,73 @@
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1920
configs/addons/counterstrikesharp/gamedata/schema_classes.txt
Normal file
1920
configs/addons/counterstrikesharp/gamedata/schema_classes.txt
Normal file
File diff suppressed because it is too large
Load Diff
305
configs/addons/counterstrikesharp/gamedata/schema_enums.txt
Normal file
305
configs/addons/counterstrikesharp/gamedata/schema_enums.txt
Normal file
@@ -0,0 +1,305 @@
|
||||
ActionType_t
|
||||
AimMatrixBlendMode
|
||||
AmmoFlags_t
|
||||
AmmoPosition_t
|
||||
AnimLoopMode_t
|
||||
AnimNodeNetworkMode
|
||||
AnimParamButton_t
|
||||
AnimParamNetworkSetting
|
||||
AnimParamType_t
|
||||
AnimPoseControl
|
||||
AnimScriptType
|
||||
AnimVRFinger_t
|
||||
AnimVRHandMotionRange_t
|
||||
AnimVRHand_t
|
||||
AnimValueSource
|
||||
AnimVectorSource
|
||||
AnimVrBoneTransformSource_t
|
||||
AnimVrFingerSplay_t
|
||||
AnimationProcessingType_t
|
||||
AnimationSnapshotType_t
|
||||
AnimationType_t
|
||||
BBoxVolumeType_t
|
||||
BaseExplosionTypes_t
|
||||
BeamClipStyle_t
|
||||
BeamType_t
|
||||
BeginDeathLifeStateTransition_t
|
||||
BinaryNodeChildOption
|
||||
BinaryNodeTiming
|
||||
Blend2DMode
|
||||
BlendKeyType
|
||||
BloomBlendMode_t
|
||||
BlurFilterType_t
|
||||
BoneMaskBlendSpace
|
||||
BoneTransformSpace_t
|
||||
BrushSolidities_e
|
||||
CAnimationGraphVisualizerPrimitiveType
|
||||
CLogicBranchList__LogicBranchListenerLastState_t
|
||||
CRR_Response__ResponseEnum_t
|
||||
CSPlayerBlockingUseAction_t
|
||||
CSPlayerState
|
||||
CSWeaponCategory
|
||||
CSWeaponMode
|
||||
CSWeaponSilencerType
|
||||
CSWeaponState_t
|
||||
CSWeaponType
|
||||
CanPlaySequence_t
|
||||
ChatIgnoreType_t
|
||||
ChickenActivity
|
||||
ChoiceBlendMethod
|
||||
ChoiceChangeMethod
|
||||
ChoiceMethod
|
||||
Class_T
|
||||
ClosestPointTestType_t
|
||||
CommandEntitySpecType_t
|
||||
CommandExecMode_t
|
||||
CompMatPropertyMutatorConditionType_t
|
||||
CompMatPropertyMutatorType_t
|
||||
CompositeMaterialInputContainerSourceType_t
|
||||
CompositeMaterialInputLooseVariableType_t
|
||||
CompositeMaterialInputTextureType_t
|
||||
CompositeMaterialMatchFilterType_t
|
||||
CompositeMaterialVarSystemVar_t
|
||||
DamageTypes_t
|
||||
DampingSpeedFunction
|
||||
DebugOverlayBits_t
|
||||
Detail2Combo_t
|
||||
DetailCombo_t
|
||||
DisableShadows_t
|
||||
Disposition_t
|
||||
DoorState_t
|
||||
EDemoBoneSelectionMode
|
||||
EGrenadeThrowState
|
||||
EInButtonState
|
||||
EKillTypes_t
|
||||
ELayoutNodeType
|
||||
EOverrideBlockLOS_t
|
||||
EStyleNodeType
|
||||
EntFinderMethod_t
|
||||
EntityDisolveType_t
|
||||
EntityDormancyType_t
|
||||
EntityIOTargetType_t
|
||||
EntitySubclassScope_t
|
||||
Explosions
|
||||
FacingMode
|
||||
FieldNetworkOption
|
||||
FixAngleSet_t
|
||||
FlexOpCode_t
|
||||
FootFallTagFoot_t
|
||||
FootLockSubVisualization
|
||||
FootPinningTimingSource
|
||||
FootstepLandedFootSoundType_t
|
||||
ForcedCrouchState_t
|
||||
FuncDoorSpawnPos_t
|
||||
FuseVariableAccess_t
|
||||
FuseVariableType_t
|
||||
GameAnimEventIndex_t
|
||||
GrenadeType_t
|
||||
HierarchyType_t
|
||||
HitGroup_t
|
||||
HitboxLerpType_t
|
||||
HorizJustification_e
|
||||
Hull_t
|
||||
IChoreoServices__ChoreoState_t
|
||||
IChoreoServices__ScriptState_t
|
||||
IKChannelMode
|
||||
IKSolverType
|
||||
IKTargetCoordinateSystem
|
||||
IKTargetSource
|
||||
IkEndEffectorType
|
||||
IkTargetType
|
||||
InheritableBoolType_t
|
||||
InputBitMask_t
|
||||
InputLayoutVariation_t
|
||||
ItemFlagTypes_t
|
||||
JiggleBoneSimSpace
|
||||
JointAxis_t
|
||||
JointMotion_t
|
||||
JumpCorrectionMethod
|
||||
LatchDirtyPermission_t
|
||||
LayoutPositionType_e
|
||||
LessonPanelLayoutFileTypes_t
|
||||
LifeState_t
|
||||
MaterialProxyType_t
|
||||
Materials
|
||||
MatterialAttributeTagType_t
|
||||
MedalRank_t
|
||||
MeshDrawPrimitiveFlags_t
|
||||
MissingParentInheritBehavior_t
|
||||
ModelBoneFlexComponent_t
|
||||
ModelConfigAttachmentType_t
|
||||
ModelSkeletonData_t__BoneFlags_t
|
||||
ModifyDamageReturn_t
|
||||
MoodType_t
|
||||
MorphBundleType_t
|
||||
MorphFlexControllerRemapType_t
|
||||
MoveCollide_t
|
||||
MoveLinearAuthoredPos_t
|
||||
MoveMountingAmount_t
|
||||
MoveType_t
|
||||
NavAttributeEnum
|
||||
NavDirType
|
||||
ObjectTypeFlags_t
|
||||
ObserverInterpState_t
|
||||
ObserverMode_t
|
||||
OnFrame
|
||||
PFNoiseModifier_t
|
||||
PFNoiseTurbulence_t
|
||||
PFNoiseType_t
|
||||
PFuncVisualizationType_t
|
||||
ParticleAlphaReferenceType_t
|
||||
ParticleAttachment_t
|
||||
ParticleCollisionMode_t
|
||||
ParticleColorBlendMode_t
|
||||
ParticleColorBlendType_t
|
||||
ParticleControlPointAxis_t
|
||||
ParticleDepthFeatheringMode_t
|
||||
ParticleDetailLevel_t
|
||||
ParticleDirectionNoiseType_t
|
||||
ParticleEndcapMode_t
|
||||
ParticleFalloffFunction_t
|
||||
ParticleFloatBiasType_t
|
||||
ParticleFloatInputMode_t
|
||||
ParticleFloatMapType_t
|
||||
ParticleFloatRandomMode_t
|
||||
ParticleFloatType_t
|
||||
ParticleFogType_t
|
||||
ParticleHitboxBiasType_t
|
||||
ParticleHitboxDataSelection_t
|
||||
ParticleImpulseType_t
|
||||
ParticleLightBehaviorChoiceList_t
|
||||
ParticleLightFogLightingMode_t
|
||||
ParticleLightTypeChoiceList_t
|
||||
ParticleLightUnitChoiceList_t
|
||||
ParticleLightingQuality_t
|
||||
ParticleLightnintBranchBehavior_t
|
||||
ParticleModelType_t
|
||||
ParticleOmni2LightTypeChoiceList_t
|
||||
ParticleOrientationChoiceList_t
|
||||
ParticleOrientationSetMode_t
|
||||
ParticleOutputBlendMode_t
|
||||
ParticleParentSetMode_t
|
||||
ParticlePinDistance_t
|
||||
ParticlePostProcessPriorityGroup_t
|
||||
ParticleRotationLockType_t
|
||||
ParticleSelection_t
|
||||
ParticleSequenceCropOverride_t
|
||||
ParticleSetMethod_t
|
||||
ParticleSortingChoiceList_t
|
||||
ParticleTextureLayerBlendType_t
|
||||
ParticleTopology_t
|
||||
ParticleTraceMissBehavior_t
|
||||
ParticleTraceSet_t
|
||||
ParticleTransformType_t
|
||||
ParticleVRHandChoiceList_t
|
||||
ParticleVecType_t
|
||||
PerformanceMode_t
|
||||
PermModelInfo_t__FlagEnum
|
||||
PetGroundType_t
|
||||
PlayerAnimEvent_t
|
||||
PlayerConnectedState
|
||||
PointTemplateClientOnlyEntityBehavior_t
|
||||
PointTemplateOwnerSpawnGroupType_t
|
||||
PointWorldTextJustifyHorizontal_t
|
||||
PointWorldTextJustifyVertical_t
|
||||
PointWorldTextReorientMode_t
|
||||
PoseType_t
|
||||
PropDoorRotatingOpenDirection_e
|
||||
PropDoorRotatingSpawnPos_t
|
||||
PulseInstructionCode_t
|
||||
PulseMethodCallMode_t
|
||||
PulseValueType_t
|
||||
QuestProgress__Reason
|
||||
RagdollPoseControl
|
||||
RenderBufferFlags_t
|
||||
RenderFx_t
|
||||
RenderMode_t
|
||||
RenderMultisampleType_t
|
||||
RenderPrimitiveType_t
|
||||
RenderSlotType_t
|
||||
ResetCycleOption
|
||||
RumbleEffect_t
|
||||
ScalarExpressionType_t
|
||||
SceneOnPlayerDeath_t
|
||||
ScriptedConflictResponse_t
|
||||
ScriptedMoveTo_t
|
||||
ScriptedMoveType_t
|
||||
ScriptedOnDeath_t
|
||||
SelectorTagBehavior_t
|
||||
SeqCmd_t
|
||||
SeqPoseSetting_t
|
||||
ShadowType_t
|
||||
ShakeCommand_t
|
||||
ShardSolid_t
|
||||
ShatterDamageCause
|
||||
ShatterGlassStressType
|
||||
ShatterPanelMode
|
||||
SimpleConstraintSoundProfile__SimpleConstraintsSoundProfileKeypoints_t
|
||||
SolidType_t
|
||||
SolveIKChainAnimNodeDebugSetting
|
||||
SosActionSortType_t
|
||||
SosActionStopType_t
|
||||
SosEditItemType_t
|
||||
SosGroupType_t
|
||||
SoundEventStartType_t
|
||||
SoundFlags_t
|
||||
SpawnDebugOverrideState_t
|
||||
SpawnDebugRestrictionOverrideState_t
|
||||
SpawnPointCoopEnemy__BotDefaultBehavior_t
|
||||
SpriteCardPerParticleScale_t
|
||||
SpriteCardShaderType_t
|
||||
SpriteCardTextureChannel_t
|
||||
SpriteCardTextureType_t
|
||||
StanceOverrideMode
|
||||
StanceType_t
|
||||
StandardLightingAttenuationStyle_t
|
||||
StateActionBehavior
|
||||
StepPhase
|
||||
SubclassVDataChangeType_t
|
||||
SurroundingBoundsType_t
|
||||
TOGGLE_STATE
|
||||
TRAIN_CODE
|
||||
TakeDamageFlags_t
|
||||
TextureRepetitionMode_t
|
||||
ThreeState_t
|
||||
TimelineCompression_t
|
||||
Touch_t
|
||||
TrackOrientationType_t
|
||||
TrainOrientationType_t
|
||||
TrainVelocityType_t
|
||||
VMixChannelOperation_t
|
||||
VMixFilterSlope_t
|
||||
VMixFilterType_t
|
||||
VMixLFOShape_t
|
||||
VMixPannerType_t
|
||||
VMixProcessorType_t
|
||||
VMixSubgraphSwitchInterpolationType_t
|
||||
VPhysXAggregateData_t__VPhysXFlagEnum_t
|
||||
VPhysXBodyPart_t__VPhysXFlagEnum_t
|
||||
VPhysXConstraintParams_t__EnumFlags0_t
|
||||
VPhysXJoint_t__Flags_t
|
||||
ValueRemapperHapticsType_t
|
||||
ValueRemapperInputType_t
|
||||
ValueRemapperMomentumType_t
|
||||
ValueRemapperOutputType_t
|
||||
ValueRemapperRatchetType_t
|
||||
VectorExpressionType_t
|
||||
VectorFloatExpressionType_t
|
||||
VelocityMetricMode
|
||||
VertJustification_e
|
||||
ViewFadeMode_t
|
||||
WaterLevel_t
|
||||
WeaponAttackType_t
|
||||
WeaponSound_t
|
||||
WorldTextPanelHorizontalAlign_t
|
||||
WorldTextPanelOrientation_t
|
||||
WorldTextPanelVerticalAlign_t
|
||||
attributeprovidertypes_t
|
||||
doorCheck_e
|
||||
fieldtype_t
|
||||
filter_t
|
||||
gear_slot_t
|
||||
loadout_slot_t
|
||||
navproperties_t
|
||||
soundlevel_t
|
||||
vote_create_failed_t
|
||||
4
configs/addons/counterstrikesharp/plugins/README.txt
Normal file
4
configs/addons/counterstrikesharp/plugins/README.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
Place plugins in this folder. Each plugin should be in its own subfolder, e.g.
|
||||
|
||||
TestPlugin/TestPlugin.dll
|
||||
AnotherPlugin/AnotherPlugin.dll
|
||||
1
configs/addons/counterstrikesharp/source/README.txt
Normal file
1
configs/addons/counterstrikesharp/source/README.txt
Normal file
@@ -0,0 +1 @@
|
||||
Place your source code for plugins here.
|
||||
@@ -1,5 +0,0 @@
|
||||
"Metamod Plugin"
|
||||
{
|
||||
"alias" "counterstrikesharp"
|
||||
"file" "addons/counterstrikesharp/bin/linuxsteamrt64/counterstrikesharp"
|
||||
}
|
||||
62
docs/src/content/docs/features/admin-framework.md
Normal file
62
docs/src/content/docs/features/admin-framework.md
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: Admin Framework
|
||||
description: A guide on using the Admin Framework in plugins.
|
||||
---
|
||||
|
||||
## Admin Framework
|
||||
|
||||
CounterStrikeSharp has a basic framework which allows plugin developers to assign permissions to commands. When CSS is initialized, a list of admins are loaded from `configs/admins.json`.
|
||||
|
||||
## Adding Admins
|
||||
|
||||
Adding an Admin is as simple as creating a new entry in the `configs/admins.json` file. The important things you need to declare are the SteamID identifier and the permissions they have. CounterStrikeSharp will do all the heavy-lifting to decipher your SteamID. If you're familar with SourceMod, permission definitions are slightly different as they're defined by an array of strings instead of a string of characters.
|
||||
|
||||
```json
|
||||
{
|
||||
"ZoNiCaL": {
|
||||
"identity": "76561198808392634",
|
||||
"flags": ["@css/changemap", "@css/generic"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also manually assign permissions to players in code with `AddPlayerPermissions` and `RemovePlayerPermissions`. These changes are not saved to `configs/admins.json`.
|
||||
|
||||
## Assigning permissions to a Command
|
||||
|
||||
Assigning permissions to a Command is as easy as tagging the Command method (function callback) with a `RequiresPermissions` attribute.
|
||||
|
||||
```csharp
|
||||
[RequiresPermissions("@css/slay", "@custom/permission")]
|
||||
public void OnMyCommand(CCSPlayerController? caller, CommandInfo info)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
CounterStrikeSharp handles all of the permission checks behind the scenes for you.
|
||||
|
||||
### Standard Permissions
|
||||
|
||||
Because the flag system is just a list of strings associated with a user, there is no real list of letter based flags like there was previously in something like SourceMod. This means as a plugin author you can declare your own flags, scoped with an `@` symbol, like `@roflmuffin/guns`, which might be the permission to allow spawning of guns in a given command.
|
||||
|
||||
However there is a somewhat standardised list of flags that it is advised you use if you are adding functionality that aligns with their purpose, and these are based on the original SourceMod flags:
|
||||
|
||||
```shell
|
||||
@css/reservation # Reserved slot access.
|
||||
@css/generic # Generic admin.
|
||||
@css/kick # Kick other players.
|
||||
@css/ban # Ban other players.
|
||||
@css/unban # Remove bans.
|
||||
@css/vip # General vip status.
|
||||
@css/slay # Slay/harm other players.
|
||||
@css/changemap # Change the map or major gameplay features.
|
||||
@css/cvar # Change most cvars.
|
||||
@css/config # Execute config files.
|
||||
@css/chat # Special chat privileges.
|
||||
@css/vote # Start or create votes.
|
||||
@css/password # Set a password on the server.
|
||||
@css/rcon # Use RCON commands.
|
||||
@css/cheats # Change sv_cheats or use cheating commands.
|
||||
@css/root # Magically enables all flags and ignores immunity values.
|
||||
```
|
||||
@@ -64,3 +64,39 @@ Command String: custom_command "Test Quoted" 5 13
|
||||
First Argument: custom_command
|
||||
Second Argument: Test Quoted
|
||||
```
|
||||
|
||||
## Helper Attribute
|
||||
|
||||
CounterStrikeSharp provides the `CommandHelper` attribute for Command methods (function callback) to simplify the process of checking for the correct amount of arguments and to restrict commands to being executed by the server console or by players (or both!).
|
||||
|
||||
```csharp
|
||||
[ConsoleCommand("freeze", "Freezes a client.")]
|
||||
[CommandHelper(minArgs: 1, usage: "[target]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnFreezeCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
If a client tries to execute the command without the `[target]` argument, it will print a message to them in chat:
|
||||
|
||||
```shell
|
||||
[CSS] Expected usage: "!freeze [target]".
|
||||
```
|
||||
|
||||
If a command is executed by the wrong user, it will print a message to them:
|
||||
|
||||
```shell
|
||||
[CSS] This command can only be executed by clients.
|
||||
```
|
||||
|
||||
Valid `CommandUsage` values:
|
||||
|
||||
```csharp
|
||||
public enum CommandUsage
|
||||
{
|
||||
CLIENT_AND_SERVER = 0,
|
||||
CLIENT_ONLY,
|
||||
SERVER_ONLY
|
||||
}
|
||||
```
|
||||
|
||||
26
docs/src/content/docs/reference/core-configuration.md
Normal file
26
docs/src/content/docs/reference/core-configuration.md
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
title: Core Configuration
|
||||
description: Summary for core configuration values
|
||||
---
|
||||
|
||||
## PublicChatTrigger
|
||||
|
||||
List of characters to use for public chat triggers.
|
||||
|
||||
## SilentChatTrigger
|
||||
|
||||
List of characters to use for silent chat triggers.
|
||||
|
||||
## FollowCS2ServerGuidelines
|
||||
|
||||
Per [CS2 Server Guidelines](https://blog.counter-strike.net/index.php/server_guidelines/), certain plugin
|
||||
functionality will trigger all of the game server owner's Game Server Login Tokens
|
||||
(GSLTs) to get banned when executed on a Counter-Strike 2 game server.
|
||||
|
||||
Enabling this option will block plugins from using functionality that is known to cause this.
|
||||
This option only has any effect on CS2. Note that this does NOT guarantee that you cannot
|
||||
receive a ban.
|
||||
|
||||
:::note
|
||||
Disable this option at your own risk.
|
||||
:::
|
||||
@@ -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" />
|
||||
|
||||
Submodule libraries/hl2sdk-cs2 updated: f653b1ec62...492b8f9663
24596
libraries/nlohmann/json.hpp
Normal file
24596
libraries/nlohmann/json.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
)
|
||||
10
makefiles/metamod/configure_metamod.cmake
Normal file
10
makefiles/metamod/configure_metamod.cmake
Normal 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
|
||||
)
|
||||
5
makefiles/metamod/counterstrikesharp.vdf.in
Normal file
5
makefiles/metamod/counterstrikesharp.vdf.in
Normal file
@@ -0,0 +1,5 @@
|
||||
"Metamod Plugin"
|
||||
{
|
||||
"alias" "counterstrikesharp"
|
||||
"file" "addons/counterstrikesharp/bin/${COUNTERSTRIKESHARP_VDF_PLATFORM}/counterstrikesharp"
|
||||
}
|
||||
@@ -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()
|
||||
20
makefiles/windows.base.cmake
Normal file
20
makefiles/windows.base.cmake
Normal 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
|
||||
)
|
||||
@@ -24,10 +24,14 @@ using System.Runtime.InteropServices;
|
||||
using System.Runtime.Loader;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Core.Attributes.Registration;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Events;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Listeners;
|
||||
using CounterStrikeSharp.API.Modules.Timers;
|
||||
using McMaster.NETCore.Plugins;
|
||||
using CounterStrikeSharp.API.Modules.Config;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
@@ -154,19 +158,50 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
var wrappedHandler = new Action<int, IntPtr>((i, ptr) =>
|
||||
{
|
||||
if (i == -1)
|
||||
var caller = (i != -1) ? new CCSPlayerController(NativeAPI.GetEntityFromIndex(i + 1)) : null;
|
||||
var command = new CommandInfo(ptr, caller);
|
||||
|
||||
var methodInfo = handler?.GetMethodInfo();
|
||||
// Do not execute if we shouldn't be calling this command.
|
||||
var helperAttribute = methodInfo?.GetCustomAttribute<CommandHelperAttribute>();
|
||||
if (helperAttribute != null)
|
||||
{
|
||||
handler?.Invoke(null, new CommandInfo(ptr, null));
|
||||
switch (helperAttribute.WhoCanExcecute)
|
||||
{
|
||||
case CommandUsage.CLIENT_AND_SERVER: break; // Allow command through.
|
||||
case CommandUsage.CLIENT_ONLY:
|
||||
if (caller == null || !caller.IsValid) { command.ReplyToCommand("[CSS] This command can only be executed by clients."); return; } break;
|
||||
case CommandUsage.SERVER_ONLY:
|
||||
if (caller != null && caller.IsValid) { command.ReplyToCommand("[CSS] This command can only be executed by the server."); return; } break;
|
||||
default: throw new ArgumentException("Unrecognised CommandUsage value passed in CommandHelperAttribute.");
|
||||
}
|
||||
|
||||
// Technically the command itself counts as the first argument,
|
||||
// but we'll just ignore that for this check.
|
||||
if (helperAttribute.MinArgs != 0 && command.ArgCount - 1 < helperAttribute.MinArgs)
|
||||
{
|
||||
command.ReplyToCommand($"[CSS] Expected usage: \"!{command.ArgByIndex(0)} {helperAttribute.Usage}\".");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not execute command if we do not have the correct permissions.
|
||||
var permissions = methodInfo?.GetCustomAttribute<RequiresPermissions>()?.RequiredPermissions;
|
||||
if (permissions != null && !AdminManager.PlayerHasPermissions(caller, permissions))
|
||||
{
|
||||
command.ReplyToCommand("[CSS] You do not have the correct permissions to execute this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
var entity = new CCSPlayerController(NativeAPI.GetEntityFromIndex(i + 1));
|
||||
var command = new CommandInfo(ptr, entity);
|
||||
handler?.Invoke(entity.IsValid ? entity : null, command);
|
||||
handler?.Invoke(caller, command);
|
||||
});
|
||||
|
||||
var methodInfo = handler?.GetMethodInfo();
|
||||
var helperAttribute = methodInfo?.GetCustomAttribute<CommandHelperAttribute>();
|
||||
|
||||
var subscriber = new CallbackSubscriber(handler, wrappedHandler, () => { RemoveCommand(name, handler); });
|
||||
NativeAPI.AddCommand(name, description, false, (int)ConCommandFlags.FCVAR_LINKED_CONCOMMAND, subscriber.GetInputArgument());
|
||||
NativeAPI.AddCommand(name, description, (helperAttribute?.WhoCanExcecute == CommandUsage.SERVER_ONLY),
|
||||
(int)ConCommandFlags.FCVAR_LINKED_CONCOMMAND, subscriber.GetInputArgument());
|
||||
CommandHandlers[handler] = subscriber;
|
||||
}
|
||||
|
||||
@@ -300,6 +335,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>
|
||||
|
||||
32
managed/CounterStrikeSharp.API/Core/BasePluginConfig.cs
Normal file
32
managed/CounterStrikeSharp.API/Core/BasePluginConfig.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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.""");
|
||||
}
|
||||
}
|
||||
}
|
||||
120
managed/CounterStrikeSharp.API/Core/CoreConfig.cs
Normal file
120
managed/CounterStrikeSharp.API/Core/CoreConfig.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializable instance of the CoreConfig
|
||||
/// </summary>
|
||||
internal sealed partial class CoreConfigData
|
||||
{
|
||||
[JsonPropertyName("PublicChatTrigger")] public string PublicChatTrigger { get; internal set; } = "!";
|
||||
|
||||
[JsonPropertyName("SilentChatTrigger")] public string SilentChatTrigger { get; internal set; } = "/";
|
||||
|
||||
[JsonPropertyName("FollowCS2ServerGuidelines")] public bool FollowCS2ServerGuidelines { get; internal set; } = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configuration related to the Core API.
|
||||
/// </summary>
|
||||
public static partial class CoreConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// List of characters to use for public chat triggers.
|
||||
/// </summary>
|
||||
public static string PublicChatTrigger => _coreConfig.PublicChatTrigger;
|
||||
|
||||
/// <summary>
|
||||
/// List of characters to use for silent chat triggers.
|
||||
/// </summary>
|
||||
public static string SilentChatTrigger => _coreConfig.SilentChatTrigger;
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Per <see href="http://blog.counter-strike.net/index.php/server_guidelines/"/>, certain plugin
|
||||
/// functionality will trigger all of the game server owner's Game Server Login Tokens
|
||||
/// (GSLTs) to get banned when executed on a Counter-Strike 2 game server.
|
||||
/// </para>
|
||||
///
|
||||
/// <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>
|
||||
///
|
||||
/// <para>
|
||||
/// Disable this option at your own risk.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public static bool FollowCS2ServerGuidelines => _coreConfig.FollowCS2ServerGuidelines;
|
||||
}
|
||||
|
||||
public static partial class CoreConfig
|
||||
{
|
||||
private static CoreConfigData _coreConfig = new CoreConfigData();
|
||||
|
||||
static CoreConfig()
|
||||
{
|
||||
CommandUtils.AddStandaloneCommand("css_core_reload", "Reloads the core configuration file.", ReloadCoreConfigCommand);
|
||||
}
|
||||
|
||||
[RequiresPermissions("@css/config")]
|
||||
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
private static void ReloadCoreConfigCommand(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
var rootDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.Parent;
|
||||
Load(Path.Combine(rootDir.FullName, "configs", "core.json"));
|
||||
}
|
||||
|
||||
public static void Load(string coreConfigPath)
|
||||
{
|
||||
if (!File.Exists(coreConfigPath))
|
||||
{
|
||||
Console.WriteLine($"Core configuration could not be found at path '{coreConfigPath}', fallback values will be used.");
|
||||
return;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Console.WriteLine($"Failed to load core configuration: {ex}, fallback values will be used.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,6 @@ namespace CounterStrikeSharp.API.Core;
|
||||
class LoadedGameData
|
||||
{
|
||||
[JsonPropertyName("signatures")] public Signatures? Signatures { get; set; }
|
||||
|
||||
|
||||
[JsonPropertyName("offsets")] public Offsets? Offsets { get; set; }
|
||||
}
|
||||
|
||||
@@ -37,36 +35,70 @@ public static class GameData
|
||||
|
||||
public static void Load(string gameDataPath)
|
||||
{
|
||||
_methods = JsonSerializer.Deserialize<Dictionary<string, LoadedGameData>>(File.ReadAllText(gameDataPath));
|
||||
try
|
||||
{
|
||||
_methods = JsonSerializer.Deserialize<Dictionary<string, LoadedGameData>>(File.ReadAllText(gameDataPath))!;
|
||||
|
||||
Console.WriteLine($"Loaded game data with {_methods.Count} methods.");
|
||||
Console.WriteLine($"Loaded game data with {_methods.Count} methods.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,10 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Menu;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
@@ -57,32 +59,38 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
public void InitGlobalContext()
|
||||
{
|
||||
Console.WriteLine("Loading GameData");
|
||||
Console.WriteLine("Loading CoreConfig from \"configs/core.json\"");
|
||||
CoreConfig.Load(Path.Combine(rootDir.FullName, "configs", "core.json"));
|
||||
|
||||
Console.WriteLine("Loading GameData from \"gamedata/gamedata.json\"");
|
||||
GameData.Load(Path.Combine(rootDir.FullName, "gamedata", "gamedata.json"));
|
||||
|
||||
for (int i = 1; i <= 9; i++)
|
||||
Console.WriteLine("Loading Admins from \"configs/admins.json\"");
|
||||
AdminManager.Load(Path.Combine(rootDir.FullName, "configs", "admins.json"));
|
||||
|
||||
for (var i = 1; i <= 9; i++)
|
||||
{
|
||||
AddCommand("css_" + i, "Command Key Handler", (player, info) =>
|
||||
CommandUtils.AddStandaloneCommand("css_" + i, "Command Key Handler", (player, info) =>
|
||||
{
|
||||
if (player == null) return;
|
||||
var key = Convert.ToInt32(info.GetArg(0).Split("_")[1]);
|
||||
ChatMenus.OnKeyPress(player, key);
|
||||
}, false);
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -90,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);
|
||||
@@ -165,7 +175,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
|
||||
private PluginContext? FindPluginByIdOrName(string query)
|
||||
{
|
||||
|
||||
|
||||
PluginContext? plugin = null;
|
||||
if (Int32.TryParse(query, out var pluginNumber))
|
||||
{
|
||||
@@ -178,21 +188,34 @@ namespace CounterStrikeSharp.API.Core
|
||||
return plugin;
|
||||
}
|
||||
|
||||
[RequiresPermissions("can_execute_css_commands")]
|
||||
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
private void OnCSSCommand(CCSPlayerController? caller, CommandInfo info)
|
||||
{
|
||||
Utilities.ReplyToCommand(caller, " CounterStrikeSharp was created and is maintained by Michael \"roflmuffin\" Wilson.\n" +
|
||||
var currentVersion = Api.GetVersion();
|
||||
|
||||
info.ReplyToCommand(" CounterStrikeSharp was created and is maintained by Michael \"roflmuffin\" Wilson.\n" +
|
||||
" Counter-Strike Sharp uses code borrowed from SourceMod, Source.Python, FiveM, Saul Rennison and CS2Fixes.\n" +
|
||||
" See ACKNOWLEDGEMENTS.md for more information.", true);
|
||||
" See ACKNOWLEDGEMENTS.md for more information.\n" +
|
||||
" Current API Version: " + currentVersion, true);
|
||||
return;
|
||||
}
|
||||
|
||||
[RequiresPermissions("can_execute_css_commands")]
|
||||
[CommandHelper(minArgs: 1,
|
||||
usage: "[option]\n" +
|
||||
" list - List all plugins currently loaded.\n" +
|
||||
" start / load - Loads a plugin not currently loaded.\n" +
|
||||
" stop / unload - Unloads a plugin currently loaded.\n" +
|
||||
" restart / reload - Reloads a plugin currently loaded.",
|
||||
whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
private void OnCSSPluginCommand(CCSPlayerController? caller, CommandInfo info)
|
||||
{
|
||||
switch (info.GetArg(1))
|
||||
{
|
||||
case "list":
|
||||
{
|
||||
Utilities.ReplyToCommand(caller, $" List of all plugins currently loaded by CounterStrikeSharp: {_loadedPlugins.Count} plugins loaded.", true);
|
||||
info.ReplyToCommand($" List of all plugins currently loaded by CounterStrikeSharp: {_loadedPlugins.Count} plugins loaded.", true);
|
||||
|
||||
foreach (var plugin in _loadedPlugins)
|
||||
{
|
||||
@@ -205,7 +228,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
sb.Append(" ");
|
||||
sb.Append(plugin.Description);
|
||||
}
|
||||
Utilities.ReplyToCommand(caller, sb.ToString(), true);
|
||||
info.ReplyToCommand(sb.ToString(), true);
|
||||
|
||||
}
|
||||
|
||||
@@ -216,7 +239,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
if (info.ArgCount < 2)
|
||||
{
|
||||
Utilities.ReplyToCommand(caller, "Valid usage: css_plugins start/load [relative plugin path || absolute plugin path] (e.g \"TestPlugin\", \"plugins/TestPlugin/TestPlugin.dll\")\n", true);
|
||||
info.ReplyToCommand("Valid usage: css_plugins start/load [relative plugin path || absolute plugin path] (e.g \"TestPlugin\", \"plugins/TestPlugin/TestPlugin.dll\")\n", true);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -231,7 +254,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
path = Path.Combine(rootDir.FullName, path);
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
LoadPlugin(path);
|
||||
@@ -240,7 +263,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
Console.WriteLine($"Failed to load plugin {path} with error {e}");
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -249,7 +272,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
if (info.ArgCount < 2)
|
||||
{
|
||||
Utilities.ReplyToCommand(caller, "Valid usage: css_plugins stop/unload [plugin name || #plugin id] (e.g \"TestPlugin\", \"1\")\n", true);
|
||||
info.ReplyToCommand("Valid usage: css_plugins stop/unload [plugin name || #plugin id] (e.g \"TestPlugin\", \"1\")\n", true);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -257,7 +280,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
PluginContext? plugin = FindPluginByIdOrName(pluginIdentifier);
|
||||
if (plugin == null)
|
||||
{
|
||||
Utilities.ReplyToCommand(caller, $"Could not unload plugin \"{pluginIdentifier}\")", true);
|
||||
info.ReplyToCommand($"Could not unload plugin \"{pluginIdentifier}\")", true);
|
||||
break;
|
||||
}
|
||||
plugin.Unload();
|
||||
@@ -270,7 +293,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
if (info.ArgCount < 2)
|
||||
{
|
||||
Utilities.ReplyToCommand(caller, "Valid usage: css_plugins restart/reload [plugin name || #plugin id] (e.g \"TestPlugin\", \"#1\")\n", true);
|
||||
info.ReplyToCommand("Valid usage: css_plugins restart/reload [plugin name || #plugin id] (e.g \"TestPlugin\", \"#1\")\n", true);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -279,7 +302,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
|
||||
if (plugin == null)
|
||||
{
|
||||
Utilities.ReplyToCommand(caller, $"Could not reload plugin \"{pluginIdentifier}\")", true);
|
||||
info.ReplyToCommand($"Could not reload plugin \"{pluginIdentifier}\")", true);
|
||||
break;
|
||||
}
|
||||
plugin.Unload(true);
|
||||
@@ -288,7 +311,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
|
||||
default:
|
||||
Utilities.ReplyToCommand(caller, "Valid usage: css_plugins [option]\n" +
|
||||
info.ReplyToCommand("Valid usage: css_plugins [option]\n" +
|
||||
" list - List all plugins currently loaded.\n" +
|
||||
" start / load - Loads a plugin not currently loaded.\n" +
|
||||
" stop / unload - Unloads a plugin currently loaded.\n" +
|
||||
@@ -299,35 +322,11 @@ namespace CounterStrikeSharp.API.Core
|
||||
|
||||
}
|
||||
|
||||
public void RegisterPluginCommands()
|
||||
private void RegisterPluginCommands()
|
||||
{
|
||||
AddCommand("css", "Counter-Strike Sharp options.", OnCSSCommand, false);
|
||||
AddCommand("css_plugins", "Counter-Strike Sharp plugin options.", OnCSSPluginCommand, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary way for base CSS to add commands without a plugin context
|
||||
*/
|
||||
private void AddCommand(string name, string description, CommandInfo.CommandCallback handler, bool serverOnly)
|
||||
{
|
||||
var wrappedHandler = new Action<int, IntPtr>((i, ptr) =>
|
||||
{
|
||||
if (i == -1)
|
||||
{
|
||||
handler?.Invoke(null, new CommandInfo(ptr, null));
|
||||
return;
|
||||
}
|
||||
|
||||
if (serverOnly) return;
|
||||
|
||||
var entity = new CCSPlayerController(NativeAPI.GetEntityFromIndex(i + 1));
|
||||
var command = new CommandInfo(ptr, entity);
|
||||
handler?.Invoke(entity.IsValid ? entity : null, command);
|
||||
});
|
||||
|
||||
var subscriber = new BasePlugin.CallbackSubscriber(handler, wrappedHandler, () => { });
|
||||
NativeAPI.AddCommand(name, description, serverOnly, (int)ConCommandFlags.FCVAR_LINKED_CONCOMMAND,
|
||||
subscriber.GetInputArgument());
|
||||
CommandUtils.AddStandaloneCommand("css", "Counter-Strike Sharp options.", OnCSSCommand);
|
||||
CommandUtils.AddStandaloneCommand("css_plugins", "Counter-Strike Sharp plugin options.", OnCSSPluginCommand);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.StackTrace);
|
||||
Console.WriteLine(e);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,13 +14,15 @@
|
||||
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
|
||||
*/
|
||||
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CGlowProperty
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
// m_bGlowing
|
||||
public ref bool IsGlowing => ref Schema.GetRef<bool>(this.Handle, "CGlowProperty", "m_bGlowing");
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,6 @@ namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CBaseEntity
|
||||
{
|
||||
public Vector AbsVelocity => Schema.GetDeclaredClass<Vector>(this.Handle, "CBaseEntity", "m_vecAbsVelocity");
|
||||
|
||||
public void Teleport(Vector position, QAngle angles, Vector velocity)
|
||||
{
|
||||
VirtualFunction.CreateVoid<IntPtr, IntPtr, IntPtr, IntPtr>(Handle, GameData.GetOffset("CBaseEntity_Teleport"))(
|
||||
@@ -18,10 +16,9 @@ public partial class CBaseEntity
|
||||
/// Shorthand for accessing an entity's CBodyComponent?.SceneNode?.AbsOrigin;
|
||||
/// </summary>
|
||||
public Vector? AbsOrigin => CBodyComponent?.SceneNode?.AbsOrigin;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Shorthand for accessing an entity's CBodyComponent?.SceneNode?.AbsRotation;
|
||||
/// </summary>
|
||||
public QAngle? AbsRotation => CBodyComponent?.SceneNode?.AbsRotation;
|
||||
|
||||
}
|
||||
@@ -38,6 +38,18 @@ public partial class CCSPlayerController
|
||||
{
|
||||
VirtualFunctions.ClientPrint(this.Handle, HudDestination.Center, message, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
public void PrintToCenterHtml(string message)
|
||||
{
|
||||
var @event = new EventShowSurvivalRespawnStatus(true)
|
||||
{
|
||||
LocToken = message,
|
||||
Duration = 5,
|
||||
Userid = this
|
||||
};
|
||||
@event.FireEvent(false);
|
||||
}
|
||||
|
||||
|
||||
public bool IsBot => ((PlayerFlags)Flags).HasFlag(PlayerFlags.FL_FAKECLIENT);
|
||||
|
||||
@@ -49,7 +61,7 @@ public partial class CCSPlayerController
|
||||
{
|
||||
VirtualFunctions.SwitchTeam(this.Handle, (byte)team);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Switches the team of the player, has the same effect as the "jointeam" console command.
|
||||
/// <remarks>
|
||||
@@ -62,4 +74,9 @@ public partial class CCSPlayerController
|
||||
VirtualFunction.CreateVoid<IntPtr, CsTeam>(Handle, GameData.GetOffset("CCSPlayerController_ChangeTeam"))(Handle,
|
||||
team);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the active pawns button state. Will work even if the player is dead or observing.
|
||||
/// </summary>
|
||||
public PlayerButtons Buttons => (PlayerButtons)Pawn.Value.MovementServices!.Buttons.ButtonStates[0];
|
||||
}
|
||||
@@ -51,9 +51,4 @@ public partial class CEntityIdentity
|
||||
{
|
||||
public unsafe CEntityInstance EntityInstance => new(Unsafe.Read<IntPtr>((void*)Handle));
|
||||
public unsafe CHandle<CEntityInstance> EntityHandle => new(Handle + 0x10);
|
||||
public unsafe string DesignerName => Utilities.ReadStringUtf8(Handle + 0x20);
|
||||
public unsafe PointerTo<CEntityIdentity> Prev => new(Handle + 0x58);
|
||||
public unsafe PointerTo<CEntityIdentity> Next => new(Handle + 0x60);
|
||||
public unsafe PointerTo<CEntityIdentity> PrevByClass => new(Handle + 0x68);
|
||||
public unsafe PointerTo<CEntityIdentity> NextByClass => new(Handle + 0x70);
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public class CInButtonState : NativeObject
|
||||
{
|
||||
public CInButtonState(IntPtr pointer) : base(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
public unsafe ref PlayerButtons Value =>
|
||||
ref Unsafe.AsRef<PlayerButtons>((void*)(Handle + Schema.GetSchemaOffset("CInButtonState", "m_pButtonStates")));
|
||||
}
|
||||
|
||||
public partial class CPlayer_MovementServices
|
||||
{
|
||||
public CInButtonState ButtonState =>
|
||||
Schema.GetDeclaredClass<CInButtonState>(this.Handle, "CPlayer_MovementServices", "m_nButtons");
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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}");
|
||||
|
||||
@@ -103,6 +103,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
public void Invoke()
|
||||
{
|
||||
InvokeNativeInternal();
|
||||
GlobalCleanUp();
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
|
||||
@@ -2,7 +2,19 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<EnablePackageValidation>true</EnablePackageValidation>
|
||||
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<Authors>Roflmuffin</Authors>
|
||||
<Description>Official server side runtime assembly for CounterStrikeSharp</Description>
|
||||
<PackageProjectUrl>http://docs.cssharp.dev/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/roflmuffin/CounterStrikeSharp</RepositoryUrl>
|
||||
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Modules\Commands\CommandInfo" />
|
||||
|
||||
35
managed/CounterStrikeSharp.API/Marshaling.cs
Normal file
35
managed/CounterStrikeSharp.API/Marshaling.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace CounterStrikeSharp.API;
|
||||
|
||||
public static class Marshaling
|
||||
{
|
||||
public static ColorMarshaler ColorMarshaler = new();
|
||||
}
|
||||
|
||||
public interface ICustomMarshal<T>
|
||||
{
|
||||
T NativeToManaged(IntPtr pointer);
|
||||
|
||||
void ManagedToNative(IntPtr pointer, T managedObj);
|
||||
}
|
||||
|
||||
public class ColorMarshaler : ICustomMarshal<Color>
|
||||
{
|
||||
public Color NativeToManaged(IntPtr pointer)
|
||||
{
|
||||
var color = Marshal.ReadInt32(pointer);
|
||||
var alpha = (byte)((color >> 24) & 0xFF);
|
||||
var blue = (byte)((color >> 16) & 0xFF);
|
||||
var green = (byte)((color >> 8) & 0xFF);
|
||||
var red = (byte)(color & 0xFF);
|
||||
return Color.FromArgb(alpha, red, green, blue);
|
||||
}
|
||||
|
||||
public void ManagedToNative(IntPtr pointer, Color managedObj)
|
||||
{
|
||||
Marshal.WriteInt32(pointer, (managedObj.A << 24) | (managedObj.B << 16) | (managedObj.G << 8) | managedObj.R);
|
||||
}
|
||||
}
|
||||
213
managed/CounterStrikeSharp.API/Modules/Admin/AdminManager.cs
Normal file
213
managed/CounterStrikeSharp.API/Modules/Admin/AdminManager.cs
Normal file
@@ -0,0 +1,213 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using System.Reflection;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
public partial class AdminData
|
||||
{
|
||||
[JsonPropertyName("identity")] public required string Identity { get; init; }
|
||||
[JsonPropertyName("flags")] public required HashSet<string> Flags { get; init; }
|
||||
}
|
||||
|
||||
public static class AdminManager
|
||||
{
|
||||
private static readonly Dictionary<SteamID, AdminData> Admins = new();
|
||||
|
||||
static AdminManager()
|
||||
{
|
||||
CommandUtils.AddStandaloneCommand("css_admins_reload", "Reloads the admin file.", ReloadAdminsCommand);
|
||||
CommandUtils.AddStandaloneCommand("css_admins_list", "List admins and their flags.", ListAdminsCommand);
|
||||
}
|
||||
|
||||
[RequiresPermissions("can_reload_admins")]
|
||||
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
private static void ReloadAdminsCommand(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
Admins.Clear();
|
||||
var rootDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.Parent;
|
||||
Load(Path.Combine(rootDir.FullName, "configs", "admins.json"));
|
||||
}
|
||||
|
||||
[RequiresPermissions("can_reload_admins")]
|
||||
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
private static void ListAdminsCommand(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
foreach (var (steamId, data) in Admins)
|
||||
{
|
||||
command.ReplyToCommand($"{steamId.SteamId64}, {steamId.SteamId2} - {string.Join(", ", data.Flags)}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void Load(string adminDataPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(adminDataPath))
|
||||
{
|
||||
Console.WriteLine("Admin data file not found. Skipping admin data load.");
|
||||
return;
|
||||
}
|
||||
|
||||
var adminsFromFile = JsonSerializer.Deserialize<Dictionary<string, AdminData>>(File.ReadAllText(adminDataPath), new JsonSerializerOptions() { ReadCommentHandling = JsonCommentHandling.Skip });
|
||||
if (adminsFromFile == null) { throw new FileNotFoundException(); }
|
||||
foreach (var adminDef in adminsFromFile.Values)
|
||||
{
|
||||
if (SteamID.TryParse(adminDef.Identity, out var steamId))
|
||||
{
|
||||
if (Admins.ContainsKey(steamId!))
|
||||
{
|
||||
Admins[steamId!].Flags.UnionWith(adminDef.Flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
Admins.Add(steamId!, adminDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"Loaded admin data with {Admins.Count} admins.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Failed to load admin data: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Grabs the admin data for a player that was loaded from "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="steamId">SteamID object of the player.</param>
|
||||
/// <returns>AdminData class if data found, null if not.</returns>
|
||||
public static AdminData? GetPlayerAdminData(SteamID steamId)
|
||||
{
|
||||
return Admins.GetValueOrDefault(steamId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if a player has access to a certain set of permission flags.
|
||||
/// </summary>
|
||||
/// <param name="player">Player or server console.</param>
|
||||
/// <param name="flags">Flags to look for in the players permission flags.</param>
|
||||
/// <returns>True if flags are present, false if not.</returns>
|
||||
public static bool PlayerHasPermissions(CCSPlayerController? player, params string[] flags)
|
||||
{
|
||||
// This is here for cases where the server console is attempting to call commands.
|
||||
// The server console should have access to all commands, regardless of permissions.
|
||||
if (player == null) return true;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return false; }
|
||||
var playerData = GetPlayerAdminData((SteamID)player.SteamID);
|
||||
return playerData?.Flags.IsSupersetOf(flags) ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if a player has access to a certain set of permission flags.
|
||||
/// </summary>
|
||||
/// <param name="steamId">Steam ID object.</param>
|
||||
/// <param name="flags">Flags to look for in the players permission flags.</param>
|
||||
/// <returns>True if flags are present, false if not.</returns>
|
||||
public static bool PlayerHasPermissions(SteamID steamId, params string[] flags)
|
||||
{
|
||||
var playerData = GetPlayerAdminData(steamId);
|
||||
return playerData?.Flags.IsSupersetOf(flags) ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily adds a permission flag to the player. These flags are not saved to
|
||||
/// "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="player">Player controller to add a flag to.</param>
|
||||
/// <param name="flags">Flags to add for the player.</param>
|
||||
public static void AddPlayerPermissions(CCSPlayerController? player, params string[] flags)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
|
||||
AddPlayerPermissions((SteamID)player.SteamID, flags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily adds a permission flag to the player. These flags are not saved to
|
||||
/// "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="steamId">SteamID to add a flag to.</param>
|
||||
/// <param name="flags">Flags to add for the player.</param>
|
||||
public static void AddPlayerPermissions(SteamID steamId, params string[] flags)
|
||||
{
|
||||
var data = GetPlayerAdminData(steamId);
|
||||
if (data == null)
|
||||
{
|
||||
data = new AdminData()
|
||||
{
|
||||
Identity = steamId.SteamId64.ToString(),
|
||||
Flags = new(flags)
|
||||
};
|
||||
|
||||
Admins[steamId] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var flag in flags)
|
||||
{
|
||||
data.Flags.Add(flag);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily removes a permission flag to the player. These flags are not saved to
|
||||
/// "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="player">Player controller to remove flags from.</param>
|
||||
/// <param name="flags">Flags to remove from the player.</param>
|
||||
public static void RemovePlayerPermissions(CCSPlayerController? player, params string[] flags)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
|
||||
|
||||
RemovePlayerPermissions((SteamID)player.SteamID, flags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily removes a permission flag to the player. These flags are not saved to
|
||||
/// "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="steamId">Steam ID to remove flags from.</param>
|
||||
/// <param name="flags">Flags to remove from the player.</param>
|
||||
public static void RemovePlayerPermissions(SteamID steamId, params string[] flags)
|
||||
{
|
||||
var data = GetPlayerAdminData(steamId);
|
||||
if (data == null) return;
|
||||
|
||||
data.Flags.ExceptWith(flags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a players admin data. This is not saved to "configs/admins.json"
|
||||
/// </summary>
|
||||
/// <param name="player">Player controller to remove admin data from.</param>
|
||||
public static void RemovePlayerAdminData(CCSPlayerController? player)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
|
||||
|
||||
RemovePlayerAdminData((SteamID)player.SteamID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a players admin data. This is not saved to "configs/admins.json"
|
||||
/// </summary>
|
||||
/// <param name="steamId">Steam ID remove admin data from.</param>
|
||||
public static void RemovePlayerAdminData(SteamID steamId)
|
||||
{
|
||||
Admins.Remove(steamId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class RequiresPermissions : Attribute
|
||||
{
|
||||
public string[] RequiredPermissions { get; }
|
||||
|
||||
public RequiresPermissions(params string[] permissions)
|
||||
{
|
||||
RequiredPermissions = permissions;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Commands
|
||||
{
|
||||
public enum CommandUsage
|
||||
{
|
||||
CLIENT_AND_SERVER = 0,
|
||||
CLIENT_ONLY,
|
||||
SERVER_ONLY
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public class CommandHelperAttribute : Attribute
|
||||
{
|
||||
public int MinArgs { get; }
|
||||
public string Usage { get; }
|
||||
public CommandUsage WhoCanExcecute { get; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="minArgs">The minimum amount of arguments required to execute this command.</param>
|
||||
/// <param name="usage">If the command fails, this string is printed to the caller to show the CommandUtils intended usage.</param>
|
||||
/// <param name="whoCanExecute">Restricts the command so it can only be executed by players, the server console, or both (see CommandUsage).</param>
|
||||
public CommandHelperAttribute(int minArgs = 0, string usage = "", CommandUsage whoCanExecute = CommandUsage.CLIENT_AND_SERVER)
|
||||
{
|
||||
MinArgs = minArgs;
|
||||
Usage = usage;
|
||||
WhoCanExcecute = whoCanExecute;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,10 +43,11 @@ namespace CounterStrikeSharp.API.Modules.Commands
|
||||
public string ArgByIndex(int index) => NativeAPI.CommandGetArgByIndex(Handle, index);
|
||||
public string GetArg(int index) => NativeAPI.CommandGetArgByIndex(Handle, index);
|
||||
|
||||
public void ReplyToCommand(string message) {
|
||||
public void ReplyToCommand(string message, bool console = false) {
|
||||
if (_player != null)
|
||||
{
|
||||
_player.PrintToChat(message);
|
||||
if (console) { _player.PrintToConsole(message); }
|
||||
else _player.PrintToChat(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@ using System;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Entities
|
||||
{
|
||||
public class SteamID
|
||||
public class SteamID : IEquatable<SteamID>
|
||||
{
|
||||
const long Base = 76561197960265728;
|
||||
public ulong SteamId64 { get; set; }
|
||||
@@ -12,7 +12,6 @@ namespace CounterStrikeSharp.API.Modules.Entities
|
||||
|
||||
public static explicit operator SteamID(ulong u) => new(u);
|
||||
public static explicit operator SteamID(string s) => new(s);
|
||||
|
||||
ulong ParseId(string id)
|
||||
{
|
||||
var parts = id.Split(':');
|
||||
@@ -46,5 +45,55 @@ namespace CounterStrikeSharp.API.Modules.Entities
|
||||
}
|
||||
|
||||
public override string ToString() => $"[SteamID {SteamId64}, {SteamId2}, {SteamId3}]";
|
||||
|
||||
public bool Equals(SteamID? other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return SteamId64 == other.SteamId64;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((SteamID)obj);
|
||||
}
|
||||
|
||||
public static bool TryParse(string s, out SteamID? steamId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ulong.TryParse(s, out var steamid64))
|
||||
{
|
||||
steamId = new SteamID(steamid64);
|
||||
return true;
|
||||
}
|
||||
|
||||
steamId = new SteamID(s);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
steamId = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return SteamId64.GetHashCode();
|
||||
}
|
||||
|
||||
public static bool operator ==(SteamID? left, SteamID? right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(SteamID? left, SteamID? right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ namespace CounterStrikeSharp.API.Modules.Events
|
||||
_ when type == typeof(string) => GetString(name),
|
||||
_ when type == typeof(bool) => GetBool(name),
|
||||
_ when type == typeof(ulong) => GetUint64(name),
|
||||
_ when type == typeof(long) => GetUint64(name),
|
||||
_ when type == typeof(long) => (long)GetUint64(name),
|
||||
_ when type == typeof(CCSPlayerController) => GetPlayer(name),
|
||||
_ => throw new NotSupportedException(),
|
||||
};
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
@@ -52,7 +52,7 @@ namespace CounterStrikeSharp.API.Modules.Memory
|
||||
{ typeof(string), DataType.DATA_TYPE_STRING },
|
||||
{ typeof(long), DataType.DATA_TYPE_LONG },
|
||||
{ typeof(ulong), DataType.DATA_TYPE_ULONG },
|
||||
{ typeof(short), DataType.DATA_TYPE_VARIANT },
|
||||
{ typeof(short), DataType.DATA_TYPE_SHORT },
|
||||
{ typeof(sbyte), DataType.DATA_TYPE_UCHAR },
|
||||
{ typeof(byte), DataType.DATA_TYPE_CHAR },
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
|
||||
@@ -9,8 +10,53 @@ public class Schema
|
||||
{
|
||||
private static Dictionary<Tuple<string, string>, short> _schemaOffsets = new();
|
||||
|
||||
private static HashSet<string> _cs2BadList = new HashSet<string>()
|
||||
{
|
||||
"m_bIsValveDS",
|
||||
"m_bIsQuestEligible",
|
||||
// "m_iItemDefinitionIndex", // as of 2023.11.11 this is currently not blocked
|
||||
"m_iEntityLevel",
|
||||
"m_iItemIDHigh",
|
||||
"m_iItemIDLow",
|
||||
"m_iAccountID",
|
||||
"m_iEntityQuality",
|
||||
|
||||
"m_bInitialized",
|
||||
"m_szCustomName",
|
||||
"m_iAttributeDefinitionIndex",
|
||||
"m_iRawValue32",
|
||||
"m_iRawInitialValue32",
|
||||
"m_flValue", // MNetworkAlias "m_iRawValue32"
|
||||
"m_flInitialValue", // MNetworkAlias "m_iRawInitialValue32"
|
||||
"m_bSetBonus",
|
||||
"m_nRefundableCurrency",
|
||||
|
||||
"m_OriginalOwnerXuidLow",
|
||||
"m_OriginalOwnerXuidHigh",
|
||||
|
||||
"m_nFallbackPaintKit",
|
||||
"m_nFallbackSeed",
|
||||
"m_flFallbackWear",
|
||||
"m_nFallbackStatTrak",
|
||||
|
||||
"m_iCompetitiveWins",
|
||||
"m_iCompetitiveRanking",
|
||||
"m_iCompetitiveRankType",
|
||||
"m_iCompetitiveRankingPredicted_Win",
|
||||
"m_iCompetitiveRankingPredicted_Loss",
|
||||
"m_iCompetitiveRankingPredicted_Tie",
|
||||
|
||||
"m_nActiveCoinRank",
|
||||
"m_nMusicID",
|
||||
};
|
||||
|
||||
public static short GetSchemaOffset(string className, string propertyName)
|
||||
{
|
||||
if (CoreConfig.FollowCS2ServerGuidelines && _cs2BadList.Contains(propertyName))
|
||||
{
|
||||
throw new Exception($"Cannot set or get '{className}::{propertyName}' with \"FollowCS2ServerGuidelines\" option enabled.");
|
||||
}
|
||||
|
||||
var key = new Tuple<string, string>(className, propertyName);
|
||||
if (!_schemaOffsets.TryGetValue(key, out var offset))
|
||||
{
|
||||
@@ -28,6 +74,11 @@ public class Schema
|
||||
|
||||
public static void SetSchemaValue<T>(IntPtr handle, string className, string propertyName, T value)
|
||||
{
|
||||
if (CoreConfig.FollowCS2ServerGuidelines && _cs2BadList.Contains(propertyName))
|
||||
{
|
||||
throw new Exception($"Cannot set or get '{className}::{propertyName}' with \"FollowCS2ServerGuidelines\" option enabled.");
|
||||
}
|
||||
|
||||
NativeAPI.SetSchemaValueByName<T>(handle, (int)typeof(T).ToDataType(), className, propertyName, value);
|
||||
}
|
||||
|
||||
@@ -96,4 +147,30 @@ public class Schema
|
||||
{
|
||||
SetSchemaValue(pointer, className, memberName, value);
|
||||
}
|
||||
|
||||
|
||||
public static T GetCustomMarshalledType<T>(IntPtr pointer, string className, string memberName)
|
||||
{
|
||||
var type = typeof(T);
|
||||
object result = type switch
|
||||
{
|
||||
_ when type == typeof(Color) => Marshaling.ColorMarshaler.NativeToManaged(pointer + GetSchemaOffset(className, memberName)),
|
||||
_ => throw new NotSupportedException(),
|
||||
};
|
||||
|
||||
return (T)result;
|
||||
}
|
||||
|
||||
public static void SetCustomMarshalledType<T>(IntPtr pointer, string className, string memberName, T value)
|
||||
{
|
||||
var type = typeof(T);
|
||||
switch (type)
|
||||
{
|
||||
case var _ when value is Color c:
|
||||
Marshaling.ColorMarshaler.ManagedToNative(pointer + GetSchemaOffset(className, memberName), c);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
public class CommandUtils
|
||||
{
|
||||
public static void AddStandaloneCommand(string name, string description, CommandInfo.CommandCallback handler)
|
||||
{
|
||||
var wrappedHandler = new Action<int, IntPtr>((i, ptr) =>
|
||||
{
|
||||
var caller = (i != -1) ? new CCSPlayerController(NativeAPI.GetEntityFromIndex(i + 1)) : null;
|
||||
var command = new CommandInfo(ptr, caller);
|
||||
|
||||
var methodInfo = handler?.GetMethodInfo();
|
||||
// Do not execute if we shouldn't be calling this command.
|
||||
var helperAttribute = methodInfo?.GetCustomAttribute<CommandHelperAttribute>();
|
||||
if (helperAttribute != null)
|
||||
{
|
||||
switch (helperAttribute.WhoCanExcecute)
|
||||
{
|
||||
case CommandUsage.CLIENT_AND_SERVER: break; // Allow command through.
|
||||
case CommandUsage.CLIENT_ONLY:
|
||||
if (caller == null || !caller.IsValid) { command.ReplyToCommand("[CSS] This command can only be executed by clients."); return; }
|
||||
break;
|
||||
case CommandUsage.SERVER_ONLY:
|
||||
if (caller != null && caller.IsValid) { command.ReplyToCommand("[CSS] This command can only be executed by the server."); return; }
|
||||
break;
|
||||
default: throw new ArgumentException("Unrecognised CommandUsage value passed in CommandHelperAttribute.");
|
||||
}
|
||||
|
||||
// Technically the command itself counts as the first argument,
|
||||
// but we'll just ignore that for this check.
|
||||
if (helperAttribute.MinArgs != 0 && command.ArgCount - 1 < helperAttribute.MinArgs)
|
||||
{
|
||||
command.ReplyToCommand($"[CSS] Expected usage: \"!{command.ArgByIndex(0)} {helperAttribute.Usage}\".");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not execute command if we do not have the correct permissions.
|
||||
var permissions = methodInfo?.GetCustomAttribute<RequiresPermissions>()?.RequiredPermissions;
|
||||
if (permissions != null && !AdminManager.PlayerHasPermissions(caller, permissions))
|
||||
{
|
||||
command.ReplyToCommand("[CSS] You do not have the correct permissions to execute this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
handler?.Invoke(caller, command);
|
||||
});
|
||||
|
||||
var methodInfo = handler?.GetMethodInfo();
|
||||
var helperAttribute = methodInfo?.GetCustomAttribute<CommandHelperAttribute>();
|
||||
|
||||
var subscriber = new BasePlugin.CallbackSubscriber(handler, wrappedHandler, () => { });
|
||||
NativeAPI.AddCommand(name, description, (helperAttribute?.WhoCanExcecute == CommandUsage.SERVER_ONLY),
|
||||
(int)ConCommandFlags.FCVAR_LINKED_CONCOMMAND, subscriber.GetInputArgument());
|
||||
}
|
||||
}
|
||||
13
managed/CounterStrikeSharp.API/Modules/Utils/matrix3x4_t.cs
Normal file
13
managed/CounterStrikeSharp.API/Modules/Utils/matrix3x4_t.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
public partial class matrix3x4_t : NativeObject
|
||||
{
|
||||
public matrix3x4_t(IntPtr pointer) : base(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
public unsafe ref float this[int row, int column] => ref Unsafe.Add(ref *(float*)Handle, row * 4 + column);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"profiles": {
|
||||
"CounterStrikeSharp.API": {
|
||||
"commandName": "Project",
|
||||
"nativeDebugging": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@ namespace CounterStrikeSharp.API
|
||||
public static IEnumerable<T> FindAllEntitiesByDesignerName<T>(string designerName) where T : CEntityInstance
|
||||
{
|
||||
var pEntity = new CEntityIdentity(NativeAPI.GetFirstActiveEntity());
|
||||
for (; pEntity.Handle != IntPtr.Zero; pEntity = pEntity.Next.Value)
|
||||
for (; pEntity != null && pEntity.Handle != IntPtr.Zero; pEntity = pEntity.Next)
|
||||
{
|
||||
if (!pEntity.DesignerName.Contains(designerName)) continue;
|
||||
yield return new PointerTo<T>(pEntity.Handle).Value;
|
||||
@@ -79,7 +79,7 @@ namespace CounterStrikeSharp.API
|
||||
{
|
||||
var controller = GetPlayerFromIndex(i);
|
||||
|
||||
if (!controller.IsValid || controller.UserId < 0)
|
||||
if (!controller.IsValid || controller.UserId == -1)
|
||||
continue;
|
||||
|
||||
players.Add(controller);
|
||||
@@ -88,6 +88,7 @@ namespace CounterStrikeSharp.API
|
||||
return players;
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static void ReplyToCommand(CCSPlayerController? player, string msg, bool console = false)
|
||||
{
|
||||
if (player != null)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/**
|
||||
* This project has been copied & modified from the demofile-net project under the MIT license.
|
||||
* This project has been copied & modified from the demofile-net project under the MIT license.
|
||||
* See ACKNOWLEDGEMENTS file for more information.
|
||||
* https://github.com/saul/demofile-net
|
||||
*/
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
@@ -20,7 +21,14 @@ internal static partial class Program
|
||||
"GameTick_t",
|
||||
"AttachmentHandle_t",
|
||||
"CGameSceneNodeHandle",
|
||||
"HSequence"
|
||||
"HSequence",
|
||||
"CAttributeManager::cached_attribute_float_t",
|
||||
"QuestProgress::Reason",
|
||||
"IChoreoServices::ScriptState_t",
|
||||
"IChoreoServices::ChoreoState_t",
|
||||
"SpawnPointCoopEnemy::BotDefaultBehavior_t",
|
||||
"CLogicBranchList::LogicBranchListenerLastState_t",
|
||||
"SimpleConstraintSoundProfile::SimpleConstraintsSoundProfileKeypoints_t"
|
||||
};
|
||||
|
||||
public static string SanitiseTypeName(string typeName) => typeName.Replace(":", "");
|
||||
@@ -35,7 +43,7 @@ internal static partial class Program
|
||||
var allEnums = new SortedDictionary<string, SchemaEnum>();
|
||||
var allClasses = new SortedDictionary<string, SchemaClass>();
|
||||
|
||||
var schemaFiles = new[] { "server.json", "!GlobalTypes.json" };
|
||||
var schemaFiles = new[] { "server.json" };
|
||||
|
||||
foreach (var schemaFile in schemaFiles)
|
||||
{
|
||||
@@ -200,28 +208,20 @@ internal static partial class Program
|
||||
|
||||
foreach (var field in schemaClass.Fields)
|
||||
{
|
||||
var defaultValue = field.Type.Category switch
|
||||
// Putting these in the too hard basket for now.
|
||||
if (field.Name == "m_VoteOptions" || field.Type.Name.Contains("CEntityOutputTemplate") ||
|
||||
field.Type.Name.Contains("CVariantBase") ||
|
||||
field.Type.Name == "HSCRIPT" || field.Type.Name == "KeyValues3") continue;
|
||||
|
||||
if (field.Type is { Category: SchemaTypeCategory.Atomic, Atomic: SchemaAtomicCategory.Collection })
|
||||
{
|
||||
SchemaTypeCategory.DeclaredClass =>
|
||||
" = new();",
|
||||
SchemaTypeCategory.FixedArray =>
|
||||
field.Type.IsString
|
||||
? $" = \"\";"
|
||||
: $" = Array.Empty<{field.Type.Inner!.CsTypeName}>();",
|
||||
SchemaTypeCategory.Atomic when field.Type.Atomic == SchemaAtomicCategory.Collection =>
|
||||
$" = new NetworkedVector<{field.Type.Inner!.CsTypeName}>();",
|
||||
_ => null
|
||||
};
|
||||
if (IgnoreClasses.Contains(field.Type.Inner!.Name)) continue;
|
||||
}
|
||||
|
||||
var handleParams = $"this.Handle, \"{schemaClassName}\", \"{field.Name}\"";
|
||||
|
||||
builder.AppendLine($" // {field.Name}");
|
||||
foreach (var (metadataKey, value) in field.Metadata)
|
||||
{
|
||||
builder.AppendLine($" // {metadataKey}{(value == "" ? "" : $" \"{value}\"")}");
|
||||
}
|
||||
|
||||
|
||||
if (field.Type is { Category: SchemaTypeCategory.FixedArray, CsTypeName: "string" })
|
||||
{
|
||||
var getter = $"return Schema.GetString({handleParams});";
|
||||
@@ -237,7 +237,7 @@ internal static partial class Program
|
||||
builder.AppendLine();
|
||||
}
|
||||
// Networked Strings require UTF8 encoding/decoding
|
||||
else if ( field.Type is { Category: SchemaTypeCategory.Atomic, CsTypeName: "string" })
|
||||
else if (field.Type is { Category: SchemaTypeCategory.Atomic, CsTypeName: "string" })
|
||||
{
|
||||
var getter = $"return Schema.GetUtf8String({handleParams});";
|
||||
var setter = $"Schema.SetString({handleParams}, value);";
|
||||
@@ -253,19 +253,23 @@ internal static partial class Program
|
||||
}
|
||||
else if (field.Type.Category == SchemaTypeCategory.FixedArray)
|
||||
{
|
||||
var getter = $"Schema.GetFixedArray<{SanitiseTypeName(field.Type.Inner!.CsTypeName)}>({handleParams}, {field.Type.ArraySize});";
|
||||
var getter =
|
||||
$"Schema.GetFixedArray<{SanitiseTypeName(field.Type.Inner!.CsTypeName)}>({handleParams}, {field.Type.ArraySize});";
|
||||
builder.AppendLine(
|
||||
$" public Span<{SanitiseTypeName(field.Type.Inner!.CsTypeName)}> {schemaClass.CsPropertyNameForField(schemaClassName, field)} => {getter}");
|
||||
builder.AppendLine();
|
||||
}
|
||||
else if (field.Type.Category == SchemaTypeCategory.DeclaredClass && !IgnoreClasses.Contains(field.Type.Name))
|
||||
else if (field.Type.Category == SchemaTypeCategory.DeclaredClass &&
|
||||
!IgnoreClasses.Contains(field.Type.Name))
|
||||
{
|
||||
var getter = $"Schema.GetDeclaredClass<{SanitiseTypeName(field.Type.CsTypeName)}>({handleParams});";
|
||||
builder.AppendLine(
|
||||
$" public {SanitiseTypeName(field.Type.CsTypeName)} {schemaClass.CsPropertyNameForField(schemaClassName, field)} => {getter}");
|
||||
builder.AppendLine();
|
||||
}
|
||||
else if ((field.Type.Category == SchemaTypeCategory.Builtin || field.Type.Category == SchemaTypeCategory.DeclaredEnum) && !IgnoreClasses.Contains(field.Type.Name))
|
||||
else if ((field.Type.Category == SchemaTypeCategory.Builtin ||
|
||||
field.Type.Category == SchemaTypeCategory.DeclaredEnum) &&
|
||||
!IgnoreClasses.Contains(field.Type.Name))
|
||||
{
|
||||
var getter = $"ref Schema.GetRef<{SanitiseTypeName(field.Type.CsTypeName)}>({handleParams});";
|
||||
builder.AppendLine(
|
||||
@@ -275,12 +279,26 @@ internal static partial class Program
|
||||
else if (field.Type.Category == SchemaTypeCategory.Ptr)
|
||||
{
|
||||
var inner = field.Type.Inner!;
|
||||
Debug.Assert(inner.Category == SchemaTypeCategory.DeclaredClass);
|
||||
if (inner.Category != SchemaTypeCategory.DeclaredClass) continue;
|
||||
|
||||
builder.AppendLine(
|
||||
$" public {SanitiseTypeName(field.Type.CsTypeName)} {schemaClass.CsPropertyNameForField(schemaClassName, field)} => Schema.GetPointer<{SanitiseTypeName(inner.CsTypeName)}>({handleParams});");
|
||||
builder.AppendLine();
|
||||
}
|
||||
}
|
||||
else if (field.Type is { Category: SchemaTypeCategory.Atomic, Name: "Color" })
|
||||
{
|
||||
var getter = $"return Schema.GetCustomMarshalledType<{field.Type.CsTypeName}>({handleParams});";
|
||||
var setter = $"Schema.SetCustomMarshalledType<{field.Type.CsTypeName}>({handleParams}, value);";
|
||||
builder.AppendLine(
|
||||
$" public {SanitiseTypeName(field.Type.CsTypeName)} {schemaClass.CsPropertyNameForField(schemaClassName, field)}");
|
||||
builder.AppendLine($" {{");
|
||||
builder.AppendLine(
|
||||
$" get {{ {getter} }}");
|
||||
builder.AppendLine(
|
||||
$" set {{ {setter} }}");
|
||||
builder.AppendLine($" }}");
|
||||
builder.AppendLine();
|
||||
}
|
||||
else if (field.Type.Category == SchemaTypeCategory.Atomic)
|
||||
{
|
||||
var getter = $"Schema.GetDeclaredClass<{SanitiseTypeName(field.Type.CsTypeName)}>({handleParams});";
|
||||
@@ -341,4 +359,4 @@ internal static partial class Program
|
||||
|
||||
builder.AppendLine("}");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -32,6 +32,7 @@ public partial record SchemaClass(
|
||||
case ("CEnvScreenOverlay", "m_bIsActive"): return "IsOverlayActive";
|
||||
case ("CPlayerSprayDecal", "m_nEntity"): return "DecalEntity";
|
||||
case ("CHandleTest", "m_Handle"): return "TestHandle";
|
||||
case ("FilterTeam", "m_iFilterTeam"): return "Value";
|
||||
}
|
||||
|
||||
string CleanFieldName(string fieldName) =>
|
||||
|
||||
@@ -1,14 +1,39 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace CounterStrikeSharp.SchemaGen;
|
||||
|
||||
public record SchemaFieldType(
|
||||
string Name,
|
||||
SchemaTypeCategory Category,
|
||||
SchemaAtomicCategory? Atomic,
|
||||
SchemaFieldType? Inner,
|
||||
int? ArraySize)
|
||||
public record SchemaFieldType
|
||||
{
|
||||
public SchemaFieldType(string Name,
|
||||
SchemaTypeCategory Category,
|
||||
SchemaAtomicCategory? Atomic,
|
||||
SchemaFieldType? Inner)
|
||||
{
|
||||
this.Name = Name;
|
||||
this.Category = Category;
|
||||
this.Atomic = Atomic;
|
||||
this.Inner = Inner;
|
||||
|
||||
if (this.Name == "GameTime_t")
|
||||
{
|
||||
this.Category = SchemaTypeCategory.Builtin;
|
||||
this.Name = "float32";
|
||||
}
|
||||
else if
|
||||
(this.Name == "CPlayerSlot" || this.Name == "HSequence" || this.Name == "CSplitScreenSlot" || this.Name == "GameTick_t")
|
||||
{
|
||||
this.Category = SchemaTypeCategory.Builtin;
|
||||
this.Name = "int32";
|
||||
}
|
||||
else if (this.Name == "CBitVec< 64 >")
|
||||
{
|
||||
this.Category = SchemaTypeCategory.FixedArray;
|
||||
this.Inner = new SchemaFieldType("uint8", SchemaTypeCategory.Builtin, null, null);
|
||||
this.Name = "uint8[8]";
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsString =>
|
||||
Category == SchemaTypeCategory.FixedArray
|
||||
&& Inner!.Category == SchemaTypeCategory.Builtin
|
||||
@@ -46,19 +71,22 @@ public record SchemaFieldType(
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(name), name, $"Unknown built-in: {name}")
|
||||
};
|
||||
|
||||
private static string AtomicToCsTypeName(string name, SchemaAtomicCategory atomic, SchemaFieldType? inner) => atomic switch
|
||||
{
|
||||
SchemaAtomicCategory.Basic => name switch
|
||||
private static string AtomicToCsTypeName(string name, SchemaAtomicCategory atomic, SchemaFieldType? inner) =>
|
||||
atomic switch
|
||||
{
|
||||
"CUtlString" or "CUtlSymbolLarge" => "string",
|
||||
"CEntityHandle" => "CHandle<CEntityInstance>",
|
||||
"CNetworkedQuantizedFloat" => "float",
|
||||
_ => name
|
||||
},
|
||||
SchemaAtomicCategory.T => $"{name.Split('<')[0]}<{inner!.CsTypeName}>",
|
||||
SchemaAtomicCategory.Collection => $"NetworkedVector<{inner!.CsTypeName}>",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(atomic), atomic, $"Unsupported atomic: {atomic}")
|
||||
};
|
||||
SchemaAtomicCategory.Basic => name switch
|
||||
{
|
||||
"CUtlString" or "CUtlSymbolLarge" => "string",
|
||||
"CEntityHandle" => "CHandle<CEntityInstance>",
|
||||
"CNetworkedQuantizedFloat" => "float",
|
||||
"RotationVector" => "Vector",
|
||||
_ => name
|
||||
},
|
||||
SchemaAtomicCategory.T => $"{name.Split('<')[0]}<{inner!.CsTypeName}>",
|
||||
SchemaAtomicCategory.Collection => $"NetworkedVector<{inner!.CsTypeName}>",
|
||||
SchemaAtomicCategory.Unknown => "CBitVec",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(atomic), atomic, $"Unsupported atomic: {atomic}")
|
||||
};
|
||||
|
||||
public string CsTypeName => Category switch
|
||||
{
|
||||
@@ -72,4 +100,34 @@ public record SchemaFieldType(
|
||||
SchemaTypeCategory.DeclaredEnum => Name,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(Category), Category, $"Unsupported type category: {Category}")
|
||||
};
|
||||
}
|
||||
|
||||
public string Name { get; init; }
|
||||
public SchemaTypeCategory Category { get; init; }
|
||||
public SchemaAtomicCategory? Atomic { get; init; }
|
||||
public SchemaFieldType? Inner { get; init; }
|
||||
|
||||
public int? ArraySize
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Category == SchemaTypeCategory.FixedArray)
|
||||
{
|
||||
// Extract number from name, e.g. `uint16[2]`
|
||||
var match = Regex.Match(this.Name, @"\[(\d+)\]$");
|
||||
return match.Success ? int.Parse(match.Groups[1].Value) : null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Deconstruct(out string Name, out SchemaTypeCategory Category, out SchemaAtomicCategory? Atomic,
|
||||
out SchemaFieldType? Inner, out int? ArraySize)
|
||||
{
|
||||
Name = this.Name;
|
||||
Category = this.Category;
|
||||
Atomic = this.Atomic;
|
||||
Inner = this.Inner;
|
||||
ArraySize = this.ArraySize;
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,11 @@
|
||||
*/
|
||||
|
||||
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;
|
||||
@@ -31,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";
|
||||
@@ -41,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}");
|
||||
|
||||
@@ -135,6 +163,10 @@ namespace TestPlugin
|
||||
var activeWeapon = @event.Userid.PlayerPawn.Value.WeaponServices?.ActiveWeapon.Value;
|
||||
var weapons = @event.Userid.PlayerPawn.Value.WeaponServices?.MyWeapons;
|
||||
|
||||
// Set player to random colour
|
||||
player.PlayerPawn.Value.Render = Color.FromArgb(Random.Shared.Next(0, 255),
|
||||
Random.Shared.Next(0, 255), Random.Shared.Next(0, 255));
|
||||
|
||||
Server.NextFrame(() =>
|
||||
{
|
||||
player.PrintToCenter(string.Join("\n", weapons.Select(x => x.Value.DesignerName)));
|
||||
|
||||
@@ -5,9 +5,12 @@
|
||||
<Platforms>AnyCPU;x86</Platforms>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj" />
|
||||
<ProjectReference Include="..\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj">
|
||||
<Private>false</Private>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
130
src/core/cs2_sdk/interfaces/CUtlTSHash.h
Normal file
130
src/core/cs2_sdk/interfaces/CUtlTSHash.h
Normal file
@@ -0,0 +1,130 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
using UtlTsHashHandleT = std::uint64_t;
|
||||
|
||||
class CUtlMemoryPool {
|
||||
public:
|
||||
// returns number of allocated blocks
|
||||
int BlockSize() const {
|
||||
return m_blocks_per_blob_;
|
||||
}
|
||||
int Count() const {
|
||||
return m_block_allocated_size_;
|
||||
}
|
||||
int PeakCount() const {
|
||||
return m_peak_alloc_;
|
||||
}
|
||||
private:
|
||||
std::int32_t m_block_size_ = 0; // 0x0558
|
||||
std::int32_t m_blocks_per_blob_ = 0; // 0x055C
|
||||
std::int32_t m_grow_mode_ = 0; // 0x0560
|
||||
std::int32_t m_blocks_allocated_ = 0; // 0x0564
|
||||
std::int32_t m_block_allocated_size_ = 0; // 0x0568
|
||||
std::int32_t m_peak_alloc_ = 0; // 0x056C
|
||||
};
|
||||
|
||||
template <class T, class Keytype = std::uint64_t>
|
||||
class CUtlTSHash {
|
||||
public:
|
||||
// Invalid handle.
|
||||
static UtlTsHashHandleT InvalidHandle(void) {
|
||||
return static_cast<UtlTsHashHandleT>(0);
|
||||
}
|
||||
|
||||
// Returns the number of elements in the hash table
|
||||
[[nodiscard]] int BlockSize() const {
|
||||
return m_entry_memory_.BlockSize();
|
||||
}
|
||||
[[nodiscard]] int Count() const {
|
||||
return m_entry_memory_.Count();
|
||||
}
|
||||
|
||||
// Returns elements in the table
|
||||
std::vector<T> GetElements(void);
|
||||
public:
|
||||
// Templatized for memory tracking purposes
|
||||
template <typename DataT>
|
||||
struct HashFixedDataInternalT {
|
||||
Keytype m_ui_key;
|
||||
HashFixedDataInternalT<DataT>* m_next;
|
||||
DataT m_data;
|
||||
};
|
||||
|
||||
using HashFixedDataT = HashFixedDataInternalT<T>;
|
||||
|
||||
// Templatized for memory tracking purposes
|
||||
template <typename DataT>
|
||||
struct HashFixedStructDataInternalT {
|
||||
DataT m_data;
|
||||
Keytype m_ui_key;
|
||||
char pad_0x0020[0x8];
|
||||
};
|
||||
|
||||
using HashFixedStructDataT = HashFixedStructDataInternalT<T>;
|
||||
|
||||
struct HashStructDataT {
|
||||
char pad_0x0000[0x10]; // 0x0000
|
||||
std::array<HashFixedStructDataT, 256> m_list;
|
||||
};
|
||||
|
||||
struct HashAllocatedDataT {
|
||||
public:
|
||||
auto GetList() {
|
||||
return m_list_;
|
||||
}
|
||||
private:
|
||||
char pad_0x0000[0x18]; // 0x0000
|
||||
std::array<HashFixedDataT, 128> m_list_;
|
||||
};
|
||||
|
||||
// Templatized for memory tracking purposes
|
||||
template <typename DataT>
|
||||
struct HashBucketDataInternalT {
|
||||
DataT m_data;
|
||||
HashFixedDataInternalT<DataT>* m_next;
|
||||
Keytype m_ui_key;
|
||||
};
|
||||
|
||||
using HashBucketDataT = HashBucketDataInternalT<T>;
|
||||
|
||||
struct HashUnallocatedDataT {
|
||||
HashUnallocatedDataT* m_next_ = nullptr; // 0x0000
|
||||
Keytype m_6114; // 0x0008
|
||||
Keytype m_ui_key; // 0x0010
|
||||
Keytype m_i_unk_1; // 0x0018
|
||||
std::array<HashBucketDataT, 256> m_current_block_list; // 0x0020
|
||||
};
|
||||
|
||||
struct HashBucketT {
|
||||
HashStructDataT* m_struct_data = nullptr;
|
||||
void* m_mutex_list = nullptr;
|
||||
HashAllocatedDataT* m_allocated_data = nullptr;
|
||||
HashUnallocatedDataT* m_unallocated_data = nullptr;
|
||||
};
|
||||
|
||||
CUtlMemoryPool m_entry_memory_;
|
||||
HashBucketT m_buckets_;
|
||||
bool m_needs_commit_ = false;
|
||||
};
|
||||
|
||||
template <class T, class Keytype>
|
||||
std::vector<T> CUtlTSHash<T, Keytype>::GetElements(void) {
|
||||
std::vector<T> list;
|
||||
|
||||
const int n_count = Count();
|
||||
auto n_index = 0;
|
||||
auto& unallocated_data = m_buckets_.m_unallocated_data;
|
||||
for (auto element = unallocated_data; element; element = element->m_next_) {
|
||||
for (auto i = 0; i < BlockSize() && i != n_count; i++) {
|
||||
list.emplace_back(element->m_current_block_list.at(i).m_data);
|
||||
n_index++;
|
||||
|
||||
if (n_index >= n_count)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
@@ -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"));
|
||||
}
|
||||
};
|
||||
@@ -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() {
|
||||
|
||||
@@ -1,127 +1,394 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* CS2Fixes
|
||||
* Copyright (C) 2023 Source2ZE
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program 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
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "../../utils/virtual.h"
|
||||
#include <platform.h>
|
||||
|
||||
struct CSchemaNetworkValue {
|
||||
union {
|
||||
const char* m_sz_value;
|
||||
int m_n_value;
|
||||
float m_f_value;
|
||||
uintptr_t m_p_value;
|
||||
};
|
||||
#include <vector>
|
||||
#include <utils/virtual.h>
|
||||
#include <string_view>
|
||||
#include <array>
|
||||
#include "core/cs2_sdk/interfaces/CUtlTSHash.h"
|
||||
#define CS2
|
||||
|
||||
#define CSCHEMATYPE_GETSIZES_INDEX 3
|
||||
#define SCHEMASYSTEM_TYPE_SCOPES_OFFSET 0x190
|
||||
#define SCHEMASYSTEMTYPESCOPE_OFF1 0x47E
|
||||
#define SCHEMASYSTEMTYPESCOPE_OFF2 0x2808
|
||||
#define SCHEMASYSTEM_FIND_DECLARED_CLASS_TYPE 2
|
||||
|
||||
class CSchemaClassInfo;
|
||||
class CSchemaSystemTypeScope;
|
||||
class CSchemaType;
|
||||
|
||||
struct SchemaMetadataEntryData_t;
|
||||
struct SchemaMetadataSetData_t;
|
||||
struct SchemaClassInfoData_t;
|
||||
|
||||
using SchemaString_t = const char*;
|
||||
|
||||
template <typename T> struct SchemaArray
|
||||
{
|
||||
public:
|
||||
T* begin() const { return m_data; }
|
||||
|
||||
T* end() const { return m_data + m_size; }
|
||||
|
||||
T* m_data;
|
||||
unsigned int m_size;
|
||||
};
|
||||
|
||||
struct SchemaMetadataEntryData_t {
|
||||
const char *m_name;
|
||||
CSchemaNetworkValue* m_value;
|
||||
struct CSchemaNetworkValue
|
||||
{
|
||||
union
|
||||
{
|
||||
const char* m_sz_value;
|
||||
int m_n_value;
|
||||
float m_f_value;
|
||||
std::uintptr_t m_p_value;
|
||||
};
|
||||
};
|
||||
|
||||
struct CSchemaClassBinding
|
||||
{
|
||||
CSchemaClassBinding* parent;
|
||||
const char* m_binary_name; // ex: C_World
|
||||
const char* m_module_name; // ex: libclient.so
|
||||
const char* m_class_name; // ex: client
|
||||
void* m_class_info_old_synthesized;
|
||||
void* m_class_info;
|
||||
void* m_this_module_binding_pointer;
|
||||
CSchemaType* m_schema_type;
|
||||
};
|
||||
|
||||
struct SchemaEnumeratorInfoData_t
|
||||
{
|
||||
SchemaString_t m_name;
|
||||
|
||||
union
|
||||
{
|
||||
unsigned char m_value_char;
|
||||
unsigned short m_value_short;
|
||||
unsigned int m_value_int;
|
||||
unsigned long long m_value;
|
||||
};
|
||||
|
||||
char pad_0x0010[0x10]; // 0x0010
|
||||
};
|
||||
|
||||
class CSchemaEnumInfo
|
||||
{
|
||||
public:
|
||||
SchemaEnumeratorInfoData_t m_field_;
|
||||
};
|
||||
|
||||
class CSchemaEnumBinding
|
||||
{
|
||||
public:
|
||||
virtual const char* GetBindingName() = 0;
|
||||
virtual CSchemaClassBinding* AsClassBinding() = 0;
|
||||
virtual CSchemaEnumBinding* AsEnumBinding() = 0;
|
||||
virtual const char* GetBinaryName() = 0;
|
||||
virtual const char* GetProjectName() = 0;
|
||||
|
||||
public:
|
||||
char* m_binding_name_; // 0x0008
|
||||
char* m_dll_name_; // 0x0010
|
||||
std::int8_t m_align_; // 0x0018
|
||||
char pad_0x0019[0x3]; // 0x0019
|
||||
std::int16_t m_size_; // 0x001C
|
||||
std::int16_t m_flags_; // 0x001E
|
||||
SchemaEnumeratorInfoData_t* m_enum_info_;
|
||||
char pad_0x0028[0x8]; // 0x0028
|
||||
CSchemaSystemTypeScope* m_type_scope_; // 0x0030
|
||||
char pad_0x0038[0x8]; // 0x0038
|
||||
std::int32_t m_i_unk1_; // 0x0040
|
||||
};
|
||||
|
||||
enum SchemaClassFlags_t
|
||||
{
|
||||
SCHEMA_CLASS_HAS_VIRTUAL_MEMBERS = 1,
|
||||
SCHEMA_CLASS_IS_ABSTRACT = 2,
|
||||
SCHEMA_CLASS_HAS_TRIVIAL_CONSTRUCTOR = 4,
|
||||
SCHEMA_CLASS_HAS_TRIVIAL_DESTRUCTOR = 8,
|
||||
SCHEMA_CLASS_TEMP_HACK_HAS_NOSCHEMA_MEMBERS = 16,
|
||||
SCHEMA_CLASS_TEMP_HACK_HAS_CONSTRUCTOR_LIKE_METHODS = 32,
|
||||
SCHEMA_CLASS_TEMP_HACK_HAS_DESTRUCTOR_LIKE_METHODS = 64,
|
||||
SCHEMA_CLASS_IS_NOSCHEMA_CLASS = 128,
|
||||
};
|
||||
|
||||
enum ETypeCategory
|
||||
{
|
||||
Schema_Builtin = 0,
|
||||
Schema_Ptr = 1,
|
||||
Schema_Bitfield = 2,
|
||||
Schema_FixedArray = 3,
|
||||
Schema_Atomic = 4,
|
||||
Schema_DeclaredClass = 5,
|
||||
Schema_DeclaredEnum = 6,
|
||||
Schema_None = 7
|
||||
};
|
||||
|
||||
enum EAtomicCategory
|
||||
{
|
||||
Atomic_Basic,
|
||||
Atomic_T,
|
||||
Atomic_CollectionOfT,
|
||||
Atomic_TT,
|
||||
Atomic_I,
|
||||
Atomic_None
|
||||
};
|
||||
|
||||
#define __thiscall
|
||||
|
||||
class CSchemaType
|
||||
{
|
||||
public:
|
||||
bool GetSizes(int* out_size1, uint8_t* unk_probably_not_size)
|
||||
{
|
||||
return reinterpret_cast<int(__thiscall*)(void*, int*, uint8_t*)>(
|
||||
_vtable[CSCHEMATYPE_GETSIZES_INDEX])(this, out_size1, unk_probably_not_size);
|
||||
}
|
||||
|
||||
public:
|
||||
bool GetSize(int* out_size)
|
||||
{
|
||||
uint8_t smh = 0;
|
||||
return GetSizes(out_size, &smh);
|
||||
}
|
||||
|
||||
public:
|
||||
uintptr_t* _vtable; // 0x0000
|
||||
const char* m_name_; // 0x0008
|
||||
|
||||
CSchemaSystemTypeScope* m_type_scope_; // 0x0010
|
||||
uint8_t type_category; // ETypeCategory 0x0018
|
||||
uint8_t atomic_category; // EAtomicCategory 0x0019
|
||||
|
||||
// find out to what class pointer points.
|
||||
CSchemaType* GetRefClass()
|
||||
{
|
||||
if (type_category != Schema_Ptr)
|
||||
return nullptr;
|
||||
|
||||
auto ptr = m_schema_type_;
|
||||
while (ptr && ptr->type_category == ETypeCategory::Schema_Ptr)
|
||||
ptr = ptr->m_schema_type_;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
struct array_t
|
||||
{
|
||||
uint32_t array_size;
|
||||
uint32_t unknown;
|
||||
CSchemaType* element_type_;
|
||||
};
|
||||
|
||||
struct generic_type_t
|
||||
{
|
||||
uint64_t unknown;
|
||||
const char* m_name_; // 0x0008
|
||||
};
|
||||
|
||||
struct atomic_t
|
||||
{ // same goes for CollectionOfT
|
||||
generic_type_t* generic_type;
|
||||
uint64_t unknown;
|
||||
CSchemaType* template_typename;
|
||||
};
|
||||
|
||||
struct atomic_tt
|
||||
{
|
||||
uint64_t gap[2];
|
||||
CSchemaType* templates[2];
|
||||
};
|
||||
|
||||
struct atomic_i
|
||||
{
|
||||
uint64_t gap[2];
|
||||
uint64_t integer;
|
||||
};
|
||||
|
||||
// this union depends on CSchema implementation, all members above
|
||||
// is from base class ( CSchemaType )
|
||||
union // 0x020
|
||||
{
|
||||
CSchemaType* m_schema_type_;
|
||||
CSchemaClassInfo* m_class_info;
|
||||
CSchemaEnumBinding* m_enum_binding_;
|
||||
array_t m_array_;
|
||||
atomic_t m_atomic_t_;
|
||||
atomic_tt m_atomic_tt_;
|
||||
atomic_i m_atomic_i_;
|
||||
};
|
||||
};
|
||||
static_assert(offsetof(CSchemaType, m_schema_type_) == 0x20);
|
||||
|
||||
struct SchemaMetadataEntryData_t
|
||||
{
|
||||
SchemaString_t m_name;
|
||||
CSchemaNetworkValue* m_value;
|
||||
// CSchemaType* m_pDataType;
|
||||
// void* unaccounted;
|
||||
};
|
||||
|
||||
struct SchemaMetadataSetData_t
|
||||
{
|
||||
SchemaMetadataEntryData_t m_static_entries;
|
||||
};
|
||||
|
||||
struct SchemaClassFieldData_t
|
||||
{
|
||||
const char *m_name;
|
||||
char pad0[0x8];
|
||||
short m_offset;
|
||||
int32_t m_metadata_size;
|
||||
SchemaMetadataEntryData_t* m_metadata;
|
||||
SchemaString_t m_name; // 0x0000
|
||||
CSchemaType* m_type; // 0x0008
|
||||
std::int32_t m_single_inheritance_offset; // 0x0010
|
||||
std::int32_t m_metadata_size; // 0x0014
|
||||
SchemaMetadataEntryData_t* m_metadata; // 0x0018
|
||||
};
|
||||
|
||||
class SchemaClassInfoData_t;
|
||||
struct SchemaStaticFieldData_t
|
||||
{
|
||||
const char* name; // 0x0000
|
||||
CSchemaType* m_type; // 0x0008
|
||||
void* m_instance; // 0x0010
|
||||
char pad_0x0018[0x10]; // 0x0018
|
||||
};
|
||||
|
||||
struct SchemaBaseClassInfoData_t
|
||||
{
|
||||
unsigned int m_offset;
|
||||
SchemaClassInfoData_t *m_class;
|
||||
unsigned int m_offset;
|
||||
CSchemaClassInfo* m_class;
|
||||
};
|
||||
|
||||
class SchemaClassInfoData_t
|
||||
// Classes
|
||||
struct SchemaClassInfoData_t
|
||||
{
|
||||
public:
|
||||
auto GetName()
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
char pad_0x0000[0x8]; // 0x0000
|
||||
|
||||
auto GetFieldsSize()
|
||||
{
|
||||
return m_align;
|
||||
}
|
||||
const char* m_name; // 0x0008
|
||||
char* m_module; // 0x0010
|
||||
|
||||
auto GetFields()
|
||||
{
|
||||
return m_fields;
|
||||
}
|
||||
int m_size; // 0x0018
|
||||
std::int16_t m_align; // 0x001C
|
||||
|
||||
SchemaClassInfoData_t* GetParent()
|
||||
{
|
||||
if (!m_schema_parent)
|
||||
return nullptr;
|
||||
std::int16_t m_static_size; // 0x001E
|
||||
std::int16_t m_metadata_size; // 0x0020
|
||||
std::int16_t m_i_unk1; // 0x0022
|
||||
std::int16_t m_i_unk2; // 0x0024
|
||||
std::int16_t m_i_unk3; // 0x0026
|
||||
|
||||
return m_schema_parent->m_class;
|
||||
}
|
||||
SchemaClassFieldData_t* m_fields; // 0x0028
|
||||
|
||||
private:
|
||||
char pad_0x0000[0x8]; // 0x0000
|
||||
SchemaStaticFieldData_t* m_static_fields; // 0x0030
|
||||
SchemaBaseClassInfoData_t* m_schema_parent; // 0x0038
|
||||
|
||||
const char *m_name; // 0x0008
|
||||
char *m_module; // 0x0010
|
||||
char pad_0x0038[0x10]; // 0x0038
|
||||
|
||||
int m_size; // 0x0018
|
||||
int16_t m_align; // 0x001C
|
||||
SchemaMetadataSetData_t* m_metadata; // 0x0048
|
||||
CSchemaSystemTypeScope* m_type_scope; // 0x0050
|
||||
CSchemaType* m_shema_type; // 0x0058
|
||||
SchemaClassFlags_t m_class_flags : 8; // 0x0060
|
||||
};
|
||||
|
||||
int16_t m_static_size; // 0x001E
|
||||
int16_t m_metadata_size; // 0x0020
|
||||
int16_t m_i_unk1; // 0x0022
|
||||
int16_t m_i_unk2; // 0x0024
|
||||
int16_t m_i_unk3; // 0x0026
|
||||
class CSchemaClassInfo : public SchemaClassInfoData_t
|
||||
{
|
||||
public:
|
||||
bool GetMetaStrings(const char* metaName, std::vector<const char**>& strings);
|
||||
|
||||
SchemaClassFieldData_t *m_fields; // 0x0028
|
||||
unsigned int CalculateInheritanceDataSize(bool ignoreVirtuals = false);
|
||||
|
||||
char pad_0x0030[0x8]; // 0x0030
|
||||
SchemaBaseClassInfoData_t *m_schema_parent; // 0x0038
|
||||
bool DependsOn(CSchemaClassInfo* other);
|
||||
bool InheritsFrom(CSchemaClassInfo* other);
|
||||
bool UsesClass(CSchemaClassInfo* other);
|
||||
bool InheritsVirtuals();
|
||||
|
||||
char pad_0x0038[0x10]; // 0x0038
|
||||
void FillClassFieldsList(std::vector<SchemaClassFieldData_t*>& fields);
|
||||
void FillInheritanceList(std::vector<const char*>& inheritance);
|
||||
CSchemaClassInfo* GetParent()
|
||||
{
|
||||
if (!m_schema_parent)
|
||||
return nullptr;
|
||||
|
||||
return m_schema_parent->m_class;
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
class CSchemaSystemTypeScope
|
||||
{
|
||||
public:
|
||||
SchemaClassInfoData_t* FindDeclaredClass(const char *pClass)
|
||||
{
|
||||
public:
|
||||
CSchemaClassInfo* FindDeclaredClass(const char* class_name)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SchemaClassInfoData_t *rv = nullptr;
|
||||
CALL_VIRTUAL(void, 2, this, &rv, pClass);
|
||||
return rv;
|
||||
CSchemaClassInfo* rv = nullptr;
|
||||
CALL_VIRTUAL(void, 2, this, &rv, class_name);
|
||||
return rv;
|
||||
#else
|
||||
return CALL_VIRTUAL(SchemaClassInfoData_t*, 2, this, pClass);
|
||||
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)
|
||||
{
|
||||
return CALL_VIRTUAL(CSchemaType*, 4, this, name, pSchema);
|
||||
}
|
||||
|
||||
CSchemaType* FindTypeDeclaredClass(const char* name)
|
||||
{
|
||||
return CALL_VIRTUAL(CSchemaType*, 5, this, name);
|
||||
}
|
||||
|
||||
CSchemaType* FindTypeDeclaredEnum(const char* name)
|
||||
{
|
||||
return CALL_VIRTUAL(CSchemaType*, 6, this, name);
|
||||
}
|
||||
|
||||
CSchemaClassBinding* FindRawClassBinding(const char* name)
|
||||
{
|
||||
return CALL_VIRTUAL(CSchemaClassBinding*, 7, this, name);
|
||||
}
|
||||
|
||||
CSchemaEnumBinding* FindRawEnumBinding(const char* name)
|
||||
{
|
||||
return CALL_VIRTUAL(CSchemaEnumBinding*, 8, this, name);
|
||||
}
|
||||
|
||||
std::string_view GetScopeName() { return {m_name_.data()}; }
|
||||
|
||||
[[nodiscard]] CUtlTSHash<CSchemaClassBinding*> GetClasses() const { return m_classes_; }
|
||||
|
||||
[[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
|
||||
private:
|
||||
static constexpr unsigned int s_class_list = 0x580;
|
||||
};
|
||||
|
||||
class CSchemaSystem
|
||||
{
|
||||
public:
|
||||
auto FindTypeScopeForModule(const char *module)
|
||||
{
|
||||
return CALL_VIRTUAL(CSchemaSystemTypeScope *, 13, this, module, nullptr);
|
||||
}
|
||||
};
|
||||
public:
|
||||
CSchemaSystemTypeScope* GlobalTypeScope(void)
|
||||
{
|
||||
return CALL_VIRTUAL(CSchemaSystemTypeScope*, 11, this);
|
||||
}
|
||||
|
||||
CSchemaSystemTypeScope* FindTypeScopeForModule(const char* m_module_name)
|
||||
{
|
||||
return CALL_VIRTUAL(CSchemaSystemTypeScope*, 13, this, m_module_name, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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*>;
|
||||
|
||||
@@ -57,8 +59,8 @@ static bool InitSchemaFieldsForClass(SchemaTableMap_t* tableMap,
|
||||
return false;
|
||||
}
|
||||
|
||||
short fieldsSize = pClassInfo->GetFieldsSize();
|
||||
SchemaClassFieldData_t* pFields = pClassInfo->GetFields();
|
||||
short fieldsSize = pClassInfo->m_align;
|
||||
SchemaClassFieldData_t* pFields = pClassInfo->m_fields;
|
||||
|
||||
SchemaKeyValueMap_t* keyValueMap = new SchemaKeyValueMap_t(0, 0, DefLessFunc(uint32_t));
|
||||
keyValueMap->EnsureCapacity(fieldsSize);
|
||||
@@ -68,7 +70,7 @@ static bool InitSchemaFieldsForClass(SchemaTableMap_t* tableMap,
|
||||
SchemaClassFieldData_t& field = pFields[i];
|
||||
|
||||
keyValueMap->Insert(hash_32_fnv1a_const(field.m_name),
|
||||
{field.m_offset, IsFieldNetworked(field)});
|
||||
{field.m_single_inheritance_offset, IsFieldNetworked(field)});
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -80,16 +82,16 @@ int16_t schema::FindChainOffset(const char* className) {
|
||||
|
||||
if (!pType) return false;
|
||||
|
||||
SchemaClassInfoData_t* pClassInfo = pType->FindDeclaredClass(className);
|
||||
auto* pClassInfo = pType->FindDeclaredClass(className);
|
||||
|
||||
do {
|
||||
SchemaClassFieldData_t* pFields = pClassInfo->GetFields();
|
||||
short fieldsSize = pClassInfo->GetFieldsSize();
|
||||
SchemaClassFieldData_t* pFields = pClassInfo->m_fields;
|
||||
short fieldsSize = pClassInfo->m_align;
|
||||
for (int i = 0; i < fieldsSize; ++i) {
|
||||
SchemaClassFieldData_t& field = pFields[i];
|
||||
|
||||
if (V_strcmp(field.m_name, "__m_pChainEntity") == 0) {
|
||||
return field.m_offset;
|
||||
return field.m_single_inheritance_offset;
|
||||
}
|
||||
}
|
||||
} while ((pClassInfo = pClassInfo->GetParent()) != nullptr);
|
||||
|
||||
@@ -19,27 +19,17 @@
|
||||
|
||||
#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 {
|
||||
int16_t offset;
|
||||
int32_t offset;
|
||||
bool networked;
|
||||
};
|
||||
|
||||
|
||||
236
src/core/gameconfig.cpp
Normal file
236
src/core/gameconfig.cpp
Normal 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
51
src/core/gameconfig.h
Normal 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
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "mm_plugin.h"
|
||||
#include "core/globals.h"
|
||||
#include "core/managers/player_manager.h"
|
||||
#include "iserver.h"
|
||||
@@ -12,6 +13,7 @@
|
||||
|
||||
#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"
|
||||
@@ -22,6 +24,7 @@
|
||||
#include <public/game/server/iplayerinfo.h>
|
||||
#include <public/entity2/entitysystem.h>
|
||||
|
||||
|
||||
namespace counterstrikesharp {
|
||||
|
||||
namespace modules {
|
||||
@@ -63,6 +66,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;
|
||||
@@ -84,16 +88,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();
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ class EntityManager;
|
||||
class ChatManager;
|
||||
class ClientCommandManager;
|
||||
class ServerManager;
|
||||
class CGameConfig;
|
||||
|
||||
namespace globals {
|
||||
|
||||
@@ -100,6 +101,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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,14 @@ 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);
|
||||
|
||||
@@ -38,13 +38,130 @@
|
||||
|
||||
#include "scripting/callback_manager.h"
|
||||
#include "core/log.h"
|
||||
#include "core/cs2_sdk/interfaces/cschemasystem.h"
|
||||
#include "core/utils.h"
|
||||
#include "core/memory.h"
|
||||
#include "interfaces/cs2_interfaces.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "metamod_oslink.h"
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace counterstrikesharp {
|
||||
|
||||
SH_DECL_HOOK2_void(
|
||||
ConCommandHandle, Dispatch, SH_NOATTRIB, false, const CCommandContext&, const CCommand&);
|
||||
json WriteTypeJson(json obj, CSchemaType* current_type)
|
||||
{
|
||||
obj["name"] = current_type->m_name_;
|
||||
obj["category"] = current_type->type_category;
|
||||
|
||||
void ConCommandInfo::HookChange(CallbackT cb, bool post) {
|
||||
if (current_type->type_category == Schema_Atomic) {
|
||||
obj["atomic"] = current_type->atomic_category;
|
||||
|
||||
if (current_type->atomic_category == Atomic_T &&
|
||||
current_type->m_atomic_t_.generic_type != nullptr) {
|
||||
obj["outer"] = current_type->m_atomic_t_.generic_type->m_name_;
|
||||
}
|
||||
|
||||
if (current_type->atomic_category == Atomic_T ||
|
||||
current_type->atomic_category == Atomic_CollectionOfT) {
|
||||
obj["inner"] =
|
||||
WriteTypeJson(json::object(), current_type->m_atomic_t_.template_typename);
|
||||
}
|
||||
} else if (current_type->type_category == Schema_FixedArray) {
|
||||
obj["inner"] = WriteTypeJson(json::object(), current_type->m_array_.element_type_);
|
||||
} else if (current_type->type_category == Schema_Ptr) {
|
||||
obj["inner"] = WriteTypeJson(json::object(), current_type->m_schema_type_);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
CON_COMMAND(dump_schema, "dump schema symbols")
|
||||
{
|
||||
std::vector<std::string> classNames;
|
||||
std::vector<std::string> enumNames;
|
||||
// Reading these from a static file since I cannot seem to get the
|
||||
// CSchemaSystemTypeScope->GetClasses() to return anything on linux.
|
||||
std::ifstream inputClasses(utils::GamedataDirectory() + "/schema_classes.txt");
|
||||
std::ifstream inputEnums(utils::GamedataDirectory() + "/schema_enums.txt");
|
||||
std::ofstream output(utils::GamedataDirectory() + "/schema.json");
|
||||
std::string line;
|
||||
|
||||
while (std::getline(inputClasses, line)) {
|
||||
if (!line.empty() && line.back() == '\r') {
|
||||
line.pop_back();
|
||||
}
|
||||
classNames.push_back(line);
|
||||
}
|
||||
|
||||
while (std::getline(inputEnums, line)) {
|
||||
if (!line.empty() && line.back() == '\r') {
|
||||
line.pop_back();
|
||||
}
|
||||
enumNames.push_back(line);
|
||||
}
|
||||
|
||||
CSchemaSystemTypeScope* pType =
|
||||
interfaces::pSchemaSystem->FindTypeScopeForModule(MODULE_PREFIX "server" MODULE_EXT);
|
||||
|
||||
json j;
|
||||
j["classes"] = json::object();
|
||||
j["enums"] = json::object();
|
||||
|
||||
for (const auto& line : classNames) {
|
||||
SchemaClassInfoData_t* pClassInfo = pType->FindDeclaredClass(line.c_str());
|
||||
if (!pClassInfo)
|
||||
continue;
|
||||
|
||||
short fieldsSize = pClassInfo->m_align;
|
||||
SchemaClassFieldData_t* pFields = pClassInfo->m_fields;
|
||||
|
||||
j["classes"][pClassInfo->m_name] = json::object();
|
||||
if (pClassInfo->m_schema_parent) {
|
||||
j["classes"][pClassInfo->m_name]["parent"] =
|
||||
pClassInfo->m_schema_parent->m_class->m_name;
|
||||
}
|
||||
|
||||
j["classes"][pClassInfo->m_name]["fields"] = json::array();
|
||||
|
||||
for (int i = 0; i < fieldsSize; ++i) {
|
||||
SchemaClassFieldData_t& field = pFields[i];
|
||||
|
||||
j["classes"][pClassInfo->m_name]["fields"].push_back({
|
||||
{"name", field.m_name},
|
||||
{"type", WriteTypeJson(json::object(), field.m_type)},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& line : enumNames) {
|
||||
auto* pEnumInfo = pType->FindDeclaredEnum(line.c_str());
|
||||
if (!pEnumInfo)
|
||||
continue;
|
||||
|
||||
j["enums"][pEnumInfo->m_binding_name_] = json::object();
|
||||
j["enums"][pEnumInfo->m_binding_name_]["align"] = pEnumInfo->m_align_;
|
||||
j["enums"][pEnumInfo->m_binding_name_]["items"] = json::array();
|
||||
|
||||
for (int i = 0; i < pEnumInfo->m_size_; ++i) {
|
||||
auto& field = pEnumInfo->m_enum_info_[i];
|
||||
|
||||
j["enums"][pEnumInfo->m_binding_name_]["items"].push_back({
|
||||
{"name", field.m_name},
|
||||
{"value", field.m_value},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Msg("Schema dumped to %s\n", (utils::GamedataDirectory() + "/schema.json").c_str());
|
||||
output << std::setw(2) << j << std::endl;
|
||||
}
|
||||
|
||||
SH_DECL_HOOK2_void(ConCommandHandle, Dispatch, SH_NOATTRIB, false, const CCommandContext&,
|
||||
const CCommand&);
|
||||
|
||||
void ConCommandInfo::HookChange(CallbackT cb, bool post)
|
||||
{
|
||||
if (post) {
|
||||
this->callback_post->AddListener(cb);
|
||||
} else {
|
||||
@@ -52,7 +169,8 @@ void ConCommandInfo::HookChange(CallbackT cb, bool post) {
|
||||
}
|
||||
}
|
||||
|
||||
void ConCommandInfo::UnhookChange(CallbackT cb, bool post) {
|
||||
void ConCommandInfo::UnhookChange(CallbackT cb, bool post)
|
||||
{
|
||||
if (post) {
|
||||
if (this->callback_post && this->callback_post->GetFunctionCount()) {
|
||||
callback_post->RemoveListener(cb);
|
||||
@@ -64,8 +182,7 @@ void ConCommandInfo::UnhookChange(CallbackT cb, bool post) {
|
||||
}
|
||||
}
|
||||
|
||||
ConCommandManager::ConCommandManager()
|
||||
: last_command_client(-1) {}
|
||||
ConCommandManager::ConCommandManager() : last_command_client(-1) {}
|
||||
|
||||
ConCommandManager::~ConCommandManager() {}
|
||||
|
||||
@@ -73,16 +190,17 @@ void ConCommandManager::OnAllInitialized() {}
|
||||
|
||||
void ConCommandManager::OnShutdown() {}
|
||||
|
||||
void CommandCallback(const CCommandContext& context, const CCommand& command) {
|
||||
bool rval = globals::conCommandManager.InternalDispatch(
|
||||
context.GetPlayerSlot(), &command);
|
||||
void CommandCallback(const CCommandContext& context, const CCommand& command)
|
||||
{
|
||||
bool rval = globals::conCommandManager.InternalDispatch(context.GetPlayerSlot(), &command);
|
||||
|
||||
if (rval) {
|
||||
RETURN_META(MRES_SUPERCEDE);
|
||||
}
|
||||
}
|
||||
|
||||
void CommandCallback_Post(const CCommandContext& context, const CCommand& command) {
|
||||
void CommandCallback_Post(const CCommandContext& context, const CCommand& command)
|
||||
{
|
||||
bool rval = globals::conCommandManager.InternalDispatch_Post(context.GetPlayerSlot(), &command);
|
||||
|
||||
if (rval) {
|
||||
@@ -90,14 +208,14 @@ void CommandCallback_Post(const CCommandContext& context, const CCommand& comman
|
||||
}
|
||||
}
|
||||
|
||||
ConCommandInfo* ConCommandManager::AddOrFindCommand(const char* name,
|
||||
const char* description,
|
||||
bool server_only,
|
||||
int flags) {
|
||||
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);
|
||||
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;
|
||||
@@ -120,11 +238,13 @@ ConCommandInfo* ConCommandManager::AddOrFindCommand(const char* 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);
|
||||
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;
|
||||
@@ -132,24 +252,22 @@ ConCommandInfo* ConCommandManager::AddOrFindCommand(const char* 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);
|
||||
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);
|
||||
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;
|
||||
//
|
||||
// SH_ADD_HOOK(ConCommandHandle, Dispatch, pointerConCommand->handle,
|
||||
// SH_STATIC(CommandCallback), false); SH_ADD_HOOK(ConCommandHandle,
|
||||
// Dispatch, pointerConCommand->handle, SH_STATIC(CommandCallback_Post),
|
||||
// true);
|
||||
p_info->callback_pre = globals::callbackManager.CreateCallback(name);
|
||||
p_info->callback_post = globals::callbackManager.CreateCallback(name);
|
||||
p_info->server_only = server_only;
|
||||
}
|
||||
|
||||
return p_info;
|
||||
@@ -158,8 +276,9 @@ ConCommandInfo* ConCommandManager::AddOrFindCommand(const char* name,
|
||||
return p_info;
|
||||
}
|
||||
|
||||
ConCommandInfo* ConCommandManager::AddCommand(
|
||||
const char* name, const char* description, bool server_only, int flags, CallbackT callback) {
|
||||
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;
|
||||
@@ -170,10 +289,12 @@ ConCommandInfo* ConCommandManager::AddCommand(
|
||||
return p_info;
|
||||
}
|
||||
|
||||
bool ConCommandManager::RemoveCommand(const char* name, CallbackT callback) {
|
||||
bool ConCommandManager::RemoveCommand(const char* name, CallbackT callback)
|
||||
{
|
||||
auto strName = std::string(strdup(name));
|
||||
ConCommandInfo* p_info = m_cmd_lookup[strName];
|
||||
if (!p_info) return false;
|
||||
if (!p_info)
|
||||
return false;
|
||||
|
||||
if (p_info->callback_pre && p_info->callback_pre->GetFunctionCount()) {
|
||||
p_info->callback_pre->RemoveListener(callback);
|
||||
@@ -184,24 +305,16 @@ bool ConCommandManager::RemoveCommand(const char* name, CallbackT 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);
|
||||
|
||||
bool success;
|
||||
auto it = std::remove_if(m_cmd_list.begin(), m_cmd_list.end(),
|
||||
[p_info](ConCommandInfo* i) { return p_info == i; });
|
||||
|
||||
if ((success = it != m_cmd_list.end())) m_cmd_list.erase(it, m_cmd_list.end());
|
||||
// if (success) {
|
||||
// m_cmd_lookup[strName] = nullptr;
|
||||
// }
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ConCommandInfo* ConCommandManager::FindCommand(const char* name) {
|
||||
ConCommandInfo* ConCommandManager::FindCommand(const char* name)
|
||||
{
|
||||
ConCommandInfo* p_info = m_cmd_lookup[std::string(name)];
|
||||
|
||||
if (p_info == nullptr) {
|
||||
@@ -213,7 +326,8 @@ ConCommandInfo* ConCommandManager::FindCommand(const char* name) {
|
||||
}
|
||||
|
||||
ConCommandHandle p_cmd = globals::cvars->FindCommand(name);
|
||||
if (!p_cmd.IsValid()) return nullptr;
|
||||
if (!p_cmd.IsValid())
|
||||
return nullptr;
|
||||
|
||||
p_info = new ConCommandInfo();
|
||||
p_info->command = globals::cvars->GetCommand(p_cmd);
|
||||
@@ -236,12 +350,14 @@ 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) {
|
||||
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;
|
||||
if (slot.Get() == 0 && !globals::engine->IsDedicatedServer())
|
||||
return false;
|
||||
|
||||
for (ConCommandInfo* cmdInfo : m_cmd_list) {
|
||||
if ((cmdInfo != nullptr) && strcasecmp(cmdInfo->command->GetName(), cmd) == 0) {
|
||||
@@ -270,12 +386,14 @@ bool ConCommandManager::InternalDispatch(CPlayerSlot slot, const CCommand* args)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ConCommandManager::InternalDispatch_Post(CPlayerSlot slot, const CCommand* args) {
|
||||
bool ConCommandManager::InternalDispatch_Post(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;
|
||||
if (slot.Get() == 0 && !globals::engine->IsDedicatedServer())
|
||||
return false;
|
||||
|
||||
for (ConCommandInfo* cmdInfo : m_cmd_list) {
|
||||
if ((cmdInfo != nullptr) && strcasecmp(cmdInfo->command->GetName(), cmd) == 0) {
|
||||
@@ -300,9 +418,9 @@ bool ConCommandManager::InternalDispatch_Post(CPlayerSlot slot, const CCommand*
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ConCommandManager::DispatchClientCommand(CPlayerSlot slot,
|
||||
const char* cmd,
|
||||
const CCommand* args) {
|
||||
bool ConCommandManager::DispatchClientCommand(CPlayerSlot slot, const char* cmd,
|
||||
const CCommand* args)
|
||||
{
|
||||
ConCommandInfo* p_info = m_cmd_lookup[cmd];
|
||||
if (p_info == nullptr) {
|
||||
auto found =
|
||||
@@ -316,7 +434,8 @@ bool ConCommandManager::DispatchClientCommand(CPlayerSlot slot,
|
||||
p_info = *found;
|
||||
}
|
||||
|
||||
if (p_info->server_only) return false;
|
||||
if (p_info->server_only)
|
||||
return false;
|
||||
|
||||
bool result = false;
|
||||
if (p_info->callback_pre) {
|
||||
@@ -341,4 +460,4 @@ bool ConCommandManager::DispatchClientCommand(CPlayerSlot slot,
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace counterstrikesharp
|
||||
} // namespace counterstrikesharp
|
||||
@@ -44,6 +44,8 @@ class CUtlString;
|
||||
#include "core/globals.h"
|
||||
#include "scripting/script_engine.h"
|
||||
|
||||
|
||||
|
||||
namespace counterstrikesharp {
|
||||
class ScriptCallback;
|
||||
class PluginFunction;
|
||||
|
||||
@@ -381,6 +381,7 @@ bool CPlayer::IsAuthStringValidated() const
|
||||
if (!IsFakeClient()) {
|
||||
return globals::engine->IsClientFullyAuthenticated(m_slot);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CPlayer::Authorize() { m_is_authorized = true; }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
83
src/core/memory_module.cpp
Normal file
83
src/core/memory_module.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -21,7 +21,7 @@ inline std::string GameDirectory() {
|
||||
}
|
||||
|
||||
inline std::string PluginDirectory() { return GameDirectory() + "/addons/counterstrikesharp"; }
|
||||
inline std::string GamedataDirectory() { return GameDirectory() + "/addons/counterstrikesharp/gamedata"; }
|
||||
|
||||
inline std::string ConfigDirectory() { return PluginDirectory() + "/config"; }
|
||||
} // namespace utils
|
||||
} // namespace counterstrikesharp
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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>
|
||||
|
||||
4
src/scripting/listeners/server.yaml
Normal file
4
src/scripting/listeners/server.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
OnServerHibernationUpdate: isHibernating:bool
|
||||
OnGameServerSteamAPIActivated:
|
||||
OnGameServerSteamAPIDeactivated:
|
||||
OnHostNameChanged: hostname:string
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
#include <ios>
|
||||
#include <sstream>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "scripting/autonative.h"
|
||||
#include "core/function.h"
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
#include <ios>
|
||||
#include <sstream>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "scripting/autonative.h"
|
||||
#include "scripting/script_engine.h"
|
||||
|
||||
47
src/utils/string.h
Normal file
47
src/utils/string.h
Normal 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user