Compare commits

...

18 Commits

Author SHA1 Message Date
Roflmuffin
696ecadee4 fix: bad vector math 2024-03-26 12:16:53 +10:00
Roflmuffin
e4d598dba8 fix: main version number 2024-03-25 17:15:31 +10:00
Michael Wilson
5c67d88844 feat: improve version stamping (#389) 2024-03-25 17:11:16 +10:00
Michael Wilson
9d8b6beae6 .NET8 Upgrade (#261)
Co-authored-by: Frederik St-Onge <frederik.stonge@gmail.com>
Co-authored-by: B3none <24966460+B3none@users.noreply.github.com>
2024-03-25 14:16:32 +10:00
ipsvn
39604b7ad7 Fix backwards compatibility in DynamicHook (#388) 2024-03-22 23:04:09 +10:00
luxury fabka
1b1f1d04dd minor menu changes (#373)
Co-authored-by: Michael Wilson <roflmuffin@users.noreply.github.com>
Co-authored-by: Roflmuffin <shortguy014@gmail.com>
2024-03-18 05:00:31 +00:00
Ian Lucas
dbc348c1bf Add RemoveAll method to NetworkedVector class (#380) 2024-03-18 14:56:29 +10:00
Michael Wilson
d295589c44 fix: update collision groups (#376) 2024-03-13 16:10:42 +10:00
Michael Wilson
16767fd494 feat: add Server.RunOnTick method which allows tick scheduling (#374) 2024-03-11 23:08:54 +10:00
roflmuffin
36a97bfffd fix: disable autoload plugin but allow shared library loading 2024-03-09 15:29:45 +10:00
roflmuffin
178f7472c6 feat: add PluginAutoLoadEnabled config option
closes #370
2024-03-09 14:29:43 +10:00
luxury fabka
cba5144bbf remove unused arguments (#334)
Co-authored-by: Michael Wilson <roflmuffin@users.noreply.github.com>
2024-03-08 05:08:33 +00:00
roflmuffin
0de951cb6f fix: allows game events to be freed, frees event in print to center html
closes #346
2024-03-08 10:39:59 +10:00
Michael Wilson
c3d44a87bc feat: use concurrent queue for next frame & world update tasks (#365) 2024-03-06 21:08:22 +10:00
B3none
bd3c0c76e3 [no ci] Update minimum api version for shared api docs (#364) 2024-03-06 15:42:34 +10:00
Yarukon
656c0e3a84 Fix TerminateRound params (#363) 2024-03-04 10:59:02 +00:00
Michael Wilson
40c842149c fix: add error handling to OnAllPluginsLoaded 2024-03-04 17:21:50 +10:00
roflmuffin
64d1c0a9f4 chore: remove erroneous log 2024-03-04 16:56:49 +10:00
67 changed files with 4505 additions and 342 deletions

View File

@@ -3,22 +3,46 @@ name: Build & Publish
on:
push:
paths-ignore:
- 'docfx/**'
branches: [ "main", "dev" ]
- "docfx/**"
branches: ["main", "dev"]
pull_request:
branches: [ "main", "dev" ]
branches: ["main", "dev"]
env:
BUILD_TYPE: Release
jobs:
setup:
permissions:
contents: write
runs-on: ubuntu-latest
outputs:
buildnumber: ${{ steps.buildnumber.outputs.build_number }}
steps:
- name: Generate build number
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
id: buildnumber
uses: onyxmueller/build-tag-number@v1
with:
token: ${{secrets.github_token}}
build_windows:
needs: setup
runs-on: windows-latest
steps:
- name: Prepare env
shell: bash
run: echo "GITHUB_SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_ENV
- name: Fallback build number
if: ${{ github.event_name == 'pull_request' || github.ref != 'refs/heads/main' }}
shell: bash
run: echo "BUILD_NUMBER=0" >> $GITHUB_ENV
- name: Main build number
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
run: echo "BUILD_NUMBER=${{ needs.setup.outputs.buildnumber }}" >> $GITHUB_ENV
- name: Visual Studio environment
shell: cmd
run: |
@@ -34,7 +58,7 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
submodules: "recursive"
- name: Build
run: |
@@ -56,6 +80,7 @@ jobs:
path: build/output/
build_linux:
needs: setup
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:
@@ -65,9 +90,18 @@ jobs:
shell: bash
run: echo "GITHUB_SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_ENV
- name: Fallback build number
if: ${{ github.event_name == 'pull_request' || github.ref != 'refs/heads/main' }}
shell: bash
run: echo "BUILD_NUMBER=0" >> $GITHUB_ENV
- name: Main build number
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
run: echo "BUILD_NUMBER=${{ needs.setup.outputs.buildnumber }}" >> $GITHUB_ENV
- uses: actions/checkout@v3
with:
submodules: 'recursive'
submodules: "recursive"
- name: Build
run: |
@@ -87,11 +121,10 @@ jobs:
path: build/output/
build_managed:
needs: setup
permissions:
contents: write
runs-on: ubuntu-latest
outputs:
buildnumber: ${{ steps.buildnumber.outputs.build_number }}
steps:
- name: Prepare env
shell: bash
@@ -102,20 +135,17 @@ jobs:
shell: bash
run: echo "BUILD_NUMBER=0" >> $GITHUB_ENV
- name: Main build number
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
run: echo "BUILD_NUMBER=${{ needs.setup.outputs.buildnumber }}" >> $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' && github.ref == 'refs/heads/main' }}
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'
dotnet-version: "8.0.x"
- name: Install dependencies
run: dotnet restore managed/CounterStrikeSharp.sln
@@ -133,7 +163,7 @@ jobs:
- name: Publish artifacts
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
dotnet pack -c Release /p:Version=1.0.${{ env.BUILD_NUMBER }} managed/CounterStrikeSharp.API
- uses: actions/upload-artifact@v3
with:
@@ -144,7 +174,7 @@ jobs:
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
permissions:
contents: write
needs: [ "build_linux", "build_windows", "build_managed" ]
needs: ["setup", "build_linux", "build_windows", "build_managed"]
runs-on: ubuntu-latest
steps:
- name: Prepare env
@@ -171,49 +201,48 @@ jobs:
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
cp -r build/api/net8.0/publish/* build/linux/addons/counterstrikesharp/api
cp -r build/api/net8.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 *)
(cd build/linux && zip -qq -r ../../counterstrikesharp-build-${{ needs.setup.outputs.buildnumber }}-linux-${{ env.GITHUB_SHA_SHORT }}.zip *)
(cd build/windows && zip -qq -r ../../counterstrikesharp-build-${{ needs.setup.outputs.buildnumber }}-windows-${{ env.GITHUB_SHA_SHORT }}.zip *)
- name: Add dotnet runtime
run: |
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 \
curl -s -L https://download.visualstudio.microsoft.com/download/pr/c1371dc2-eed2-47be-9af3-ae060dbe3c7d/bd509e0a87629764ed47608466d183e6/aspnetcore-runtime-8.0.3-linux-x64.tar.gz \
| 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
curl -s -L https://download.visualstudio.microsoft.com/download/pr/086d1dd6-57a5-437a-a1ef-549cf702fb48/dd4a8fe6c53a1016a414d6f2925c1323/aspnetcore-runtime-8.0.3-win-x64.zip -o dotnet.zip
unzip -qq dotnet.zip -d build/windows/addons/counterstrikesharp/dotnet
- 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 *)
(cd build/linux && zip -qq -r ../../counterstrikesharp-with-runtime-build-${{ needs.setup.outputs.buildnumber }}-linux-${{ env.GITHUB_SHA_SHORT }}.zip *)
(cd build/windows && zip -qq -r ../../counterstrikesharp-with-runtime-build-${{ needs.setup.outputs.buildnumber }}-windows-${{ env.GITHUB_SHA_SHORT }}.zip *)
- name: Release
id: release
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ needs.build_managed.outputs.buildnumber }}
tag_name: v${{ needs.setup.outputs.buildnumber }}
files: |
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
counterstrikesharp-build-${{ needs.setup.outputs.buildnumber }}-windows-${{ env.GITHUB_SHA_SHORT }}.zip
counterstrikesharp-with-runtime-build-${{ needs.setup.outputs.buildnumber }}-windows-${{ env.GITHUB_SHA_SHORT }}.zip
counterstrikesharp-build-${{ needs.setup.outputs.buildnumber }}-linux-${{ env.GITHUB_SHA_SHORT }}.zip
counterstrikesharp-with-runtime-build-${{ needs.setup.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
dotnet nuget push build/api/CounterStrikeSharp.API.1.0.${{ needs.setup.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.setup.outputs.buildnumber }}.snupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
- name: Send Notification to Discord
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@0.3.2
with:
args: "A new release of CS# has been tagged (v${{ needs.build_managed.outputs.buildnumber }}) at ${{ steps.release.outputs.url }}"
args: "A new release of CS# has been tagged (v${{ needs.setup.outputs.buildnumber }}) at ${{ steps.release.outputs.url }}"

2
.gitignore vendored
View File

@@ -1,6 +1,6 @@
.ccls-cache/
.cmake/
cmake-build-debug*/
cmake-build-*/
.kdev4/
.vscode/
generated/

View File

@@ -49,6 +49,8 @@ SET(SOURCE_FILES
src/core/managers/event_manager.cpp
src/core/timer_system.h
src/core/timer_system.cpp
src/core/tick_scheduler.h
src/core/tick_scheduler.cpp
src/scripting/autonative.h
src/scripting/natives/natives_engine.cpp
src/core/engine_trace.h

View File

@@ -3,5 +3,6 @@
"SilentChatTrigger": [ "/" ],
"FollowCS2ServerGuidelines": true,
"PluginHotReloadEnabled": true,
"PluginAutoLoadEnabled": true,
"ServerLanguage": "en"
}

View File

@@ -9,7 +9,7 @@ How to write your first plugin for CounterStrikeSharp
## Creating a New Project
First, ensure you have the relevant .NET 7.0 SDK for your platform installed on your machine. You can find the links to the latest downloads on the <a href="https://dotnet.microsoft.com/en-us/download/dotnet/7.0" target="_blank"> official Microsoft download page</a>.
First, ensure you have the relevant .NET 8.0 SDK for your platform installed on your machine. You can find the links to the latest downloads on the <a href="https://dotnet.microsoft.com/en-us/download/dotnet/8.0" target="_blank"> official Microsoft download page</a>.
### Creating a Class Library
@@ -25,7 +25,7 @@ Use your IDE (Visual Studio/Rider) to add a reference to the `CounterStrikeSharp
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
@@ -65,7 +65,7 @@ public class HelloWorldPlugin : BasePlugin
}
```
Now build your project using your ide or the `dotnet build` command. You should now have a built binary file in your `bin/Debug/net7.0` subdirectory in the project.
Now build your project using your ide or the `dotnet build` command. You should now have a built binary file in your `bin/Debug/net8.0` subdirectory in the project.
### Installing your Plugin

View File

@@ -33,6 +33,10 @@ receive a ban.
When enabled, plugins are automatically reloaded when their .dll file is updated.
## PluginAutoLoadEnabled
When enabled, plugins are automatically loaded from the plugins directory on server start.
## ServerLanguage
Configures the default language to use for server commands & messages. The format for the culture name based on RFC 4646 is `languagecode2-country`/`regioncode2`, where `languagecode2` is the two-letter language code and `country/regioncode2` is the two-letter subculture code. Examples include `ja-JP` for Japanese (Japan) and `en-US` for English (United States). Defaults to "en".

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

View File

@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

View File

@@ -5,7 +5,7 @@ using MySharedTypes.Contracts;
namespace WithSharedTypes;
[MinimumApiVersion(143)]
[MinimumApiVersion(184)]
public class WithSharedTypesPlugin : BasePlugin
{
public override string ModuleName => "Example: Shared Types";
@@ -54,4 +54,4 @@ public class WithSharedTypesPlugin : BasePlugin
public override void Unload(bool hotReload)
{
}
}
}

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

View File

@@ -6,7 +6,7 @@ using MySharedTypes.Contracts;
namespace WithSharedTypesConsumer;
[MinimumApiVersion(142)]
[MinimumApiVersion(184)]
public class WithSharedTypesConsumerPlugin : BasePlugin
{
public override string ModuleName => "Example: Shared Types (Consumer)";
@@ -53,4 +53,4 @@ public class WithSharedTypesConsumerPlugin : BasePlugin
public override void Unload(bool hotReload)
{
}
}
}

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

File diff suppressed because it is too large Load Diff

View File

@@ -29,6 +29,18 @@ set(SOURCESDK_LIB ${SOURCESDK}/lib)
add_definitions(-DMETA_IS_SOURCE2)
if(DEFINED ENV{GITHUB_SHA_SHORT})
add_definitions(-DGITHUB_SHA="$ENV{GITHUB_SHA_SHORT}")
else()
add_definitions(-DGITHUB_SHA="Local")
endif()
if(DEFINED ENV{BUILD_NUMBER})
add_definitions(-DBUILD_NUMBER="$ENV{BUILD_NUMBER}")
else()
add_definitions(-DBUILD_NUMBER="0")
endif()
include_directories(
${SOURCESDK}
${SOURCESDK}/thirdparty/protobuf-3.21.8/src
@@ -51,6 +63,7 @@ include_directories(
libraries/tl
libraries/funchook/include
libraries/DynoHook/src
libraries/moodycamel
libraries
)

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

View File

@@ -13,4 +13,14 @@ public class Api
{
return Assembly.GetAssembly(typeof(BasePlugin))!.GetName().Version!.Build;
}
/// <summary>
/// Returns the assembly version of CounterStrikeSharp running on the server as a string including git commit hash
/// </summary>
/// <example>1.0.0+9d8b6be</example>
public static string GetVersionString()
{
return Assembly.GetAssembly(typeof(BasePlugin))!.GetCustomAttribute<AssemblyInformationalVersionAttribute>()!
.InformationalVersion;
}
}

View File

@@ -427,6 +427,24 @@
<Left>.\ApiCompat\v151.dll</Left>
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:CounterStrikeSharp.API.Core.FunctionReference.Create(System.Delegate)</Target>
<Left>.\ApiCompat\v151.dll</Left>
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:CounterStrikeSharp.API.Core.FunctionReference.Get(System.Int32)</Target>
<Left>.\ApiCompat\v151.dll</Left>
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:CounterStrikeSharp.API.Core.NativeAPI.GetGameFrameTime</Target>
<Left>.\ApiCompat\v151.dll</Left>
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:CounterStrikeSharp.API.Core.NativeAPI.QueueTaskForNextFrame(System.IntPtr)</Target>
@@ -451,6 +469,24 @@
<Left>.\ApiCompat\v151.dll</Left>
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:CounterStrikeSharp.API.Core.Translations.JsonStringLocalizerFactory.#ctor(CounterStrikeSharp.API.Core.Plugin.IPluginContext)</Target>
<Left>.\ApiCompat\v151.dll</Left>
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:CounterStrikeSharp.API.Modules.Memory.DynamicFunctions.DynamicHook.GetReturn``1(System.Int32)</Target>
<Left>.\ApiCompat\v151.dll</Left>
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:CounterStrikeSharp.API.Server.get_GameFrameTime</Target>
<Left>.\ApiCompat\v151.dll</Left>
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:CounterStrikeSharp.API.Core.IPlugin.OnAllPluginsLoaded(System.Boolean)</Target>
@@ -469,6 +505,12 @@
<Left>.\ApiCompat\v151.dll</Left>
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>P:CounterStrikeSharp.API.Modules.Menu.IMenu.ExitButton</Target>
<Left>.\ApiCompat\v151.dll</Left>
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0010</DiagnosticId>
<Target>T:CounterStrikeSharp.API.Core.RenderMultisampleType_t</Target>

View File

@@ -315,16 +315,6 @@ namespace CounterStrikeSharp.API.Core
}
}
public static float GetGameFrameTime(){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.SetIdentifier(0x97E331CA);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (float)ScriptContext.GlobalScriptContext.GetResult(typeof(float));
}
}
public static double GetEngineTime(){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
@@ -512,6 +502,17 @@ namespace CounterStrikeSharp.API.Core
}
}
public static void QueueTaskForFrame(int tick, InputArgument callback){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(tick);
ScriptContext.GlobalScriptContext.Push((InputArgument)callback);
ScriptContext.GlobalScriptContext.SetIdentifier(0x2F92C340);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static void QueueTaskForNextWorldUpdate(InputArgument callback){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
@@ -749,6 +750,16 @@ namespace CounterStrikeSharp.API.Core
}
}
public static void FreeEvent(IntPtr gameevent){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(gameevent);
ScriptContext.GlobalScriptContext.SetIdentifier(0x7E8B60C2);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static void FireEvent(IntPtr gameevent, bool dontbroadcast){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
@@ -1085,6 +1096,16 @@ namespace CounterStrikeSharp.API.Core
}
}
public static void RemoveAllNetworkVectorElements(IntPtr vec){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(vec);
ScriptContext.GlobalScriptContext.SetIdentifier(0x67206C08);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static short GetSchemaOffset(string classname, string propname){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();

View File

@@ -106,13 +106,13 @@ namespace CounterStrikeSharp.API.Core
[RequiresPermissions("@css/generic")]
private void OnCSSCommand(CCSPlayerController? caller, CommandInfo info)
{
var currentVersion = Api.GetVersion();
var versionString = $"v{Api.GetVersion()} ({Api.GetVersionString()})";
info.ReplyToCommand(
" CounterStrikeSharp was created and is maintained by Michael \"roflmuffin\" Wilson.\n" +
" Counter-Strike Sharp uses code borrowed from SourceMod, Source.Python, FiveM, Saul Rennison, source2gen and CS2Fixes.\n" +
" See ACKNOWLEDGEMENTS.md for more information.\n" +
" Current API Version: " + currentVersion);
" Current API Version: " + versionString);
return;
}

View File

@@ -50,6 +50,9 @@ namespace CounterStrikeSharp.API.Core
[JsonPropertyName("PluginHotReloadEnabled")]
public bool PluginHotReloadEnabled { get; set; } = true;
[JsonPropertyName("PluginAutoLoadEnabled")]
public bool PluginAutoLoadEnabled { get; set; } = true;
[JsonPropertyName("ServerLanguage")]
public string ServerLanguage { get; set; } = "en";
}
@@ -95,7 +98,13 @@ namespace CounterStrikeSharp.API.Core
/// </summary>
public static bool PluginHotReloadEnabled => _coreConfig.PluginHotReloadEnabled;
/// <summary>
/// When enabled, plugins are automatically loaded from the plugins directory on server start.
/// </summary>
public static bool PluginAutoLoadEnabled => _coreConfig.PluginAutoLoadEnabled;
public static string ServerLanguage => _coreConfig.ServerLanguage;
}
public partial class CoreConfig : IStartupService

View File

@@ -14,11 +14,9 @@
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
*/
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace CounterStrikeSharp.API.Core
@@ -30,140 +28,149 @@ namespace CounterStrikeSharp.API.Core
{
/// <summary>Delegate will be removed after the first invocation.</summary>
SingleUse,
/// <summary>Delegate will remain in memory for the lifetime of the application.</summary>
/// <summary>Delegate will remain in memory for the lifetime of the application (or until <see cref="FunctionReference.Remove"/> is called).</summary>
Permanent
}
/// <summary>
/// Represents a reference to a function that can be called from native code.
/// </summary>
public class FunctionReference
{
private readonly Delegate m_method;
public FunctionLifetime Lifetime { get; set; } = FunctionLifetime.Permanent;
public unsafe delegate void CallbackDelegate(fxScriptContext* context);
private CallbackDelegate s_callback;
private FunctionReference(Delegate method)
private static readonly ConcurrentDictionary<int, FunctionReference> IdToFunctionReferencesMap = new();
private static readonly ConcurrentDictionary<Delegate, FunctionReference> TargetMethodToFunctionReferencesMap = new();
private static readonly object ReferenceCounterLock = new();
private static int _referenceCounter;
private readonly Delegate _targetMethod;
private readonly CallbackDelegate _nativeCallback;
private readonly TaskCompletionSource _taskCompletionSource = new();
private FunctionReference(Delegate method, FunctionLifetime lifetime)
{
m_method = method;
unsafe
{
var dg = new CallbackDelegate((fxScriptContext* context) =>
{
try
{
var scriptContext = new ScriptContext(context);
if (method.Method.GetParameters().FirstOrDefault()?.ParameterType == typeof(ScriptContext))
{
var returnO = m_method.DynamicInvoke(scriptContext);
if (returnO != null)
{
scriptContext.SetResult(returnO, context);
}
return;
}
var paramsList = method.Method.GetParameters().Select((x, i) =>
{
var param = method.Method.GetParameters()[i];
object obj = null;
if (typeof(NativeObject).IsAssignableFrom(param.ParameterType))
{
obj = Activator.CreateInstance(param.ParameterType,
new[] { scriptContext.GetArgument(typeof(IntPtr), i) });
}
else
{
obj = scriptContext.GetArgument(param.ParameterType, i);
}
return obj;
}).ToArray();
var returnObj = m_method.DynamicInvoke(paramsList);
if (returnObj != null)
{
scriptContext.SetResult(returnObj, context);
}
}
catch (Exception e)
{
Application.Instance.Logger.LogError(e, "Error invoking callback");
}
finally
{
if (Lifetime == FunctionLifetime.SingleUse)
{
Remove(Identifier);
if (references.ContainsKey(m_method))
references.Remove(m_method, out _);
}
}
});
s_callback = dg;
}
Lifetime = lifetime;
_targetMethod = method;
_nativeCallback = CreateWrappedCallback();
}
/// <summary>
/// <inheritdoc cref="FunctionLifetime"/>
/// </summary>
public FunctionLifetime Lifetime { get; }
/// <summary>
/// For <see cref="FunctionLifetime.SingleUse"/> function references, this task will complete when
/// the function has finished invoking.
/// </summary>
public Task CompletionTask => _taskCompletionSource.Task;
public int Identifier { get; private set; }
public static FunctionReference Create(Delegate method)
private unsafe CallbackDelegate CreateWrappedCallback()
{
if (references.TryGetValue(method, out var existingReference))
return context =>
{
try
{
var scriptContext = new ScriptContext(context);
// Allow for manual handling of the script context
if (_targetMethod.Method.GetParameters().FirstOrDefault()?.ParameterType == typeof(ScriptContext))
{
var returnValue = _targetMethod.DynamicInvoke(scriptContext);
if (returnValue != null)
{
scriptContext.SetResult(returnValue, context);
}
return;
}
var parameterList = _targetMethod.Method.GetParameters().Select((_, i) =>
{
var parameter = _targetMethod.Method.GetParameters()[i];
return scriptContext.GetArgument(parameter.ParameterType, i);
}).ToArray();
var returnObj = _targetMethod.DynamicInvoke(parameterList);
if (returnObj != null)
{
scriptContext.SetResult(returnObj, context);
}
}
catch (Exception e)
{
Application.Instance.Logger.LogError(e, "Error invoking callback");
}
finally
{
if (Lifetime == FunctionLifetime.SingleUse)
{
RemoveSelf();
}
_taskCompletionSource.TrySetResult();
}
};
}
public static FunctionReference Create(Delegate method, FunctionLifetime lifetime = FunctionLifetime.Permanent)
{
// We always want to create a new reference if the lifetime is single use.
if (lifetime == FunctionLifetime.Permanent && TargetMethodToFunctionReferencesMap.TryGetValue(method, out var existingReference))
{
return existingReference;
}
var reference = new FunctionReference(method);
var reference = new FunctionReference(method, lifetime);
var referenceId = Register(reference);
reference.Identifier = referenceId;
return reference;
}
private static ConcurrentDictionary<int, FunctionReference> ms_references = new ConcurrentDictionary<int, FunctionReference>();
private static int ms_referenceId;
private static ConcurrentDictionary<Delegate, FunctionReference> references =
new ConcurrentDictionary<Delegate, FunctionReference>();
private static int Register(FunctionReference reference)
{
var thisRefId = ms_referenceId;
ms_references[thisRefId] = reference;
references[reference.m_method] = reference;
unchecked { ms_referenceId++; }
return thisRefId;
}
public static FunctionReference Get(int reference)
{
if (ms_references.ContainsKey(reference))
lock (ReferenceCounterLock)
{
return ms_references[reference];
}
var thisRefId = _referenceCounter;
IdToFunctionReferencesMap[thisRefId] = reference;
TargetMethodToFunctionReferencesMap[reference._targetMethod] = reference;
return null;
unchecked
{
_referenceCounter++;
}
return thisRefId;
}
}
public IntPtr GetFunctionPointer()
public IntPtr GetFunctionPointer() => Marshal.GetFunctionPointerForDelegate(_nativeCallback);
private void RemoveSelf()
{
IntPtr cb = Marshal.GetFunctionPointerForDelegate(s_callback);
return cb;
Remove(Identifier);
}
public static void Remove(int reference)
{
if (ms_references.TryGetValue(reference, out var funcRef))
if (IdToFunctionReferencesMap.TryGetValue(reference, out var functionReference))
{
ms_references.Remove(reference, out _);
if (TargetMethodToFunctionReferencesMap.ContainsKey(functionReference._targetMethod))
{
TargetMethodToFunctionReferencesMap.Remove(functionReference._targetMethod, out _);
}
IdToFunctionReferencesMap.Remove(reference, out _);
Application.Instance.Logger.LogDebug("Removing function/callback reference: {Reference}", reference);
}

View File

@@ -26,6 +26,6 @@ public partial class CCSGameRules
/// </summary>
public void TerminateRound(float delay, RoundEndReason roundEndReason)
{
VirtualFunctions.TerminateRound(Handle, roundEndReason, delay);
VirtualFunctions.TerminateRound(Handle, roundEndReason, delay, 0, 0);
}
}

View File

@@ -75,6 +75,7 @@ public partial class CCSPlayerController
Userid = this
};
@event.FireEventToClient(this);
@event.Free();
}
/// <summary>

View File

@@ -33,6 +33,11 @@ public partial class NetworkedVector<T> : NativeObject, IReadOnlyCollection<T>
}
}
public void RemoveAll()
{
NativeAPI.RemoveAllNetworkVectorElements(Handle);
}
public IEnumerator<T> GetEnumerator()
{
for (int i = 0; i < Count; i++)

View File

@@ -78,29 +78,38 @@ public class PluginManager : IPluginManager
if (!_sharedAssemblies.TryGetValue(name.FullName, out var assembly))
{
_logger.LogError("Failed to use existing shared assembly: {Name}", name);
return null;
}
return assembly;
};
foreach (var path in pluginAssemblyPaths)
if (CoreConfig.PluginAutoLoadEnabled)
{
foreach (var path in pluginAssemblyPaths)
{
try
{
LoadPlugin(path);
}
catch (Exception e)
{
_logger.LogError(e, "Failed to load plugin from {Path}", path);
}
}
}
foreach (var plugin in _loadedPluginContexts)
{
try
{
LoadPlugin(path);
plugin.Plugin?.OnAllPluginsLoaded(false);
}
catch (Exception e)
{
_logger.LogError(e, "Failed to load plugin from {Path}", path);
_logger.LogError(e, "OnAllPluginsLoaded failed");
}
}
foreach (var plugin in _loadedPluginContexts)
{
plugin.Plugin.OnAllPluginsLoaded(false);
}
}
public IEnumerable<PluginContext> GetLoadedPlugins()

View File

@@ -1,63 +1,74 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<GenerateCompatibilitySuppressionFile>true</GenerateCompatibilitySuppressionFile>
<EnablePackageValidation>true</EnablePackageValidation>
<NoWarn>$(NoWarn);CS1591;CP0003</NoWarn>
<Nullable>enable</Nullable>
<GenerateCompatibilitySuppressionFile>true</GenerateCompatibilitySuppressionFile>
<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>
<!-- <GenerateCompatibilitySuppressionFile>true</GenerateCompatibilitySuppressionFile> -->
</PropertyGroup>
<PropertyGroup>
<ApiCompatValidateAssemblies>true</ApiCompatValidateAssemblies>
<ApiCompatContractAssembly>.\ApiCompat\v151.dll</ApiCompatContractAssembly>
</PropertyGroup>
<ItemGroup>
<None Remove="Modules\Commands\CommandInfo" />
<None Remove="Modules\Disabled\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.4.0" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.DotNet.ApiCompat.Task" Version="7.0.404" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="7.0.3" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Scrutor" Version="4.2.2" />
<PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Core\Schema\" />
<Folder Include="Modules\Errors" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Modules\Disabled\**" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Remove="Modules\Disabled\**" />
</ItemGroup>
<PropertyGroup />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<!-- <Target Name="PreBuild" BeforeTargets="PreBuildEvent">-->
<!-- <Exec Command="dotnet run &#45;&#45;project ../../tooling/CodeGen.Natives" />-->
<!-- </Target>-->
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<GenerateCompatibilitySuppressionFile>true</GenerateCompatibilitySuppressionFile>
<EnablePackageValidation>true</EnablePackageValidation>
<NoWarn>$(NoWarn);CS1591;CP0003</NoWarn>
<Nullable>enable</Nullable>
<GenerateCompatibilitySuppressionFile>true</GenerateCompatibilitySuppressionFile>
<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>
<SourceControlInformationFeatureSupported>true</SourceControlInformationFeatureSupported>
<!-- <GenerateCompatibilitySuppressionFile>true</GenerateCompatibilitySuppressionFile> -->
</PropertyGroup>
<PropertyGroup>
<ApiCompatValidateAssemblies>true</ApiCompatValidateAssemblies>
<ApiCompatContractAssembly>.\ApiCompat\v151.dll</ApiCompatContractAssembly>
</PropertyGroup>
<ItemGroup>
<None Remove="Modules\Commands\CommandInfo"/>
<None Remove="Modules\Disabled\**"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.4.0"/>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0"/>
<PackageReference Include="Microsoft.DotNet.ApiCompat.Task" Version="8.0.203"/>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0"/>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0"/>
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="8.0.3"/>
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0"/>
<PackageReference Include="Scrutor" Version="4.2.2"/>
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0"/>
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.0"/>
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0"/>
<PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0"/>
</ItemGroup>
<ItemGroup>
<Folder Include="Core\Schema\"/>
<Folder Include="Modules\Errors"/>
</ItemGroup>
<ItemGroup>
<Compile Remove="Modules\Disabled\**"/>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Remove="Modules\Disabled\**"/>
</ItemGroup>
<PropertyGroup/>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<Target Name="SetSourceRevisionId" BeforeTargets="InitializeSourceControlInformation">
<Exec
Command="git describe --long --always --exclude=* --abbrev=7"
ConsoleToMSBuild="True"
IgnoreExitCode="False"
>
<Output PropertyName="SourceRevisionId" TaskParameter="ConsoleOutput"/>
</Exec>
</Target>
<!-- <Target Name="PreBuild" BeforeTargets="PreBuildEvent">-->
<!-- <Exec Command="dotnet run &#45;&#45;project ../../tooling/CodeGen.Natives" />-->
<!-- </Target>-->
</Project>

View File

@@ -1,5 +1,7 @@
// Global using directives
global using System;
global using System.Linq;
global using System.IO;
global using System.Collections.Generic;
global using CounterStrikeSharp.API.Core;

View File

@@ -18,34 +18,37 @@ namespace CounterStrikeSharp.API.Modules.Entities.Constants
{
public enum CollisionGroup
{
COLLISION_GROUP_NONE = 0,
COLLISION_GROUP_DEBRIS, // Collides with nothing but world and static stuff
COLLISION_GROUP_DEBRIS_TRIGGER, // Same as debris, but hits triggers
COLLISION_GROUP_INTERACTIVE_DEBRIS, // Collides with everything except other interactive debris or debris
COLLISION_GROUP_INTERACTIVE, // Collides with everything except interactive debris or debris
COLLISION_GROUP_NONE = 0,
COLLISION_GROUP_NEVER,
COLLISION_GROUP_TRIGGER,
COLLISION_GROUP_CONDITIONALLY_SOLID,
COLLISION_GROUP_DEFAULT,
COLLISION_GROUP_DEBRIS, // Collides with nothing but world and static stuff
COLLISION_GROUP_INTERACTIVE_DEBRIS, // Collides with everything except other interactive debris or debris
COLLISION_GROUP_INTERACTIVE, // Collides with everything except interactive debris or debris
COLLISION_GROUP_PLAYER,
COLLISION_GROUP_BREAKABLE_GLASS,
COLLISION_GROUP_VEHICLE,
COLLISION_GROUP_PLAYER_MOVEMENT, // For HL2, same as Collision_Group_Player, for
// TF2, this filters out other players and CBaseObjects
COLLISION_GROUP_NPC, // Generic NPC group
COLLISION_GROUP_IN_VEHICLE, // for any entity inside a vehicle
COLLISION_GROUP_WEAPON, // for any weapons that need collision detection
COLLISION_GROUP_VEHICLE_CLIP, // vehicle clip brush to restrict vehicle movement
COLLISION_GROUP_PROJECTILE, // Projectiles!
COLLISION_GROUP_DOOR_BLOCKER, // Blocks entities not permitted to get near moving doors
COLLISION_GROUP_PASSABLE_DOOR, // Doors that the player shouldn't collide with
COLLISION_GROUP_DISSOLVING, // Things that are dissolving are in this group
COLLISION_GROUP_PUSHAWAY, // Nonsolid on client and server, pushaway in player code
COLLISION_GROUP_PLAYER_MOVEMENT, // For HL2, same as Collision_Group_Player, for
COLLISION_GROUP_NPC_ACTOR, // Used so NPCs in scripts ignore the player.
COLLISION_GROUP_NPC_SCRIPTED, // USed for NPCs in scripts that should not collide with each other
// TF2, this filters out other players and CBaseObjects
COLLISION_GROUP_NPC, // Generic NPC group
COLLISION_GROUP_IN_VEHICLE, // for any entity inside a vehicle
COLLISION_GROUP_WEAPON, // for any weapons that need collision detection
COLLISION_GROUP_VEHICLE_CLIP, // vehicle clip brush to restrict vehicle movement
COLLISION_GROUP_PROJECTILE, // Projectiles!
COLLISION_GROUP_DOOR_BLOCKER, // Blocks entities not permitted to get near moving doors
COLLISION_GROUP_PASSABLE_DOOR, // Doors that the player shouldn't collide with
COLLISION_GROUP_DISSOLVING, // Things that are dissolving are in this group
COLLISION_GROUP_PUSHAWAY, // Nonsolid on client and server, pushaway in player code
COLLISION_GROUP_NPC_ACTOR, // Used so NPCs in scripts ignore the player.
COLLISION_GROUP_NPC_SCRIPTED, // USed for NPCs in scripts that should not collide with each other
COLLISION_GROUP_PZ_CLIP,
COLLISION_GROUP_DEBRIS_BLOCK_PROJECTILE, // Only collides with bullets
COLLISION_GROUP_PROPS,
LAST_SHARED_COLLISION_GROUP
}
}
}

View File

@@ -32,12 +32,16 @@ namespace CounterStrikeSharp.API.Modules.Events
public class GameEvent : NativeObject
{
// Used to track freeable state for manually created events.
private bool _freeable = false;
public GameEvent(IntPtr pointer) : base(pointer)
{
}
public GameEvent(string name, bool force) : this(NativeAPI.CreateEvent(name, force))
{
_freeable = true;
}
public string EventName => NativeAPI.GetEventName(Handle);
@@ -121,8 +125,28 @@ namespace CounterStrikeSharp.API.Modules.Events
protected void SetEntityIndex(string name, int value) => NativeAPI.SetEventEntityIndex(Handle, name, value);
public void FireEvent(bool dontBroadcast) => NativeAPI.FireEvent(Handle, dontBroadcast);
public void FireEvent(bool dontBroadcast)
{
NativeAPI.FireEvent(Handle, dontBroadcast);
_freeable = false;
}
public void FireEventToClient(CCSPlayerController player) => NativeAPI.FireEventToClient(Handle, (int)player.Index);
/// <summary>
/// Used to manually free the event.
/// <remarks>If <see cref="FireEvent"/> is called, Free will be called automatically.</remarks>
/// </summary>
public void Free()
{
if (!_freeable)
{
throw new InvalidOperationException("Event is not able to be freed.");
}
NativeAPI.FreeEvent(Handle);
_freeable = false;
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using CounterStrikeSharp.API.Core;
namespace CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
@@ -14,7 +14,13 @@ public class DynamicHook : NativeObject
return NativeAPI.DynamicHookGetParam<T>(Handle, (int)typeof(T).ToValidDataType(), index);
}
[Obsolete("Use GetReturn<T>() instead")]
public T GetReturn<T>(int index)
{
return GetReturn<T>();
}
public T GetReturn<T>()
{
return NativeAPI.DynamicHookGetReturn<T>(Handle, (int)typeof(T).ToValidDataType());
}
@@ -28,4 +34,4 @@ public class DynamicHook : NativeObject
{
NativeAPI.DynamicHookSetReturn(Handle, (int)typeof(T).ToValidDataType(), value);
}
}
}

View File

@@ -46,10 +46,10 @@ public static class VirtualFunctions
public static Action<IntPtr, string> SetModel = SetModelFunc.Invoke;
public static MemoryFunctionVoid<nint, RoundEndReason, float> TerminateRoundFunc =
public static MemoryFunctionVoid<nint, RoundEndReason, float, nint, byte> TerminateRoundFunc =
new(GameData.GetSignature("CCSGameRules_TerminateRound"));
public static Action<IntPtr, RoundEndReason, float> TerminateRound = TerminateRoundFunc.Invoke;
public static Action<IntPtr, RoundEndReason, float, nint, byte> TerminateRound = TerminateRoundFunc.Invoke;
public static MemoryFunctionWithReturn<string, int, IntPtr> UTIL_CreateEntityByNameFunc =
new(GameData.GetSignature("UTIL_CreateEntityByName"));

View File

@@ -30,7 +30,8 @@ public abstract class BaseMenu : IMenu
public string Title { get; set; }
public List<ChatMenuOption> MenuOptions { get; } = new();
public PostSelectAction PostSelectAction { get; set; } = PostSelectAction.Reset;
public bool ExitButton { get; set; } = true;
protected BaseMenu(string title)
{
Title = title;
@@ -76,8 +77,8 @@ public abstract class BaseMenuInstance : IMenuInstance
}
protected bool HasPrevButton => Page > 0;
protected bool HasNextButton => CurrentOffset + NumPerPage < Menu.MenuOptions.Count;
protected int MenuItemsPerPage => NumPerPage + 2 - (HasNextButton ? 1 : 0) - (HasPrevButton ? 1 : 0);
protected bool HasNextButton => Menu.MenuOptions.Count > NumPerPage && CurrentOffset + NumPerPage < Menu.MenuOptions.Count;
protected virtual int MenuItemsPerPage => NumPerPage;
public virtual void Display()
{
@@ -142,7 +143,7 @@ public abstract class BaseMenuInstance : IMenuInstance
PrevPageOffsets.Clear();
}
public void Close()
public virtual void Close()
{
MenuManager.CloseActiveMenu(Player);
}

View File

@@ -24,14 +24,15 @@ public class CenterHtmlMenu : BaseMenu
public CenterHtmlMenu(string title) : base(ModifyTitle(title))
{
}
public override ChatMenuOption AddMenuOption(string display, Action<CCSPlayerController, ChatMenuOption> onSelect, bool disabled = false)
public override ChatMenuOption AddMenuOption(string display, Action<CCSPlayerController, ChatMenuOption> onSelect,
bool disabled = false)
{
var option = new ChatMenuOption(ModifyOptionDisplay(display), disabled, onSelect);
MenuOptions.Add(option);
return option;
}
private static string ModifyTitle(string title)
{
if (title.Length > 32)
@@ -59,14 +60,15 @@ public class CenterHtmlMenuInstance : BaseMenuInstance
{
private readonly BasePlugin _plugin;
public override int NumPerPage => 5; // one less than the actual number of items per page to avoid truncated options
protected override int MenuItemsPerPage => (Menu.ExitButton ? 0 : 1) + ((HasPrevButton && HasNextButton) ? NumPerPage - 1 : NumPerPage);
public CenterHtmlMenuInstance(BasePlugin plugin, CCSPlayerController player, IMenu menu) : base(player, menu)
{
_plugin = plugin;
RemoveOnTickListener();
plugin.RegisterListener<Core.Listeners.OnTick>(Display);
}
public override void Display()
{
if (MenuManager.GetActiveMenu(Player) != this)
@@ -74,7 +76,7 @@ public class CenterHtmlMenuInstance : BaseMenuInstance
Reset();
return;
}
var builder = new StringBuilder();
builder.Append($"<b><font color='yellow'>{Menu.Title}</font></b>");
builder.AppendLine("<br>");
@@ -88,7 +90,7 @@ public class CenterHtmlMenuInstance : BaseMenuInstance
builder.Append($"<font color='{color}'>!{keyOffset++}</font> {option.Text}");
builder.AppendLine("<br>");
}
if (HasPrevButton)
{
builder.AppendFormat("<font color='yellow'>!7</font> &#60;- Prev");
@@ -101,18 +103,21 @@ public class CenterHtmlMenuInstance : BaseMenuInstance
builder.AppendLine("<br>");
}
builder.AppendFormat("<font color='red'>!9</font> -> Close");
builder.AppendLine("<br>");
if (Menu.ExitButton)
{
builder.AppendFormat("<font color='red'>!9</font> -> Close");
builder.AppendLine("<br>");
}
var currentPageText = builder.ToString();
Player.PrintToCenterHtml(currentPageText);
}
public override void Reset()
public override void Close()
{
base.Reset();
base.Close();
RemoveOnTickListener();
// Send a blank message to clear the menu
Player.PrintToCenterHtml(" ");
}

View File

@@ -18,10 +18,11 @@ using CounterStrikeSharp.API.Modules.Utils;
namespace CounterStrikeSharp.API.Modules.Menu;
public class ChatMenu: BaseMenu
public class ChatMenu : BaseMenu
{
public ChatMenu(string title) : base(title)
{
ExitButton = false;
}
}
@@ -37,15 +38,11 @@ public class ChatMenuInstance : BaseMenuInstance
Player.PrintToChat("---");
var keyOffset = 1;
for (var i = CurrentOffset;
i < Math.Min(CurrentOffset + MenuItemsPerPage, Menu.MenuOptions.Count);
i++)
for (var i = CurrentOffset; i < Math.Min(CurrentOffset + MenuItemsPerPage, Menu.MenuOptions.Count); i++)
{
var option = Menu.MenuOptions[i];
Player.PrintToChat(
$" {(option.Disabled ? ChatColors.Grey : ChatColors.Green)} !{keyOffset++} {ChatColors.Default}{option.Text}");
Player.PrintToChat($" {(option.Disabled ? ChatColors.Grey : ChatColors.Green)} !{keyOffset++} {ChatColors.Default}{option.Text}");
}
if (HasPrevButton)
@@ -57,6 +54,11 @@ public class ChatMenuInstance : BaseMenuInstance
{
Player.PrintToChat($" {ChatColors.Yellow}!8 {ChatColors.Default}-> Next");
}
if (Menu.ExitButton)
{
Player.PrintToChat($" {ChatColors.Red}!9 {ChatColors.Default}-> Close");
}
}
}

View File

@@ -42,7 +42,7 @@ public class ConsoleMenuInstance : BaseMenuInstance
{
var option = Menu.MenuOptions[i];
Player.PrintToConsole($"{(option.Disabled ? "[Enabled]" : "[Disabled] - ")} css_{keyOffset++} {option.Text}");
Player.PrintToConsole($"{(option.Disabled ? "[Disabled] - " : "[Enabled]")} css_{keyOffset++} {option.Text}");
}
if (HasPrevButton)
@@ -54,5 +54,10 @@ public class ConsoleMenuInstance : BaseMenuInstance
{
Player.PrintToConsole("css_8 -> Next");
}
if (Menu.ExitButton)
{
Player.PrintToConsole("css_9 -> Close");
}
}
}

View File

@@ -27,6 +27,7 @@ public interface IMenu
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public bool ExitButton { get; set; }
public ChatMenuOption AddMenuOption(string display, Action<CCSPlayerController, ChatMenuOption> onSelect, bool disabled = false);
}

View File

@@ -1,4 +1,4 @@
/*
/*
* This file is part of CounterStrikeSharp.
* CounterStrikeSharp is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -75,7 +75,7 @@ namespace CounterStrikeSharp.API.Modules.Utils
{
this.X += vector.X;
this.Y += vector.Y;
this.Z = this.Z = vector.Z;
this.Z += vector.Z;
}

View File

@@ -19,6 +19,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Utils;
@@ -27,19 +28,77 @@ namespace CounterStrikeSharp.API
{
public class Server
{
public static float TickInterval => NativeAPI.GetTickInterval();
/// <summary>
/// Duration of a single game tick in seconds, based on a 64 tick server (hard coded in CS2).
/// </summary>
public static float TickInterval => 0.015625f;
/// <summary>
/// Executes a command on the server, as if it was entered from the console.
/// </summary>
/// <param name="command"></param>
public static void ExecuteCommand(string command) => NativeAPI.IssueServerCommand(command);
public static string MapName => NativeAPI.GetMapName();
// public static void PrintToConsole(string message) => NativeAPI.PrintToConsole(message);
/// <summary>
/// Returns the total time the server has been running in seconds.
/// </summary>
/// <remarks>Does not increment when server is hibernating</remarks>
public static double TickedTime => NativeAPI.GetTickedTime();
/// <summary>
/// Returns the current map time in seconds, as an interval of the server's tick interval.
/// e.g. 70.046875 would represent 70 seconds of map time and the 4483rd tick of the server (70.046875 / 0.015625).
/// </summary>
/// <remarks>Increments even when server is hibernating</remarks>
public static float CurrentTime => NativeAPI.GetCurrentTime();
/// <summary>
/// Returns the current map tick count.
/// CS2 is a 64 tick server, so the value will increment by 64 every second.
/// </summary>
public static int TickCount => NativeAPI.GetTickCount();
public static float GameFrameTime => NativeAPI.GetGameFrameTime();
/// <summary>
/// Returns the total time the server has been running in seconds.
/// </summary>
/// <remarks>Increments even when server is hibernating</remarks>
public static double EngineTime => NativeAPI.GetEngineTime();
public static void PrecacheModel(string name) => NativeAPI.PrecacheModel(name);
/// <summary>
/// <inheritdoc cref="RunOnTick"/>
/// Returns Task that completes once the synchronous task has been completed.
/// </summary>
public static Task RunOnTickAsync(int tick, Action task)
{
var functionReference = FunctionReference.Create(task, FunctionLifetime.SingleUse);
NativeAPI.QueueTaskForFrame(tick, functionReference);
return functionReference.CompletionTask;
}
/// <summary>
/// Queue a task to be executed on the specified tick.
/// See <see cref="TickCount"/> to retrieve the current tick.
/// <remarks>Does not execute if the server is hibernating.</remarks>
/// </summary>
public static void RunOnTick(int tick, Action task)
{
RunOnTickAsync(tick, task);
}
/// <summary>
/// <inheritdoc cref="NextFrame"/>
/// Returns Task that completes once the synchronous task has been completed.
/// </summary>
public static Task NextFrameAsync(Action task)
{
var functionReference = FunctionReference.Create(task, FunctionLifetime.SingleUse);
NativeAPI.QueueTaskForNextFrame(functionReference);
return functionReference.CompletionTask;
}
/// <summary>
/// Queue a task to be executed on the next game frame.
@@ -47,9 +106,18 @@ namespace CounterStrikeSharp.API
/// </summary>
public static void NextFrame(Action task)
{
var functionReference = FunctionReference.Create(task);
functionReference.Lifetime = FunctionLifetime.SingleUse;
NativeAPI.QueueTaskForNextFrame(functionReference);
NextFrameAsync(task);
}
/// <summary>
/// <inheritdoc cref="NextWorldUpdate"/>
/// Returns Task that completes once the synchronous task has been completed.
/// </summary>
public static Task NextWorldUpdateAsync(Action task)
{
var functionReference = FunctionReference.Create(task, FunctionLifetime.SingleUse);
NativeAPI.QueueTaskForNextWorldUpdate(functionReference);
return functionReference.CompletionTask;
}
/// <summary>
@@ -59,9 +127,7 @@ namespace CounterStrikeSharp.API
/// <param name="task"></param>
public static void NextWorldUpdate(Action task)
{
var functionReference = FunctionReference.Create(task);
functionReference.Lifetime = FunctionLifetime.SingleUse;
NativeAPI.QueueTaskForNextWorldUpdate(functionReference);
NextWorldUpdateAsync(task);
}
public static void PrintToChatAll(string message)

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Platforms>AnyCPU;x86</Platforms>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

@@ -51,6 +51,7 @@ bool CCoreConfig::Init(char* conf_error, int conf_error_size)
SilentChatTrigger = m_json.value("SilentChatTrigger", SilentChatTrigger);
FollowCS2ServerGuidelines = m_json.value("FollowCS2ServerGuidelines", FollowCS2ServerGuidelines);
PluginHotReloadEnabled = m_json.value("PluginHotReloadEnabled", PluginHotReloadEnabled);
PluginAutoLoadEnabled = m_json.value("PluginAutoLoadEnabled", PluginAutoLoadEnabled);
ServerLanguage = m_json.value("ServerLanguage", ServerLanguage);
} catch (const std::exception& ex) {
V_snprintf(conf_error, conf_error_size, "Failed to parse CoreConfig file: %s", ex.what());

View File

@@ -29,6 +29,7 @@ class CCoreConfig
std::vector<std::string> SilentChatTrigger = { std::string("/") };
bool FollowCS2ServerGuidelines = true;
bool PluginHotReloadEnabled = true;
bool PluginAutoLoadEnabled = true;
std::string ServerLanguage = "en";
using json = nlohmann::json;

View File

@@ -1,6 +1,7 @@
#include "mm_plugin.h"
#include "core/globals.h"
#include "core/managers/player_manager.h"
#include "core/tick_scheduler.h"
#include "iserver.h"
#include "managers/event_manager.h"
#include "scripting/callback_manager.h"
@@ -79,6 +80,7 @@ EntityManager entityManager;
ChatManager chatManager;
ServerManager serverManager;
VoiceManager voiceManager;
TickScheduler tickScheduler;
bool gameLoopInitialized = false;
GetLegacyGameEventListener_t* GetLegacyGameEventListener = nullptr;

View File

@@ -47,6 +47,7 @@ class CallbackManager;
class ConVarManager;
class PlayerManager;
class MenuManager;
class TickScheduler;
class TimerSystem;
class ChatCommands;
class HookManager;
@@ -100,6 +101,7 @@ extern ChatCommands chatCommands;
extern ChatManager chatManager;
extern ServerManager serverManager;
extern VoiceManager voiceManager;
extern TickScheduler tickScheduler;
extern HookManager hookManager;
extern SourceHook::ISourceHook *source_hook;

View File

@@ -20,6 +20,7 @@
#include "scripting/callback_manager.h"
#include "core/game_system.h"
#include <concurrentqueue.h>
SH_DECL_HOOK1_void(ISource2Server, ServerHibernationUpdate, SH_NOATTRIB, 0, bool);
SH_DECL_HOOK0_void(ISource2Server, GameServerSteamAPIActivated, SH_NOATTRIB, 0);
@@ -176,17 +177,17 @@ void ServerManager::UpdateWhenNotInGame(float flFrameTime)
void ServerManager::PreWorldUpdate(bool bSimulating)
{
std::lock_guard<std::mutex> lock(m_nextWorldUpdateTasksLock);
std::vector<std::function<void()>> out_list(1024);
if (!m_nextWorldUpdateTasks.empty()) {
CSSHARP_CORE_TRACE("Executing queued tasks of size: {0} at time {1}", m_nextWorldUpdateTasks.size(),
globals::getGlobalVars()->curtime);
auto size = m_nextWorldUpdateTasks.try_dequeue_bulk(out_list.begin(), 1024);
for (size_t i = 0; i < m_nextWorldUpdateTasks.size(); i++) {
m_nextWorldUpdateTasks[i]();
if (size > 0) {
CSSHARP_CORE_TRACE("Executing queued tasks of size: {0} at time {1}", size,
globals::getGlobalVars()->curtime);
for (size_t i = 0; i < size; i++) {
out_list[i]();
}
m_nextWorldUpdateTasks.clear();
}
auto callback = globals::serverManager.on_server_pre_world_update;
@@ -200,8 +201,7 @@ void ServerManager::PreWorldUpdate(bool bSimulating)
void ServerManager::AddTaskForNextWorldUpdate(std::function<void()>&& task)
{
std::lock_guard<std::mutex> lock(m_nextWorldUpdateTasksLock);
m_nextWorldUpdateTasks.push_back(std::forward<decltype(task)>(task));
m_nextWorldUpdateTasks.enqueue(std::forward<decltype(task)>(task));
}
void ServerManager::OnPrecacheResources(IEntityResourceManifest* pResourceManifest)

View File

@@ -19,6 +19,7 @@
#include "core/globals.h"
#include "core/global_listener.h"
#include "scripting/script_engine.h"
#include <concurrentqueue.h>
#include "core/game_system.h"
@@ -56,8 +57,7 @@ private:
ScriptCallback *on_server_precache_resources;
std::vector<std::function<void()>> m_nextWorldUpdateTasks;
std::mutex m_nextWorldUpdateTasksLock;
moodycamel::ConcurrentQueue<std::function<void()>> m_nextWorldUpdateTasks;
};
} // namespace counterstrikesharp

View File

@@ -0,0 +1,45 @@
/*
* 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 "tick_scheduler.h"
namespace counterstrikesharp {
void TickScheduler::schedule(int tick, std::function<void()> callback)
{
std::lock_guard<std::mutex> lock(taskMutex);
scheduledTasks.push(std::make_pair(tick, callback));
}
std::vector<std::function<void()>> TickScheduler::getCallbacks(int currentTick)
{
std::vector<std::function<void()>> callbacksToRun;
std::lock_guard<std::mutex> lock(taskMutex);
if (scheduledTasks.empty()) {
return callbacksToRun;
}
// Process tasks due for the current tick
while (!scheduledTasks.empty() && scheduledTasks.top().first <= currentTick) {
callbacksToRun.push_back(scheduledTasks.top().second);
scheduledTasks.pop();
}
return callbacksToRun;
}
} // namespace counterstrikesharp

44
src/core/tick_scheduler.h Normal file
View File

@@ -0,0 +1,44 @@
/*
* This file is part of CounterStrikeSharp.
* CounterStrikeSharp is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CounterStrikeSharp is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
*/
#include <functional>
#include <queue>
#include <mutex>
#include <condition_variable>
namespace counterstrikesharp {
class TickScheduler
{
public:
struct TaskComparator
{
bool operator()(const std::pair<int, std::function<void()>>& a,
const std::pair<int, std::function<void()>>& b) const
{
return a.first > b.first;
}
};
void schedule(int tick, std::function<void()> callback);
std::vector<std::function<void()>> getCallbacks(int currentTick);
private:
std::priority_queue<std::pair<int, std::function<void()>>,
std::vector<std::pair<int, std::function<void()>>>,
TaskComparator>
scheduledTasks;
std::mutex taskMutex;
};
} // namespace counterstrikesharp

View File

@@ -22,6 +22,7 @@
#include "core/gameconfig.h"
#include "core/game_system.h"
#include "core/timer_system.h"
#include "core/tick_scheduler.h"
#include "core/utils.h"
#include "core/managers/entity_manager.h"
#include "igameeventsystem.h"
@@ -32,6 +33,9 @@
#include "entity2/entitysystem.h"
#include "interfaces/cs2_interfaces.h"
#define VERSION_STRING "v" BUILD_NUMBER " @ " GITHUB_SHA
#define BUILD_TIMESTAMP __DATE__ " " __TIME__
counterstrikesharp::GlobalClass* counterstrikesharp::GlobalClass::head = nullptr;
CGameEntitySystem *GameEntitySystem()
@@ -48,6 +52,7 @@ DLL_EXPORT void InvokeNative(counterstrikesharp::fxNativeContext& context)
if (context.nativeIdentifier != counterstrikesharp::hash_string_const("QUEUE_TASK_FOR_NEXT_FRAME") &&
context.nativeIdentifier != counterstrikesharp::hash_string_const("QUEUE_TASK_FOR_NEXT_WORLD_UPDATE") &&
context.nativeIdentifier != counterstrikesharp::hash_string_const("QUEUE_TASK_FOR_FRAME") &&
counterstrikesharp::globals::gameThreadId != std::this_thread::get_id())
{
counterstrikesharp::ScriptContextRaw scriptContext(context);
@@ -193,9 +198,7 @@ void CounterStrikeSharpMMPlugin::AllPluginsLoaded()
void CounterStrikeSharpMMPlugin::AddTaskForNextFrame(std::function<void()>&& task)
{
std::lock_guard<std::mutex> lock(m_nextTasksLock);
m_nextTasks.push_back(std::forward<decltype(task)>(task));
m_nextTasks.try_enqueue(std::forward<decltype(task)>(task));
}
void CounterStrikeSharpMMPlugin::Hook_GameFrame(bool simulating, bool bFirstTick, bool bLastTick)
@@ -208,19 +211,28 @@ void CounterStrikeSharpMMPlugin::Hook_GameFrame(bool simulating, bool bFirstTick
*/
globals::timerSystem.OnGameFrame(simulating);
std::lock_guard<std::mutex> lock(m_nextTasksLock);
std::vector<std::function<void()>> out_list(1024);
if (m_nextTasks.empty())
return;
auto size = m_nextTasks.try_dequeue_bulk(out_list.begin(), 1024);
CSSHARP_CORE_TRACE("Executing queued tasks of size: {0} on tick number {1}", m_nextTasks.size(),
globals::getGlobalVars()->tickcount);
if (size > 0) {
CSSHARP_CORE_TRACE("Executing queued tasks of size: {0} on tick number {1}", size,
globals::getGlobalVars()->tickcount);
for (size_t i = 0; i < m_nextTasks.size(); i++) {
m_nextTasks[i]();
for (size_t i = 0; i < size; i++) {
out_list[i]();
}
}
m_nextTasks.clear();
auto callbacks = globals::tickScheduler.getCallbacks(globals::getGlobalVars()->tickcount);
if (callbacks.size() > 0) {
CSSHARP_CORE_TRACE("Executing frame specific tasks of size: {0} on tick number {1}", callbacks.size(),
globals::getGlobalVars()->tickcount);
for (auto& callback : callbacks) {
callback();
}
}
}
// Potentially might not work
@@ -256,9 +268,9 @@ bool CounterStrikeSharpMMPlugin::Unpause(char* error, size_t maxlen) { return tr
const char* CounterStrikeSharpMMPlugin::GetLicense() { return "GNU GPLv3"; }
const char* CounterStrikeSharpMMPlugin::GetVersion() { return "0.1.0"; }
const char* CounterStrikeSharpMMPlugin::GetVersion() { return VERSION_STRING; }
const char* CounterStrikeSharpMMPlugin::GetDate() { return __DATE__; }
const char* CounterStrikeSharpMMPlugin::GetDate() { return BUILD_TIMESTAMP; }
const char* CounterStrikeSharpMMPlugin::GetLogTag() { return "CSSHARP"; }

View File

@@ -23,6 +23,7 @@
#include <sh_vector.h>
#include <vector>
#include "entitysystem.h"
#include "concurrentqueue.h"
namespace counterstrikesharp {
class ScriptCallback;
@@ -63,8 +64,7 @@ public:
const char *GetLogTag() override;
private:
std::vector<std::function<void()>> m_nextTasks;
std::mutex m_nextTasksLock;
moodycamel::ConcurrentQueue<std::function<void()>> m_nextTasks;
};
static ScriptCallback *on_activate_callback;

View File

@@ -100,10 +100,10 @@ bool load_hostfxr()
namespace css = counterstrikesharp;
#if _WIN32
std::wstring buffer =
std::wstring(css::widen(base_dir) + L"\\dotnet\\host\\fxr\\7.0.11\\hostfxr.dll");
std::wstring(css::widen(base_dir) + L"\\dotnet\\host\\fxr\\8.0.3\\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");
std::string buffer = std::string(base_dir + "/dotnet/host/fxr/8.0.3/libhostfxr.so");
CSSHARP_CORE_INFO("Loading hostfxr from {0}", buffer.c_str());
#endif

View File

@@ -32,6 +32,7 @@
#include "core/function.h"
#include "core/managers/player_manager.h"
#include "core/managers/server_manager.h"
#include "core/tick_scheduler.h"
// clang-format on
#if _WIN32
@@ -238,6 +239,15 @@ void QueueTaskForNextWorldUpdate(ScriptContext& script_context)
globals::serverManager.AddTaskForNextWorldUpdate([func]() { reinterpret_cast<voidfunc*>(func)(); });
}
void QueueTaskForFrame(ScriptContext& script_context)
{
auto tick = script_context.GetArgument<int>(0);
auto func = script_context.GetArgument<void*>(1);
typedef void(voidfunc)(void);
globals::tickScheduler.schedule(tick, reinterpret_cast<voidfunc*>(func));
}
enum InterfaceType
{
Engine,
@@ -331,6 +341,7 @@ REGISTER_NATIVES(engine, {
ScriptEngine::RegisterNativeHandler("GET_TICKED_TIME", GetTickedTime);
ScriptEngine::RegisterNativeHandler("QUEUE_TASK_FOR_NEXT_FRAME", QueueTaskForNextFrame);
ScriptEngine::RegisterNativeHandler("QUEUE_TASK_FOR_NEXT_WORLD_UPDATE", QueueTaskForNextWorldUpdate);
ScriptEngine::RegisterNativeHandler("QUEUE_TASK_FOR_FRAME", QueueTaskForFrame);
ScriptEngine::RegisterNativeHandler("GET_VALVE_INTERFACE", GetValveInterface);
ScriptEngine::RegisterNativeHandler("GET_COMMAND_PARAM_VALUE", GetCommandParamValue);
ScriptEngine::RegisterNativeHandler("PRINT_TO_SERVER_CONSOLE", PrintToServerConsole);

View File

@@ -4,7 +4,6 @@ IS_MAP_VALID: mapname:string -> bool
GET_TICK_INTERVAL: -> float
GET_CURRENT_TIME: -> float
GET_TICK_COUNT: -> int
GET_GAME_FRAME_TIME: -> float
GET_ENGINE_TIME: -> double
GET_MAX_CLIENTS: -> int
ISSUE_SERVER_COMMAND: command:string -> void
@@ -22,6 +21,7 @@ TRACE_FILTER_PROXY_SET_SHOULD_HIT_ENTITY_CALLBACK: trace_filter:pointer, callbac
NEW_TRACE_RESULT: -> pointer
GET_TICKED_TIME: -> double
QUEUE_TASK_FOR_NEXT_FRAME: callback:func -> void
QUEUE_TASK_FOR_FRAME: tick:int, callback:func -> void
QUEUE_TASK_FOR_NEXT_WORLD_UPDATE: callback:func -> void
GET_VALVE_INTERFACE: interfaceType:int, interfaceName:string -> pointer
GET_COMMAND_PARAM_VALUE: param:string, dataType:DataType_t, defaultValue:any -> any

View File

@@ -63,7 +63,7 @@ static void FireEvent(ScriptContext &script_context) {
}
static void FireEventToClient(ScriptContext& script_context) {
static void FireEventToClient(ScriptContext& script_context) {
auto game_event = script_context.GetArgument<IGameEvent*>(0);
int entityIndex = script_context.GetArgument<int>(1);
if (!game_event) {
@@ -76,7 +76,17 @@ static void FireEvent(ScriptContext &script_context) {
}
pListener->FireGameEvent(game_event);
}
}
static void FreeEvent(ScriptContext& script_context) {
auto game_event = script_context.GetArgument<IGameEvent*>(0);
if (!game_event) {
script_context.ThrowNativeError("Invalid game event");
}
globals::gameEventManager->FreeEvent(game_event);
managed_game_events.erase(std::remove(managed_game_events.begin(), managed_game_events.end(), game_event), managed_game_events.end());
}
static const char *GetEventName(ScriptContext &script_context) {
IGameEvent *game_event = script_context.GetArgument<IGameEvent *>(0);
@@ -263,6 +273,7 @@ REGISTER_NATIVES(events, {
ScriptEngine::RegisterNativeHandler("HOOK_EVENT", HookEvent);
ScriptEngine::RegisterNativeHandler("UNHOOK_EVENT", UnhookEvent);
ScriptEngine::RegisterNativeHandler("CREATE_EVENT", CreateEvent);
ScriptEngine::RegisterNativeHandler("FREE_EVENT", FreeEvent);
ScriptEngine::RegisterNativeHandler("FIRE_EVENT", FireEvent);
ScriptEngine::RegisterNativeHandler("FIRE_EVENT_TO_CLIENT", FireEventToClient);

View File

@@ -1,6 +1,7 @@
HOOK_EVENT: name:string, callback:func, isPost:bool -> void
UNHOOK_EVENT: name:string, callback:func, isPost:bool -> void
CREATE_EVENT: name:string, force:bool -> pointer
FREE_EVENT: gameEvent:pointer -> void
FIRE_EVENT: gameEvent:pointer, dontBroadcast:bool -> void
FIRE_EVENT_TO_CLIENT: gameEvent:pointer, clientIndex:int -> void
GET_EVENT_NAME: gameEvent:pointer -> string

View File

@@ -159,6 +159,13 @@ void* GetNetworkVectorElementAt(ScriptContext& script_context)
return &vec->Element(index);
}
void RemoveAllNetworkVectorElements(ScriptContext& script_context)
{
auto vec = script_context.GetArgument<CUtlVector<CEntityHandle>*>(0);
vec->RemoveAll();
}
REGISTER_NATIVES(memory, {
ScriptEngine::RegisterNativeHandler("CREATE_VIRTUAL_FUNCTION", CreateVirtualFunction);
ScriptEngine::RegisterNativeHandler("CREATE_VIRTUAL_FUNCTION_BY_SIGNATURE",
@@ -169,5 +176,6 @@ REGISTER_NATIVES(memory, {
ScriptEngine::RegisterNativeHandler("FIND_SIGNATURE", FindSignatureNative);
ScriptEngine::RegisterNativeHandler("GET_NETWORK_VECTOR_SIZE", GetNetworkVectorSize);
ScriptEngine::RegisterNativeHandler("GET_NETWORK_VECTOR_ELEMENT_AT", GetNetworkVectorElementAt);
ScriptEngine::RegisterNativeHandler("REMOVE_ALL_NETWORK_VECTOR_ELEMENTS", RemoveAllNetworkVectorElements);
})
} // namespace counterstrikesharp

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>