Compare commits

...

19 Commits
v22 ... v45

Author SHA1 Message Date
Roflmuffin
9c8e9db56e ci: set fallback build number for PRs 2023-11-13 20:23:46 +10:00
Roflmuffin
e2e0eab87d ci: run main pipeline skipping publish on PR 2023-11-13 20:19:37 +10:00
Nexd
43292bb1d2 feat: CBaseModelEntity_SetModel (#72) 2023-11-13 09:10:49 +10:00
Nexd
12c54cd4fc hotfix: deserializer couldn't call setter (#70) 2023-11-12 22:39:15 +10:00
Michael Wilson
e155a70873 Cross platform builds (#69) 2023-11-12 15:43:51 +10:00
Nexd
69d9b5d2c8 feat: Provide configuration standard for plugins (#67) 2023-11-12 14:25:06 +10:00
laper32
933fdf9d81 [Windows] feat: Windows support (#52) 2023-11-12 13:39:13 +10:00
Nexd
18e9e37a98 CoreConfig implementation on the managed side (#62)
Co-authored-by: Michael Wilson <roflmuffin@users.noreply.github.com>
2023-11-12 12:19:57 +10:00
Roflmuffin
fe236806e1 hotfix: con command hot reload 2023-11-11 10:30:05 +10:00
Nexd
2c4e9bca42 Small adjustments (#56) 2023-11-11 09:50:54 +10:00
Roflmuffin
8f3e0c226b docs: add information about flags and standard flags 2023-11-11 01:42:57 +10:00
Roflmuffin
5f6ccf2839 feat: change color marshalling to ABGR (tested against render color) 2023-11-11 00:05:13 +10:00
Roflmuffin
6c2f56793b hotfix: con command hot reload failing 2023-11-10 23:51:18 +10:00
Roflmuffin
cc7dd5ca96 ci: I have the utmost confidence 2023-11-10 20:16:04 +10:00
Roflmuffin
ebc361b2f8 ci: publish to api.nuget.org 2023-11-10 20:05:52 +10:00
Roflmuffin
c72eff2546 ci: fix nuget source 2023-11-10 20:00:09 +10:00
Roflmuffin
4b432e9efc ci: add package write permission 2023-11-10 19:52:13 +10:00
Roflmuffin
22bbf835c7 Merge remote-tracking branch 'origin/main' into main 2023-11-10 19:51:17 +10:00
Roflmuffin
092a6077c3 ci: try publishing nuget package 2023-11-10 19:49:44 +10:00
66 changed files with 1746 additions and 558 deletions

View File

@@ -5,86 +5,195 @@ on:
paths-ignore:
- 'docs/**'
branches: [ "main" ]
pull_request:
branches: [ "main" ]
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
- uses: actions/upload-artifact@v3
with:
name: counterstrikesharp-build-linux-${{ env.GITHUB_SHA_SHORT }}
path: build/output/
build_managed:
permissions:
contents: write
runs-on: ubuntu-latest
outputs:
buildnumber: ${{ steps.buildnumber.outputs.build_number }}
steps:
- name: Prepare env
shell: bash
run: echo "GITHUB_SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_ENV
- name: Set fallback build number
if: github.event_name == 'pull_request'
id: buildnumber
run: |
mkdir -p build/output/addons/counterstrikesharp/api
cp -r managed/CounterStrikeSharp.API/bin/Release/net7.0/publish/* build/output/addons/counterstrikesharp/api
echo "build_number=0" >> "$GITHUB_OUTPUT"
echo "BUILD_NUMBER=0" >> $GITHUB_ENV
# We don't need expensive submodules for the managed side.
- uses: actions/checkout@v3
- name: Generate build number
if: github.event_name == 'push'
id: buildnumber
uses: onyxmueller/build-tag-number@v1
with:
token: ${{secrets.github_token}}
- name: Build runtime v${{ env.BUILD_NUMBER }}
uses: actions/setup-dotnet@v3
with:
dotnet-version: '7.0.x'
- run: |
dotnet publish -c Release /p:Version=1.0.${{ env.BUILD_NUMBER }} managed/CounterStrikeSharp.API
dotnet pack -c Release /p:Version=1.0.${{ env.BUILD_NUMBER }} managed/CounterStrikeSharp.API
- uses: actions/upload-artifact@v3
with:
name: counterstrikesharp-build-${{ env.BUILD_NUMBER }}-${{ env.GITHUB_SHA_SHORT }}
path: build/output/
name: counterstrikesharp-build-api-${{ env.GITHUB_SHA_SHORT }}
path: managed/CounterStrikeSharp.API/bin/Release
- name: Zip CounterStrikeSharp Build
run: (cd build/output && zip -qq -r ../../counterstrikesharp-build-${{ env.BUILD_NUMBER }}-${{ env.GITHUB_SHA_SHORT }}.zip *)
publish:
if: github.event_name == 'push'
permissions:
contents: write
needs: [ "build_linux", "build_windows", "build_managed" ]
runs-on: ubuntu-latest
steps:
- name: Prepare env
shell: bash
run: echo "GITHUB_SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_ENV
- uses: actions/download-artifact@v3
with:
name: counterstrikesharp-build-windows-${{ env.GITHUB_SHA_SHORT }}
path: build/windows
- uses: actions/download-artifact@v3
with:
name: counterstrikesharp-build-linux-${{ env.GITHUB_SHA_SHORT }}
path: build/linux
- uses: actions/download-artifact@v3
with:
name: counterstrikesharp-build-api-${{ env.GITHUB_SHA_SHORT }}
path: build/api
# TODO: This stuff should really be in a matrix
- name: Add API to Artifacts
run: |
mkdir -p build/linux/addons/counterstrikesharp/api
mkdir -p build/windows/addons/counterstrikesharp/api
cp -r build/api/net7.0/publish/* build/linux/addons/counterstrikesharp/api
cp -r build/api/net7.0/publish/* build/windows/addons/counterstrikesharp/api
- name: Zip Builds
run: |
(cd build/linux && zip -qq -r ../../counterstrikesharp-build-${{ needs.build_managed.outputs.buildnumber }}-linux-${{ env.GITHUB_SHA_SHORT }}.zip *)
(cd build/windows && zip -qq -r ../../counterstrikesharp-build-${{ needs.build_managed.outputs.buildnumber }}-windows-${{ env.GITHUB_SHA_SHORT }}.zip *)
- name: Add dotnet runtime
run: |
mkdir -p build/output/addons/counterstrikesharp/dotnet
mkdir -p build/linux/addons/counterstrikesharp/dotnet
curl -s -L https://download.visualstudio.microsoft.com/download/pr/dc2c0a53-85a8-4fda-a283-fa28adb5fbe2/8ccade5bc400a5bb40cd9240f003b45c/aspnetcore-runtime-7.0.11-linux-x64.tar.gz \
| tar xvz -C build/output/addons/counterstrikesharp/dotnet
mv build/output/addons/counterstrikesharp/dotnet/shared/Microsoft.NETCore.App/7.0.11/* build/output/addons/counterstrikesharp/dotnet/shared/Microsoft.NETCore.App/
| tar xvz -C build/linux/addons/counterstrikesharp/dotnet
mv build/linux/addons/counterstrikesharp/dotnet/shared/Microsoft.NETCore.App/7.0.11/* build/linux/addons/counterstrikesharp/dotnet/shared/Microsoft.NETCore.App/
mkdir -p build/windows/addons/counterstrikesharp/dotnet
curl -s -L https://download.visualstudio.microsoft.com/download/pr/a99861c8-2e00-4587-aaef-60366ca77307/a44ceec2c5d34165ae881600f52edc43/aspnetcore-runtime-7.0.11-win-x64.zip -o dotnet.zip
unzip -qq dotnet.zip -d build/windows/addons/counterstrikesharp/dotnet
- name: Zip CounterStrikeSharp Runtime Build
run: (cd build/output && zip -qq -r ../../counterstrikesharp-with-runtime-build-${{ env.BUILD_NUMBER }}-${{ env.GITHUB_SHA_SHORT }}.zip *)
- uses: actions/upload-artifact@v3
with:
name: counterstrikesharp-with-runtime-build-${{ env.BUILD_NUMBER }}-${{ env.GITHUB_SHA_SHORT }}
path: build/output/
- name: Zip Builds
run: |
(cd build/linux && zip -qq -r ../../counterstrikesharp-with-runtime-build-${{ needs.build_managed.outputs.buildnumber }}-linux-${{ env.GITHUB_SHA_SHORT }}.zip *)
(cd build/windows && zip -qq -r ../../counterstrikesharp-with-runtime-build-${{ needs.build_managed.outputs.buildnumber }}-windows-${{ env.GITHUB_SHA_SHORT }}.zip *)
- name: Release
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ env.BUILD_NUMBER }}
tag_name: v${{ needs.build_managed.outputs.buildnumber }}
files: |
counterstrikesharp-build-${{ env.BUILD_NUMBER }}-${{ env.GITHUB_SHA_SHORT }}.zip
counterstrikesharp-with-runtime-build-${{ env.BUILD_NUMBER }}-${{ env.GITHUB_SHA_SHORT }}.zip
counterstrikesharp-build-${{ needs.build_managed.outputs.buildnumber }}-windows-${{ env.GITHUB_SHA_SHORT }}.zip
counterstrikesharp-with-runtime-build-${{ needs.build_managed.outputs.buildnumber }}-windows-${{ env.GITHUB_SHA_SHORT }}.zip
counterstrikesharp-build-${{ needs.build_managed.outputs.buildnumber }}-linux-${{ env.GITHUB_SHA_SHORT }}.zip
counterstrikesharp-with-runtime-build-${{ needs.build_managed.outputs.buildnumber }}-linux-${{ env.GITHUB_SHA_SHORT }}.zip
- name: Publish NuGet package
run: |
dotnet nuget push build/api/CounterStrikeSharp.API.1.0.${{ needs.build_managed.outputs.buildnumber }}.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
dotnet nuget push build/api/CounterStrikeSharp.API.1.0.${{ needs.build_managed.outputs.buildnumber }}.snupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate

View File

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

5
.gitignore vendored
View File

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

View File

@@ -1,6 +1,7 @@
cmake_minimum_required(VERSION 3.18)
Project(counterstrikesharp C CXX)
# You must set ASM, otherwise CMAKE_ASM_COMPILE_OBJECT will not work on MSVC
project(counterstrikesharp C CXX ASM)
include("makefiles/shared.cmake")
@@ -11,75 +12,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
libraries/nlohmann/json.hpp
src/mm_plugin.cpp
src/mm_plugin.h
libraries/hl2sdk-cs2/tier1/convar.cpp
libraries/hl2sdk-cs2/tier1/generichash.cpp
libraries/hl2sdk-cs2/entity2/entitysystem.cpp
libraries/dotnet/hostfxr.h
libraries/dotnet/coreclr_delegates.h
libraries/metamod-source/core/sourcehook/sourcehook.cpp
libraries/metamod-source/core/sourcehook/sourcehook_impl_chookidman.cpp
libraries/metamod-source/core/sourcehook/sourcehook_impl_chookmaninfo.cpp
libraries/metamod-source/core/sourcehook/sourcehook_impl_cvfnptr.cpp
libraries/metamod-source/core/sourcehook/sourcehook_impl_cproto.cpp
src/scripting/dotnet_host.h
src/scripting/dotnet_host.cpp
src/core/utils.h
src/core/globals.h
src/core/globals.cpp
src/core/gameconfig.h
src/core/gameconfig.cpp
src/core/log.h
src/core/log.cpp
src/scripting/script_engine.h
src/scripting/script_engine.cpp
src/core/global_listener.h
src/scripting/callback_manager.h
src/scripting/callback_manager.cpp
src/core/managers/event_manager.h
src/core/managers/event_manager.cpp
src/core/timer_system.h
src/core/timer_system.cpp
src/scripting/autonative.h
src/scripting/natives/natives_engine.cpp
src/core/engine_trace.h
src/core/engine_trace.cpp
src/scripting/natives/natives_callbacks.cpp
src/core/managers/player_manager.h
src/core/managers/player_manager.cpp
src/scripting/natives/natives_vector.cpp
src/scripting/natives/natives_timers.cpp
src/utils/virtual.h
src/scripting/natives/natives_events.cpp
src/core/memory.cpp
src/core/memory.h
src/core/managers/con_command_manager.cpp
src/core/managers/con_command_manager.h
src/scripting/natives/natives_commands.cpp
src/core/memory_module.h
src/core/memory_module.cpp
src/core/cs2_sdk/interfaces/cgameresourceserviceserver.h
src/core/cs2_sdk/interfaces/cschemasystem.h
src/core/cs2_sdk/interfaces/cs2_interfaces.h
src/core/cs2_sdk/interfaces/cs2_interfaces.cpp
src/core/cs2_sdk/schema.h
src/core/cs2_sdk/schema.cpp
src/core/function.cpp
src/core/function.h
src/scripting/natives/natives_memory.cpp
src/scripting/natives/natives_schema.cpp
src/scripting/natives/natives_entities.cpp
src/core/managers/entity_manager.cpp
src/core/managers/entity_manager.h
src/core/managers/chat_manager.cpp
src/core/managers/chat_manager.h
src/core/managers/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")
@@ -105,15 +117,25 @@ target_include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/src/core/cs2_sdk
)
include("makefiles/linux.base.cmake")
if (LINUX)
include("makefiles/linux.base.cmake")
set_target_properties(${PROJECT_NAME} PROPERTIES
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/addons/counterstrikesharp/bin/linuxsteamrt64"
)
elseif(WIN32)
include("makefiles/windows.base.cmake")
set_target_properties(${PROJECT_NAME} PROPERTIES
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/addons/counterstrikesharp/bin/win64"
)
endif()
# Libraries
target_link_libraries(${PROJECT_NAME} ${COUNTER_STRIKE_SHARP_LINK_LIBRARIES})
set_target_properties(${PROJECT_NAME} PROPERTIES
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/addons/counterstrikesharp/bin/linuxsteamrt64"
)
add_custom_command(
TARGET ${PROJECT_NAME} PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory

View File

@@ -10,13 +10,6 @@ This project is an ongoing migration of a previous project (titled [VSP.NET](htt
Due to the architectural changes of CS2, the plugin is being rebuilt on the ground up, to support Linux 64-bit, something which was previously impossible.
## Philosophy
As a result, there are a few key philosophies and trade-offs that drive the project.
- Only 64 bit is supported.
- .NET only supports x64 on Linux; CSGO previously only supported 32 bit servers, but CS2 supports 64 bit on Linux.
- Supporting both platforms is a lot of work for 1 person, so there are no real plans to support Windows.
## Install
Download the latest build from [here](https://github.com/roflmuffin/CounterStrikeSharp/releases). (Download the with runtime version if this is your first time installing).
@@ -96,7 +89,7 @@ I've also used the scripting context & native system that is implemented in Five
## How to Build
Building requires CMake on Linux.
Building requires CMake.
Clone the repository

View File

@@ -2,14 +2,26 @@
"Erikj": {
"identity": "76561197960265731",
"flags": [
"@css/reservation",
"@css/generic",
"@css/kick",
"@css/ban"
"@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": [
"@anotherscope/foobar"
]
"flags": ["@mycustomplugin/admin"]
}
}
}

View File

@@ -0,0 +1,5 @@
{
"PublicChatTrigger": "!",
"SilentChatTrigger": "/",
"FollowCS2ServerGuidelines": true
}

View File

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

View File

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

View File

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

View File

@@ -15,7 +15,7 @@ Adding an Admin is as simple as creating a new entry in the `configs/admins.json
{
"ZoNiCaL": {
"identity": "76561198808392634",
"flags": ["can_manipulate_players", "admin_messages"]
"flags": ["@css/changemap", "@css/generic"]
}
}
```
@@ -27,7 +27,7 @@ You can also manually assign permissions to players in code with `AddPlayerPermi
Assigning permissions to a Command is as easy as tagging the Command method (function callback) with a `RequiresPermissions` attribute.
```csharp
[RequiresPermissions("can_execute_test_command", "other_permission")]
[RequiresPermissions("@css/slay", "@custom/permission")]
public void OnMyCommand(CCSPlayerController? caller, CommandInfo info)
{
...
@@ -35,3 +35,28 @@ 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.
```

View 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.
:::

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -30,6 +30,8 @@ using CounterStrikeSharp.API.Modules.Events;
using CounterStrikeSharp.API.Modules.Entities;
using CounterStrikeSharp.API.Modules.Listeners;
using CounterStrikeSharp.API.Modules.Timers;
using McMaster.NETCore.Plugins;
using CounterStrikeSharp.API.Modules.Config;
namespace CounterStrikeSharp.API.Core
{
@@ -333,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>

View File

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

View File

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

View File

@@ -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; set; } = "!";
[JsonPropertyName("SilentChatTrigger")] public string SilentChatTrigger { get; set; } = "/";
[JsonPropertyName("FollowCS2ServerGuidelines")] public bool FollowCS2ServerGuidelines { get; 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.");
}
}
}
}

View File

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

View File

@@ -59,13 +59,16 @@ namespace CounterStrikeSharp.API.Core
}
public void InitGlobalContext()
{
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"));
Console.WriteLine("Loading Admins from \"configs/admins.json\"");
AdminManager.Load(Path.Combine(rootDir.FullName, "configs", "admins.json"));
for (int i = 1; i <= 9; i++)
for (var i = 1; i <= 9; i++)
{
CommandUtils.AddStandaloneCommand("css_" + i, "Command Key Handler", (player, info) =>
{
@@ -76,18 +79,18 @@ namespace CounterStrikeSharp.API.Core
}
Console.WriteLine("Loading C# plugins...");
int pluginCount = LoadAllPlugins();
var pluginCount = LoadAllPlugins();
Console.WriteLine($"All managed modules were loaded. {pluginCount} plugins loaded.");
RegisterPluginCommands();
}
public void LoadPlugin(string path)
private void LoadPlugin(string path)
{
var existingPlugin = FindPluginByModulePath(path);
if (existingPlugin != null)
{
throw new Exception("Plugin is already loaded.");
throw new FileLoadException("Plugin is already loaded.");
}
var plugin = new PluginContext(path, _loadedPlugins.Select(x => x.PluginId).DefaultIfEmpty(0).Max() + 1);
@@ -95,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);
@@ -317,7 +322,7 @@ namespace CounterStrikeSharp.API.Core
}
public void RegisterPluginCommands()
private void RegisterPluginCommands()
{
CommandUtils.AddStandaloneCommand("css", "Counter-Strike Sharp options.", OnCSSCommand);
CommandUtils.AddStandaloneCommand("css_plugins", "Counter-Strike Sharp plugin options.", OnCSSPluginCommand);

View File

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

View File

@@ -0,0 +1,28 @@
/*
* This file is part of CounterStrikeSharp.
* CounterStrikeSharp is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CounterStrikeSharp is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
*/
namespace CounterStrikeSharp.API.Core
{
/// <summary>
/// An interface that describes a plugin configuration.
/// </summary>
public interface IPluginConfig<T> where T: IBasePluginConfig, new()
{
T Config { get; set; }
public void OnConfigParsed(T config);
}
}

View File

@@ -18,9 +18,10 @@ using CounterStrikeSharp.API.Modules.Memory;
namespace CounterStrikeSharp.API.Core;
public partial class CGlowProperty
public partial class CBaseModelEntity
{
// m_bGlowing
public ref bool IsGlowing => ref Schema.GetRef<bool>(this.Handle, "CGlowProperty", "m_bGlowing");
}
public void SetModel(string model)
{
VirtualFunctions.SetModel(Handle, model);
}
}

View File

@@ -20,6 +20,7 @@ using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using CounterStrikeSharp.API.Core.Attributes;
using CounterStrikeSharp.API.Modules.Config;
using CounterStrikeSharp.API.Modules.Events;
using McMaster.NETCore.Plugins;
@@ -103,6 +104,7 @@ namespace CounterStrikeSharp.API.Core
_plugin = (BasePlugin)Activator.CreateInstance(pluginType)!;
_plugin.ModulePath = _path;
_plugin.RegisterAllAttributes(_plugin);
_plugin.InitializeConfig(_plugin, pluginType);
_plugin.Load(hotReload);
Console.WriteLine($"Finished loading plugin: {Name}");

View File

@@ -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" />

View File

@@ -20,11 +20,16 @@ public class ColorMarshaler : ICustomMarshal<Color>
{
public Color NativeToManaged(IntPtr pointer)
{
return Color.FromArgb(Marshal.ReadInt32(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.ToArgb());
Marshal.WriteInt32(pointer, (managedObj.A << 24) | (managedObj.B << 16) | (managedObj.G << 8) | managedObj.R);
}
}

View File

@@ -0,0 +1,79 @@
/*
* This file is part of CounterStrikeSharp.
* CounterStrikeSharp is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CounterStrikeSharp is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
*/
using System;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.Json;
using CounterStrikeSharp.API.Core;
namespace CounterStrikeSharp.API.Modules.Config
{
public static class ConfigManager
{
private static readonly DirectoryInfo? _rootDir;
private static readonly string _pluginConfigsFolderPath;
static ConfigManager()
{
_rootDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.Parent;
_pluginConfigsFolderPath = Path.Combine(_rootDir.FullName, "configs", "plugins");
}
public static T Load<T>(string pluginName) where T : IBasePluginConfig, new()
{
string directoryPath = Path.Combine(_pluginConfigsFolderPath, pluginName);
string configPath = Path.Combine(directoryPath, $"{pluginName}.json");
T config = (T)Activator.CreateInstance(typeof(T))!;
if (!File.Exists(configPath))
{
try
{
if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
StringBuilder builder = new StringBuilder();
builder.Append($"// This configuration was automatically generated by CounterStrikeSharp for plugin '{pluginName}', at {DateTimeOffset.Now:yyyy/MM/dd hh:mm:ss}\n");
builder.Append(JsonSerializer.Serialize<T>(config, new JsonSerializerOptions { WriteIndented = true }));
File.WriteAllText(configPath, builder.ToString());
return config;
}
catch (Exception ex)
{
Console.WriteLine($"Failed to generate configuration file for {pluginName}: {ex}");
}
}
try
{
config = JsonSerializer.Deserialize<T>(File.ReadAllText(configPath), new JsonSerializerOptions() { ReadCommentHandling = JsonCommentHandling.Skip })!;
}
catch (Exception ex)
{
Console.WriteLine($"Failed to parse configuration '{pluginName}': {ex}");
}
return config;
}
}
}

View File

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

View File

@@ -10,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))
{
@@ -29,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);
}

View File

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

View File

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

View File

@@ -18,6 +18,8 @@ using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes;
@@ -32,8 +34,17 @@ using CounterStrikeSharp.API.Modules.Utils;
namespace TestPlugin
{
[MinimumApiVersion(1)]
public class SamplePlugin : BasePlugin
public class SampleConfig : BasePluginConfig
{
[JsonPropertyName("IsPluginEnabled")]
public bool IsPluginEnabled { get; set; } = true;
[JsonPropertyName("LogPrefix")]
public string LogPrefix { get; set; } = "CSSharp";
}
[MinimumApiVersion(33)]
public class SamplePlugin : BasePlugin, IPluginConfig<SampleConfig>
{
public override string ModuleName => "Sample Plugin";
public override string ModuleVersion => "v1.0.0";
@@ -42,8 +53,24 @@ namespace TestPlugin
public override string ModuleDescription => "A playground of features used for testing";
public SampleConfig Config { get; set; }
// This method is called right before `Load` is called
public void OnConfigParsed(SampleConfig config)
{
// Save config instance
Config = config;
}
public override void Load(bool hotReload)
{
// Basic usage of the configuration system
if (!Config.IsPluginEnabled)
{
Console.WriteLine($"{Config.LogPrefix} {ModuleName} is disabled");
return;
}
Console.WriteLine(
$"Test Plugin has been loaded, and the hot reload flag was {hotReload}, path is {ModulePath}");

View File

@@ -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>

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

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

View File

@@ -1,3 +1,4 @@
#include "mm_plugin.h"
#include "core/globals.h"
#include "core/managers/player_manager.h"
#include "iserver.h"
@@ -12,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();
}

View File

@@ -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

View File

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

View File

@@ -24,9 +24,12 @@
#include <public/eiface.h>
#include "core/memory.h"
#include "core/log.h"
#include "core/gameconfig.h"
#include <funchook.h>
#include "core/memory_module.h"
namespace counterstrikesharp {
ChatManager::ChatManager() {}
@@ -35,11 +38,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);

View File

@@ -43,6 +43,8 @@
#include "core/memory.h"
#include "interfaces/cs2_interfaces.h"
#include <nlohmann/json.hpp>
#include "metamod_oslink.h"
using json = nlohmann::json;
namespace counterstrikesharp {
@@ -155,10 +157,11 @@ CON_COMMAND(dump_schema, "dump schema symbols")
output << std::setw(2) << j << std::endl;
}
SH_DECL_HOOK2_void(
ConCommandHandle, Dispatch, SH_NOATTRIB, false, const CCommandContext&, const CCommand&);
SH_DECL_HOOK2_void(ConCommandHandle, Dispatch, SH_NOATTRIB, false, const CCommandContext&,
const CCommand&);
void ConCommandInfo::HookChange(CallbackT cb, bool post) {
void ConCommandInfo::HookChange(CallbackT cb, bool post)
{
if (post) {
this->callback_post->AddListener(cb);
} else {
@@ -166,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);
@@ -178,8 +182,7 @@ void ConCommandInfo::UnhookChange(CallbackT cb, bool post) {
}
}
ConCommandManager::ConCommandManager()
: last_command_client(-1) {}
ConCommandManager::ConCommandManager() : last_command_client(-1) {}
ConCommandManager::~ConCommandManager() {}
@@ -187,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) {
@@ -204,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;
@@ -234,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;
@@ -246,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;
@@ -272,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;
@@ -284,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);
@@ -298,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) {
@@ -327,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);
@@ -350,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) {
@@ -384,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) {
@@ -414,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 =
@@ -430,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) {
@@ -455,4 +460,4 @@ bool ConCommandManager::DispatchClientCommand(CPlayerSlot slot,
return result;
}
} // namespace counterstrikesharp
} // namespace counterstrikesharp

View File

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

View File

@@ -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; }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,19 @@
/*
* This file is part of CounterStrikeSharp.
* CounterStrikeSharp is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CounterStrikeSharp is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
*/
#include "scripting/dotnet_host.h"
#include <dotnet/coreclr_delegates.h>
@@ -7,15 +23,19 @@
#include <locale>
#ifdef WIN32
#include <Windows.h>
#include <direct.h>
#include <Windows.h>
#include <direct.h>
#define STR(s) L##s
#define CH(c) L##c
#define DIR_SEPARATOR L'\\'
#define STR(s) L##s
#define CH(c) L##c
#define DIR_SEPARATOR L'\\'
#else
#include <dlfcn.h>
#define STR(s) s
#define CH(c) c
#define DIR_SEPARATOR '/'
#include <dlfcn.h>
#endif
#include <cassert>
@@ -24,6 +44,8 @@
#include "core/log.h"
#include "core/utils.h"
#include "utils/string.h"
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
namespace {
@@ -33,34 +55,38 @@ hostfxr_close_fn close_fptr;
hostfxr_handle cxt;
bool load_hostfxr();
load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t *assembly);
} // namespace
load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t* assembly);
} // namespace
namespace {
// Forward declarations
void *load_library(const char_t *);
void *get_export(void *, const char *);
void* load_library(const char_t*);
void* get_export(void*, const char*);
#ifdef _WINDOWS
void *load_library(const char_t *path) {
void* load_library(const char_t* path)
{
HMODULE h = ::LoadLibraryW(path);
assert(h != nullptr);
return (void *)h;
return (void*)h;
}
void *get_export(void *h, const char *name) {
void *f = ::GetProcAddress((HMODULE)h, name);
void* get_export(void* h, const char* name)
{
void* f = ::GetProcAddress((HMODULE)h, name);
assert(f != nullptr);
return f;
}
#else
void *load_library(const char_t *path) {
void *h = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
void* load_library(const char_t* path)
{
void* h = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
assert(h != nullptr);
return h;
}
void *get_export(void *h, const char *name) {
void *f = dlsym(h, name);
void* get_export(void* h, const char* name)
{
void* f = dlsym(h, name);
assert(f != nullptr);
return f;
}
@@ -68,20 +94,39 @@ void *get_export(void *h, const char *name) {
// <SnippetLoadHostFxr>
// Using the nethost library, discover the location of hostfxr and get exports
bool load_hostfxr() {
std::string baseDir = counterstrikesharp::utils::PluginDirectory();
std::string buffer = std::string(baseDir + "/dotnet/host/fxr/7.0.11/libhostfxr.so");
CSSHARP_CORE_TRACE("Loading hostfxr from {0}", buffer.c_str());
bool load_hostfxr()
{
std::string base_dir = counterstrikesharp::utils::PluginDirectory();
namespace css = counterstrikesharp;
#if _WIN32
std::wstring buffer =
std::wstring(css::widen(base_dir) + L"\\dotnet\\host\\fxr\\7.0.11\\hostfxr.dll");
CSSHARP_CORE_INFO("Loading hostfxr from {0}", css::narrow(buffer).c_str());
#else
std::string buffer = std::string(base_dir + "/dotnet/host/fxr/7.0.11/libhostfxr.so");
CSSHARP_CORE_INFO("Loading hostfxr from {0}", buffer.c_str());
#endif
// Load hostfxr and get desired exports
void *lib = load_library(buffer.c_str());
void* lib = load_library(buffer.c_str());
init_fptr = (hostfxr_initialize_for_runtime_config_fn)get_export(
lib, "hostfxr_initialize_for_runtime_config");
if (init_fptr == nullptr) {
CSSHARP_CORE_CRITICAL(
"unable to get export function: \"hostfxr_initialize_for_runtime_config\"");
return false;
}
get_delegate_fptr =
(hostfxr_get_runtime_delegate_fn)get_export(lib, "hostfxr_get_runtime_delegate");
if (!get_delegate_fptr) {
CSSHARP_CORE_CRITICAL("unable to get export function: \"hostfxr_get_runtime_delegate\"");
return false;
}
close_fptr = (hostfxr_close_fn)get_export(lib, "hostfxr_close");
if (!close_fptr) {
CSSHARP_CORE_CRITICAL("unable to get export function: \"hostfxr_close\"");
return false;
}
return (init_fptr && get_delegate_fptr && close_fptr);
}
@@ -89,12 +134,13 @@ bool load_hostfxr() {
// <SnippetInitialize>
// Load and initialize .NET Core and get desired function pointer for scenario
load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t *config_path) {
load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t* config_path)
{
// Load .NET Core
void *load_assembly_and_get_function_pointer = nullptr;
void* load_assembly_and_get_function_pointer = nullptr;
int rc = init_fptr(config_path, nullptr, &cxt);
if (rc != 0 || cxt == nullptr) {
std::cerr << "Init failed: " << std::hex << std::showbase << rc << std::endl;
CSSHARP_CORE_CRITICAL("Init failed: {0:x}", rc);
close_fptr(cxt);
return nullptr;
}
@@ -102,53 +148,75 @@ load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t
// Get the load assembly function pointer
rc = get_delegate_fptr(cxt, hdt_load_assembly_and_get_function_pointer,
&load_assembly_and_get_function_pointer);
if (rc != 0 || load_assembly_and_get_function_pointer == nullptr)
std::cerr << "Get delegate failed: " << std::hex << std::showbase << rc << std::endl;
if (rc != 0 || load_assembly_and_get_function_pointer == nullptr) {
CSSHARP_CORE_ERROR("Get delegate failed: {0:x}", rc);
}
// close_fptr(cxt);
return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer;
}
} // namespace
} // namespace
CDotNetManager::CDotNetManager() {}
CDotNetManager::~CDotNetManager() {}
bool CDotNetManager::Initialize() {
std::string baseDir = counterstrikesharp::utils::PluginDirectory();
bool CDotNetManager::Initialize()
{
const std::string base_dir = counterstrikesharp::utils::PluginDirectory();
CSSHARP_CORE_INFO("Loading .NET runtime...");
if (!load_hostfxr()) {
CSSHARP_CORE_ERROR("Failed to initialize .NET");
CSSHARP_CORE_ERROR("Failed to initialize .NET runtime.");
return false;
}
CSSHARP_CORE_INFO(".NET Runtime Initialised.");
namespace css = counterstrikesharp;
#if _WIN32
const auto wide_str =
std::wstring(css::widen(base_dir) + L"\\api\\CounterStrikeSharp.API.runtimeconfig.json");
CSSHARP_CORE_INFO("Loading CSS API, Runtime config: {}",
counterstrikesharp::narrow(wide_str).c_str());
#else
std::string wide_str =
std::string((base_dir + "/api/CounterStrikeSharp.API.runtimeconfig.json").c_str());
CSSHARP_CORE_INFO("Loading CSS API, Runtime Config: {}", wide_str);
#endif
const auto load_assembly_and_get_function_pointer = get_dotnet_load_assembly(wide_str.c_str());
if (load_assembly_and_get_function_pointer == nullptr) {
CSSHARP_CORE_ERROR("Failed to load CSS API.");
return false;
}
CSSHARP_CORE_INFO(".NET Runtime Initialised.");
std::string wideStr =
std::string((baseDir + "/api/CounterStrikeSharp.API.runtimeconfig.json").c_str());
load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer = nullptr;
load_assembly_and_get_function_pointer = get_dotnet_load_assembly(wideStr.c_str());
assert(load_assembly_and_get_function_pointer != nullptr &&
"Failure: get_dotnet_load_assembly()");
#if _WIN32
const auto dotnetlib_path =
std::wstring(css::widen(base_dir) + L"\\api\\CounterStrikeSharp.API.dll");
CSSHARP_CORE_INFO("CSS API DLL: {}", counterstrikesharp::narrow(dotnetlib_path));
#else
const std::string dotnetlib_path =
std::string((baseDir + "/api/CounterStrikeSharp.API.dll").c_str());
const char_t *dotnet_type = "CounterStrikeSharp.API.Core.Helpers, CounterStrikeSharp.API";
std::string((base_dir + "/api/CounterStrikeSharp.API.dll").c_str());
#endif
const auto dotnet_type = STR("CounterStrikeSharp.API.Core.Helpers, CounterStrikeSharp.API");
// Namespace, assembly name
typedef int(CORECLR_DELEGATE_CALLTYPE * custom_entry_point_fn)();
custom_entry_point_fn entry_point = nullptr;
int rc = load_assembly_and_get_function_pointer(dotnetlib_path.c_str(), dotnet_type,
"LoadAllPlugins", UNMANAGEDCALLERSONLY_METHOD,
nullptr, (void **)&entry_point);
const int rc = load_assembly_and_get_function_pointer(
dotnetlib_path.c_str(), dotnet_type, STR("LoadAllPlugins"), UNMANAGEDCALLERSONLY_METHOD,
nullptr, reinterpret_cast<void**>(&entry_point));
if (entry_point == nullptr) {
CSSHARP_CORE_ERROR("Trying to get entry point \"LoadAllPlugins\" but failed.");
return false;
}
assert(rc == 0 && entry_point != nullptr &&
"Failure: load_assembly_and_get_function_pointer()");
const bool success = entry_point();
if (!success) {
CSSHARP_CORE_ERROR("Failed to initialize .NET");
if (const int invoke_result_code = entry_point(); invoke_result_code == 0) {
CSSHARP_CORE_ERROR("LoadAllPlugins return failure.");
return false;
}
@@ -156,10 +224,13 @@ bool CDotNetManager::Initialize() {
return true;
}
void CDotNetManager::UnloadPlugin(PluginContext *context) {}
void CDotNetManager::UnloadPlugin(PluginContext* context) {}
void CDotNetManager::Shutdown() {
void CDotNetManager::Shutdown()
{
// CoreCLR does not currently supporting unloading... :(
// I think this is intentionally, you should handle Init/Shutdown manually.
// Better rework in the future, but not now.
}
PluginContext *CDotNetManager::FindContext(std::string path) { return nullptr; }
PluginContext* CDotNetManager::FindContext(std::string path) { return nullptr; }

View File

@@ -1,3 +1,19 @@
/*
* This file is part of CounterStrikeSharp.
* CounterStrikeSharp is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CounterStrikeSharp is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
*/
#pragma once
#include <memory>

View File

@@ -0,0 +1,4 @@
OnServerHibernationUpdate: isHibernating:bool
OnGameServerSteamAPIActivated:
OnGameServerSteamAPIDeactivated:
OnHostNameChanged: hostname:string

View File

@@ -37,11 +37,6 @@ static ConCommandInfo* AddCommand(ScriptContext& script_context)
CSSHARP_CORE_TRACE("Adding command {}, {}, {}, {}, {}", name, description, server_only, flags,
(void*)callback);
if (globals::conCommandManager.FindCommand(name)) {
script_context.ThrowNativeError("Failed to add command \"%s\", command already exists.", name);
return nullptr;
}
return globals::conCommandManager.AddCommand(name, description, server_only, flags, callback);
}

View File

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

View File

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

View File

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

View File

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

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

@@ -0,0 +1,47 @@
/**
* This file is part of CounterStrikeSharp.
* CounterStrikeSharp is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CounterStrikeSharp is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
*/
#pragma once
#include <string>
#include <sstream>
namespace counterstrikesharp {
std::wstring widen(const std::string& str)
{
std::wostringstream wstm;
const auto& ctfacet = std::use_facet<std::ctype<wchar_t>>(wstm.getloc());
for (size_t i = 0; i < str.size(); ++i) {
wstm << ctfacet.widen(str[i]);
}
return wstm.str();
}
std::string narrow(const std::wstring& str)
{
std::ostringstream stm;
// Incorrect code from the link
// const ctype<char>& ctfacet = use_facet<ctype<char>>(stm.getloc());
// Correct code.
const auto& ctfacet = std::use_facet<std::ctype<wchar_t>>(stm.getloc());
for (size_t i = 0; i < str.size(); ++i) {
stm << ctfacet.narrow(str[i], 0);
}
return stm.str();
}
}