mirror of
https://github.com/roflmuffin/CounterStrikeSharp.git
synced 2025-12-06 08:03:12 -08:00
Compare commits
64 Commits
v110
...
docs/menu-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39b476ccad | ||
|
|
5695c3f922 | ||
|
|
9071d51ecd | ||
|
|
0a32962f4a | ||
|
|
271705b377 | ||
|
|
cdcddbb5f3 | ||
|
|
91f51d0c5c | ||
|
|
e97f804294 | ||
|
|
4f805b18e2 | ||
|
|
e1f9b5635e | ||
|
|
59bff4f500 | ||
|
|
a2581d8e91 | ||
|
|
e7d190a6f7 | ||
|
|
5513d5710a | ||
|
|
e5c223699c | ||
|
|
fa37c222d9 | ||
|
|
3b633fafc7 | ||
|
|
765c56a38a | ||
|
|
204850fb55 | ||
|
|
bac31b9190 | ||
|
|
289f95a6b7 | ||
|
|
7b45a884d4 | ||
|
|
6ea6d0a22d | ||
|
|
12523455c0 | ||
|
|
db63fdc00c | ||
|
|
57747f2e1c | ||
|
|
66b5f77a2d | ||
|
|
8dbcb6d531 | ||
|
|
2f0d34b271 | ||
|
|
2a59544fbc | ||
|
|
f80f2ae949 | ||
|
|
f68a0abc61 | ||
|
|
a07dd9d7d4 | ||
|
|
d527038fba | ||
|
|
ca85922270 | ||
|
|
b837479f98 | ||
|
|
1e42f72655 | ||
|
|
1ad1828e30 | ||
|
|
563a5d7b3a | ||
|
|
983b673b4c | ||
|
|
74fd0e0832 | ||
|
|
44e3f2240c | ||
|
|
8af219e7a8 | ||
|
|
bff04e7795 | ||
|
|
d495ac6230 | ||
|
|
f78abf0c81 | ||
|
|
bcacc42d0e | ||
|
|
8235d5e728 | ||
|
|
56739356d5 | ||
|
|
aaba87551d | ||
|
|
a3466dd5d1 | ||
|
|
c8604760f2 | ||
|
|
d50a945317 | ||
|
|
55396e005c | ||
|
|
98b2b01992 | ||
|
|
a537be89e4 | ||
|
|
c07d5d2aa9 | ||
|
|
1cc95555fe | ||
|
|
378c28dfd0 | ||
|
|
c7343c3b7a | ||
|
|
62f6b09f50 | ||
|
|
2a15a8de71 | ||
|
|
1d6bee02cd | ||
|
|
72e1f22e14 |
26
.github/workflows/cmake-single-platform.yml
vendored
26
.github/workflows/cmake-single-platform.yml
vendored
@@ -117,7 +117,21 @@ jobs:
|
||||
with:
|
||||
dotnet-version: '7.0.x'
|
||||
|
||||
- run: |
|
||||
- name: Install dependencies
|
||||
run: dotnet restore managed/CounterStrikeSharp.sln
|
||||
|
||||
- name: Run tests
|
||||
run: dotnet test --logger trx --results-directory "TestResults-${{ env.GITHUB_SHA_SHORT }}" managed/CounterStrikeSharp.API.Tests/CounterStrikeSharp.API.Tests.csproj
|
||||
|
||||
- name: Upload dotnet test results
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: test-results-${{ env.GITHUB_SHA_SHORT }}
|
||||
path: TestResults-${{ env.GITHUB_SHA_SHORT }}
|
||||
if: ${{ always() }}
|
||||
|
||||
- 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
|
||||
|
||||
@@ -182,6 +196,7 @@ jobs:
|
||||
(cd build/windows && zip -qq -r ../../counterstrikesharp-with-runtime-build-${{ needs.build_managed.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 }}
|
||||
@@ -194,4 +209,11 @@ jobs:
|
||||
- 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.build_managed.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 }}"
|
||||
@@ -22,6 +22,22 @@ saul/demofile-net, https://github.com/saul/demofile-net/blob/main/LICENSE:
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
neverlosecc/source2gen, https://github.com/neverlosecc/source2gen
|
||||
source2gen - Source2 games SDK generator
|
||||
Copyright 2023 neverlosecc
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Source2ZE/CS2Fixes:
|
||||
alliedmodders/sourcemod:
|
||||
Source-Python-Dev-Team/Source.Python:
|
||||
|
||||
@@ -18,6 +18,7 @@ SET(SOURCE_FILES
|
||||
src/mm_plugin.h
|
||||
libraries/hl2sdk-cs2/tier1/convar.cpp
|
||||
libraries/hl2sdk-cs2/tier1/generichash.cpp
|
||||
libraries/hl2sdk-cs2/entity2/entityidentity.cpp
|
||||
libraries/hl2sdk-cs2/entity2/entitysystem.cpp
|
||||
libraries/dotnet/hostfxr.h
|
||||
libraries/dotnet/coreclr_delegates.h
|
||||
@@ -75,6 +76,7 @@ SET(SOURCE_FILES
|
||||
src/scripting/natives/natives_memory.cpp
|
||||
src/scripting/natives/natives_schema.cpp
|
||||
src/scripting/natives/natives_entities.cpp
|
||||
src/scripting/natives/natives_voice.cpp
|
||||
src/core/managers/entity_manager.cpp
|
||||
src/core/managers/entity_manager.h
|
||||
src/core/managers/chat_manager.cpp
|
||||
@@ -83,7 +85,9 @@ SET(SOURCE_FILES
|
||||
src/core/managers/server_manager.h
|
||||
src/scripting/natives/natives_server.cpp
|
||||
libraries/nlohmann/json.hpp
|
||||
src/scripting/natives/natives_dynamichooks.cpp
|
||||
src/core/managers/voice_manager.cpp
|
||||
src/core/managers/voice_manager.h
|
||||
src/scripting/natives/natives_dynamichooks.cpp
|
||||
)
|
||||
|
||||
|
||||
|
||||
1
CODEOWNERS
Normal file
1
CODEOWNERS
Normal file
@@ -0,0 +1 @@
|
||||
* @roflmuffin
|
||||
13
README.md
13
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||
CounterStrikeSharp is a server side modding framework for Counter-Strike: Global Offensive. This project attempts to implement a .NET Core scripting layer on top of a Metamod Source Plugin, allowing developers to create plugins that interact with the game server in a modern language (C#) to facilitate the creation of maintainable and testable code.
|
||||
|
||||
[Come and join our Discord](https://discord.gg/X7r3PmuYKq)
|
||||
[Come and join our Discord](https://discord.gg/eAZU3guKWU)
|
||||
|
||||
## History
|
||||
|
||||
@@ -14,7 +14,7 @@ Due to the architectural changes of CS2, the plugin is being rebuilt on the grou
|
||||
|
||||
Download the latest build from [here](https://github.com/roflmuffin/CounterStrikeSharp/releases). (Download the with runtime version if this is your first time installing).
|
||||
|
||||
Detailed installation instructions can be found in the [docs](https://docs.cssharp.dev/guides/getting-started/).
|
||||
Detailed installation instructions can be found in the [docs](https://docs.cssharp.dev/docs/guides/getting-started.html).
|
||||
|
||||
## What works?
|
||||
|
||||
@@ -39,11 +39,11 @@ These features are the core of the platform and work pretty well/have a low risk
|
||||
|
||||
## Links
|
||||
|
||||
- [Join the Discord](https://discord.gg/X7r3PmuYKq): Ask questions, provide suggestions
|
||||
- [Join the Discord](https://discord.gg/eAZU3guKWU): Ask questions, provide suggestions
|
||||
- [Read the docs](https://docs.cssharp.dev/): Getting started guide, hello world plugin example
|
||||
- [Issue tracker](https://github.com/roflmuffin/CounterStrikeSharp/issues): Raise any issues here
|
||||
- [Builds](https://github.com/roflmuffin/CounterStrikeSharp/actions): Download latest unstable dev snapshot
|
||||
- [Install Docs](https://docs.cssharp.dev/guides/getting-started/): Installation instructions
|
||||
- [Install Docs](https://docs.cssharp.dev/docs/guides/getting-started.html): Installation instructions
|
||||
- [Example Plugin](managed/TestPlugin/TestPlugin.cs): Test plugin with basic functionality
|
||||
|
||||
## Examples
|
||||
@@ -91,8 +91,8 @@ public class HelloWorldPlugin : BasePlugin
|
||||
|
||||
## Credits
|
||||
|
||||
A lot of code has been borrowed from SourceMod as well as Source.Python, two pioneering source engine plugin frameworks which this project lends a lot of its credit to.
|
||||
I've also used the scripting context & native system that is implemented in FiveM for GTA5. Also shoutout to the [CS2Fixes](https://github.com/Source2ZE/CS2Fixes) project for providing good reverse-engineering information so shortly after CS2 release.
|
||||
A lot of code has been borrowed from [SourceMod](https://github.com/alliedmodders/sourcemod) as well as [Source.Python](https://github.com/Source-Python-Dev-Team/Source.Python), two pioneering source engine plugin frameworks which this project lends a lot of its credit to.
|
||||
I've also used the scripting context & native system that is implemented in [FiveM](https://github.com/citizenfx/fivem) for GTA5. Also shoutout to the [CS2Fixes](https://github.com/Source2ZE/CS2Fixes) project for providing good reverse-engineering information so shortly after CS2 release.
|
||||
|
||||
## How to Build
|
||||
|
||||
@@ -128,3 +128,4 @@ Build
|
||||
```bash
|
||||
cmake --build . --config Debug
|
||||
```
|
||||
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
"PublicChatTrigger": [ "!" ],
|
||||
"SilentChatTrigger": [ "/" ],
|
||||
"FollowCS2ServerGuidelines": true,
|
||||
"PluginHotReloadEnabled": true
|
||||
"PluginHotReloadEnabled": true,
|
||||
"ServerLanguage": "en"
|
||||
}
|
||||
@@ -28,8 +28,8 @@
|
||||
},
|
||||
"CCSPlayerController_Respawn": {
|
||||
"offsets": {
|
||||
"windows": 241,
|
||||
"linux": 243
|
||||
"windows": 242,
|
||||
"linux": 244
|
||||
}
|
||||
},
|
||||
"CCSPlayerPawn_Respawn": {
|
||||
@@ -74,6 +74,13 @@
|
||||
"linux": "\\x55\\x48\\x89\\xF2\\x48\\x89\\xE5\\x41\\x54\\x49\\x89\\xFC\\x48\\x8D\\x2A\\x2A\\x48\\x83\\x2A\\x2A\\x2A\\x8D\\x05\\x2A\\x2A\\x2A\\x00\\x48\\x8B\\x30\\x48\\x8B\\x06\\xFF\\x2A\\x2A\\x48\\x8B\\x45\\x2A\\x48\\x8D\\x2A\\x2A\\x4C\\x89\\x2A\\x48\\x89\\x45\\x2A\\x2A\\x68\\xFC"
|
||||
}
|
||||
},
|
||||
"CCSPlayer_WeaponServices_CanUse": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x48\\x89\\x5C\\x24\\x10\\x48\\x89\\x6C\\x24\\x18\\x56\\x57\\x41\\x56\\x48\\x83\\xEC\\x30\\x80\\xB9\\xA8\\x00\\x00\\x00\\x00",
|
||||
"linux": "\\x48\\x85\\xF6\\x0F\\x84\\x2A\\x2A\\x2A\\x2A\\x55\\x31\\xC9\\x48\\x89\\xE5\\x41\\x55\\x49\\x89\\xFD"
|
||||
}
|
||||
},
|
||||
"CCSPlayer_ItemServices_DropActivePlayerWeapon": {
|
||||
"offsets": {
|
||||
"windows": 18,
|
||||
@@ -82,7 +89,7 @@
|
||||
},
|
||||
"CCSPlayer_ItemServices_RemoveWeapons": {
|
||||
"offsets": {
|
||||
"windows": 21,
|
||||
"windows": 19,
|
||||
"linux": 20
|
||||
}
|
||||
},
|
||||
@@ -113,6 +120,13 @@
|
||||
"linux": "\\x48\\x85\\xFF\\x74\\x4B\\x55\\x48\\x89\\xE5\\x41\\x56"
|
||||
}
|
||||
},
|
||||
"CEntityInstance_AcceptInput": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x48\\x89\\x5C\\x24\\x10\\x48\\x89\\x74\\x24\\x18\\x57\\x48\\x83\\xEC\\x40\\x49\\x8B\\xF0",
|
||||
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x49\\x89\\xFF\\x41\\x56\\x48\\x8D\\x7D\\xC0"
|
||||
}
|
||||
},
|
||||
"LegacyGameEventListener": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
@@ -122,14 +136,21 @@
|
||||
},
|
||||
"CBasePlayerPawn_CommitSuicide": {
|
||||
"offsets": {
|
||||
"windows": 356,
|
||||
"linux": 356
|
||||
"windows": 357,
|
||||
"linux": 357
|
||||
}
|
||||
},
|
||||
"CBasePlayerPawn_RemovePlayerItem": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"linux": "\\x55\\x48\\x89\\x2A\\x41\\x2A\\x49\\x89\\x2A\\x41\\x2A\\x49\\x89\\x2A\\xE8\\x2A\\x2A\\x2A\\x2A\\x49\\x39",
|
||||
"windows": "\\x48\\x85\\xD2\\x0F\\x84\\x2A\\x2A\\x2A\\x2A\\x48\\x89\\x5C\\x24\\x08\\x57\\x48\\x83\\xEC\\x30\\x48\\x8B\\xDA"
|
||||
}
|
||||
},
|
||||
"CBaseEntity_Teleport": {
|
||||
"offsets": {
|
||||
"windows": 148,
|
||||
"linux": 147
|
||||
"windows": 149,
|
||||
"linux": 148
|
||||
}
|
||||
},
|
||||
"CBaseEntity_TakeDamageOld": {
|
||||
@@ -153,6 +174,20 @@
|
||||
"linux": "\\x55\\xBA\\xFF\\xFF\\xFF\\xFF\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x41\\x55\\x49"
|
||||
}
|
||||
},
|
||||
"StateChanged": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x40\\x55\\x53\\x56\\x41\\x55\\x41\\x57\\x48\\x8D\\x6C\\x24\\xB0",
|
||||
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x41\\x55\\x41\\x54\\x53\\x89\\xD3"
|
||||
}
|
||||
},
|
||||
"NetworkStateChanged": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x4C\\x8B\\xC9\\x48\\x8B\\x09\\x48\\x85\\xC9\\x74\\x2A\\x48\\x8B\\x41\\x10",
|
||||
"linux": "\\x4C\\x8B\\x07\\x4D\\x85\\xC0\\x74\\x2A\\x49\\x8B\\x40\\x10"
|
||||
}
|
||||
},
|
||||
"GameEntitySystem": {
|
||||
"offsets": {
|
||||
"windows": 88,
|
||||
@@ -164,5 +199,12 @@
|
||||
"windows": 91,
|
||||
"linux": 91
|
||||
}
|
||||
},
|
||||
"CEntityIOOutput_FireOutputInternal": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x48\\x8B\\xC4\\x4C\\x89\\x48\\x20\\x55\\x57\\x41\\x54\\x41\\x56",
|
||||
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x41\\x55\\x41\\x54\\x53\\x48\\x83\\xEC\\x58\\x4C\\x8B\\x6F\\x08"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,23 +5,52 @@ description: How to get started installing & using CounterStrikeSharp.
|
||||
|
||||
# Getting Started
|
||||
|
||||
How to get started installing & using CounterStrikeSharp.
|
||||
In this guide you will learn how to install CounterStrikeSharp onto your vanilla Counter-Strike 2 server. `CounterStrikeSharp` uses `Metamod:Source` as its main way of communicating with the game server, so both frameworks will need to be installed.
|
||||
|
||||
If you're more of a visual person, here is a <a href="https://www.youtube.com/watch?v=FlsKzStHJuY" target="_blank">Youtube video</a> that covers everything.
|
||||
|
||||
## Prerequisites
|
||||
- <a href="https://www.sourcemm.net/downloads.php/?branch=master" target="_blank">Metamod: Source 2.X Dev Build</a>
|
||||
- <a href="https://github.com/roflmuffin/CounterStrikeSharp/releases" target="_blank">CounterStrikeSharp With Runtime</a>
|
||||
|
||||
## Installing Metamod
|
||||
|
||||
`CounterStrikeSharp` uses `Metamod:Source` as its main way of communicating with the game server. To install it, you can follow the detailed instructions found <a href="https://cs2.poggu.me/metamod/installation/" target="_blank">here</a>.
|
||||
1. Extract Metamod and copy the `/addons/` directory to `/game/csgo/`.
|
||||
2. Inside `/game/csgo/`, locate `gameinfo.gi`.
|
||||
3. Create a new line underneath `Game_LowViolence csgo_lv` and add `Game csgo/addons/metamod`.
|
||||
4. Restart your game server.
|
||||
|
||||
Your `gameinfo.gi` should look like <a href="../../images/gameinfogi-example.png" target="_blank">this</a>. Type `meta list` in your server console to see if Metamod is loaded.
|
||||
|
||||
## Installing CounterStrikeSharp
|
||||
|
||||
Download the latest release of CounterStrikeSharp from <a href="https://github.com/roflmuffin/CounterStrikeSharp/actions/workflows/cmake-single-platform.yml" target="_blank">GitHub actions build pages</a> (just choose the latest development snapshot). **You may need to be logged into GitHub to gain access to the downloads**.
|
||||
1. Extract CounterStrikeSharp and copy the `/addons/` directory to `/game/csgo/`.
|
||||
2. Restart your game server.
|
||||
|
||||
Running the command `meta list` in the console should show 1 plugin loaded 🎉
|
||||
|
||||
```shell
|
||||
meta list
|
||||
Listing 1 plugin:
|
||||
[01] CounterStrikeSharp (0.1.0) by Roflmuffin
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> If this is your first time installing, you will need to download the `with-runtime` version. This includes a copy of the .NET runtime, which is required to run the plugin.
|
||||
> Depending on the os you might also either need to install `libicu` / `icu-libs` / `libicu-dev` using your package manager for .NET to run or setting `DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true` in your servers environment variables. You can find more infos about that <a href="https://github.com/dotnet/runtime/blob/main/docs/design/features/globalization-invariant-mode.md#enabling-the-invariant-mode" target="_blank">here</a>
|
||||
>
|
||||
> Subsequent upgrades will not require the runtime, unless a version bump of the .NET runtime is required (i.e. from 7.0.x to 8.0.x). We will inform you when this occurs.
|
||||
> For Windows servers, you must have <a href="https://aka.ms/vs/17/release/vc_redist.x64.exe" target="_blank">Visual Studio Redistributables</a> installed otherwise CounterStrikeSharp will not work.
|
||||
|
||||
Extract the `addons` folder to the `/csgo/` directory of the dedicated server. The contents of your addons folder should contain both the `counterstrikesharp` folder and the `metamod` folder as seen below.
|
||||
## Upgrading CounterStrikeSharp
|
||||
|
||||
To upgrade CounterStrikeSharp you simply need to download the latest release and copy it to your server, the same as the original installation.
|
||||
|
||||
CounterStrikeSharp is designed in a way where your configuration files will not be overwritten if you do this. As CounterStrikeSharp is already installed, you may download the non `with-runtime` build, but you will need to ensure your .NET runtime is up-to-date yourself.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- If this is your first time installing, you **MUST** download the `with-runtime` version. This includes a copy of the .NET runtime, which is required to run the plugin.
|
||||
- Depending on your OS you might also either need to install `libicu` / `icu-libs` / `libicu-dev` using your package manager for .NET to run.
|
||||
- If you get `Unknown Command` when typing `meta list` into your console, double-check the folders are copied over correctly and that your `gameinfo.gi` file is correctly modified.
|
||||
|
||||
Your folder structure should look like this:
|
||||
|
||||
```shell
|
||||
<server_path>/game/csgo/addons > tree -L 2
|
||||
@@ -41,15 +70,3 @@ addons
|
||||
├── metamod.vdf
|
||||
└── metamod_x64.vdf
|
||||
```
|
||||
|
||||
## Start the Server
|
||||
|
||||
Launch your CS2 dedicated server as normal. If everything is working correctly, you should see a message in the console that says `CSSharp: CounterStrikeSharp.API Loaded Successfully.`.
|
||||
|
||||
Running the command `meta list` in the console should show 1 plugin loaded 🎉
|
||||
|
||||
```shell
|
||||
meta list
|
||||
Listing 1 plugin:
|
||||
[01] CounterStrikeSharp (0.1.0) by Roflmuffin
|
||||
```
|
||||
@@ -32,3 +32,7 @@ receive a ban.
|
||||
## PluginHotReloadEnabled
|
||||
|
||||
When enabled, plugins are automatically reloaded when their .dll file is updated.
|
||||
|
||||
## 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".
|
||||
5
docfx/examples/WithEntityOutputHooks.md
Normal file
5
docfx/examples/WithEntityOutputHooks.md
Normal file
@@ -0,0 +1,5 @@
|
||||
[!INCLUDE [WithEntityOutputHooks](../../examples/WithEntityOutputHooks/README.md)]
|
||||
|
||||
<a href="https://github.com/roflmuffin/CounterStrikeSharp/tree/main/examples/WithEntityOutputHooks" class="btn btn-secondary">View project on Github <i class="bi bi-github"></i></a>
|
||||
|
||||
[!code-csharp[](../../examples/WithEntityOutputHooks/WithEntityOutputHooksPlugin.cs)]
|
||||
5
docfx/examples/WithTranslations.md
Normal file
5
docfx/examples/WithTranslations.md
Normal file
@@ -0,0 +1,5 @@
|
||||
[!INCLUDE [WithTranslations](../../examples/WithTranslations/README.md)]
|
||||
|
||||
<a href="https://github.com/roflmuffin/CounterStrikeSharp/tree/main/examples/WithTranslations" class="btn btn-secondary">View project on Github <i class="bi bi-github"></i></a>
|
||||
|
||||
[!code-csharp[](../../examples/WithTranslations/WithTranslationsPlugin.cs)]
|
||||
5
docfx/examples/WithVoiceOverrides.md
Normal file
5
docfx/examples/WithVoiceOverrides.md
Normal file
@@ -0,0 +1,5 @@
|
||||
[!INCLUDE [WithVoiceOverrides](../../examples/WithVoiceOverrides/README.md)]
|
||||
|
||||
<a href="https://github.com/roflmuffin/CounterStrikeSharp/tree/main/examples/WithVoiceOverrides" class="btn btn-secondary">View project on Github <i class="bi bi-github"></i></a>
|
||||
|
||||
[!code-csharp[](../../examples/WithVoiceOverrides/WithVoiceOverridesPlugin.cs)]
|
||||
@@ -7,9 +7,15 @@ items:
|
||||
href: WithConfig.md
|
||||
- name: Dependency Injection
|
||||
href: WithDependencyInjection.md
|
||||
- name: Entity Output Hooks
|
||||
href: WithEntityOutputHooks.md
|
||||
- name: Game Event Handlers
|
||||
href: WithGameEventHandlers.md
|
||||
- name: Database (Dapper)
|
||||
href: WithDatabase.md
|
||||
- name: Translations
|
||||
href: WithTranslations.md
|
||||
- name: Voice Overrides
|
||||
href: WithVoiceOverrides.md
|
||||
- name: Warcraft Plugin
|
||||
href: WarcraftPlugin.md
|
||||
|
||||
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="50" height="50" viewBox="0 0 13.229 13.229"><path d="M9.91 0 4.881.039c-.545.004-1.073.02-1.577.057A6.957 6.957 0 0 0 .403.934c-.233.124-.403.338-.403.6a.652.652 0 0 0 .98.563l.004-.002a5.434 5.434 0 0 1 2.098-.66c2.123-.227 3.599.534 4.595 1.428 1.095.982 2.108 2.982 1.74 5.175-.22 1.313-.826 2.42-1.77 3.28l-.002.002c-.171.156-.312.337-.312.568a.674.674 0 0 0 1.1.52c.027-.02.058-.049.073-.063a7.327 7.327 0 0 0 1.76-2.566l2.679-6.357c.126-.287.232-.583.265-.871.142-1.26-.531-1.985-1.383-2.32-.436-.172-1.05-.224-1.72-.23L9.91 0Zm.725.802c.476.02.94.363.88.99-.073.758-.83 1.287-1.16 1.785h1.16c.07.102.07.433 0 .535-.583-.057-1.233-.047-1.874-.045-.274-1.001 1.42-1.674 1.249-2.498-.081-.389-.636-.4-.536.179h-.669c-.012-.663.475-.966.95-.946Z" style="display:inline;opacity:1;fill:#ff6000;fill-opacity:1;stroke-width:.0470931" transform="translate(0 .334)"/><g style="display:inline"><path fill="#a179dc" d="M7.52 5.702a.743.743 0 0 0-.09-.374.715.715 0 0 0-.272-.264C6.16 4.489 5.163 3.916 4.167 3.34a.734.734 0 0 0-.796.008C2.974 3.583.987 4.72.395 5.064a.692.692 0 0 0-.363.638V9.17c0 .139.03.26.088.367.06.109.15.2.275.27.592.344 2.579 1.482 2.976 1.717a.735.735 0 0 0 .796.007c.996-.575 1.995-1.148 2.992-1.723a.713.713 0 0 0 .275-.271.747.747 0 0 0 .087-.367V5.702"/><path fill="#280068" d="M3.788 7.424.12 9.538c.06.109.15.2.275.27.592.344 2.579 1.482 2.976 1.717a.735.735 0 0 0 .796.007c.996-.575 1.995-1.148 2.992-1.723a.713.713 0 0 0 .275-.271L3.788 7.424"/><path fill="#390091" d="M7.52 5.702a.743.743 0 0 0-.09-.374L3.787 7.425l3.646 2.112a.748.748 0 0 0 .087-.367V5.702"/><path fill="#fff" d="M5.948 6.635v.395h.395v-.395h.197v.395h.395v.197H6.54v.395h.395v.198H6.54v.394h-.197V7.82h-.395v.394H5.75V7.82h-.395v-.198h.395v-.395h-.395V7.03h.395v-.395Zm.395.592h-.395v.395h.395z"/><path fill="#fff" d="M3.796 4.652c1.03 0 1.93.56 2.41 1.39l-.004-.007-1.212.698a1.385 1.385 0 0 0-1.178-.683h-.016a1.386 1.386 0 1 0 1.208 2.066l-.006.01 1.21.7a2.783 2.783 0 0 1-2.38 1.394h-.032a2.784 2.784 0 1 1 0-5.568z"/></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" fill="none"><path fill="#F89A0F" d="m37.456 1.262-19.008.148a91.03 91.03 0 0 0-5.96.215A26.294 26.294 0 0 0 1.523 4.793C.643 5.26 0 6.07 0 7.06a2.464 2.464 0 0 0 3.704 2.128l.015-.007a20.538 20.538 0 0 1 7.93-2.495c8.024-.858 13.602 2.018 17.367 5.397 4.138 3.712 7.967 11.271 6.576 19.56-.831 4.962-3.122 9.146-6.69 12.397l-.007.007c-.646.59-1.18 1.274-1.18 2.147a2.547 2.547 0 0 0 4.158 1.965c.102-.075.22-.185.276-.238a27.692 27.692 0 0 0 6.652-9.698l10.126-24.027c.476-1.085.877-2.203 1.001-3.292.537-4.762-2.007-7.502-5.227-8.769-1.648-.65-3.968-.846-6.5-.869l-.745-.004Zm2.74 3.032c1.799.075 3.553 1.372 3.326 3.741-.276 2.865-3.137 4.865-4.384 6.747h4.384c.264.385.264 1.637 0 2.022-2.204-.215-4.66-.178-7.083-.17-1.036-3.783 5.367-6.327 4.72-9.441-.306-1.47-2.403-1.512-2.025.676h-2.529c-.045-2.506 1.796-3.651 3.59-3.575Z"/><path fill="#A079DF" d="M28.422 21.551a2.807 2.807 0 0 0-.34-1.413 2.7 2.7 0 0 0-1.028-.998c-3.772-2.174-7.54-4.34-11.304-6.516a2.774 2.774 0 0 0-3.01.03c-1.5.888-9.01 5.186-11.247 6.486A2.616 2.616 0 0 0 .12 21.55V34.66c0 .525.113.982.333 1.387a2.6 2.6 0 0 0 1.039 1.02c2.237 1.3 9.748 5.602 11.248 6.49a2.778 2.778 0 0 0 3.008.026c3.765-2.173 7.54-4.339 11.309-6.512.434-.24.794-.594 1.04-1.024.226-.427.339-.904.328-1.387l-.004-13.108Z"/><path fill="#270068" d="M14.317 28.06.454 36.05a2.6 2.6 0 0 0 1.039 1.02c2.237 1.3 9.748 5.601 11.248 6.49a2.778 2.778 0 0 0 3.008.026c3.765-2.173 7.54-4.339 11.309-6.512.434-.24.794-.594 1.04-1.024l-13.781-7.99Z"/><path fill="#390091" d="M28.422 21.551a2.807 2.807 0 0 0-.34-1.413l-13.769 7.925 13.78 7.983c.227-.427.34-.904.33-1.387V21.55Z"/><path fill="#fff" d="M22.48 25.078v1.492h1.494v-1.492h.744v1.492h1.493v.745h-1.493v1.493h1.493v.748h-1.493v1.49h-.744v-1.49H22.48v1.49h-.748v-1.49H20.24v-.748h1.493v-1.493H20.24v-.745h1.493v-1.492h.748Zm1.494 2.237H22.48v1.493h1.493v-1.493Z"/><path fill="#fff" d="M14.347 17.583c3.893 0 7.295 2.116 9.109 5.253l-.015-.026-4.58 2.638a5.236 5.236 0 0 0-4.453-2.582h-.06a5.238 5.238 0 1 0 4.565 7.809l-.023.038 4.574 2.645a10.519 10.519 0 0 1-8.996 5.27h-.12a10.522 10.522 0 1 1 0-21.045Z"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
BIN
docfx/images/gameinfogi-example.png
Normal file
BIN
docfx/images/gameinfogi-example.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 275 KiB |
@@ -14,6 +14,7 @@ description: Write Counter-Strike 2 server plugins in C#.
|
||||
<span>CounterStrikeSharp is a simpler way to write CS2 server plugins.</span>
|
||||
<div>
|
||||
<a href="docs/guides/getting-started.md" class="btn btn-primary btn-lg fw-bold my-5">Get Started <i class="bi bi-arrow-right"></a>
|
||||
<a href="https://github.com/roflmuffin/CounterStrikeSharp/releases/latest" class="btn btn-secondary btn-lg fw-bold my-5">Download <i class="bi bi-download"></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ export default {
|
||||
},
|
||||
{
|
||||
icon: "discord",
|
||||
href: "https://discord.gg/X7r3PmuYKq",
|
||||
href: "https://discord.gg/eAZU3guKWU",
|
||||
title: "Discord",
|
||||
},
|
||||
],
|
||||
|
||||
2
examples/WithEntityOutputHooks/README.md
Normal file
2
examples/WithEntityOutputHooks/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# With Entity Output Hooks
|
||||
This example shows how to implement hooks for entity output, such as StartTouch, OnPickup etc.
|
||||
12
examples/WithEntityOutputHooks/WithEntityOutputHooks.csproj
Normal file
12
examples/WithEntityOutputHooks/WithEntityOutputHooks.csproj
Normal file
@@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\managed\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,43 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Core.Attributes.Registration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace WithEntityOutputHooks;
|
||||
|
||||
[MinimumApiVersion(80)]
|
||||
public class WithEntityOutputHooksPlugin : BasePlugin
|
||||
{
|
||||
public override string ModuleName => "Example: With Entity Output Hooks";
|
||||
public override string ModuleVersion => "1.0.0";
|
||||
public override string ModuleAuthor => "CounterStrikeSharp & Contributors";
|
||||
public override string ModuleDescription => "A simple plugin that showcases entity output hooks";
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
HookEntityOutput("weapon_knife", "OnPlayerPickup", (CEntityIOOutput output, string name, CEntityInstance activator, CEntityInstance caller, CVariant value, float delay) =>
|
||||
{
|
||||
Logger.LogInformation("weapon_knife called OnPlayerPickup ({name}, {activator}, {caller}, {delay})", name, activator.DesignerName, caller.DesignerName, delay);
|
||||
|
||||
return HookResult.Continue;
|
||||
});
|
||||
}
|
||||
|
||||
// Output hooks can use wildcards to match multiple entities
|
||||
[EntityOutputHook("*", "OnPlayerPickup")]
|
||||
public HookResult OnPickup(CEntityIOOutput output, string name, CEntityInstance activator, CEntityInstance caller, CVariant value, float delay)
|
||||
{
|
||||
Logger.LogInformation("[EntityOutputHook Attribute] Called OnPlayerPickup ({name}, {activator}, {caller}, {delay})", name, activator.DesignerName, caller.DesignerName, delay);
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
// Output hooks can use wildcards to match multiple output names
|
||||
[EntityOutputHook("func_buyzone", "*")]
|
||||
public HookResult OnTouchStart(CEntityIOOutput output, string name, CEntityInstance activator, CEntityInstance caller, CVariant value, float delay)
|
||||
{
|
||||
Logger.LogInformation("[EntityOutputHook Attribute] Buyzone called output ({name}, {activator}, {caller}, {delay})", name, activator.DesignerName, caller.DesignerName, delay);
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
}
|
||||
4
examples/WithMenus/README.md
Normal file
4
examples/WithMenus/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# With Commands
|
||||
This is an example that shows how to register chat & console commands.
|
||||
|
||||
All commands that are prefixed with "css_" will automatically be registered as a chat command without the prefix. i.e. `css_ping` can be called with `!ping` or `/ping`.
|
||||
12
examples/WithMenus/WithCommands.csproj
Normal file
12
examples/WithMenus/WithCommands.csproj
Normal file
@@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\managed\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
63
examples/WithMenus/WithCommandsPlugin.cs
Normal file
63
examples/WithMenus/WithCommandsPlugin.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Core.Attributes.Registration;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
|
||||
namespace WithCommands;
|
||||
|
||||
[MinimumApiVersion(80)]
|
||||
public class WithCommandsPlugin : BasePlugin
|
||||
{
|
||||
public override string ModuleName => "Example: With Commands";
|
||||
public override string ModuleVersion => "1.0.0";
|
||||
public override string ModuleAuthor => "CounterStrikeSharp & Contributors";
|
||||
public override string ModuleDescription => "A simple plugin that registers some commands";
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
// All commands that are prefixed with "css_" will automatically be registered as a chat command without the prefix.
|
||||
// i.e. `css_ping` can be called with `!ping` or `/ping`.
|
||||
// Commands can be registered using the instance `AddCommand` method.
|
||||
AddCommand("css_ping", "Responds to the caller with \"pong\"", (player, commandInfo) =>
|
||||
{
|
||||
// The player is null, then the command has been called by the server console.
|
||||
if (player == null)
|
||||
{
|
||||
commandInfo.ReplyToCommand("pong server");
|
||||
return;
|
||||
}
|
||||
|
||||
commandInfo.ReplyToCommand("pong");
|
||||
});
|
||||
}
|
||||
|
||||
// Commands can also be registered using the `Command` attribute.
|
||||
[ConsoleCommand("css_hello", "Responds to the caller with \"pong\"")]
|
||||
// The `CommandHelper` attribute can be used to provide additional information about the command.
|
||||
[CommandHelper(minArgs: 1, usage: "[name]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
[RequiresPermissions("@css/cvar")]
|
||||
public void OnHelloCommand(CCSPlayerController? player, CommandInfo commandInfo)
|
||||
{
|
||||
// The first argument is the command name, in this case "css_hello".
|
||||
commandInfo.GetArg(0); // css_hello
|
||||
|
||||
// The second argument is the first argument passed to the command, in this case "name".
|
||||
// The `minArgs` helper parameter is used to ensure that the second argument is present.
|
||||
var name = commandInfo.GetArg(1);
|
||||
|
||||
commandInfo.ReplyToCommand($"Hello {name}");
|
||||
}
|
||||
|
||||
// Permissions can be added to commands using the `RequiresPermissions` attribute.
|
||||
// See the admin documentation for more information on permissions.
|
||||
[RequiresPermissions("@css/kick")]
|
||||
[CommandHelper(minArgs: 1, usage: "[id]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnSpecialCommand(CCSPlayerController? player, CommandInfo commandInfo)
|
||||
{
|
||||
var id = commandInfo.GetArg(1);
|
||||
|
||||
Server.ExecuteCommand($"kick {id}");
|
||||
}
|
||||
}
|
||||
8
examples/WithTranslations/README.md
Normal file
8
examples/WithTranslations/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# With Translations
|
||||
This example shows how to use an `IStringLocalizer` and language `json` files to provide localization for your plugins.
|
||||
|
||||
## How to use
|
||||
1. Add the `IStringLocalizer` to your services with dependency injection, or use the `Localizer` provided on the plugin instance.
|
||||
2. Add a `lang` folder to your plugin, and add a `json` file for each language you want to support. The name of the file should be a locale code, like `en.json` or `fr.json` etc.
|
||||
3. Ensure that the `lang` folder is shipped with your plugin, see the example `.csproj` file for an example to auto-copy to the output folder.
|
||||
4. Use the `IStringLocalizer` to localize your strings.
|
||||
15
examples/WithTranslations/WithTranslations.csproj
Normal file
15
examples/WithTranslations/WithTranslations.csproj
Normal file
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\managed\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="lang\**\*.*" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
52
examples/WithTranslations/WithTranslationsPlugin.cs
Normal file
52
examples/WithTranslations/WithTranslationsPlugin.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System.Globalization;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Core.Attributes.Registration;
|
||||
using CounterStrikeSharp.API.Core.Translations;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace WithTranslations;
|
||||
|
||||
[MinimumApiVersion(80)]
|
||||
public class WithTranslationsPlugin : BasePlugin
|
||||
{
|
||||
public override string ModuleName => "Example: With Translations";
|
||||
public override string ModuleVersion => "1.0.0";
|
||||
public override string ModuleAuthor => "CounterStrikeSharp & Contributors";
|
||||
public override string ModuleDescription => "A simple plugin that provides translations";
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
// A global `Localizer` is provided on the plugin instance.
|
||||
// You can also use dependency injection to inject `IStringLocalizer` in your own services.
|
||||
Logger.LogInformation("This message is in the server language: {Message}", Localizer["test.translation"]);
|
||||
|
||||
// IStringLocalizer can accept standard string format arguments.
|
||||
// "This number has 2 decimal places {0:n2}" -> "This number has 2 decimal places 123.55"
|
||||
Logger.LogInformation(Localizer["test.format", 123.551]);
|
||||
|
||||
// This message has colour codes
|
||||
Server.PrintToChatAll(Localizer["test.colors"]);
|
||||
|
||||
// This message has colour codes and formatted values
|
||||
Server.PrintToChatAll(Localizer["test.colors.withformat", 123.551]);
|
||||
}
|
||||
|
||||
[ConsoleCommand("css_replylanguage", "Test Translations")]
|
||||
public void OnCommandReplyLanguage(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
// Commands are executed in a players provided culture (or fallback to server culture).
|
||||
// Players can configure their language using the `!lang` or `css_lang` command.
|
||||
Logger.LogInformation("Current Culture is {Culture}", CultureInfo.CurrentCulture);
|
||||
command.ReplyToCommand(Localizer["test.translation"]);
|
||||
|
||||
if (player != null)
|
||||
{
|
||||
// You can also get the players language using the `GetLanguage` extension method.
|
||||
// This will always return a culture, defaulting to the server culture if the user has not configured it.
|
||||
var language = player.GetLanguage();
|
||||
}
|
||||
}
|
||||
}
|
||||
3
examples/WithTranslations/lang/en-GB.json
Normal file
3
examples/WithTranslations/lang/en-GB.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"test.translation": "This is the english (GB) translation"
|
||||
}
|
||||
6
examples/WithTranslations/lang/en.json
Normal file
6
examples/WithTranslations/lang/en.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"test.translation": "This is the english translation",
|
||||
"test.format": "This number has 2 decimal places {0:n2}",
|
||||
"test.colors": "{orange}This{default} text has {green}green{default} text",
|
||||
"test.colors.withformat": "{orange}{0:n2}{default}"
|
||||
}
|
||||
3
examples/WithTranslations/lang/fr.json
Normal file
3
examples/WithTranslations/lang/fr.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"test.translation": "This is the french translation"
|
||||
}
|
||||
2
examples/WithVoiceOverrides/README.md
Normal file
2
examples/WithVoiceOverrides/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# With Voice Overrides
|
||||
Provides examples how to manipulate player voice flags & listening overrides to prevent certain players from hearing others.
|
||||
12
examples/WithVoiceOverrides/WithVoiceOverrides.csproj
Normal file
12
examples/WithVoiceOverrides/WithVoiceOverrides.csproj
Normal file
@@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\managed\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
79
examples/WithVoiceOverrides/WithVoiceOverridesPlugin.cs
Normal file
79
examples/WithVoiceOverrides/WithVoiceOverridesPlugin.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Core.Attributes.Registration;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace WithVoiceOverrides;
|
||||
|
||||
[MinimumApiVersion(80)]
|
||||
public class WithVoiceOverridesPlugin : BasePlugin
|
||||
{
|
||||
public override string ModuleName => "Example: With Voice Overrides";
|
||||
public override string ModuleVersion => "1.0.0";
|
||||
public override string ModuleAuthor => "CounterStrikeSharp & Contributors";
|
||||
public override string ModuleDescription => "A plugin that manipulates voice flags";
|
||||
|
||||
[ConsoleCommand("css_hearall")]
|
||||
public void OnHearAllCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (caller is null) return;
|
||||
|
||||
if (caller.VoiceFlags.HasFlag(VoiceFlags.ListenAll))
|
||||
{
|
||||
caller.VoiceFlags = VoiceFlags.Normal;
|
||||
command.ReplyToCommand("Voice set back to default");
|
||||
}
|
||||
else
|
||||
{
|
||||
caller.VoiceFlags = VoiceFlags.ListenAll;
|
||||
command.ReplyToCommand("Can hear both teams");
|
||||
}
|
||||
}
|
||||
|
||||
[ConsoleCommand("css_muteself")]
|
||||
public void OnMuteSelfCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (caller is null) return;
|
||||
|
||||
if (caller.VoiceFlags.HasFlag(VoiceFlags.Muted))
|
||||
{
|
||||
caller.VoiceFlags = VoiceFlags.Normal;
|
||||
command.ReplyToCommand("Unmuted yourself");
|
||||
}
|
||||
else
|
||||
{
|
||||
caller.VoiceFlags = VoiceFlags.Muted;
|
||||
command.ReplyToCommand("Muted yourself");
|
||||
}
|
||||
}
|
||||
|
||||
[ConsoleCommand("css_muteothers")]
|
||||
[CommandHelper(minArgs: 1, usage: "[target]")]
|
||||
public void OnMuteOthersCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (caller is null) return;
|
||||
|
||||
var targetResult = command.GetArgTargetResult(1);
|
||||
|
||||
foreach (var player in targetResult.Players)
|
||||
{
|
||||
if (player == caller) continue;
|
||||
|
||||
|
||||
var existingOverride = caller.GetListenOverride(player);
|
||||
if (existingOverride == ListenOverride.Mute)
|
||||
{
|
||||
caller.SetListenOverride(player, ListenOverride.Default);
|
||||
command.ReplyToCommand($"Now hearing {player.PlayerName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
caller.SetListenOverride(player, ListenOverride.Mute);
|
||||
command.ReplyToCommand($"Muted {player.PlayerName}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Submodule libraries/hl2sdk-cs2 updated: d7ed476064...2f9dd2e61a
@@ -14,8 +14,11 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING
|
||||
# TODO: Use C++20 instead.
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
Set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||
Set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
|
||||
if (LINUX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
|
||||
endif()
|
||||
|
||||
set(CMAKE_STATIC_LIBRARY_PREFIX "")
|
||||
|
||||
set(SOURCESDK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libraries/hl2sdk-cs2)
|
||||
@@ -51,8 +54,4 @@ include_directories(
|
||||
libraries
|
||||
)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/metamod/configure_metamod.cmake)
|
||||
|
||||
if (LINUX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||
endif()
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/metamod/configure_metamod.cmake)
|
||||
110
managed/CounterStrikeSharp.API.Tests/AdminTests.cs
Normal file
110
managed/CounterStrikeSharp.API.Tests/AdminTests.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CounterStrikeSharp.API.Tests;
|
||||
|
||||
[Collection("Logging collection")]
|
||||
public class AdminTests
|
||||
{
|
||||
public AdminTests()
|
||||
{
|
||||
AdminManager.LoadAdminData(TestUtils.GetTestPath("admins.json"));
|
||||
AdminManager.LoadAdminGroups(TestUtils.GetTestPath("admin_groups.json"));
|
||||
AdminManager.LoadCommandOverrides(TestUtils.GetTestPath("admin_overrides.json"));
|
||||
AdminManager.MergeGroupPermsIntoAdmins();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldReturnValidAdminData()
|
||||
{
|
||||
var adminData = AdminManager.GetPlayerAdminData((SteamID)76561197960265731);
|
||||
Assert.NotNull(adminData);
|
||||
Assert.Equal("#css/admin", adminData.Groups.Single());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldUseGroupImmunity()
|
||||
{
|
||||
var adminData = AdminManager.GetPlayerAdminData((SteamID)76561197960265731);
|
||||
Assert.NotNull(adminData);
|
||||
Assert.Equal(125u, AdminManager.GetPlayerImmunity((SteamID)76561197960265731)); // Group immunity is 125, Admin immunity is 100
|
||||
AdminManager.SetPlayerImmunity((SteamID)76561197960265731, 150u);
|
||||
Assert.Equal(150u, AdminManager.GetPlayerImmunity((SteamID)76561197960265731)); // Group immunity is 125, Admin immunity is 100
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldReturnNullAdminData()
|
||||
{
|
||||
var adminData = AdminManager.GetPlayerAdminData((SteamID)76561197960265732);
|
||||
Assert.Null(adminData);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldReturnValidCommandOverrides()
|
||||
{
|
||||
var adminData = AdminManager.GetPlayerAdminData((SteamID)76561197960265731);
|
||||
Assert.NotNull(adminData);
|
||||
Assert.True(adminData.CommandOverrides["fake_command"]);
|
||||
Assert.False(adminData.CommandOverrides["css"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldHandleWildcardDomainFlags()
|
||||
{
|
||||
// User has @mycustomplugin/* so should have the admin subflag despite it not being in their group.
|
||||
Assert.True(AdminManager.PlayerHasPermissions((SteamID)76561197960265731, "@mycustomplugin/admin"));
|
||||
|
||||
// User has @mycustomplugin/* so should have the admin subflag despite it not existing anywhere else.
|
||||
Assert.True(AdminManager.PlayerHasPermissions((SteamID)76561197960265731, "@mycustomplugin/fake"));
|
||||
|
||||
// User has @css/root so should have the slay subflag despite it not being in their group.
|
||||
Assert.True(AdminManager.PlayerHasPermissions((SteamID)76561197960265731, "@css/slay"));
|
||||
|
||||
// Flag provided explicitly in the admins.json file
|
||||
Assert.True(AdminManager.PlayerHasPermissions((SteamID)76561197960265731, "@css/custom-flag-2"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldAddFlagsAtRuntime()
|
||||
{
|
||||
// Existing player
|
||||
Assert.False(AdminManager.PlayerHasPermissions((SteamID)76561197960265731, "@runtime/flag1"));
|
||||
AdminManager.AddPlayerPermissions((SteamID)76561197960265731, "@runtime/flag1");
|
||||
Assert.True(AdminManager.PlayerHasPermissions((SteamID)76561197960265731, "@runtime/flag1"));
|
||||
|
||||
// Non-existent player
|
||||
Assert.False(AdminManager.PlayerHasPermissions((SteamID)76561197960265730, "@runtime/flag1"));
|
||||
AdminManager.AddPlayerPermissions((SteamID)76561197960265730, "@runtime/flag1");
|
||||
Assert.True(AdminManager.PlayerHasPermissions((SteamID)76561197960265730, "@runtime/flag1"));
|
||||
|
||||
AdminManager.ClearPlayerPermissions((SteamID)76561197960265730);
|
||||
Assert.False(AdminManager.PlayerHasPermissions((SteamID)76561197960265730, "@runtime/flag1"));
|
||||
Assert.Empty(AdminManager.GetPlayerAdminData((SteamID)76561197960265730)!.GetAllFlags());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldAddPlayerToGroup()
|
||||
{
|
||||
AdminManager.AddPlayerToGroup(new SteamID("STEAM_0:1:3"), "#css/root");
|
||||
var adminData = AdminManager.GetPlayerAdminData(new SteamID("STEAM_0:1:3"));
|
||||
Assert.NotNull(adminData);
|
||||
Assert.Equal("#css/root", adminData.Groups.Single());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldAddPlayerPermissionOverridesAtRuntime()
|
||||
{
|
||||
Assert.False(AdminManager.PlayerHasCommandOverride((SteamID)76561197960265731, "runtime_command"));
|
||||
AdminManager.SetPlayerCommandOverride((SteamID)76561197960265731, "runtime_command", true);
|
||||
Assert.True(AdminManager.PlayerHasCommandOverride((SteamID)76561197960265731, "runtime_command"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldAddCommandPermissionOverridesAtRuntime()
|
||||
{
|
||||
Assert.False(AdminManager.CommandIsOverriden("runtime_command"));
|
||||
AdminManager.AddPermissionOverride("runtime_command", "@runtime/override");
|
||||
Assert.True(AdminManager.CommandIsOverriden("runtime_command"));
|
||||
Assert.Equal("@runtime/override", AdminManager.GetPermissionOverrides("runtime_command").Single());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2"/>
|
||||
<PackageReference Include="Moq" Version="4.20.70" />
|
||||
<PackageReference Include="xunit" Version="2.4.2"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Resources\**\*.*" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="lang\en.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="lang\en-GB.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="lang\fr.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,23 @@
|
||||
using CounterStrikeSharp.API.Core.Logging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Tests.Fixtures;
|
||||
|
||||
public class CoreLoggingFixture : IDisposable
|
||||
{
|
||||
public CoreLoggingFixture()
|
||||
{
|
||||
var serviceProvider = new ServiceCollection()
|
||||
.AddLogging(builder =>
|
||||
{
|
||||
builder.ClearProviders();
|
||||
builder.AddCoreLogging(TestUtils.GetTestPath(""));
|
||||
})
|
||||
.BuildServiceProvider();
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
// TODO release managed resources here
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace CounterStrikeSharp.API.Tests.Fixtures;
|
||||
|
||||
[CollectionDefinition("Logging collection")]
|
||||
public class LoggingCollection : ICollectionFixture<CoreLoggingFixture>
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"#css/admin": {
|
||||
"flags": [
|
||||
"@css/reservation",
|
||||
"@css/generic",
|
||||
"@css/kick",
|
||||
"@css/ban",
|
||||
"@css/unban",
|
||||
"@css/vip",
|
||||
"@css/changemap",
|
||||
"@css/cvar",
|
||||
"@css/config",
|
||||
"@css/chat",
|
||||
"@css/vote",
|
||||
"@css/password",
|
||||
"@css/rcon",
|
||||
"@css/cheats",
|
||||
"@css/root"
|
||||
],
|
||||
"immunity": 125
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"example_command": {
|
||||
"flags": [
|
||||
"@css/custom-permission"
|
||||
],
|
||||
"check_type": "all",
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
23
managed/CounterStrikeSharp.API.Tests/Resources/admins.json
Normal file
23
managed/CounterStrikeSharp.API.Tests/Resources/admins.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Erikj": {
|
||||
"identity": "76561197960265731",
|
||||
"immunity": 100,
|
||||
"flags": [
|
||||
"@css/custom-flag-1",
|
||||
"@css/custom-flag-2",
|
||||
"@mycustomplugin/*"
|
||||
],
|
||||
"groups": [
|
||||
"#css/admin"
|
||||
],
|
||||
"command_overrides": {
|
||||
"css_plugins": true,
|
||||
"css": false,
|
||||
"fake_command": true
|
||||
}
|
||||
},
|
||||
"Another erikj": {
|
||||
"identity": "STEAM_0:1:1",
|
||||
"flags": ["@mycustomplugin/admin"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"test.translation": "This is the english (GB) translation"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"test.translation": "This is the english translation",
|
||||
"test.format": "This number has 2 decimal places {0:n2}",
|
||||
"test.colors": "{orange}This{default} text has {green}green{default} text",
|
||||
"test.colors.withformat": "{orange}{0:n2}{default}"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"test.translation": "This is the french translation"
|
||||
}
|
||||
52
managed/CounterStrikeSharp.API.Tests/SteamIDTests.cs
Normal file
52
managed/CounterStrikeSharp.API.Tests/SteamIDTests.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
|
||||
namespace CounterStrikeSharp.API.Tests;
|
||||
|
||||
public class SteamIdTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(76561197960524373ul, 76561197960524373ul, "STEAM_0:1:129322", "[U:1:258645]", 258645, SteamAccountType.Individual, SteamAccountInstance.Desktop, SteamAccountUniverse.Public, true)]
|
||||
[InlineData(258645, 76561197960524373ul, "STEAM_0:1:129322", "[U:1:258645]", 258645, SteamAccountType.Individual, SteamAccountInstance.Desktop, SteamAccountUniverse.Public, true)]
|
||||
[InlineData(76561197960265728ul, 76561197960265728ul, "STEAM_0:0:0", "[U:1:0]", 0, SteamAccountType.Individual, SteamAccountInstance.Desktop, SteamAccountUniverse.Public, false)]
|
||||
[InlineData(103582791429521412ul, 103582791429521412ul, "STEAM_0:0:13510796734627842", "[g:1:27021593469255684]", 4, SteamAccountType.Clan, SteamAccountInstance.All, SteamAccountUniverse.Public, true)]
|
||||
public void ValidateSteamId(ulong parseValue, ulong steamId64, string steamId2, string steamId3, int steamId32, SteamAccountType accountType, SteamAccountInstance accountInstance, SteamAccountUniverse accountUniverse, bool valid)
|
||||
{
|
||||
var steamId = new SteamID(parseValue);
|
||||
|
||||
Assert.Equal(steamId64, steamId.SteamId64);
|
||||
Assert.Equal(steamId2, steamId.SteamId2);
|
||||
Assert.Equal(steamId3, steamId.SteamId3);
|
||||
Assert.Equal(steamId32, steamId.SteamId32);
|
||||
Assert.Equal(accountType, steamId.AccountType);
|
||||
Assert.Equal(accountInstance, steamId.AccountInstance);
|
||||
Assert.Equal(accountUniverse, steamId.AccountUniverse);
|
||||
Assert.Equal(valid, steamId.IsValid());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(76561197960524373ul, 76561197960524373ul)]
|
||||
[InlineData("STEAM_0:1:129322", 76561197960524373ul)]
|
||||
[InlineData("[U:1:258645]", 76561197960524373ul)]
|
||||
public void CanCastLongToString(dynamic parseable, ulong longValue)
|
||||
{
|
||||
Assert.Equal(longValue, ((SteamID)parseable).SteamId64);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanUseValueEquality()
|
||||
{
|
||||
var steamId1 = new SteamID(76561197960524373ul);
|
||||
var steamId2 = new SteamID(76561197960524373ul);
|
||||
var steamId3 = new SteamID(76561197960265728ul);
|
||||
|
||||
Assert.True(steamId1 == steamId2);
|
||||
Assert.True(steamId1 != steamId3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ThrowsOutOfRangeException()
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new SteamID(0));
|
||||
}
|
||||
}
|
||||
14
managed/CounterStrikeSharp.API.Tests/TestUtils.cs
Normal file
14
managed/CounterStrikeSharp.API.Tests/TestUtils.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace CounterStrikeSharp.API.Tests;
|
||||
|
||||
public static class TestUtils
|
||||
{
|
||||
public static string GetTestPath(string relativePath)
|
||||
{
|
||||
var codeBaseUrl = new Uri(Assembly.GetExecutingAssembly().Location);
|
||||
var codeBasePath = Uri.UnescapeDataString(codeBaseUrl.AbsolutePath);
|
||||
var dirPath = Path.GetDirectoryName(codeBasePath);
|
||||
return Path.Combine(dirPath, "Resources", relativePath);
|
||||
}
|
||||
}
|
||||
92
managed/CounterStrikeSharp.API.Tests/TranslationTests.cs
Normal file
92
managed/CounterStrikeSharp.API.Tests/TranslationTests.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System.Globalization;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Plugin;
|
||||
using CounterStrikeSharp.API.Core.Translations;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Moq;
|
||||
|
||||
namespace CounterStrikeSharp.API.Tests;
|
||||
|
||||
public class TranslationTests
|
||||
{
|
||||
private readonly JsonStringLocalizerFactory _factory;
|
||||
private readonly IStringLocalizer _localizer;
|
||||
|
||||
public TranslationTests()
|
||||
{
|
||||
var pluginContextMock = new Mock<IPluginContext>();
|
||||
pluginContextMock.SetupGet(x => x.FilePath).Returns(TestUtils.GetTestPath("test_plugin.dll"));
|
||||
_factory = new JsonStringLocalizerFactory(pluginContextMock.Object);
|
||||
_localizer = _factory.Create(this.GetType());
|
||||
|
||||
// This is generally derived from the core config, but for the sake of these tests we default to `en`.
|
||||
var serverCulture = CultureInfo.GetCultureInfo("en");
|
||||
CultureInfo.DefaultThreadCurrentUICulture = serverCulture;
|
||||
CultureInfo.DefaultThreadCurrentCulture = serverCulture;
|
||||
CultureInfo.CurrentUICulture = serverCulture;
|
||||
CultureInfo.CurrentCulture = serverCulture;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TranslatesLanguagesCorrectly()
|
||||
{
|
||||
using (new WithTemporaryCulture(CultureInfo.GetCultureInfo("en")))
|
||||
{
|
||||
Assert.Equal("This is the english translation", _localizer["test.translation"]);
|
||||
}
|
||||
|
||||
using (new WithTemporaryCulture(CultureInfo.InvariantCulture))
|
||||
{
|
||||
Assert.Equal("This is the english translation", _localizer["test.translation"]);
|
||||
}
|
||||
|
||||
using (new WithTemporaryCulture(CultureInfo.GetCultureInfo("en-US")))
|
||||
{
|
||||
Assert.Equal("This is the english translation", _localizer["test.translation"]);
|
||||
}
|
||||
|
||||
using (new WithTemporaryCulture(CultureInfo.GetCultureInfo("en-GB")))
|
||||
{
|
||||
Assert.Equal("This is the english (GB) translation", _localizer["test.translation"]);
|
||||
}
|
||||
|
||||
using (new WithTemporaryCulture(CultureInfo.GetCultureInfo("fr")))
|
||||
{
|
||||
Assert.Equal("This is the french translation", _localizer["test.translation"]);
|
||||
}
|
||||
|
||||
using (new WithTemporaryCulture(CultureInfo.GetCultureInfo("fr-FR")))
|
||||
{
|
||||
Assert.Equal("This is the french translation", _localizer["test.translation"]);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldFallbackToServerLanguage()
|
||||
{
|
||||
using (new WithTemporaryCulture(CultureInfo.GetCultureInfo("de")))
|
||||
{
|
||||
Assert.Equal("This is the english translation", _localizer["test.translation"]);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldReturnKeyIfNotFound()
|
||||
{
|
||||
Assert.Equal("test.notfound", _localizer["test.notfound"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldHandleFormatStrings()
|
||||
{
|
||||
Assert.Equal("This number has 2 decimal places 0.25", _localizer["test.format", 0.251]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesColorFormatting()
|
||||
{
|
||||
// Sets invisible pre-color if there is a color code in the string.
|
||||
Assert.Equal(" \x10This\x01 text has \x04green\x01 text", _localizer["test.colors"]);
|
||||
Assert.Equal($" {'\x10'}1.25\x01", _localizer["test.colors.withformat", 1.25]);
|
||||
}
|
||||
}
|
||||
1
managed/CounterStrikeSharp.API.Tests/Usings.cs
Normal file
1
managed/CounterStrikeSharp.API.Tests/Usings.cs
Normal file
@@ -0,0 +1 @@
|
||||
global using Xunit;
|
||||
BIN
managed/CounterStrikeSharp.API/ApiCompat/v151.dll
Normal file
BIN
managed/CounterStrikeSharp.API/ApiCompat/v151.dll
Normal file
Binary file not shown.
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -7,6 +8,7 @@ using CounterStrikeSharp.API.Core.Hosting;
|
||||
using CounterStrikeSharp.API.Core.Logging;
|
||||
using CounterStrikeSharp.API.Core.Plugin;
|
||||
using CounterStrikeSharp.API.Core.Plugin.Host;
|
||||
using CounterStrikeSharp.API.Core.Translations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -38,6 +40,7 @@ public static class Bootstrap
|
||||
services.AddSingleton<IScriptHostConfiguration, ScriptHostConfiguration>();
|
||||
services.AddScoped<Application>();
|
||||
services.AddSingleton<IPluginManager, PluginManager>();
|
||||
services.AddSingleton<IPlayerLanguageManager, PlayerLanguageManager>();
|
||||
services.AddScoped<IPluginContextQueryHandler, PluginContextQueryHandler>();
|
||||
|
||||
services.Scan(i => i.FromCallingAssembly()
|
||||
|
||||
@@ -312,6 +312,16 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetMaxClients(){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0x5DF2E20D);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
return (int)ScriptContext.GlobalScriptContext.GetResult(typeof(int));
|
||||
}
|
||||
}
|
||||
|
||||
public static void IssueServerCommand(string command){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
@@ -654,6 +664,32 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
public static void HookEntityOutput(string classname, string outputname, InputArgument callback, HookMode mode){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(classname);
|
||||
ScriptContext.GlobalScriptContext.Push(outputname);
|
||||
ScriptContext.GlobalScriptContext.Push((InputArgument)callback);
|
||||
ScriptContext.GlobalScriptContext.Push(mode);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0x15245242);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
}
|
||||
}
|
||||
|
||||
public static void UnhookEntityOutput(string classname, string outputname, InputArgument callback, HookMode mode){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(classname);
|
||||
ScriptContext.GlobalScriptContext.Push(outputname);
|
||||
ScriptContext.GlobalScriptContext.Push((InputArgument)callback);
|
||||
ScriptContext.GlobalScriptContext.Push(mode);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0x87DBD139);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
}
|
||||
}
|
||||
|
||||
public static void HookEvent(string name, InputArgument callback, bool ispost){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
@@ -1038,6 +1074,18 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsSchemaFieldNetworked(string classname, string propname){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(classname);
|
||||
ScriptContext.GlobalScriptContext.Push(propname);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0xFE413B0C);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
return (bool)ScriptContext.GlobalScriptContext.GetResult(typeof(bool));
|
||||
}
|
||||
}
|
||||
|
||||
public static T GetSchemaValueByName<T>(IntPtr instance, int returntype, string classname, string propname){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
@@ -1285,5 +1333,51 @@ namespace CounterStrikeSharp.API.Core
|
||||
return (bool)ScriptContext.GlobalScriptContext.GetResult(typeof(bool));
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetClientListening(IntPtr receiver, IntPtr sender, uint listen){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(receiver);
|
||||
ScriptContext.GlobalScriptContext.Push(sender);
|
||||
ScriptContext.GlobalScriptContext.Push(listen);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0xD38BEE77);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
}
|
||||
}
|
||||
|
||||
public static ListenOverride GetClientListening(IntPtr receiver, IntPtr sender){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(receiver);
|
||||
ScriptContext.GlobalScriptContext.Push(sender);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0xE95644E3);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
return (ListenOverride)ScriptContext.GlobalScriptContext.GetResult(typeof(ListenOverride));
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetClientVoiceFlags(IntPtr client, uint flags){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(client);
|
||||
ScriptContext.GlobalScriptContext.Push(flags);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0x48EB2FC8);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
}
|
||||
}
|
||||
|
||||
public static uint GetClientVoiceFlags(IntPtr client){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(client);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0x9685205C);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
return (uint)ScriptContext.GlobalScriptContext.GetResult(typeof(uint));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,13 +14,16 @@
|
||||
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
|
||||
*/
|
||||
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using CounterStrikeSharp.API.Core.Hosting;
|
||||
using CounterStrikeSharp.API.Core.Plugin;
|
||||
using CounterStrikeSharp.API.Core.Plugin.Host;
|
||||
using CounterStrikeSharp.API.Core.Translations;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Menu;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -40,10 +43,11 @@ namespace CounterStrikeSharp.API.Core
|
||||
private readonly CoreConfig _coreConfig;
|
||||
private readonly IPluginManager _pluginManager;
|
||||
private readonly IPluginContextQueryHandler _pluginContextQueryHandler;
|
||||
private readonly IPlayerLanguageManager _playerLanguageManager;
|
||||
|
||||
public Application(ILoggerFactory loggerFactory, IScriptHostConfiguration scriptHostConfiguration,
|
||||
GameDataProvider gameDataProvider, CoreConfig coreConfig, IPluginManager pluginManager,
|
||||
IPluginContextQueryHandler pluginContextQueryHandler)
|
||||
IPluginContextQueryHandler pluginContextQueryHandler, IPlayerLanguageManager playerLanguageManager)
|
||||
{
|
||||
Logger = loggerFactory.CreateLogger("Core");
|
||||
_scriptHostConfiguration = scriptHostConfiguration;
|
||||
@@ -51,6 +55,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
_coreConfig = coreConfig;
|
||||
_pluginManager = pluginManager;
|
||||
_pluginContextQueryHandler = pluginContextQueryHandler;
|
||||
_playerLanguageManager = playerLanguageManager;
|
||||
_instance = this;
|
||||
}
|
||||
|
||||
@@ -61,29 +66,31 @@ namespace CounterStrikeSharp.API.Core
|
||||
_coreConfig.Load();
|
||||
_gameDataProvider.Load();
|
||||
|
||||
var adminGroupsPath = Path.Combine(_scriptHostConfiguration.RootPath, "configs", "admin_groups.json");
|
||||
Logger.LogInformation("Loading Admin Groups from {Path}", adminGroupsPath);
|
||||
AdminManager.LoadAdminGroups(adminGroupsPath);
|
||||
|
||||
var adminPath = Path.Combine(_scriptHostConfiguration.RootPath, "configs", "admins.json");
|
||||
Logger.LogInformation("Loading Admins from {Path}", adminPath);
|
||||
AdminManager.LoadAdminData(adminPath);
|
||||
|
||||
var adminGroupsPath = Path.Combine(_scriptHostConfiguration.RootPath, "configs", "admin_groups.json");
|
||||
Logger.LogInformation("Loading Admin Groups from {Path}", adminGroupsPath);
|
||||
AdminManager.LoadAdminGroups(adminGroupsPath);
|
||||
|
||||
var overridePath = Path.Combine(_scriptHostConfiguration.RootPath, "configs", "admin_overrides.json");
|
||||
Logger.LogInformation("Loading Admin Command Overrides from {Path}", overridePath);
|
||||
AdminManager.LoadCommandOverrides(overridePath);
|
||||
|
||||
AdminManager.MergeGroupPermsIntoAdmins();
|
||||
AdminManager.AddCommands();
|
||||
|
||||
_pluginManager.Load();
|
||||
|
||||
for (var i = 1; i <= 9; i++)
|
||||
{
|
||||
CommandUtils.AddStandaloneCommand("css_" + i, "Command Key Handler", (player, info) =>
|
||||
CommandUtils.AddStandaloneCommand($"css_{i}", "Command Key Handler", (player, info) =>
|
||||
{
|
||||
if (player == null) return;
|
||||
var key = Convert.ToInt32(info.GetArg(0).Split("_")[1]);
|
||||
ChatMenus.OnKeyPress(player, key);
|
||||
|
||||
MenuManager.OnKeyPress(player, key);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -98,7 +105,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
|
||||
info.ReplyToCommand(
|
||||
" CounterStrikeSharp was created and is maintained by Michael \"roflmuffin\" Wilson.\n" +
|
||||
" Counter-Strike Sharp uses code borrowed from SourceMod, Source.Python, FiveM, Saul Rennison and CS2Fixes.\n" +
|
||||
" 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, true);
|
||||
return;
|
||||
@@ -145,14 +152,14 @@ namespace CounterStrikeSharp.API.Core
|
||||
case "start":
|
||||
case "load":
|
||||
{
|
||||
if (info.ArgCount < 2)
|
||||
if (info.ArgCount < 3)
|
||||
{
|
||||
info.ReplyToCommand(
|
||||
"Valid usage: css_plugins start/load [relative plugin path || absolute plugin path] (e.g \"TestPlugin\", \"plugins/TestPlugin/TestPlugin.dll\")\n",
|
||||
true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// If our arugment doesn't end in ".dll" - try and construct a path similar to PluginName/PluginName.dll.
|
||||
// We'll assume we have a full path if we have ".dll".
|
||||
var path = info.GetArg(2);
|
||||
@@ -172,23 +179,25 @@ namespace CounterStrikeSharp.API.Core
|
||||
try
|
||||
{
|
||||
_pluginManager.LoadPlugin(path);
|
||||
} catch (Exception e)
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
info.ReplyToCommand($"Could not load plugin \"{path}\")", true);
|
||||
info.ReplyToCommand($"Could not load plugin \"{path}\"", true);
|
||||
Logger.LogError(e, "Could not load plugin \"{Path}\"", path);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
plugin.Load(false);
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "stop":
|
||||
case "unload":
|
||||
{
|
||||
if (info.ArgCount < 2)
|
||||
if (info.ArgCount < 3)
|
||||
{
|
||||
info.ReplyToCommand(
|
||||
"Valid usage: css_plugins stop/unload [plugin name || #plugin id] (e.g \"TestPlugin\", \"1\")\n",
|
||||
@@ -200,7 +209,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
IPluginContext? plugin = _pluginContextQueryHandler.FindPluginByIdOrName(pluginIdentifier);
|
||||
if (plugin == null)
|
||||
{
|
||||
info.ReplyToCommand($"Could not unload plugin \"{pluginIdentifier}\")", true);
|
||||
info.ReplyToCommand($"Could not unload plugin \"{pluginIdentifier}\"", true);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -211,7 +220,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
case "restart":
|
||||
case "reload":
|
||||
{
|
||||
if (info.ArgCount < 2)
|
||||
if (info.ArgCount < 3)
|
||||
{
|
||||
info.ReplyToCommand(
|
||||
"Valid usage: css_plugins restart/reload [plugin name || #plugin id] (e.g \"TestPlugin\", \"#1\")\n",
|
||||
@@ -224,7 +233,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
|
||||
if (plugin == null)
|
||||
{
|
||||
info.ReplyToCommand($"Could not reload plugin \"{pluginIdentifier}\")", true);
|
||||
info.ReplyToCommand($"Could not reload plugin \"{pluginIdentifier}\"", true);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -244,11 +253,44 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
[CommandHelper(usage: "[language code, e.g. \"de\", \"pl\", \"en\"]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
private void OnLangCommand(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
if (player == null) return;
|
||||
|
||||
SteamID steamId = (SteamID)player.SteamID;
|
||||
|
||||
if (command.ArgCount == 1)
|
||||
{
|
||||
var language = _playerLanguageManager.GetLanguage(steamId);
|
||||
command.ReplyToCommand(string.Format("Current language is \"{0}\" ({1})", language.Name, language.NativeName));
|
||||
return;
|
||||
}
|
||||
|
||||
if (command.ArgCount != 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var language = command.GetArg(1);
|
||||
var cultureInfo = CultureInfo.GetCultures(CultureTypes.AllCultures).Single(x => x.Name == language);
|
||||
_playerLanguageManager.SetLanguage(steamId, cultureInfo);
|
||||
command.ReplyToCommand($"Language set to {cultureInfo.NativeName}");
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
command.ReplyToCommand("Language not found.");
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterPluginCommands()
|
||||
{
|
||||
CommandUtils.AddStandaloneCommand("css", "Counter-Strike Sharp options.", OnCSSCommand);
|
||||
CommandUtils.AddStandaloneCommand("css_plugins", "Counter-Strike Sharp plugin options.",
|
||||
OnCSSPluginCommand);
|
||||
CommandUtils.AddStandaloneCommand("css_lang", "Set Counter-Strike Sharp language.", OnLangCommand);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Attributes.Registration;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||
public class EntityOutputHookAttribute : Attribute
|
||||
{
|
||||
public string Classname { get; }
|
||||
public string OutputName { get; }
|
||||
|
||||
public EntityOutputHookAttribute(string classname, string outputName)
|
||||
{
|
||||
Classname = classname;
|
||||
OutputName = outputName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace CounterStrikeSharp.API.Core.Attributes;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class SchemaMemberAttribute : Attribute
|
||||
{
|
||||
public string ClassName { get; }
|
||||
public string MemberName { get; }
|
||||
|
||||
public SchemaMemberAttribute(string className, string memberName)
|
||||
{
|
||||
ClassName = className;
|
||||
MemberName = memberName;
|
||||
}
|
||||
}
|
||||
@@ -21,11 +21,14 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Core.Attributes.Registration;
|
||||
using CounterStrikeSharp.API.Core.Translations;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Events;
|
||||
using CounterStrikeSharp.API.Modules.Timers;
|
||||
using CounterStrikeSharp.API.Modules.Config;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
@@ -36,6 +39,13 @@ namespace CounterStrikeSharp.API.Core
|
||||
|
||||
public BasePlugin()
|
||||
{
|
||||
RegisterListener<Listeners.OnMapEnd>(() =>
|
||||
{
|
||||
foreach (KeyValuePair<Delegate, EntityIO.EntityOutputCallback> callback in EntitySingleOutputHooks)
|
||||
{
|
||||
UnhookSingleEntityOutputInternal(callback.Value.Classname, callback.Value.Output, callback.Value.Handler);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public abstract string ModuleName { get; }
|
||||
@@ -50,6 +60,8 @@ namespace CounterStrikeSharp.API.Core
|
||||
public string ModuleDirectory => Path.GetDirectoryName(ModulePath);
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public IStringLocalizer Localizer { get; set; }
|
||||
|
||||
public virtual void Load(bool hotReload)
|
||||
{
|
||||
}
|
||||
@@ -108,6 +120,12 @@ namespace CounterStrikeSharp.API.Core
|
||||
public readonly Dictionary<Delegate, CallbackSubscriber> Listeners =
|
||||
new Dictionary<Delegate, CallbackSubscriber>();
|
||||
|
||||
public readonly Dictionary<Delegate, CallbackSubscriber> EntityOutputHooks =
|
||||
new Dictionary<Delegate, CallbackSubscriber>();
|
||||
|
||||
internal readonly Dictionary<Delegate, EntityIO.EntityOutputCallback> EntitySingleOutputHooks =
|
||||
new Dictionary<Delegate, EntityIO.EntityOutputCallback>();
|
||||
|
||||
public readonly List<Timer> Timers = new List<Timer>();
|
||||
|
||||
public delegate HookResult GameEventHandler<T>(T @event, GameEventInfo info) where T : GameEvent;
|
||||
@@ -156,39 +174,53 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
var caller = (i != -1) ? new CCSPlayerController(NativeAPI.GetEntityFromIndex(i + 1)) : null;
|
||||
var command = new CommandInfo(ptr, caller);
|
||||
|
||||
using var temporaryCulture = new WithTemporaryCulture(caller.GetLanguage());
|
||||
|
||||
var methodInfo = handler?.GetMethodInfo();
|
||||
|
||||
if (!AdminManager.CommandIsOverriden(name))
|
||||
// We do not need to do permission checks on commands executed from the server console.
|
||||
// The server will always be allowed to execute commands (unless marked as client only like above)
|
||||
if (caller != null)
|
||||
{
|
||||
// Do not execute command if we do not have the correct permissions.
|
||||
var permissions = methodInfo?.GetCustomAttributes<BaseRequiresPermissions>();
|
||||
if (permissions != null)
|
||||
var adminData = AdminManager.GetPlayerAdminData(caller!.AuthorizedSteamID);
|
||||
var permissionsToCheck = new List<BaseRequiresPermissions>();
|
||||
|
||||
|
||||
// If our command is overriden, we dynamically create a new permissions attribute
|
||||
// based on the data that is stored in admin_overrides.json.
|
||||
if (AdminManager.CommandIsOverriden(name))
|
||||
{
|
||||
foreach (var attr in permissions)
|
||||
var data = AdminManager.GetCommandOverrideData(name);
|
||||
if (data != null)
|
||||
{
|
||||
attr.Command = name;
|
||||
if (!attr.CanExecuteCommand(caller))
|
||||
{
|
||||
command.ReplyToCommand("[CSS] You do not have the correct permissions to execute this command.");
|
||||
return;
|
||||
}
|
||||
var attrType = (data.CheckType == "all") ? typeof(RequiresPermissions) : typeof(RequiresPermissionsOr);
|
||||
var attr = (BaseRequiresPermissions)Activator.CreateInstance(attrType, args: AdminManager.GetPermissionOverrides(name));
|
||||
|
||||
if (attr != null) permissionsToCheck.Add(attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If this command has it's permissions overriden, we will do an AND check for all permissions.
|
||||
else
|
||||
{
|
||||
// I don't know if this is the most sane implementation of this, can be edited in code review.
|
||||
var data = AdminManager.GetCommandOverrideData(name);
|
||||
if (data != null)
|
||||
// The permissions for this command are not being overriden here, so we
|
||||
// grab the permissions to check straight from the attribute.
|
||||
else
|
||||
{
|
||||
var permissions = methodInfo?.GetCustomAttributes<BaseRequiresPermissions>();
|
||||
if (permissions != null) permissionsToCheck.AddRange(permissions);
|
||||
}
|
||||
|
||||
foreach (var attr in permissionsToCheck)
|
||||
{
|
||||
var attrType = (data.CheckType == "all") ? typeof(RequiresPermissions) : typeof(RequiresPermissionsOr);
|
||||
var attr = (BaseRequiresPermissions)Activator.CreateInstance(attrType, args: AdminManager.GetPermissionOverrides(name));
|
||||
attr.Command = name;
|
||||
if (!attr.CanExecuteCommand(caller))
|
||||
{
|
||||
command.ReplyToCommand("[CSS] You do not have the correct permissions to execute this command.");
|
||||
var responseStr = (attr.GetType() == typeof(RequiresPermissions)) ?
|
||||
"You are missing the correct permissions" : "You do not have one of the correct permissions";
|
||||
|
||||
var flags = attr.Permissions.Except(adminData?.GetAllFlags() ?? new HashSet<string>());
|
||||
flags = flags.Except(adminData?.Groups ?? new HashSet<string>());
|
||||
command.ReplyToCommand($"[CSS] {responseStr} ({string.Join(", ", flags)}) to execute this command.");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -196,15 +228,17 @@ namespace CounterStrikeSharp.API.Core
|
||||
|
||||
// Do not execute if we shouldn't be calling this command.
|
||||
var helperAttribute = methodInfo?.GetCustomAttribute<CommandHelperAttribute>();
|
||||
if (helperAttribute != null)
|
||||
if (helperAttribute != null)
|
||||
{
|
||||
switch (helperAttribute.WhoCanExcecute)
|
||||
{
|
||||
case CommandUsage.CLIENT_AND_SERVER: break; // Allow command through.
|
||||
case CommandUsage.CLIENT_ONLY:
|
||||
if (caller == null || !caller.IsValid) { command.ReplyToCommand("[CSS] This command can only be executed by clients."); return; } break;
|
||||
if (caller == null || !caller.IsValid) { command.ReplyToCommand("[CSS] This command can only be executed by clients."); return; }
|
||||
break;
|
||||
case CommandUsage.SERVER_ONLY:
|
||||
if (caller != null && caller.IsValid) { command.ReplyToCommand("[CSS] This command can only be executed by the server."); return; } break;
|
||||
if (caller != null && caller.IsValid) { command.ReplyToCommand("[CSS] This command can only be executed by the server."); return; }
|
||||
break;
|
||||
default: throw new ArgumentException("Unrecognised CommandUsage value passed in CommandHelperAttribute.");
|
||||
}
|
||||
|
||||
@@ -212,7 +246,12 @@ namespace CounterStrikeSharp.API.Core
|
||||
// but we'll just ignore that for this check.
|
||||
if (helperAttribute.MinArgs != 0 && command.ArgCount - 1 < helperAttribute.MinArgs)
|
||||
{
|
||||
command.ReplyToCommand($"[CSS] Expected usage: \"!{command.ArgByIndex(0)} {helperAttribute.Usage}\".");
|
||||
// Remove the "css_" from the beginning of the command name if it's present.
|
||||
// Most of the time, users will be calling commands from chat.
|
||||
var commandCalled = command.ArgByIndex(0);
|
||||
var properCommandName = (commandCalled.StartsWith("css_")) ? commandCalled.Replace("css_", "") : commandCalled;
|
||||
|
||||
command.ReplyToCommand($"[CSS] Expected usage: \"!{properCommandName} {helperAttribute.Usage}\".");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -354,6 +393,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
this.RegisterAttributeHandlers(instance);
|
||||
this.RegisterConsoleCommandAttributeHandlers(instance);
|
||||
this.RegisterEntityOutputAttributeHandlers(instance);
|
||||
}
|
||||
|
||||
public void InitializeConfig(object instance, Type pluginType)
|
||||
@@ -430,6 +470,77 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterEntityOutputAttributeHandlers(object instance)
|
||||
{
|
||||
var handlers = instance.GetType()
|
||||
.GetMethods()
|
||||
.Where(method => method.GetCustomAttributes<EntityOutputHookAttribute>().Any())
|
||||
.ToArray();
|
||||
|
||||
foreach (var handler in handlers)
|
||||
{
|
||||
var attributes = handler.GetCustomAttributes<EntityOutputHookAttribute>();
|
||||
foreach (var outputInfo in attributes)
|
||||
{
|
||||
HookEntityOutput(outputInfo.Classname, outputInfo.OutputName, handler.CreateDelegate<EntityIO.EntityOutputHandler>(instance));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void HookEntityOutput(string classname, string outputName, EntityIO.EntityOutputHandler handler, HookMode mode = HookMode.Pre)
|
||||
{
|
||||
var subscriber = new CallbackSubscriber(handler, handler,
|
||||
() => UnhookEntityOutput(classname, outputName, handler));
|
||||
|
||||
NativeAPI.HookEntityOutput(classname, outputName, subscriber.GetInputArgument(), mode);
|
||||
EntityOutputHooks[handler] = subscriber;
|
||||
}
|
||||
|
||||
public void UnhookEntityOutput(string classname, string outputName, EntityIO.EntityOutputHandler handler, HookMode mode = HookMode.Pre)
|
||||
{
|
||||
if (!EntityOutputHooks.TryGetValue(handler, out var subscriber)) return;
|
||||
|
||||
NativeAPI.UnhookEntityOutput(classname, outputName, subscriber.GetInputArgument(), mode);
|
||||
FunctionReference.Remove(subscriber.GetReferenceIdentifier());
|
||||
EntityOutputHooks.Remove(handler);
|
||||
}
|
||||
|
||||
public void HookSingleEntityOutput(CEntityInstance entityInstance, string outputName, EntityIO.EntityOutputHandler handler)
|
||||
{
|
||||
// since we wrap around the plugin handler we need to do this to ensure that the plugin callback is only called
|
||||
// if the entity instance is the same.
|
||||
EntityIO.EntityOutputHandler internalHandler = (output, name, activator, caller, value, delay) =>
|
||||
{
|
||||
if (caller == entityInstance)
|
||||
{
|
||||
return handler(output, name, activator, caller, value, delay);
|
||||
}
|
||||
|
||||
return HookResult.Continue;
|
||||
};
|
||||
|
||||
HookEntityOutput(entityInstance.DesignerName, outputName, internalHandler);
|
||||
|
||||
// because of ^ we would not be able to unhook since we passed the 'internalHandler' and that's what is being stored, not the original handler
|
||||
// but the plugin could only pass the original handler for unhooking.
|
||||
// (this dictionary does not needed to be cleared on dispose as it has no unmanaged reference and those are already being disposed, but on map end)
|
||||
// (the internal class is needed to be able to remove them on map start)
|
||||
EntitySingleOutputHooks[handler] = new EntityIO.EntityOutputCallback(entityInstance.DesignerName, outputName, internalHandler);
|
||||
}
|
||||
|
||||
public void UnhookSingleEntityOutput(CEntityInstance entityInstance, string outputName, EntityIO.EntityOutputHandler handler)
|
||||
{
|
||||
UnhookSingleEntityOutputInternal(entityInstance.DesignerName, outputName, handler);
|
||||
}
|
||||
|
||||
private void UnhookSingleEntityOutputInternal(string classname, string outputName, EntityIO.EntityOutputHandler handler)
|
||||
{
|
||||
if (!EntitySingleOutputHooks.TryGetValue(handler, out var internalHandler)) return;
|
||||
|
||||
UnhookEntityOutput(classname, outputName, internalHandler.Handler);
|
||||
EntitySingleOutputHooks.Remove(handler);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
@@ -464,6 +575,11 @@ namespace CounterStrikeSharp.API.Core
|
||||
subscriber.Dispose();
|
||||
}
|
||||
|
||||
foreach (var subscriber in EntityOutputHooks.Values)
|
||||
{
|
||||
subscriber.Dispose();
|
||||
}
|
||||
|
||||
foreach (var timer in Timers)
|
||||
{
|
||||
timer.Kill();
|
||||
|
||||
@@ -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
|
||||
@@ -23,6 +23,8 @@ using CounterStrikeSharp.API.Modules.Utils;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using CounterStrikeSharp.API.Core.Hosting;
|
||||
using CounterStrikeSharp.API.Core.Logging;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
@@ -46,6 +48,9 @@ namespace CounterStrikeSharp.API.Core
|
||||
|
||||
[JsonPropertyName("PluginHotReloadEnabled")]
|
||||
public bool PluginHotReloadEnabled { get; set; } = true;
|
||||
|
||||
[JsonPropertyName("ServerLanguage")]
|
||||
public string ServerLanguage { get; set; } = "en";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -88,6 +93,8 @@ namespace CounterStrikeSharp.API.Core
|
||||
/// When enabled, plugins are automatically reloaded when their .dll file is updated.
|
||||
/// </summary>
|
||||
public static bool PluginHotReloadEnabled => _coreConfig.PluginHotReloadEnabled;
|
||||
|
||||
public static string ServerLanguage => _coreConfig.ServerLanguage;
|
||||
}
|
||||
|
||||
public partial class CoreConfig : IStartupService
|
||||
@@ -138,6 +145,29 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to load core configuration, fallback values will be used");
|
||||
}
|
||||
|
||||
var serverCulture = CultureInfo.GetCultures(CultureTypes.AllCultures)
|
||||
.FirstOrDefault(x => x.Name == ServerLanguage);
|
||||
if (serverCulture == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogWarning("Server Language \"{ServerLanguage}\" is not supported, falling back to \"en\"",
|
||||
ServerLanguage);
|
||||
_coreConfig.ServerLanguage = "en";
|
||||
serverCulture = new CultureInfo("en");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_logger.LogWarning("Server is running in invariant mode, translations will not be available.");
|
||||
serverCulture = CultureInfo.InvariantCulture;
|
||||
}
|
||||
}
|
||||
|
||||
CultureInfo.DefaultThreadCurrentUICulture = serverCulture;
|
||||
CultureInfo.DefaultThreadCurrentCulture = serverCulture;
|
||||
CultureInfo.CurrentUICulture = serverCulture;
|
||||
CultureInfo.CurrentCulture = serverCulture;
|
||||
|
||||
_logger.LogInformation("Successfully loaded core configuration");
|
||||
}
|
||||
|
||||
@@ -34,34 +34,58 @@ public class Offsets
|
||||
|
||||
public sealed class GameDataProvider : IStartupService
|
||||
{
|
||||
private readonly string _gameDataFilePath;
|
||||
private readonly string _gameDataDirectoryPath;
|
||||
public Dictionary<string,LoadedGameData> Methods;
|
||||
private readonly ILogger<GameDataProvider> _logger;
|
||||
|
||||
public GameDataProvider(IScriptHostConfiguration scriptHostConfiguration, ILogger<GameDataProvider> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_gameDataFilePath = Path.Join(scriptHostConfiguration.GameDataPath, "gamedata.json");
|
||||
_gameDataDirectoryPath = scriptHostConfiguration.GameDataPath;
|
||||
}
|
||||
|
||||
public void Load()
|
||||
{
|
||||
try
|
||||
{
|
||||
Methods = JsonSerializer.Deserialize<Dictionary<string, LoadedGameData>>(File.ReadAllText(_gameDataFilePath))!;
|
||||
Methods = new Dictionary<string, LoadedGameData>();
|
||||
|
||||
foreach (string filePath in Directory.EnumerateFiles(_gameDataDirectoryPath, "*.json"))
|
||||
{
|
||||
string jsonContent = File.ReadAllText(filePath);
|
||||
Dictionary<string, LoadedGameData> loadedMethods = JsonSerializer.Deserialize<Dictionary<string, LoadedGameData>>(jsonContent)!;
|
||||
|
||||
foreach (KeyValuePair<string, LoadedGameData> loadedMethod in loadedMethods)
|
||||
{
|
||||
if (Methods.ContainsKey(loadedMethod.Key))
|
||||
{
|
||||
_logger.LogWarning("GameData Method \"{Key}\" loaded a duplicate entry from {filePath}.", loadedMethod.Key, filePath);
|
||||
}
|
||||
|
||||
Methods[loadedMethod.Key] = loadedMethod.Value;
|
||||
}
|
||||
|
||||
if (loadedMethods != null)
|
||||
{
|
||||
_logger.LogInformation("Successfully loaded {Count} game data entries from {Path}", loadedMethods.Count, filePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Unable to load game data entries from {Path}, game data file is empty", filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to load game data");
|
||||
}
|
||||
|
||||
_logger.LogInformation("Successfully loaded {Count} game data entries from {Path}", Methods.Count, _gameDataFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
public static class GameData
|
||||
{
|
||||
internal static GameDataProvider GameDataProvider { get; set; } = null!;
|
||||
|
||||
public static string GetSignature(string key)
|
||||
{
|
||||
Application.Instance.Logger.LogDebug("Getting signature: {Key}", key);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
@@ -54,6 +55,8 @@ namespace CounterStrikeSharp.API.Core
|
||||
string ModulePath { get; internal set; }
|
||||
|
||||
ILogger Logger { get; set; }
|
||||
|
||||
IStringLocalizer Localizer { get; set; }
|
||||
|
||||
void RegisterAllAttributes(object instance);
|
||||
|
||||
|
||||
@@ -93,6 +93,13 @@ namespace CounterStrikeSharp.API.Core
|
||||
[ListenerName("OnClientDisconnectPost")]
|
||||
public delegate void OnClientDisconnectPost(int playerSlot);
|
||||
|
||||
/// <summary>
|
||||
/// Called when a client transmits voice data
|
||||
/// </summary>
|
||||
/// <param name="playerSlot">The player slot of the client.</param>
|
||||
[ListenerName("OnClientVoice")]
|
||||
public delegate void OnClientVoice(int playerSlot);
|
||||
|
||||
/// <summary>
|
||||
/// Called when a client has been authorized by Steam.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
@@ -26,4 +26,6 @@ public partial class CBaseEntity
|
||||
/// Shorthand for accessing an entity's CBodyComponent?.SceneNode?.AbsRotation;
|
||||
/// </summary>
|
||||
public QAngle? AbsRotation => CBodyComponent?.SceneNode?.AbsRotation;
|
||||
|
||||
public T? GetVData<T>() where T : CEntitySubclassVDataBase => (T)Activator.CreateInstance(typeof(T), Marshal.ReadIntPtr(SubclassID.Handle + 8));
|
||||
}
|
||||
@@ -14,4 +14,13 @@ public partial class CBasePlayerPawn
|
||||
{
|
||||
VirtualFunction.CreateVoid<IntPtr, bool, bool>(Handle, GameData.GetOffset("CBasePlayerPawn_CommitSuicide"))(Handle, explode, force);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove Player Item
|
||||
/// </summary>
|
||||
/// <param name="weapon"></param>
|
||||
public void RemovePlayerItem(CBasePlayerWeapon weapon)
|
||||
{
|
||||
VirtualFunctions.RemovePlayerItemVirtual(Handle, weapon.Handle);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CBasePlayerWeapon
|
||||
{
|
||||
public CBasePlayerWeaponVData? VData => GetVData<CBasePlayerWeaponVData>();
|
||||
}
|
||||
@@ -10,17 +10,13 @@ namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CCSPlayerController
|
||||
{
|
||||
public int? UserId
|
||||
{
|
||||
get
|
||||
{
|
||||
return NativeAPI.GetUseridFromIndex((int)this.Index);
|
||||
}
|
||||
}
|
||||
public int? UserId => NativeAPI.GetUseridFromIndex((int)Index);
|
||||
public CsTeam Team => (CsTeam)TeamNum;
|
||||
|
||||
public IntPtr GiveNamedItem(string item)
|
||||
{
|
||||
if (!PlayerPawn.IsValid) return 0;
|
||||
if (PlayerPawn.Value == null) return 0;
|
||||
if (!PlayerPawn.Value.IsValid) return 0;
|
||||
if (PlayerPawn.Value.ItemServices == null) return 0;
|
||||
|
||||
@@ -35,7 +31,7 @@ public partial class CCSPlayerController
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
return this.GiveNamedItem(itemString);
|
||||
return GiveNamedItem(itemString);
|
||||
}
|
||||
|
||||
public void PrintToConsole(string message)
|
||||
@@ -45,12 +41,12 @@ public partial class CCSPlayerController
|
||||
|
||||
public void PrintToChat(string message)
|
||||
{
|
||||
VirtualFunctions.ClientPrint(this.Handle, HudDestination.Chat, message, 0, 0, 0, 0);
|
||||
VirtualFunctions.ClientPrint(Handle, HudDestination.Chat, message, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
public void PrintToCenter(string message)
|
||||
{
|
||||
VirtualFunctions.ClientPrint(this.Handle, HudDestination.Center, message, 0, 0, 0, 0);
|
||||
VirtualFunctions.ClientPrint(Handle, HudDestination.Center, message, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
public void PrintToCenterHtml(string message) => PrintToCenterHtml(message, 5);
|
||||
@@ -72,14 +68,16 @@ public partial class CCSPlayerController
|
||||
public void DropActiveWeapon()
|
||||
{
|
||||
if (!PlayerPawn.IsValid) return;
|
||||
if (PlayerPawn.Value == null) return;
|
||||
if (!PlayerPawn.Value.IsValid) return;
|
||||
if (PlayerPawn.Value.ItemServices == null) return;
|
||||
if (PlayerPawn.Value.WeaponServices == null) return;
|
||||
if (!PlayerPawn.Value.WeaponServices.ActiveWeapon.IsValid) return;
|
||||
|
||||
CCSPlayer_ItemServices itemServices = new CCSPlayer_ItemServices(PlayerPawn.Value.ItemServices.Handle);
|
||||
CCSPlayer_WeaponServices weponServices = new CCSPlayer_WeaponServices(PlayerPawn.Value.WeaponServices.Handle);
|
||||
itemServices.DropActivePlayerWeapon(weponServices.ActiveWeapon.Value);
|
||||
CCSPlayer_WeaponServices weaponServices = new CCSPlayer_WeaponServices(PlayerPawn.Value.WeaponServices.Handle);
|
||||
|
||||
itemServices.DropActivePlayerWeapon(weaponServices.ActiveWeapon.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -88,6 +86,7 @@ public partial class CCSPlayerController
|
||||
public void RemoveWeapons()
|
||||
{
|
||||
if (!PlayerPawn.IsValid) return;
|
||||
if (PlayerPawn.Value == null) return;
|
||||
if (!PlayerPawn.Value.IsValid) return;
|
||||
if (PlayerPawn.Value.ItemServices == null) return;
|
||||
|
||||
@@ -103,6 +102,7 @@ public partial class CCSPlayerController
|
||||
public void CommitSuicide(bool explode, bool force)
|
||||
{
|
||||
if (!PlayerPawn.IsValid) return;
|
||||
if (PlayerPawn.Value == null) return;
|
||||
if (!PlayerPawn.Value.IsValid) return;
|
||||
|
||||
PlayerPawn.Value.CommitSuicide(explode, force);
|
||||
@@ -114,6 +114,7 @@ public partial class CCSPlayerController
|
||||
public void Respawn()
|
||||
{
|
||||
if (!PlayerPawn.IsValid) return;
|
||||
if (PlayerPawn.Value == null) return;
|
||||
if (!PlayerPawn.Value.IsValid) return;
|
||||
|
||||
VirtualFunctions.CCSPlayerPawn_Respawn(PlayerPawn.Value.Handle);
|
||||
@@ -128,7 +129,7 @@ public partial class CCSPlayerController
|
||||
/// <param name="team">The team to switch to</param>
|
||||
public void SwitchTeam(CsTeam team)
|
||||
{
|
||||
VirtualFunctions.SwitchTeam(this.Handle, (byte)team);
|
||||
VirtualFunctions.SwitchTeam(Handle, (byte)team);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -151,7 +152,7 @@ public partial class CCSPlayerController
|
||||
/// <returns>ConVar string value</returns>
|
||||
public string GetConVarValue(string conVar)
|
||||
{
|
||||
return NativeAPI.GetClientConvarValue(this.Slot, conVar);
|
||||
return NativeAPI.GetClientConvarValue(Slot, conVar);
|
||||
}
|
||||
|
||||
public string GetConVarValue(ConVar? conVar)
|
||||
@@ -177,7 +178,7 @@ public partial class CCSPlayerController
|
||||
throw new InvalidOperationException("'SetFakeClientConVar' can only be called for fake clients (bots)");
|
||||
}
|
||||
|
||||
NativeAPI.SetFakeClientConvarValue(this.Slot, conVar, value);
|
||||
NativeAPI.SetFakeClientConvarValue(Slot, conVar, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -202,6 +203,21 @@ public partial class CCSPlayerController
|
||||
|
||||
public void ExecuteClientCommand(string command) => NativeAPI.IssueClientCommand(Slot, command);
|
||||
|
||||
/// <summary>
|
||||
/// Overrides who a player can hear in voice chat.
|
||||
/// </summary>
|
||||
/// <param name="sender">Player talking in the voice chat</param>
|
||||
/// <param name="override">Whether the talker should be heard</param>
|
||||
public void SetListenOverride(CCSPlayerController sender, ListenOverride @override)
|
||||
{
|
||||
NativeAPI.SetClientListening(Handle, sender.Handle, (Byte)@override);
|
||||
}
|
||||
|
||||
public ListenOverride GetListenOverride(CCSPlayerController sender)
|
||||
{
|
||||
return NativeAPI.GetClientListening(Handle, sender.Handle);
|
||||
}
|
||||
|
||||
public int Slot => (int)Index - 1;
|
||||
|
||||
/// <summary>
|
||||
@@ -211,8 +227,8 @@ public partial class CCSPlayerController
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!this.IsValid) return null;
|
||||
var authorizedSteamId = NativeAPI.GetPlayerAuthorizedSteamid(this.Slot);
|
||||
if (!IsValid) return null;
|
||||
var authorizedSteamId = NativeAPI.GetPlayerAuthorizedSteamid(Slot);
|
||||
if ((long)authorizedSteamId == -1) return null;
|
||||
|
||||
return (SteamID)authorizedSteamId;
|
||||
@@ -227,11 +243,20 @@ public partial class CCSPlayerController
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!this.IsValid) return null;
|
||||
var ipAddress = NativeAPI.GetPlayerIpAddress(this.Slot);
|
||||
if (!IsValid) return null;
|
||||
var ipAddress = NativeAPI.GetPlayerIpAddress(Slot);
|
||||
if (string.IsNullOrWhiteSpace(ipAddress)) return null;
|
||||
|
||||
return ipAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines how the player interacts with voice chat.
|
||||
/// </summary>
|
||||
public VoiceFlags VoiceFlags
|
||||
{
|
||||
get => (VoiceFlags)NativeAPI.GetClientVoiceFlags(Handle);
|
||||
set => NativeAPI.SetClientVoiceFlags(Handle, (Byte)value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CCSWeaponBase
|
||||
{
|
||||
public new CCSWeaponBaseVData? VData => GetVData<CCSWeaponBaseVData>();
|
||||
}
|
||||
49
managed/CounterStrikeSharp.API/Core/Model/CEntityIOOutput.cs
Normal file
49
managed/CounterStrikeSharp.API/Core/Model/CEntityIOOutput.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CEntityIOOutput
|
||||
{
|
||||
public EntityIOConnection_t? Connections => Utilities.GetPointer<EntityIOConnection_t>(Handle + 8);
|
||||
|
||||
public EntityIOOutputDesc_t Description => new(Marshal.ReadIntPtr(Handle + 16));
|
||||
}
|
||||
|
||||
public class EntityIOOutputDesc_t : NativeObject
|
||||
{
|
||||
public EntityIOOutputDesc_t(IntPtr pointer) : base(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
public string Name => Utilities.ReadStringUtf8(Handle + 0);
|
||||
public unsafe ref uint Flags => ref Unsafe.AsRef<uint>((void*)(Handle + 8));
|
||||
public unsafe ref uint OutputOffset => ref Unsafe.AsRef<uint>((void*)(Handle + 16));
|
||||
}
|
||||
|
||||
public class EntityIOConnection_t : EntityIOConnectionDesc_t
|
||||
{
|
||||
public EntityIOConnection_t(IntPtr pointer) : base(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
public unsafe ref bool MarkedForRemoval => ref Unsafe.AsRef<bool>((void*)(Handle + 40));
|
||||
|
||||
public EntityIOConnection_t? Next => Utilities.GetPointer<EntityIOConnection_t>(Handle + 48);
|
||||
}
|
||||
|
||||
public class EntityIOConnectionDesc_t : NativeObject
|
||||
{
|
||||
public EntityIOConnectionDesc_t(IntPtr pointer) : base(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
public string TargetDesc => Utilities.ReadStringUtf8(Handle + 0);
|
||||
public string TargetInput => Utilities.ReadStringUtf8(Handle + 8);
|
||||
public string ValueOverride => Utilities.ReadStringUtf8(Handle + 16);
|
||||
public CEntityHandle Target => new(Handle + 24);
|
||||
public unsafe ref EntityIOTargetType_t TargetType => ref Unsafe.AsRef<EntityIOTargetType_t>((void*)(Handle + 28));
|
||||
public unsafe ref int TimesToFire => ref Unsafe.AsRef<int>((void*)(Handle + 32));
|
||||
public unsafe ref float Delay => ref Unsafe.AsRef<float>((void*)(Handle + 36));
|
||||
}
|
||||
@@ -58,6 +58,24 @@ public partial class CEntityInstance : IEquatable<CEntityInstance>
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls a named input method on an entity.
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// entity.AcceptInput("Break");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </summary>
|
||||
/// <param name="inputName">Input action name</param>
|
||||
/// <param name="activator">Entity which initiated the action, <see langword="null"/> for no entity</param>
|
||||
/// <param name="caller">Entity that is sending the event, <see langword="null"/> for no entity</param>
|
||||
/// <param name="value">String variant value to send with the event</param>
|
||||
/// <param name="outputId">Unknown, defaults to 0</param>
|
||||
public void AcceptInput(string inputName, CEntityInstance? activator = null, CEntityInstance? caller = null, string value = "", int outputId = 0)
|
||||
{
|
||||
VirtualFunctions.AcceptInput(Handle, inputName, activator?.Handle ?? IntPtr.Zero, caller?.Handle ?? IntPtr.Zero, value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class CEntityIdentity
|
||||
|
||||
13
managed/CounterStrikeSharp.API/Core/Model/CVariant.cs
Normal file
13
managed/CounterStrikeSharp.API/Core/Model/CVariant.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Placeholder for CVariant
|
||||
/// <see href="https://github.com/alliedmodders/hl2sdk/blob/cs2/public/variant.h"/>
|
||||
/// <remarks>A lot of entity outputs do not use this value</remarks>
|
||||
/// </summary>
|
||||
public class CVariant : NativeObject
|
||||
{
|
||||
public CVariant(IntPtr pointer) : base(pointer)
|
||||
{
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ public interface IPluginContext
|
||||
IPlugin Plugin { get; }
|
||||
int PluginId { get; }
|
||||
|
||||
string FilePath { get; }
|
||||
void Load(bool hotReload);
|
||||
void Unload(bool hotReload);
|
||||
}
|
||||
@@ -20,8 +20,11 @@ using System.Threading.Tasks;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Core.Hosting;
|
||||
using CounterStrikeSharp.API.Core.Logging;
|
||||
using CounterStrikeSharp.API.Core.Translations;
|
||||
using McMaster.NETCore.Plugins;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Serilog;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
@@ -43,6 +46,8 @@ namespace CounterStrikeSharp.API.Core.Plugin
|
||||
private readonly string _path;
|
||||
private readonly FileSystemWatcher _fileWatcher;
|
||||
private readonly IServiceProvider _applicationServiceProvider;
|
||||
|
||||
public string FilePath => _path;
|
||||
|
||||
// TOOD: ServiceCollection
|
||||
private ILogger _logger = CoreLogging.Factory.CreateLogger<PluginContext>();
|
||||
@@ -163,8 +168,12 @@ namespace CounterStrikeSharp.API.Core.Plugin
|
||||
method?.Invoke(pluginServiceCollection, new object[] { serviceCollection });
|
||||
}
|
||||
}
|
||||
|
||||
serviceCollection.AddSingleton<IPluginContext>(this);
|
||||
serviceCollection.TryAddSingleton<IStringLocalizerFactory, JsonStringLocalizerFactory>();
|
||||
serviceCollection.TryAddTransient(typeof(IStringLocalizer<>), typeof(StringLocalizer<>));
|
||||
serviceCollection.TryAddTransient(typeof(IStringLocalizer), typeof(StringLocalizer));
|
||||
|
||||
serviceCollection.AddSingleton(this);
|
||||
ServiceProvider = serviceCollection.BuildServiceProvider();
|
||||
|
||||
var minimumApiVersion = pluginType.GetCustomAttribute<MinimumApiVersion>()?.Version;
|
||||
@@ -185,6 +194,7 @@ namespace CounterStrikeSharp.API.Core.Plugin
|
||||
|
||||
Plugin.ModulePath = _path;
|
||||
Plugin.RegisterAllAttributes(Plugin);
|
||||
Plugin.Localizer = ServiceProvider.GetRequiredService<IStringLocalizer>();
|
||||
Plugin.Logger = ServiceProvider.GetRequiredService<ILoggerFactory>().CreateLogger(pluginType);
|
||||
|
||||
Plugin.InitializeConfig(Plugin, pluginType);
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Globalization;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Translations;
|
||||
|
||||
public interface IPlayerLanguageManager
|
||||
{
|
||||
void SetLanguage(SteamID steamId, CultureInfo cultureInfo);
|
||||
CultureInfo GetLanguage(SteamID steamId);
|
||||
CultureInfo GetDefaultLanguage();
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Translations
|
||||
{
|
||||
public class JsonResourceManager
|
||||
{
|
||||
private static readonly JsonDocumentOptions _jsonDocumentOptions = new()
|
||||
{
|
||||
CommentHandling = JsonCommentHandling.Skip,
|
||||
AllowTrailingCommas = true,
|
||||
};
|
||||
|
||||
private ConcurrentDictionary<string, ConcurrentDictionary<string, string>> _resourcesCache = new();
|
||||
|
||||
public JsonResourceManager(string resourcesPath)
|
||||
{
|
||||
ResourcesPath = resourcesPath;
|
||||
}
|
||||
|
||||
public string ResourcesPath { get; }
|
||||
|
||||
public virtual ConcurrentDictionary<string, string> GetResourceSet(string cultureName)
|
||||
{
|
||||
TryLoadResourceSet(cultureName);
|
||||
_resourcesCache.TryGetValue(cultureName, out ConcurrentDictionary<string, string> resources);
|
||||
|
||||
return resources;
|
||||
}
|
||||
|
||||
public virtual ConcurrentDictionary<string, string> GetResourceSet(CultureInfo culture, bool tryParents)
|
||||
{
|
||||
TryLoadResourceSet(culture);
|
||||
|
||||
if (tryParents)
|
||||
{
|
||||
var allResources = new ConcurrentDictionary<string, string>();
|
||||
do
|
||||
{
|
||||
TryLoadResourceSet(culture);
|
||||
if (_resourcesCache.TryGetValue(culture.Name, out ConcurrentDictionary<string, string> resources))
|
||||
{
|
||||
foreach (var entry in resources)
|
||||
{
|
||||
allResources.TryAdd(entry.Key, entry.Value);
|
||||
}
|
||||
}
|
||||
|
||||
culture = culture.Parent;
|
||||
} while (culture != CultureInfo.InvariantCulture);
|
||||
|
||||
return allResources;
|
||||
}
|
||||
else
|
||||
{
|
||||
_resourcesCache.TryGetValue(culture.Name, out ConcurrentDictionary<string, string> resources);
|
||||
|
||||
return resources;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetFallbackString(string name)
|
||||
{
|
||||
GetResourceSet("en");
|
||||
|
||||
if (_resourcesCache.ContainsKey("en"))
|
||||
{
|
||||
if (_resourcesCache["en"].TryGetValue(name, out string value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual string GetString(string name)
|
||||
{
|
||||
var culture = CultureInfo.CurrentUICulture;
|
||||
GetResourceSet(culture, tryParents: true);
|
||||
|
||||
if (_resourcesCache.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (_resourcesCache.ContainsKey(culture.Name))
|
||||
{
|
||||
if (_resourcesCache[culture.Name].TryGetValue(name, out string value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
culture = culture.Parent;
|
||||
} while (culture != culture.Parent);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual string? GetString(string name, CultureInfo culture)
|
||||
{
|
||||
var values = GetResourceSet(culture, tryParents: true);
|
||||
|
||||
if (values.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return values.TryGetValue(name, out string value)
|
||||
? value
|
||||
: null;
|
||||
}
|
||||
|
||||
private void TryLoadResourceSet(string cultureName)
|
||||
{
|
||||
if (!_resourcesCache.ContainsKey(cultureName))
|
||||
{
|
||||
var file = Path.Combine(ResourcesPath, $"{cultureName}.json");
|
||||
|
||||
var resources = LoadJsonResources(file);
|
||||
|
||||
_resourcesCache.TryAdd(cultureName,
|
||||
new ConcurrentDictionary<string, string>(resources.ToDictionary(r => r.Key, r => r.Value)));
|
||||
}
|
||||
}
|
||||
|
||||
private void TryLoadResourceSet(CultureInfo culture)
|
||||
{
|
||||
TryLoadResourceSet(culture.Name);
|
||||
}
|
||||
|
||||
private static IDictionary<string, string> LoadJsonResources(string filePath)
|
||||
{
|
||||
var resources = new Dictionary<string, string>();
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
using var reader = new StreamReader(filePath);
|
||||
|
||||
using var document = JsonDocument.Parse(reader.BaseStream, _jsonDocumentOptions);
|
||||
|
||||
resources = document.RootElement.EnumerateObject().ToDictionary(e => e.Name, e => e.Value.ToString());
|
||||
}
|
||||
|
||||
return resources;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Resources;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Translations;
|
||||
|
||||
public class JsonStringLocalizer : IStringLocalizer
|
||||
{
|
||||
private readonly JsonResourceManager _resourceManager;
|
||||
private readonly JsonStringProvider _resourceStringProvider;
|
||||
|
||||
public JsonStringLocalizer(string langPath)
|
||||
{
|
||||
_resourceManager = new JsonResourceManager(langPath);
|
||||
_resourceStringProvider = new(_resourceManager);
|
||||
}
|
||||
|
||||
public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
|
||||
{
|
||||
return GetAllStrings(includeParentCultures, CultureInfo.CurrentUICulture);
|
||||
}
|
||||
|
||||
public LocalizedString this[string name]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (name == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
var value = GetStringSafely(name);
|
||||
|
||||
return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
|
||||
}
|
||||
}
|
||||
|
||||
public LocalizedString this[string name, params object[] arguments]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (name == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
var format = GetStringSafely(name);
|
||||
var value = string.Format(format ?? name, arguments);
|
||||
|
||||
return new LocalizedString(name, value, resourceNotFound: format == null);
|
||||
}
|
||||
}
|
||||
|
||||
protected string? GetStringSafely(string name, CultureInfo? culture = null)
|
||||
{
|
||||
if (name == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
culture = culture ?? CultureInfo.CurrentUICulture;
|
||||
|
||||
var result = _resourceManager.GetString(name, culture);
|
||||
|
||||
// Fallback to en if running in invariant mode.
|
||||
if (result == null && culture.Equals(CultureInfo.InvariantCulture))
|
||||
{
|
||||
result = _resourceManager.GetFallbackString(name);
|
||||
}
|
||||
|
||||
// Fallback to the default culture (en-US) if the resource is not found for the current culture.
|
||||
if (result == null && !culture.Equals(CultureInfo.DefaultThreadCurrentUICulture))
|
||||
{
|
||||
result = _resourceManager.GetString(name, CultureInfo.DefaultThreadCurrentUICulture!);
|
||||
}
|
||||
|
||||
return result?.ReplaceColorTags();
|
||||
}
|
||||
|
||||
protected virtual IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures, CultureInfo culture)
|
||||
{
|
||||
if (culture == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(culture));
|
||||
}
|
||||
|
||||
var resourceNames = includeParentCultures
|
||||
? GetResourceNamesFromCultureHierarchy(culture)
|
||||
: _resourceStringProvider.GetAllResourceStrings(culture, true);
|
||||
|
||||
foreach (var name in resourceNames)
|
||||
{
|
||||
var value = GetStringSafely(name, culture);
|
||||
yield return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetResourceNamesFromCultureHierarchy(CultureInfo startingCulture)
|
||||
{
|
||||
var currentCulture = startingCulture;
|
||||
var resourceNames = new HashSet<string>();
|
||||
|
||||
while (currentCulture != currentCulture.Parent)
|
||||
{
|
||||
var cultureResourceNames = _resourceStringProvider.GetAllResourceStrings(currentCulture, false);
|
||||
|
||||
if (cultureResourceNames != null)
|
||||
{
|
||||
foreach (var resourceName in cultureResourceNames)
|
||||
{
|
||||
resourceNames.Add(resourceName);
|
||||
}
|
||||
}
|
||||
|
||||
currentCulture = currentCulture.Parent;
|
||||
}
|
||||
|
||||
return resourceNames;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using CounterStrikeSharp.API.Core.Hosting;
|
||||
using CounterStrikeSharp.API.Core.Plugin;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Translations;
|
||||
|
||||
public class JsonStringLocalizerFactory : IStringLocalizerFactory
|
||||
{
|
||||
private readonly IPluginContext _pluginContext;
|
||||
|
||||
public JsonStringLocalizerFactory(IPluginContext pluginContext)
|
||||
{
|
||||
_pluginContext = pluginContext;
|
||||
}
|
||||
|
||||
public IStringLocalizer Create(Type resourceSource)
|
||||
{
|
||||
return new JsonStringLocalizer(Path.Join(Path.GetDirectoryName(_pluginContext.FilePath), "lang"));
|
||||
}
|
||||
|
||||
public IStringLocalizer Create(string baseName, string location)
|
||||
{
|
||||
return new JsonStringLocalizer(Path.Join(Path.GetDirectoryName(_pluginContext.FilePath), "lang"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Resources;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Translations;
|
||||
|
||||
public class JsonStringProvider
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, IList<string>> _resourceNamesCache = new();
|
||||
private readonly JsonResourceManager _jsonResourceManager;
|
||||
|
||||
public JsonStringProvider(JsonResourceManager jsonResourceManager)
|
||||
{
|
||||
_jsonResourceManager = jsonResourceManager;
|
||||
}
|
||||
|
||||
private string GetResourceCacheKey(CultureInfo culture)
|
||||
{
|
||||
return $"Culture={culture.Name}";
|
||||
}
|
||||
|
||||
public IList<string> GetAllResourceStrings(CultureInfo culture, bool throwOnMissing)
|
||||
{
|
||||
var cacheKey = GetResourceCacheKey(culture);
|
||||
|
||||
return _resourceNamesCache.GetOrAdd(cacheKey, _ =>
|
||||
{
|
||||
var resourceSet = _jsonResourceManager.GetResourceSet(culture, tryParents: false);
|
||||
if (resourceSet == null)
|
||||
{
|
||||
if (throwOnMissing)
|
||||
{
|
||||
throw new MissingManifestResourceException($"The manifest resource for the culture '{culture.Name}' is missing.");
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
var names = new List<string>();
|
||||
foreach (var entry in resourceSet)
|
||||
{
|
||||
names.Add(entry.Key);
|
||||
}
|
||||
|
||||
return names;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Globalization;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Translations;
|
||||
|
||||
public static class PlayerLanguageExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the players configured language, as set using the "css_lang" command.
|
||||
/// </summary>
|
||||
public static CultureInfo GetLanguage(this CCSPlayerController? player)
|
||||
{
|
||||
if (player == null || !player.IsValid) return PlayerLanguageManager.Instance.GetDefaultLanguage();
|
||||
|
||||
return PlayerLanguageManager.Instance.GetLanguage((SteamID)player.SteamID);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Globalization;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Translations;
|
||||
|
||||
public class PlayerLanguageManager : IPlayerLanguageManager
|
||||
{
|
||||
private readonly ConcurrentDictionary<SteamID, CultureInfo> _playerLanguages = new();
|
||||
|
||||
public static IPlayerLanguageManager Instance { get; private set; } = null!;
|
||||
|
||||
public PlayerLanguageManager()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
public void SetLanguage(SteamID steamId, CultureInfo cultureInfo)
|
||||
{
|
||||
_playerLanguages[steamId] = cultureInfo;
|
||||
}
|
||||
|
||||
public CultureInfo GetLanguage(SteamID steamId)
|
||||
{
|
||||
return _playerLanguages.TryGetValue(steamId, out var cultureInfo) ? cultureInfo : GetDefaultLanguage();
|
||||
}
|
||||
|
||||
public CultureInfo GetDefaultLanguage()
|
||||
{
|
||||
return CultureInfo.DefaultThreadCurrentUICulture!;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.Reflection;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Translations;
|
||||
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static string ReplaceColorTags(this string message)
|
||||
{
|
||||
var modifiedValue = message;
|
||||
foreach (var field in typeof(ChatColors).GetFields())
|
||||
{
|
||||
string pattern = $"{{{field.Name}}}";
|
||||
if (modifiedValue.Contains(pattern, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
modifiedValue = modifiedValue.Replace(pattern, field.GetValue(null)?.ToString() ?? string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
return modifiedValue.Equals(message) ? message : $" {modifiedValue}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Translations;
|
||||
|
||||
internal class StringLocalizer : IStringLocalizer
|
||||
{
|
||||
private IStringLocalizer _localizer;
|
||||
|
||||
public StringLocalizer(IStringLocalizerFactory factory)
|
||||
{
|
||||
var type = typeof(StringLocalizer);
|
||||
var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
|
||||
_localizer = factory.Create(string.Empty, assemblyName.FullName);
|
||||
}
|
||||
|
||||
public LocalizedString this[string name] => _localizer[name];
|
||||
|
||||
public LocalizedString this[string name, params object[] arguments] => _localizer[name, arguments];
|
||||
|
||||
public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures) => _localizer.GetAllStrings(includeParentCultures);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Globalization;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Translations;
|
||||
|
||||
public sealed class WithTemporaryCulture : IDisposable
|
||||
{
|
||||
private readonly CultureInfo _originalCulture;
|
||||
|
||||
public WithTemporaryCulture(CultureInfo culture)
|
||||
{
|
||||
_originalCulture = CultureInfo.CurrentCulture;
|
||||
SetCulture(culture);
|
||||
}
|
||||
|
||||
private void SetCulture(CultureInfo cultureInfo)
|
||||
{
|
||||
CultureInfo.CurrentCulture = cultureInfo;
|
||||
CultureInfo.CurrentUICulture = cultureInfo;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
SetCulture(_originalCulture);
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,8 @@
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<EnablePackageValidation>true</EnablePackageValidation>
|
||||
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||
<NoWarn>$(NoWarn);CS1591;CP0003</NoWarn>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<Authors>Roflmuffin</Authors>
|
||||
<Description>Official server side runtime assembly for CounterStrikeSharp</Description>
|
||||
<PackageProjectUrl>http://docs.cssharp.dev/</PackageProjectUrl>
|
||||
@@ -16,6 +15,10 @@
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApiCompatValidateAssemblies>true</ApiCompatValidateAssemblies>
|
||||
<ApiCompatContractAssembly>.\ApiCompat\v151.dll</ApiCompatContractAssembly>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Modules\Commands\CommandInfo" />
|
||||
<None Remove="Modules\Disabled\**" />
|
||||
@@ -23,8 +26,10 @@
|
||||
<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" />
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
@@ -64,18 +66,18 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
// Loop over each of the admins. If one of our admins is in a group,
|
||||
// add the flags from the group to their admin definition and change
|
||||
// the admin's immunity if it's higher.
|
||||
foreach (var adminData in Admins.Values)
|
||||
foreach (var (admin, data) in Admins)
|
||||
{
|
||||
var groups = adminData.Groups;
|
||||
var groups = data.Groups;
|
||||
foreach (var group in groups)
|
||||
{
|
||||
// roflmuffin is probably smart enough to condense this function down ;)
|
||||
if (Groups.TryGetValue(group, out var groupData))
|
||||
{
|
||||
adminData.Flags.UnionWith(groupData.Flags);
|
||||
if (groupData.Immunity > adminData.Immunity)
|
||||
AddPlayerPermissions(admin, groupData.Flags.ToArray());
|
||||
if (groupData.Immunity > data.Immunity)
|
||||
{
|
||||
adminData.Immunity = groupData.Immunity;
|
||||
data.Immunity = groupData.Immunity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,9 +95,21 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
// This is here for cases where the server console is attempting to call commands.
|
||||
// The server console should have access to all commands, regardless of groups.
|
||||
if (player == null) return true;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return false; }
|
||||
var playerData = GetPlayerAdminData((SteamID)player.AuthorizedSteamID);
|
||||
return playerData?.Groups.IsSupersetOf(groups) ?? false;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot || player.IsHLTV) { return false; }
|
||||
|
||||
var playerData = GetPlayerAdminData(player.AuthorizedSteamID);
|
||||
if (playerData == null) return false;
|
||||
|
||||
var playerGroups = groups.ToHashSet();
|
||||
foreach (var domain in playerData.Flags)
|
||||
{
|
||||
if (playerData.DomainHasRootFlag(domain.Key))
|
||||
{
|
||||
playerGroups.ExceptWith(groups.Where(group => group.Contains(domain.Key + '/')));
|
||||
}
|
||||
}
|
||||
|
||||
return playerData.Groups.IsSupersetOf(playerGroups);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -104,31 +118,44 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
/// <param name="steamId">SteamID of the player.</param>
|
||||
/// <param name="groups">Groups to check for.</param>
|
||||
/// <returns>True if a player is part of all of the groups provided, false if not.</returns>
|
||||
public static bool PlayerInGroup(SteamID steamId, params string[] groups)
|
||||
public static bool PlayerInGroup(SteamID? steamId, params string[] groups)
|
||||
{
|
||||
if (steamId == null) return false;
|
||||
var playerData = GetPlayerAdminData(steamId);
|
||||
return playerData?.Groups.IsSupersetOf(groups) ?? false;
|
||||
if (playerData == null) return false;
|
||||
|
||||
var playerGroups = groups.ToHashSet<string>();
|
||||
foreach (var domain in playerData.Flags)
|
||||
{
|
||||
if (playerData.DomainHasRootFlag(domain.Key))
|
||||
{
|
||||
playerGroups.ExceptWith(groups.Where(group => group.Contains(domain.Key + '/')));
|
||||
}
|
||||
}
|
||||
|
||||
return playerData.Groups.IsSupersetOf(playerGroups);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a player to a group.
|
||||
/// </summary>
|
||||
/// <param name="player">Player controller.</param>
|
||||
/// <param name="groups">Groups to add the player to.</param>
|
||||
public static void AddPlayerToGroup(CCSPlayerController? player, params string[] groups)
|
||||
/// <summary>
|
||||
/// Adds a player to a group. This does NOT modify the immunity of the player (see SetPlayerImmunity).
|
||||
/// </summary>
|
||||
/// <param name="player">Player controller.</param>
|
||||
/// <param name="groups">Groups to add the player to.</param>
|
||||
public static void AddPlayerToGroup(CCSPlayerController? player, params string[] groups)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return; }
|
||||
AddPlayerToGroup((SteamID)player.AuthorizedSteamID, groups);
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot || player.IsHLTV) { return; }
|
||||
AddPlayerToGroup(player.AuthorizedSteamID, groups);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a player to a group.
|
||||
/// Adds a player to a group. This does NOT modify the immunity of the player (see SetPlayerImmunity).
|
||||
/// </summary>
|
||||
/// <param name="steamId">SteamID of the player.</param>
|
||||
/// <param name="groups">Groups to add the player to.</param>
|
||||
public static void AddPlayerToGroup(SteamID steamId, params string[] groups)
|
||||
public static void AddPlayerToGroup(SteamID? steamId, params string[] groups)
|
||||
{
|
||||
if (steamId == null) return;
|
||||
var data = GetPlayerAdminData(steamId);
|
||||
if (data == null)
|
||||
{
|
||||
@@ -144,7 +171,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
if (Groups.TryGetValue(group, out var groupDef))
|
||||
{
|
||||
data.Flags.UnionWith(groupDef.Flags);
|
||||
AddPlayerPermissions(steamId, groupDef.Flags.ToArray());
|
||||
groupDef.CommandOverrides.ToList().ForEach(x => data.CommandOverrides[x.Key] = x.Value);
|
||||
}
|
||||
}
|
||||
@@ -160,8 +187,8 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
public static void RemovePlayerFromGroup(CCSPlayerController? player, bool removeInheritedFlags = true, params string[] groups)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return; }
|
||||
RemovePlayerFromGroup((SteamID)player.AuthorizedSteamID, true, groups);
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot || player.IsHLTV) { return; }
|
||||
RemovePlayerFromGroup(player.AuthorizedSteamID, true, groups);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -170,8 +197,9 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
/// <param name="steamId">SteamID of the player.</param>
|
||||
/// <param name="removeInheritedFlags">If true, all of the flags that the player inherited from being in the group will be removed.</param>
|
||||
/// <param name="groups"></param>
|
||||
public static void RemovePlayerFromGroup(SteamID steamId, bool removeInheritedFlags = true, params string[] groups)
|
||||
public static void RemovePlayerFromGroup(SteamID? steamId, bool removeInheritedFlags = true, params string[] groups)
|
||||
{
|
||||
if (steamId == null) return;
|
||||
var data = GetPlayerAdminData(steamId);
|
||||
if (data == null) return;
|
||||
|
||||
@@ -183,7 +211,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
if (Groups.TryGetValue(group, out var groupDef))
|
||||
{
|
||||
data.Flags.ExceptWith(groupDef.Flags);
|
||||
RemovePlayerPermissions(steamId, groupDef.Flags.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using CounterStrikeSharp.API.Core.Logging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
|
||||
public static partial class AdminManager
|
||||
{
|
||||
static AdminManager()
|
||||
public static void AddCommands()
|
||||
{
|
||||
CommandUtils.AddStandaloneCommand("css_admins_reload", "Reloads the admin file.", ReloadAdminsCommand);
|
||||
CommandUtils.AddStandaloneCommand("css_admins_list", "List admins and their flags.", ListAdminsCommand);
|
||||
@@ -37,6 +33,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
Admins.Clear();
|
||||
var rootDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.Parent;
|
||||
LoadAdminData(Path.Combine(rootDir.FullName, "configs", "admins.json"));
|
||||
MergeGroupPermsIntoAdmins();
|
||||
}
|
||||
|
||||
[RequiresPermissions(permissions:"@css/generic")]
|
||||
@@ -45,7 +42,11 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
foreach (var (steamId, data) in Admins)
|
||||
{
|
||||
command.ReplyToCommand($"{steamId.SteamId64}, {steamId.SteamId2} - {string.Join(", ", data.Flags)}");
|
||||
command.ReplyToCommand($"{steamId.SteamId64}, {steamId.SteamId2} - FLAGS: ");
|
||||
foreach (var domain in data.Flags.Keys)
|
||||
{
|
||||
command.ReplyToCommand($" Domain {domain}: {string.Join(", ", data.Flags[domain])}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +57,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
Groups.Clear();
|
||||
var rootDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.Parent;
|
||||
LoadAdminGroups(Path.Combine(rootDir.FullName, "configs", "admin_groups.json"));
|
||||
MergeGroupPermsIntoAdmins();
|
||||
}
|
||||
|
||||
[RequiresPermissions(permissions: "@css/generic")]
|
||||
|
||||
@@ -3,24 +3,110 @@ using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using System.Reflection;
|
||||
using System.Numerics;
|
||||
using System.Linq;
|
||||
using CounterStrikeSharp.API.Core.Logging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Numerics;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using System.Diagnostics.Eventing.Reader;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
public partial class AdminData
|
||||
{
|
||||
[JsonPropertyName("identity")] public required string Identity { get; init; }
|
||||
[JsonPropertyName("flags")] public HashSet<string> Flags { get; init; } = new();
|
||||
// Flags loaded from file. Do not use this for actual comparisons.
|
||||
[JsonPropertyName("flags")] public HashSet<string> _flags { get; init; } = new();
|
||||
|
||||
[JsonPropertyName("immunity")] public uint Immunity { get; set; } = 0;
|
||||
[JsonPropertyName("command_overrides")] public Dictionary<string, bool> CommandOverrides { get; init; } = new();
|
||||
|
||||
// Key is the domain of the flag "e.g "css, os, kzsurf"). This should NOT include the @ character.
|
||||
// Value is a hashmap of the flags inside of the domain (e.g "@css/generic")
|
||||
public Dictionary<string, HashSet<string>> Flags { get; init; } = new();
|
||||
|
||||
public void InitalizeFlags()
|
||||
{
|
||||
AddFlags(_flags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if a domain has a root flag inside of it.
|
||||
/// </summary>
|
||||
/// <param name="domain">Domain to check for.</param>
|
||||
/// <returns>True if "@{domain}/root" or "@{domain}/*" is present, false if not.</returns>
|
||||
public bool DomainHasRootFlag(string domain)
|
||||
{
|
||||
if (!Flags.ContainsKey(domain)) return false;
|
||||
if (Flags[domain].Contains("@" + domain + "/root")) return true;
|
||||
else if (Flags[domain].Contains("@" + domain + "/*")) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all domains for flags.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string[] GetFlagDomains()
|
||||
{
|
||||
return Flags.Keys.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a HashSet of all flags.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public HashSet<string> GetAllFlags()
|
||||
{
|
||||
var flags = new HashSet<string>();
|
||||
foreach (var domainFlags in Flags.Values)
|
||||
{
|
||||
flags.UnionWith(domainFlags);
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
public void AddFlags(HashSet<string> flags)
|
||||
{
|
||||
var domains = flags.Where(
|
||||
flag => flag.StartsWith(PermissionCharacters.UserPermissionChar))
|
||||
.Distinct()
|
||||
.Select(domain => domain.Split('/').First()[1..]);
|
||||
|
||||
foreach (var domain in domains)
|
||||
{
|
||||
if (!Flags.ContainsKey(domain))
|
||||
{
|
||||
Flags[domain] = new HashSet<string>();
|
||||
}
|
||||
Flags[domain].UnionWith(flags.Where(flag => flag.StartsWith(PermissionCharacters.UserPermissionChar + domain + '/')).ToHashSet());
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveFlags(HashSet<string> flags)
|
||||
{
|
||||
var domains = flags.Where(
|
||||
flag => flag.StartsWith(PermissionCharacters.UserPermissionChar))
|
||||
.Distinct()
|
||||
.Select(domain => domain.Split('/').First()[1..]);
|
||||
|
||||
foreach (var domain in domains)
|
||||
{
|
||||
if (!Flags.ContainsKey(domain)) continue;
|
||||
var domainFlags = flags.Where(flag => flag.StartsWith(PermissionCharacters.UserPermissionChar + domain + '/')).ToHashSet();
|
||||
Flags[domain].ExceptWith(flags);
|
||||
if (Flags[domain].Count() == 0) Flags.Remove(domain);
|
||||
}
|
||||
}
|
||||
|
||||
public bool DomainHasFlags(string domain, string[] flags, bool ignoreRoot = false)
|
||||
{
|
||||
if (!Flags.ContainsKey(domain)) return false;
|
||||
if (DomainHasRootFlag(domain) && !ignoreRoot) return true;
|
||||
return Flags[domain].IsSupersetOf(flags);
|
||||
}
|
||||
}
|
||||
|
||||
public static partial class AdminManager
|
||||
@@ -39,16 +125,28 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
_logger.LogWarning("Admin data file not found. Skipping admin data load.");
|
||||
return;
|
||||
}
|
||||
|
||||
var adminsFromFile = JsonSerializer.Deserialize<Dictionary<string, AdminData>>(File.ReadAllText(adminDataPath), new JsonSerializerOptions() { ReadCommentHandling = JsonCommentHandling.Skip });
|
||||
var settings = new JsonSerializerOptions() { ReadCommentHandling = JsonCommentHandling.Skip };
|
||||
var adminsFromFile = JsonSerializer.Deserialize<Dictionary<string, AdminData>>(File.ReadAllText(adminDataPath), settings);
|
||||
if (adminsFromFile == null) { throw new FileNotFoundException(); }
|
||||
|
||||
foreach (var adminDef in adminsFromFile.Values)
|
||||
{
|
||||
adminDef.InitalizeFlags();
|
||||
Console.WriteLine($"Domains: {adminDef.Flags.Count}");
|
||||
|
||||
if (SteamID.TryParse(adminDef.Identity, out var steamId))
|
||||
{
|
||||
if (Admins.ContainsKey(steamId!))
|
||||
{
|
||||
Admins[steamId!].Flags.UnionWith(adminDef.Flags);
|
||||
// Merge domains together if we already have pre-existing values.
|
||||
foreach (var (domain, flags) in adminDef.Flags)
|
||||
{
|
||||
if (Admins[steamId!].Flags.ContainsKey(domain))
|
||||
{
|
||||
Admins[steamId!].Flags[domain].UnionWith(flags);
|
||||
}
|
||||
}
|
||||
|
||||
if (adminDef.Immunity > Admins[steamId!].Immunity)
|
||||
{
|
||||
Admins[steamId!].Immunity = adminDef.Immunity;
|
||||
@@ -69,22 +167,45 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Grabs the admin data for a player that was loaded from "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="steamId">SteamID object of the player.</param>
|
||||
/// <returns>AdminData class if data found, null if not.</returns>
|
||||
public static AdminData? GetPlayerAdminData(SteamID steamId)
|
||||
/// <summary>
|
||||
/// Grabs the admin data for a player that was loaded from "configs/admins.json" and "configs/admins_groups.json".
|
||||
/// </summary>
|
||||
/// <param name="player">Player controller</param>
|
||||
/// <returns>AdminData class if data found, null if not.</returns>
|
||||
public static AdminData? GetPlayerAdminData(CCSPlayerController? player)
|
||||
{
|
||||
if (player == null) return null;
|
||||
return GetPlayerAdminData(player.AuthorizedSteamID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Grabs the admin data for a player that was loaded from "configs/admins.json" and "configs/admins_groups.json".
|
||||
/// </summary>
|
||||
/// <param name="steamId">SteamID object of the player.</param>
|
||||
/// <returns>AdminData class if data found, null if not.</returns>
|
||||
public static AdminData? GetPlayerAdminData(SteamID? steamId)
|
||||
{
|
||||
if (steamId == null) return null;
|
||||
return Admins.GetValueOrDefault(steamId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a players admin data. This is not saved to "configs/admins.json"
|
||||
/// </summary>
|
||||
/// <param name="steamId">Steam ID remove admin data from.</param>
|
||||
public static void RemovePlayerAdminData(SteamID steamId)
|
||||
/// <summary>
|
||||
/// Removes a players admin data. This is not saved to "configs/admins.json"
|
||||
/// </summary>
|
||||
/// <param name="player">Player controller</param>
|
||||
public static void RemovePlayerAdminData(CCSPlayerController? player)
|
||||
{
|
||||
if (player == null) return;
|
||||
RemovePlayerAdminData(player.AuthorizedSteamID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a players admin data. This is not saved to "configs/admins.json"
|
||||
/// </summary>
|
||||
/// <param name="steamId">Steam ID remove admin data from.</param>
|
||||
public static void RemovePlayerAdminData(SteamID? steamId)
|
||||
{
|
||||
if (steamId == null) return;
|
||||
Admins.Remove(steamId);
|
||||
}
|
||||
|
||||
@@ -101,9 +222,8 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
// This is here for cases where the server console is attempting to call commands.
|
||||
// The server console should have access to all commands, regardless of permissions.
|
||||
if (player == null) return true;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return false; }
|
||||
var playerData = GetPlayerAdminData(player.AuthorizedSteamID);
|
||||
return playerData?.Flags.IsSupersetOf(flags) ?? false;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot || player.IsHLTV) { return false; }
|
||||
return PlayerHasPermissions(player.AuthorizedSteamID, flags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -112,10 +232,36 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
/// <param name="steamId">Steam ID object.</param>
|
||||
/// <param name="flags">Flags to look for in the players permission flags.</param>
|
||||
/// <returns>True if flags are present, false if not.</returns>
|
||||
public static bool PlayerHasPermissions(SteamID steamId, params string[] flags)
|
||||
public static bool PlayerHasPermissions(SteamID? steamId, params string[] flags)
|
||||
{
|
||||
if (steamId == null) return false;
|
||||
var playerData = GetPlayerAdminData(steamId);
|
||||
return playerData?.Flags.IsSupersetOf(flags) ?? false;
|
||||
if (playerData == null) return false;
|
||||
|
||||
// Check to see that all of the domains in the flags that we're checking are
|
||||
// present in our player data.
|
||||
var localDomains = flags.Where(
|
||||
flag => flag.StartsWith(PermissionCharacters.UserPermissionChar))
|
||||
.Distinct()
|
||||
.Select(domain => domain.Split('/').First()[1..])
|
||||
.ToHashSet();
|
||||
var playerFlagDomains = playerData.GetFlagDomains().ToHashSet();
|
||||
if (!playerFlagDomains.IsSupersetOf(localDomains)) return false;
|
||||
|
||||
// Loop through all of the domains and see if we have the required flags
|
||||
// for every domain.
|
||||
bool returnValue = true;
|
||||
foreach (var domain in playerData.Flags)
|
||||
{
|
||||
if (!playerData.DomainHasFlags(domain.Key,
|
||||
flags
|
||||
.Where(flag => flag.StartsWith(PermissionCharacters.UserPermissionChar + domain.Key + '/'))
|
||||
.ToArray()))
|
||||
{
|
||||
returnValue = false; break;
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -135,8 +281,8 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
// This is here for cases where the server console is attempting to call commands.
|
||||
// The server console should have access to all commands, regardless of permissions.
|
||||
if (player == null) return true;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return false; }
|
||||
var playerData = GetPlayerAdminData((SteamID)player.AuthorizedSteamID);
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot || player.IsHLTV) { return false; }
|
||||
var playerData = GetPlayerAdminData(player.AuthorizedSteamID);
|
||||
return playerData?.CommandOverrides.ContainsKey(command) ?? false;
|
||||
}
|
||||
|
||||
@@ -147,8 +293,9 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
/// <param name="steamId">Steam ID object.</param>
|
||||
/// <param name="command">Name of the command to check for.</param>
|
||||
/// <returns>True if override exists, false if not.</returns>
|
||||
public static bool PlayerHasCommandOverride(SteamID steamId, string command)
|
||||
public static bool PlayerHasCommandOverride(SteamID? steamId, string command)
|
||||
{
|
||||
if (steamId == null) return false;
|
||||
var playerData = GetPlayerAdminData(steamId);
|
||||
return playerData?.CommandOverrides.ContainsKey(command) ?? false;
|
||||
}
|
||||
@@ -164,8 +311,8 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
// This is here for cases where the server console is attempting to call commands.
|
||||
// The server console should have access to all commands, regardless of permissions.
|
||||
if (player == null) return true;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return false; }
|
||||
var playerData = GetPlayerAdminData((SteamID)player.AuthorizedSteamID);
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot || player.IsHLTV) { return false; }
|
||||
var playerData = GetPlayerAdminData(player.AuthorizedSteamID);
|
||||
return playerData?.CommandOverrides.GetValueOrDefault(command) ?? false;
|
||||
}
|
||||
|
||||
@@ -175,8 +322,9 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
/// <param name="steamId">Steam ID object.</param>
|
||||
/// <param name="command">Name of the command to check for.</param>
|
||||
/// <returns>True if override is active, false if not.</returns>
|
||||
public static bool GetPlayerCommandOverrideState(SteamID steamId, string command)
|
||||
public static bool GetPlayerCommandOverrideState(SteamID? steamId, string command)
|
||||
{
|
||||
if (steamId == null) return false;
|
||||
var playerData = GetPlayerAdminData(steamId);
|
||||
return playerData?.CommandOverrides.GetValueOrDefault(command) ?? false;
|
||||
}
|
||||
@@ -192,8 +340,8 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
// This is here for cases where the server console is attempting to call commands.
|
||||
// The server console should have access to all commands, regardless of permissions.
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return; }
|
||||
SetPlayerCommandOverride((SteamID)player.AuthorizedSteamID, command, state);
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot || player.IsHLTV) { return; }
|
||||
SetPlayerCommandOverride(player.AuthorizedSteamID, command, state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -202,8 +350,9 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
/// <param name="steamId">SteamID to add a flag to.</param>
|
||||
/// <param name="command">Name of the command to check for.</param>
|
||||
/// <param name="state">New state of the command override.</param>
|
||||
public static void SetPlayerCommandOverride(SteamID steamId, string command, bool state)
|
||||
public static void SetPlayerCommandOverride(SteamID? steamId, string command, bool state)
|
||||
{
|
||||
if (steamId == null) return;
|
||||
var data = GetPlayerAdminData(steamId);
|
||||
if (data == null)
|
||||
{
|
||||
@@ -234,8 +383,8 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
public static void AddPlayerPermissions(CCSPlayerController? player, params string[] flags)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
|
||||
AddPlayerPermissions((SteamID)player.AuthorizedSteamID, flags);
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot || player.IsHLTV) return;
|
||||
AddPlayerPermissions(player.AuthorizedSteamID, flags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -244,27 +393,23 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
/// </summary>
|
||||
/// <param name="steamId">SteamID to add a flag to.</param>
|
||||
/// <param name="flags">Flags to add for the player.</param>
|
||||
public static void AddPlayerPermissions(SteamID steamId, params string[] flags)
|
||||
public static void AddPlayerPermissions(SteamID? steamId, params string[] flags)
|
||||
{
|
||||
if (steamId == null) return;
|
||||
var data = GetPlayerAdminData(steamId);
|
||||
if (data == null)
|
||||
{
|
||||
data = new AdminData()
|
||||
{
|
||||
Identity = steamId.SteamId64.ToString(),
|
||||
Flags = new(flags),
|
||||
Flags = new(),
|
||||
Groups = new()
|
||||
};
|
||||
|
||||
Admins[steamId] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var flag in flags)
|
||||
{
|
||||
data.Flags.Add(flag);
|
||||
}
|
||||
Admins[steamId] = data;
|
||||
|
||||
Admins[steamId].AddFlags(flags.ToHashSet<string>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -276,9 +421,9 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
public static void RemovePlayerPermissions(CCSPlayerController? player, params string[] flags)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot || player.IsHLTV) return;
|
||||
|
||||
RemovePlayerPermissions((SteamID)player.AuthorizedSteamID, flags);
|
||||
RemovePlayerPermissions(player.AuthorizedSteamID, flags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -287,13 +432,12 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
/// </summary>
|
||||
/// <param name="steamId">Steam ID to remove flags from.</param>
|
||||
/// <param name="flags">Flags to remove from the player.</param>
|
||||
public static void RemovePlayerPermissions(SteamID steamId, params string[] flags)
|
||||
public static void RemovePlayerPermissions(SteamID? steamId, params string[] flags)
|
||||
{
|
||||
if (steamId == null) return;
|
||||
var data = GetPlayerAdminData(steamId);
|
||||
if (data == null) return;
|
||||
|
||||
data.Flags.ExceptWith(flags);
|
||||
Admins[steamId] = data;
|
||||
Admins[steamId].RemoveFlags(flags.ToHashSet<string>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -304,9 +448,9 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
public static void ClearPlayerPermissions(CCSPlayerController? player)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot || player.IsHLTV) return;
|
||||
|
||||
ClearPlayerPermissions((SteamID)player.AuthorizedSteamID);
|
||||
ClearPlayerPermissions(player.AuthorizedSteamID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -314,8 +458,9 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
/// "configs/admins.json".
|
||||
/// </summary>
|
||||
/// <param name="steamId">Steam ID to remove flags from.</param>
|
||||
public static void ClearPlayerPermissions(SteamID steamId)
|
||||
public static void ClearPlayerPermissions(SteamID? steamId)
|
||||
{
|
||||
if (steamId == null) return;
|
||||
var data = GetPlayerAdminData(steamId);
|
||||
if (data == null) return;
|
||||
|
||||
@@ -333,9 +478,9 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
public static void SetPlayerImmunity(CCSPlayerController? player, uint value)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot || player.IsHLTV) return;
|
||||
|
||||
SetPlayerImmunity((SteamID)player.AuthorizedSteamID, value);
|
||||
SetPlayerImmunity(player.AuthorizedSteamID, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -343,8 +488,9 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
/// </summary>
|
||||
/// <param name="steamId">Steam ID of the player.</param>
|
||||
/// <param name="value">New immunity value.</param>
|
||||
public static void SetPlayerImmunity(SteamID steamId, uint value)
|
||||
public static void SetPlayerImmunity(SteamID? steamId, uint value)
|
||||
{
|
||||
if (steamId == null) return;
|
||||
var data = GetPlayerAdminData(steamId);
|
||||
if (data == null) return;
|
||||
|
||||
@@ -352,13 +498,44 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
Admins[steamId] = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if a player can target another player based on their immunity value.
|
||||
/// </summary>
|
||||
/// <param name="caller">Caller of the command.</param>
|
||||
/// <param name="target">Target of the command.</param>
|
||||
/// <returns></returns>
|
||||
public static bool CanPlayerTarget(CCSPlayerController? caller, CCSPlayerController? target)
|
||||
/// <summary>
|
||||
/// Returns the immunity value for a player.
|
||||
/// </summary>
|
||||
/// <param name="player">Player controller.</param>
|
||||
/// <returns> If an immunity value is present in "configs/admins_groups.json"
|
||||
/// and in "configs/admins.json", the returned value will be the greater of the two.
|
||||
/// If the value is overriden with SetPlayerImmunity, that value is returned instead.</returns>
|
||||
public static uint GetPlayerImmunity(CCSPlayerController? player)
|
||||
{
|
||||
if (player == null) return 0;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot || player.IsHLTV) return 0;
|
||||
|
||||
return GetPlayerImmunity(player.AuthorizedSteamID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the immunity value for a player.
|
||||
/// </summary>
|
||||
/// <param name="steamId">Steam ID of the player.</param>
|
||||
/// <returns> If an immunity value is present in "configs/admins_groups.json"
|
||||
/// and in "configs/admins.json", the returned value will be the greater of the two.
|
||||
/// If the value is overriden with SetPlayerImmunity, that value is returned instead.</returns>
|
||||
public static uint GetPlayerImmunity(SteamID? steamId)
|
||||
{
|
||||
if (steamId == null) return 0;
|
||||
var data = GetPlayerAdminData(steamId);
|
||||
if (data == null) return 0;
|
||||
|
||||
return data.Immunity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if a player can target another player based on their immunity value.
|
||||
/// </summary>
|
||||
/// <param name="caller">Caller of the command.</param>
|
||||
/// <param name="target">Target of the command.</param>
|
||||
/// <returns></returns>
|
||||
public static bool CanPlayerTarget(CCSPlayerController? caller, CCSPlayerController? target)
|
||||
{
|
||||
// The server console should be able to target everyone.
|
||||
if (caller == null) return true;
|
||||
@@ -366,10 +543,10 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
if (target == null) return false;
|
||||
if (!target.IsValid || target.Connected != PlayerConnectedState.PlayerConnected) return false;
|
||||
|
||||
var callerData = GetPlayerAdminData((SteamID)caller.AuthorizedSteamID);
|
||||
var callerData = GetPlayerAdminData(caller.AuthorizedSteamID);
|
||||
if (callerData == null) return false;
|
||||
|
||||
var targetData = GetPlayerAdminData((SteamID)target.AuthorizedSteamID);
|
||||
var targetData = GetPlayerAdminData(target.AuthorizedSteamID);
|
||||
if (targetData == null) return true;
|
||||
|
||||
return callerData.Immunity >= targetData.Immunity;
|
||||
@@ -381,12 +558,15 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
/// <param name="caller">Caller of the command.</param>
|
||||
/// <param name="target">Target of the command.</param>
|
||||
/// <returns></returns>
|
||||
public static bool CanPlayerTarget(SteamID caller, SteamID target)
|
||||
public static bool CanPlayerTarget(SteamID? caller, SteamID? target)
|
||||
{
|
||||
if (caller == null) return false;
|
||||
if (target == null) return false;
|
||||
|
||||
var callerData = GetPlayerAdminData(caller);
|
||||
if (callerData == null) return false;
|
||||
|
||||
var targetData = GetPlayerAdminData(caller);
|
||||
var targetData = GetPlayerAdminData(target);
|
||||
if (targetData == null) return true;
|
||||
|
||||
return callerData.Immunity >= targetData.Immunity;
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
/// <summary>
|
||||
/// The permissions for the command.
|
||||
/// </summary>
|
||||
public string[] Permissions { get; }
|
||||
public HashSet<string> Permissions { get; }
|
||||
/// <summary>
|
||||
/// The name of the command that is attached to this attribute.
|
||||
/// </summary>
|
||||
@@ -23,7 +23,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
|
||||
public BaseRequiresPermissions(params string[] permissions)
|
||||
{
|
||||
Permissions = permissions;
|
||||
Permissions = permissions.ToHashSet();
|
||||
Command = "";
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,14 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
var groupPermissions = Permissions.Where(perm => perm.StartsWith(PermissionCharacters.GroupPermissionChar));
|
||||
var userPermissions = Permissions.Where(perm => perm.StartsWith(PermissionCharacters.UserPermissionChar));
|
||||
|
||||
if (!AdminManager.PlayerInGroup(caller, groupPermissions.ToArray())) return false;
|
||||
if (!AdminManager.PlayerHasPermissions(caller, userPermissions.ToArray())) return false;
|
||||
if (!AdminManager.PlayerHasPermissions(caller, userPermissions.ToArray()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!AdminManager.PlayerInGroup(caller, groupPermissions.ToArray()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -20,12 +20,28 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
}
|
||||
if (!base.CanExecuteCommand(caller)) return false;
|
||||
|
||||
var groupPermissions = Permissions.Where(perm => perm.StartsWith(PermissionCharacters.GroupPermissionChar));
|
||||
var userPermissions = Permissions.Where(perm => perm.StartsWith(PermissionCharacters.UserPermissionChar));
|
||||
|
||||
var adminData = AdminManager.GetPlayerAdminData(caller.AuthorizedSteamID);
|
||||
if (adminData == null) return false;
|
||||
return (groupPermissions.Intersect(adminData.Groups).Count() + userPermissions.Intersect(adminData.Flags).Count()) > 0;
|
||||
|
||||
// Check to see if the caller has a root flag for any of the domains in our permissions.
|
||||
// If they do, remove all of the user flags and groups that belong to the domain
|
||||
// from our permission check.
|
||||
var domains = Permissions.Where(
|
||||
flag => flag.StartsWith(PermissionCharacters.GroupPermissionChar))
|
||||
.Distinct()
|
||||
.Select(domain => domain.Split('/').First()[1..]);
|
||||
|
||||
foreach (var domain in domains)
|
||||
{
|
||||
if (adminData.DomainHasRootFlag(domain))
|
||||
{
|
||||
Permissions.RemoveWhere(flag => flag.Contains(domain + '/'));
|
||||
}
|
||||
}
|
||||
|
||||
var groupPermissions = Permissions.Where(perm => perm.StartsWith(PermissionCharacters.GroupPermissionChar));
|
||||
var userPermissions = Permissions.Where(perm => perm.StartsWith(PermissionCharacters.UserPermissionChar));
|
||||
return (groupPermissions.Intersect(adminData.Groups).Count() + userPermissions.Intersect(adminData.GetAllFlags()).Count()) > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace CounterStrikeSharp.API.Modules.Commands
|
||||
|
||||
public IntPtr Handle { get; }
|
||||
|
||||
internal CommandInfo(IntPtr pointer, CCSPlayerController player)
|
||||
internal CommandInfo(IntPtr pointer, CCSPlayerController? player)
|
||||
{
|
||||
Handle = pointer;
|
||||
CallingPlayer = player;
|
||||
|
||||
@@ -90,11 +90,11 @@ public class Target
|
||||
case TargetType.TeamSpec:
|
||||
return player.TeamNum == (byte)CsTeam.Spectator;
|
||||
case TargetType.GroupAll:
|
||||
return true;
|
||||
return !player.IsHLTV;
|
||||
case TargetType.GroupBots:
|
||||
return player.IsBot;
|
||||
case TargetType.GroupHumans:
|
||||
return !player.IsBot;
|
||||
return !player.IsBot && !player.IsHLTV;
|
||||
case TargetType.GroupAlive:
|
||||
return player.PlayerPawn is { IsValid: true, Value.LifeState: (byte)LifeState_t.LIFE_ALIVE };
|
||||
case TargetType.GroupDead:
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Listeners;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Cvars
|
||||
{
|
||||
public class ConVar
|
||||
{
|
||||
public delegate void ConVarChangedCallback(ConVar convar, string oldValue, string newValue);
|
||||
|
||||
public IntPtr Handle { get; private set; }
|
||||
|
||||
internal ConVar(IntPtr handle)
|
||||
{
|
||||
Handle = handle;
|
||||
}
|
||||
|
||||
public ConVar(string name, string value, string description, ConVarFlags flags, bool hasMinValue, float minValue, bool hasMaxValue, float maxValue) :
|
||||
this(NativeAPI.CreateConvar(name, value, description, (int)flags, hasMinValue, minValue, hasMaxValue, maxValue))
|
||||
{
|
||||
}
|
||||
|
||||
public ConVar(string name, string value, string description = null, ConVarFlags flags = ConVarFlags.None, float? minValue = null, float? maxValue = null) :
|
||||
this(name, value, description, flags, minValue.HasValue, minValue?? 0, maxValue.HasValue, maxValue?? 0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public string Name => NativeAPI.ConvarGetName(Handle);
|
||||
|
||||
public string StringValue
|
||||
{
|
||||
get { return NativeAPI.ConvarGetStringValue(Handle); }
|
||||
set { NativeAPI.ConvarSetStringValue(Handle, value); }
|
||||
}
|
||||
|
||||
public float FloatValue
|
||||
{
|
||||
get { return Convert.ToSingle(NativeAPI.ConvarGetStringValue(Handle)); }
|
||||
set { NativeAPI.ConvarSetStringValue(Handle, value.ToString("n2")); }
|
||||
}
|
||||
|
||||
public int IntValue
|
||||
{
|
||||
get { return Convert.ToInt32(NativeAPI.ConvarGetStringValue(Handle)); }
|
||||
set { NativeAPI.ConvarSetStringValue(Handle, value.ToString()); }
|
||||
}
|
||||
|
||||
public bool BoolValue
|
||||
{
|
||||
get { return NativeAPI.ConvarGetStringValue(Handle) == "1"; }
|
||||
set { NativeAPI.ConvarSetStringValue(Handle, value ? "1" : "0"); }
|
||||
}
|
||||
|
||||
public ConVarFlags Flags
|
||||
{
|
||||
get { return (ConVarFlags) NativeAPI.ConvarGetFlags(Handle); }
|
||||
set { NativeAPI.ConvarSetFlags(Handle, (int)value); }
|
||||
}
|
||||
|
||||
public bool Public
|
||||
{
|
||||
get { return Flags.HasFlag(ConVarFlags.Notify); }
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
Flags |= ConVarFlags.Notify;
|
||||
}
|
||||
else
|
||||
{
|
||||
Flags &= ~ConVarFlags.Notify;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ConVar Find(string name)
|
||||
{
|
||||
var ptr = NativeAPI.FindConvar(name);
|
||||
if (ptr == IntPtr.Zero) return null;
|
||||
|
||||
return new ConVar(ptr);
|
||||
}
|
||||
|
||||
public void Unregister()
|
||||
{
|
||||
NativeAPI.ConvarUnregister(Handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* This file is part of CounterStrikeSharp.
|
||||
* CounterStrikeSharp is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* CounterStrikeSharp is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
|
||||
*/
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Engine.Trace
|
||||
{
|
||||
public enum ContentMasks
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* This file is part of CounterStrikeSharp.
|
||||
* CounterStrikeSharp is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* CounterStrikeSharp is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
|
||||
*/
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Engine.Trace
|
||||
{
|
||||
public enum RayType
|
||||
{
|
||||
EndPoint,
|
||||
Infinite
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
/*
|
||||
* 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.Runtime.InteropServices;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Listeners;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Engine.Trace
|
||||
{
|
||||
public class SimpleTraceFilter : NativeObject
|
||||
{
|
||||
public SimpleTraceFilter(IntPtr cPtr) : base(cPtr)
|
||||
{
|
||||
}
|
||||
|
||||
public SimpleTraceFilter(int indexToIgnore) : base(NativeAPI.NewSimpleTraceFilter(indexToIgnore))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class TraceFilterProxy : NativeObject
|
||||
{
|
||||
private ITraceFilter _filter;
|
||||
private FunctionReference.CallbackDelegate getTypeCallback;
|
||||
private FunctionReference.CallbackDelegate shouldHitCallback;
|
||||
|
||||
public TraceFilterProxy(IntPtr cPtr) : base(cPtr)
|
||||
{
|
||||
}
|
||||
|
||||
public TraceFilterProxy(ITraceFilter filter) : base(NativeAPI.NewTraceFilterProxy())
|
||||
{
|
||||
_filter = filter;
|
||||
|
||||
/*
|
||||
getTypeCallback = Utilities.SafeExecute(intPtr =>
|
||||
{
|
||||
var marshal = new CMarshalObject();
|
||||
marshal.PushInt((int) _filter.GetTraceType());
|
||||
return marshal.GetPointer();
|
||||
});
|
||||
*/
|
||||
|
||||
/*shouldHitCallback = Utilities.SafeExecute(ptr =>
|
||||
{
|
||||
var marshal = new CMarshalObject(ptr, true);
|
||||
var entity = marshal.GetValue<BaseEntity>();
|
||||
var contentMask = marshal.GetInt();
|
||||
|
||||
var isValidEntity = _filter.ShouldHitEntity(entity, contentMask);
|
||||
|
||||
var response = new CMarshalObject();
|
||||
response.PushInt(isValidEntity ? 1 : 0);
|
||||
|
||||
return response.GetPointer();
|
||||
});*/
|
||||
|
||||
unsafe
|
||||
{
|
||||
getTypeCallback = (fxScriptContext* context) =>
|
||||
{
|
||||
var scriptContext = new ScriptContext(context);
|
||||
|
||||
scriptContext.Push(_filter.GetTraceType());
|
||||
};
|
||||
|
||||
shouldHitCallback = (fxScriptContext* context) =>
|
||||
{
|
||||
var scriptContext = new ScriptContext(context);
|
||||
|
||||
var entity = new BaseEntity(scriptContext.GetArgument<int>(0));
|
||||
var contentMask = scriptContext.GetArgument<int>(1);
|
||||
|
||||
var isValidEntity = _filter.ShouldHitEntity(entity, contentMask);
|
||||
|
||||
Console.WriteLine($"Returning {isValidEntity} to `ShouldHitEntity`");
|
||||
|
||||
scriptContext.SetResult(isValidEntity, context);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
NativeAPI.TraceFilterProxySetTraceTypeCallback(Handle, Marshal.GetFunctionPointerForDelegate(getTypeCallback));
|
||||
NativeAPI.TraceFilterProxySetShouldHitEntityCallback(Handle, Marshal.GetFunctionPointerForDelegate(shouldHitCallback));
|
||||
/*NativeAPI.TraceFilterProxySetTraceTypeCallback(Handle, getTypeCallback);
|
||||
NativePINVOKE.TraceFilterProxy_SetGetTraceTypeCallback(ptr, getTypeCallback.ToHandle());
|
||||
NativePINVOKE.TraceFilterProxy_SetShouldHitEntityCallback(ptr, shouldHitCallback.ToHandle());*/
|
||||
}
|
||||
}
|
||||
|
||||
public enum TraceType
|
||||
{
|
||||
Everything = 0,
|
||||
WorldOnly, // NOTE: This does *not* test static props!!!
|
||||
EntitiesOnly, // NOTE: This version will *not* test static props
|
||||
EverythingFilterProps, // NOTE: This version will pass the IHandleEntity for props through the filter, unlike all other filters
|
||||
};
|
||||
|
||||
public class CustomTraceFilter : TraceFilter
|
||||
{
|
||||
private Func<BaseEntity, bool> _filter;
|
||||
|
||||
public CustomTraceFilter(Func<BaseEntity, bool> filter)
|
||||
{
|
||||
_filter = filter;
|
||||
}
|
||||
public override bool ShouldHitEntity(BaseEntity entity, int contentMask)
|
||||
{
|
||||
return _filter.Invoke(entity);
|
||||
}
|
||||
|
||||
public override TraceType GetTraceType()
|
||||
{
|
||||
return TraceType.Everything;
|
||||
}
|
||||
}
|
||||
|
||||
public class ExclusionTraceFilter : TraceFilter
|
||||
{
|
||||
private int _indexToExclude;
|
||||
|
||||
public ExclusionTraceFilter(int indexToExclude)
|
||||
{
|
||||
this._indexToExclude = indexToExclude;
|
||||
}
|
||||
public override bool ShouldHitEntity(BaseEntity entity, int contentMask)
|
||||
{
|
||||
if (entity.Index == _indexToExclude) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override TraceType GetTraceType()
|
||||
{
|
||||
return TraceType.Everything;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class TraceFilter : ITraceFilter
|
||||
{
|
||||
public abstract bool ShouldHitEntity(BaseEntity entity, int contentMask);
|
||||
public abstract TraceType GetTraceType();
|
||||
}
|
||||
|
||||
public interface ITraceFilter
|
||||
{
|
||||
bool ShouldHitEntity(BaseEntity entity, int contentMask);
|
||||
TraceType GetTraceType();
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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 CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Engine.Trace
|
||||
{
|
||||
public class TraceEngine
|
||||
{
|
||||
public static IntPtr CreateRay(RayType rayType, Vector vec1, Vector vec2)
|
||||
{
|
||||
return NativeAPI.CreateRay1((int) rayType, vec1.Handle, vec2.Handle);
|
||||
}
|
||||
|
||||
public static IntPtr CreateRay(Vector vec1, Vector vec2, Vector vec3, Vector vec4)
|
||||
{
|
||||
return NativeAPI.CreateRay2(vec1.Handle, vec2.Handle, vec3.Handle, vec4.Handle);
|
||||
}
|
||||
|
||||
public static TraceResult TraceRay(IntPtr ray, uint mask, ITraceFilter filter)
|
||||
{
|
||||
var tr = new TraceResult();
|
||||
var proxy = new TraceFilterProxy(filter);
|
||||
NativeAPI.TraceRay(ray, tr.Handle, proxy.Handle, mask);
|
||||
return tr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* 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 CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Engine.Trace
|
||||
{
|
||||
public class TraceResult : NativeObject
|
||||
{
|
||||
public TraceResult(IntPtr cPtr) : base(cPtr)
|
||||
{
|
||||
}
|
||||
|
||||
public TraceResult() : base(NativeAPI.NewTraceResult())
|
||||
{
|
||||
}
|
||||
|
||||
public bool DidHit()
|
||||
{
|
||||
// return NativeAPI.TraceDidHit(Handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
public BaseEntity Entity
|
||||
{
|
||||
get
|
||||
{
|
||||
var entity = new BaseEntity(NativeAPI.TraceResultEntity(Handle));
|
||||
if (entity?.IsNetworked == true)
|
||||
{
|
||||
return entity;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/*public TraceSurface Surface => NativePINVOKE.CGameTrace_surface_get(ptr).ToObject<TraceSurface>();
|
||||
public int Hitbox => NativePINVOKE.CGameTrace_hitbox_get(ptr);
|
||||
public int Hitgroup => NativePINVOKE.CGameTrace_hitgroup_get(ptr);
|
||||
public float FractionLeftSolid => NativePINVOKE.CGameTrace_fractionleftsolid_get(ptr);
|
||||
public int PhysicsBone => NativePINVOKE.CGameTrace_physicsbone_get(ptr);*/
|
||||
|
||||
/*public Vector StartPosition => NativePINVOKE.CBaseTrace_startpos_get(ptr).ToObject<Vector>();
|
||||
public Vector EndPosition => NativePINVOKE.CBaseTrace_endpos_get(ptr).ToObject<Vector>();*/
|
||||
}
|
||||
}
|
||||
@@ -1,449 +0,0 @@
|
||||
/*
|
||||
* 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.Drawing;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Entities
|
||||
{
|
||||
public class BaseEntity : NativeObject
|
||||
{
|
||||
public BaseEntity(int index) : base(NativeAPI.BaseentityFromIndex(index))
|
||||
{
|
||||
_index = index;
|
||||
}
|
||||
|
||||
public BaseEntity(IntPtr pointer) : base(pointer)
|
||||
{
|
||||
_index = NativeAPI.IndexFromBaseentity(pointer);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0}", Index);
|
||||
}
|
||||
|
||||
#region Props
|
||||
public int GetProp(PropType type, string name) => NativeAPI.EntityGetPropInt(Index, (int) type, name);
|
||||
|
||||
public T GetProp<T>(PropType type, string name) => (T) (NativeAPI.EntityGetPropInt(Index, (int) type, name) as object);
|
||||
|
||||
public void SetProp(PropType type, string name, int value) => NativeAPI.EntitySetPropInt(Index, (int)type, name, value);
|
||||
|
||||
public void SetProp<T>(PropType type, string name, T value) => NativeAPI.EntitySetPropInt(Index, (int)type, name, (int)(object)value);
|
||||
|
||||
public bool GetPropBool(PropType type, string name) => GetProp(type, name) == 1;
|
||||
|
||||
public void SetPropBool(PropType type, string name, bool value) => SetProp(type, name, value ? 1 : 0);
|
||||
|
||||
public float GetPropFloat(PropType type, string name) => NativeAPI.EntityGetPropFloat(Index, (int) type, name);
|
||||
|
||||
public void SetPropFloat(PropType type, string name, float value) => NativeAPI.EntitySetPropFloat(Index, (int)type, name, value);
|
||||
|
||||
public Vector GetPropVector(PropType type, string name) => new(NativeAPI.EntityGetPropVector(Index, (int) type, name));
|
||||
|
||||
public void SetPropVector(PropType type, string name, Vector vector) => NativeAPI.EntitySetPropVector(Index, (int) type, name, vector.Handle);
|
||||
|
||||
public string GetPropString(PropType type, string name) => NativeAPI.EntityGetPropString(Index, (int)type, name);
|
||||
|
||||
public void SetPropString(PropType type, string name, string value) => NativeAPI.EntitySetPropString(Index, (int)type, name, value);
|
||||
|
||||
public BaseEntity GetPropEnt(PropType type, string name)
|
||||
{
|
||||
var returnVal = NativeAPI.EntityGetPropEnt(Index, (int) type, name);
|
||||
if (returnVal < 0) return null;
|
||||
return BaseEntity.FromIndex(returnVal);
|
||||
}
|
||||
|
||||
public BaseEntity GetPropEntByOffset(int offset)
|
||||
{
|
||||
var returnVal = NativeAPI.EntityGetPropEntByOffset(Index, offset);
|
||||
if (returnVal < 0) return null;
|
||||
return BaseEntity.FromIndex(returnVal);
|
||||
}
|
||||
|
||||
public void SetPropEnt(PropType type, string name, int index) => NativeAPI.EntitySetPropEnt(Index, (int) type, name, index);
|
||||
|
||||
#endregion
|
||||
|
||||
#region KeyValues
|
||||
public string GetKeyValue(string name) => NativeAPI.EntityGetKeyvalue(Index, name);
|
||||
|
||||
public Vector GetKeyValueVector(string name)
|
||||
{
|
||||
var values = GetKeyValue(name).Split(new []{" "}, StringSplitOptions.None).Select(float.Parse).ToArray();
|
||||
return new Vector(values[0], values[1], values[2]);
|
||||
}
|
||||
|
||||
public float GetKeyValueFloat(string name)
|
||||
{
|
||||
return Convert.ToSingle(GetKeyValue(name));
|
||||
}
|
||||
|
||||
public void SetKeyValue(string name, string value) => NativeAPI.EntitySetKeyvalue(Index, name, value);
|
||||
|
||||
public void SetKeyValueFloat(string name, float value) => SetKeyValue(name, value.ToString());
|
||||
|
||||
public void SetKeyValueVector(string name, Vector vec)
|
||||
{
|
||||
string strValue = $"{vec.X} {vec.Y} {vec.Z}";
|
||||
NativeAPI.EntitySetKeyvalue(Index, name, strValue);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public bool IsPlayer => NativeAPI.EntityIsPlayer(Index);
|
||||
public bool IsWeapon => NativeAPI.EntityIsWeapon(Index);
|
||||
|
||||
public bool IsNetworked => NativeAPI.EntityIsNetworked(Index);
|
||||
public bool IsValid => NativeAPI.EntityIsValid(Index);
|
||||
|
||||
/*public bool IsPlayer => NativePINVOKE.CBaseEntityWrapper_IsPlayer(ptr);
|
||||
public bool IsWeapon => NativePINVOKE.CBaseEntityWrapper_IsWeapon(ptr);
|
||||
public bool IsMoving => NativePINVOKE.CBaseEntityWrapper_IsMoving(ptr);*/
|
||||
|
||||
|
||||
public Vector Origin
|
||||
{
|
||||
get => GetKeyValueVector("origin");
|
||||
set => SetKeyValueVector("origin", value);
|
||||
}
|
||||
|
||||
|
||||
public Vector Maxs
|
||||
{
|
||||
get => GetPropVector(PropType.Send, "m_Collision.m_vecMaxs");
|
||||
set => SetPropVector(PropType.Send, "m_Collision.m_vecMaxs", value);
|
||||
}
|
||||
|
||||
public Vector Mins
|
||||
{
|
||||
get => GetPropVector(PropType.Send, "m_Collision.m_vecMins");
|
||||
set => SetPropVector(PropType.Send, "m_Collision.m_vecMins", value);
|
||||
}
|
||||
|
||||
public int EntityFlags
|
||||
{
|
||||
get => GetProp(PropType.Data, "m_iEFlags");
|
||||
set => SetProp(PropType.Data, "m_iEFlags", value);
|
||||
}
|
||||
|
||||
public SolidType SolidType
|
||||
{
|
||||
get => (SolidType)GetProp(PropType.Send, "m_Collision.m_nSolidType");
|
||||
set => SetProp(PropType.Send, "m_Collision.m_nSolidType", (int)value);
|
||||
}
|
||||
|
||||
public SolidFlags SolidFlags
|
||||
{
|
||||
get => GetProp<SolidFlags>(PropType.Send, "m_Collision.m_usSolidFlags");
|
||||
set => SetProp(PropType.Send, "m_Collision.m_usSolidFlags", value);
|
||||
}
|
||||
|
||||
public CollisionGroup CollisionGroup
|
||||
{
|
||||
get => GetProp<CollisionGroup>(PropType.Send, "m_CollisionGroup");
|
||||
set => SetProp(PropType.Send, "m_CollisionGroup", value);
|
||||
}
|
||||
|
||||
// TODO: ENTITY RENDER COLOR
|
||||
public Color Color
|
||||
{
|
||||
get
|
||||
{
|
||||
int offset = NativeAPI.FindDatamapInfo(Index, "m_clrRender");
|
||||
|
||||
int r = NativeAPI.EntityGetProp(Index, offset + 0, 8);
|
||||
int g = NativeAPI.EntityGetProp(Index, offset + 1, 8);
|
||||
int b = NativeAPI.EntityGetProp(Index, offset + 2, 8);
|
||||
int a = NativeAPI.EntityGetProp(Index, offset + 3, 8);
|
||||
return Color.FromArgb(a, r, g, b);
|
||||
}
|
||||
set
|
||||
{
|
||||
int offset = NativeAPI.FindDatamapInfo(Index, "m_clrRender");
|
||||
|
||||
NativeAPI.EntitySetProp(Index, offset + 0, 8, value.R);
|
||||
NativeAPI.EntitySetProp(Index, offset + 1, 8, value.G);
|
||||
NativeAPI.EntitySetProp(Index, offset + 2, 8, value.B);
|
||||
NativeAPI.EntitySetProp(Index, offset + 3, 8, value.A);
|
||||
}
|
||||
}
|
||||
|
||||
public float Elasticity
|
||||
{
|
||||
get => GetPropFloat(PropType.Send, "m_flElasticity");
|
||||
set => SetPropFloat(PropType.Send, "m_flElasticity", value);
|
||||
}
|
||||
|
||||
public BaseEntity GroundEntity
|
||||
{
|
||||
get => GetPropEnt(PropType.Data, "m_hGroundEntity");
|
||||
set => SetPropEnt(PropType.Data, "m_hGroundEntity", value.Index);
|
||||
}
|
||||
|
||||
public Team Team
|
||||
{
|
||||
get => GetProp<Team>(PropType.Send, "m_iTeamNum");
|
||||
set => SetProp(PropType.Send, "m_iTeamNum", value);
|
||||
}
|
||||
|
||||
public RenderFx RenderFx
|
||||
{
|
||||
get => (RenderFx)GetProp(PropType.Send, "m_nRenderFX");
|
||||
set => SetProp(PropType.Send, "m_nRenderFX", (int)value);
|
||||
}
|
||||
|
||||
public RenderMode RenderMode
|
||||
{
|
||||
get => (RenderMode)GetProp(PropType.Send, "m_nRenderMode");
|
||||
set => SetProp(PropType.Send, "m_nRenderMode", (int)value);
|
||||
}
|
||||
|
||||
public MoveType MoveType
|
||||
{
|
||||
get => (MoveType)GetProp(PropType.Send, "movetype");
|
||||
set => SetProp(PropType.Send, "movetype", (int)value);
|
||||
}
|
||||
|
||||
public new IntPtr Handle => NativeAPI.BaseentityFromIndex(Index);
|
||||
|
||||
public int ParentHandle
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
return GetProp(PropType.Data, "m_pParent");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
set => SetProp(PropType.Data, "m_pParent", value);
|
||||
}
|
||||
|
||||
public Vector Angles
|
||||
{
|
||||
get => GetKeyValueVector("angles");
|
||||
set => SetKeyValueVector("angles", value);
|
||||
}
|
||||
|
||||
public string TargetName
|
||||
{
|
||||
get => GetKeyValue("targetname");
|
||||
set => SetKeyValue("targetname", value);
|
||||
}
|
||||
|
||||
// TODO: Entity Owner Handle
|
||||
|
||||
public Vector AngVelocity
|
||||
{
|
||||
get => GetPropVector(PropType.Data, "m_vecAngVelocity");
|
||||
set => SetPropVector(PropType.Data, "m_vecAngVelocity", value);
|
||||
}
|
||||
|
||||
public Vector BaseVelocity
|
||||
{
|
||||
get => GetPropVector(PropType.Data, "m_vecBaseVelocity");
|
||||
set => SetPropVector(PropType.Data, "m_vecBaseVelocity", value);
|
||||
}
|
||||
|
||||
public string DamageFilter
|
||||
{
|
||||
get => GetKeyValue("damagefilter");
|
||||
set => SetKeyValue("damagefilter", value);
|
||||
}
|
||||
|
||||
public int Effects
|
||||
{
|
||||
get => GetProp(PropType.Data, "m_fEffects");
|
||||
set => SetProp(PropType.Data, "m_fEffects", value);
|
||||
}
|
||||
|
||||
public float Friction
|
||||
{
|
||||
get => GetPropFloat(PropType.Data, "m_flFriction");
|
||||
set => SetPropFloat(PropType.Data, "m_flFriction", value);
|
||||
}
|
||||
|
||||
public string GlobalName
|
||||
{
|
||||
get => GetKeyValue("globalname");
|
||||
set => SetKeyValue("globalname", value);
|
||||
}
|
||||
|
||||
public float Gravity
|
||||
{
|
||||
get => GetPropFloat(PropType.Data, "m_flGravity");
|
||||
set => SetPropFloat(PropType.Data, "m_flGravity", value);
|
||||
}
|
||||
|
||||
public int HammerId
|
||||
{
|
||||
get => GetProp(PropType.Data, "m_iHammerID");
|
||||
set => SetProp(PropType.Data, "m_iHammerID", value);
|
||||
}
|
||||
public int Health
|
||||
{
|
||||
get => GetProp(PropType.Data, "m_iHealth");
|
||||
set => SetProp(PropType.Data, "m_iHealth", value);
|
||||
}
|
||||
|
||||
public float LocalTime
|
||||
{
|
||||
get => GetPropFloat(PropType.Data, "m_flLocalTime");
|
||||
set => SetPropFloat(PropType.Data, "m_flLocalTime", value);
|
||||
}
|
||||
|
||||
public int MaxHealth
|
||||
{
|
||||
get => GetProp(PropType.Data, "m_iMaxHealth");
|
||||
set => SetProp(PropType.Data, "m_iMaxHealth", value);
|
||||
}
|
||||
|
||||
public string ParentName
|
||||
{
|
||||
get => GetKeyValue("parentname");
|
||||
set => SetKeyValue("parentname", value);
|
||||
}
|
||||
|
||||
public float ShadowCastDistance
|
||||
{
|
||||
get => GetPropFloat(PropType.Send, "m_flShadowCastDistance");
|
||||
set => SetPropFloat(PropType.Send, "m_flShadowCastDistance", value);
|
||||
}
|
||||
public int SpawnFlags
|
||||
{
|
||||
get => GetProp(PropType.Data, "m_spawnflags");
|
||||
set => SetProp(PropType.Data, "m_spawnflags", value);
|
||||
}
|
||||
|
||||
public float Speed
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
return GetPropFloat(PropType.Data, "m_flLaggedMovementValue");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return GetKeyValueFloat("speed");
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
try
|
||||
{
|
||||
SetPropFloat(PropType.Data, "m_flLaggedMovementValue", value);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
SetKeyValueFloat("speed", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string Target
|
||||
{
|
||||
get => GetKeyValue("target");
|
||||
set => SetKeyValue("target", value);
|
||||
}
|
||||
|
||||
public Vector Velocity
|
||||
{
|
||||
get => GetPropVector(PropType.Data, "m_vecVelocity");
|
||||
set => SetPropVector(PropType.Data, "m_vecVelocity", value);
|
||||
}
|
||||
|
||||
public int WaterLevel
|
||||
{
|
||||
get => GetProp(PropType.Data, "m_nWaterLevel");
|
||||
set => SetProp(PropType.Data, "m_nWaterLevel", value);
|
||||
}
|
||||
|
||||
public Vector Rotation
|
||||
{
|
||||
get => GetPropVector(PropType.Data, "m_angRotation");
|
||||
set => SetPropVector(PropType.Data, "m_angRotation", value);
|
||||
}
|
||||
|
||||
private int? _index;
|
||||
public int Index
|
||||
{
|
||||
get
|
||||
{
|
||||
return _index.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public string ClassName => NativeAPI.EntityGetClassname(Index);
|
||||
|
||||
public Vector AbsVelocity
|
||||
{
|
||||
get => GetPropVector(PropType.Data, "m_vecAbsVelocity");
|
||||
set => SetPropVector(PropType.Data, "m_vecAbsVelocity", value);
|
||||
}
|
||||
|
||||
public Vector AbsOrigin
|
||||
{
|
||||
get => GetPropVector(PropType.Data, "m_vecAbsOrigin");
|
||||
set => SetPropVector(PropType.Data, "m_vecAbsOrigin", value);
|
||||
}
|
||||
|
||||
public void Spawn() => NativeAPI.EntitySpawn(Index);
|
||||
|
||||
public void AcceptInput(string name) => NativeAPI.AcceptInput(Index, name);
|
||||
|
||||
public static BaseEntity Create(string className)
|
||||
{
|
||||
var index = NativeAPI.EntityCreateByClassname(className);
|
||||
if (index < 0) return null;
|
||||
return new BaseEntity(index);
|
||||
}
|
||||
|
||||
public static BaseEntity FromIndex(int index)
|
||||
{
|
||||
if (index < 0) return null;
|
||||
var entity = new BaseEntity(index);
|
||||
if (!entity.IsValid) return null;
|
||||
return entity;
|
||||
}
|
||||
|
||||
public static BaseEntity FindByClassname(int startIndex, string className)
|
||||
{
|
||||
var index = NativeAPI.EntityFindByClassname(startIndex, className);
|
||||
if (index < 0) return null;
|
||||
return new BaseEntity(index);
|
||||
}
|
||||
|
||||
public static BaseEntity FindByNetClass(int startIndex, string className)
|
||||
{
|
||||
var index = NativeAPI.EntityFindByNetclass(startIndex, className);
|
||||
if (index < 0) return null;
|
||||
return new BaseEntity(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* This file is part of CounterStrikeSharp.
|
||||
* CounterStrikeSharp is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* CounterStrikeSharp is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
|
||||
*/
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Entities.Constants
|
||||
{
|
||||
public enum DamageType
|
||||
{
|
||||
DMG_GENERIC = 0, // generic damage was done
|
||||
DMG_CRUSH = (1 << 0), // crushed by falling or moving object.
|
||||
// = NOTE: It's assumed crush damage is occurring as a result of physics collision, so no extra physics force is generated by crush damage.
|
||||
DMG_BULLET = (1 << 1), // shot
|
||||
DMG_SLASH = (1 << 2), // cut, clawed, stabbed
|
||||
DMG_BURN = (1 << 3), // heat burned
|
||||
DMG_VEHICLE = (1 << 4), // hit by a vehicle
|
||||
DMG_FALL = (1 << 5), // fell too far
|
||||
DMG_BLAST = (1 << 6), // explosive blast damage
|
||||
DMG_CLUB = (1 << 7), // crowbar, punch, headbutt
|
||||
DMG_SHOCK = (1 << 8), // electric shock
|
||||
DMG_SONIC = (1 << 9), // sound pulse shockwave
|
||||
DMG_ENERGYBEAM = (1 << 10), // laser or other high energy beam
|
||||
DMG_PREVENT_PHYSICS_FORCE = (1 << 11), // Prevent a physics force
|
||||
DMG_NEVERGIB = (1 << 12), // with this bit OR'd in, no damage type will be able to gib victims upon death
|
||||
DMG_ALWAYSGIB = (1 << 13), // with this bit OR'd in, any damage type can be made to gib victims upon death.
|
||||
DMG_DROWN = (1 << 14), // Drowning
|
||||
|
||||
DMG_PARALYZE = (1 << 15), // slows affected creature down
|
||||
DMG_NERVEGAS = (1 << 16), // nerve toxins, very bad
|
||||
DMG_POISON = (1 << 17), // blood poisoning - heals over time like drowning damage
|
||||
DMG_RADIATION = (1 << 18), // radiation exposure
|
||||
DMG_DROWNRECOVER = (1 << 19), // drowning recovery
|
||||
DMG_ACID = (1 << 20), // toxic chemicals or acid burns
|
||||
DMG_SLOWBURN = (1 << 21), // in an oven
|
||||
|
||||
DMG_REMOVENORAGDOLL = (1 << 22), // with this bit OR'd in, no ragdoll will be created, and the target will be quietly removed.
|
||||
// = use this to kill an entity that you've already got a server-side ragdoll for
|
||||
|
||||
DMG_PHYSGUN = (1 << 23), // Hit by manipulator. Usually doesn't do any damage.
|
||||
DMG_PLASMA = (1 << 24), // Shot by Cremator
|
||||
DMG_AIRBOAT = (1 << 25), // Hit by the airboat's gun
|
||||
|
||||
DMG_DISSOLVE = (1 << 26), // Dissolving!
|
||||
DMG_BLAST_SURFACE = (1 << 27), // A blast on the surface of water that cannot harm things underwater
|
||||
DMG_DIRECT = (1 << 28),
|
||||
DMG_BUCKSHOT = (1 << 29), // not quite a bullet. Little, rounder, different.
|
||||
DMG_HEADSHOT = (1 << 30)
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Entities.Constants
|
||||
{
|
||||
[Flags]
|
||||
public enum EntityFlags
|
||||
{
|
||||
Onground = (1 << 0), // At rest / on the ground
|
||||
Ducking = (1 << 1), // Player flag -- Player is fully crouched
|
||||
Waterjump = (1 << 3), // player jumping out of water
|
||||
Ontrain = (1 << 4), // Player is _controlling_ a train, so movement commands should be ignored on client during prediction.
|
||||
Inrain = (1 << 5), // Indicates the entity is standing in rain
|
||||
Frozen = (1 << 6), // Player is frozen for 3rd person camera
|
||||
Atcontrols = (1 << 7), // Player can't move, but keeps key inputs for controlling another entity
|
||||
Client = (1 << 8), // Is a player
|
||||
Fakeclient = (1 << 9), // Fake client, simulated server side; don't send network messages to them
|
||||
Inwater = (1 << 10), // In water
|
||||
Fly = (1 << 11), // Changes the SV_Movestep() behavior to not need to be on ground
|
||||
Swim = (1 << 12), // Changes the SV_Movestep() behavior to not need to be on ground (but stay in water)
|
||||
Conveyor = (1 << 13),
|
||||
Npc = (1 << 14),
|
||||
Godmode = (1 << 15),
|
||||
Notarget = (1 << 16),
|
||||
Aimtarget = (1 << 17), // set if the crosshair needs to aim onto the entity
|
||||
Partialground = (1 << 18), // not all corners are valid
|
||||
Staticprop = (1 << 19), // Eetsa static prop!
|
||||
Graphed = (1 << 20), // worldgraph has this ent listed as something that blocks a connection
|
||||
Grenade = (1 << 21),
|
||||
Stepmovement = (1 << 22), // Changes the SV_Movestep() behavior to not do any processing
|
||||
Donttouch = (1 << 23), // Doesn't generate touch functions, generates Untouch() for anything it was touching when this flag was set
|
||||
Basevelocity = (1 << 24), // Base velocity has been applied this frame (used to convert base velocity into momentum)
|
||||
Worldbrush = (1 << 25), // Not moveable/removeable brush entity (really part of the world, but represented as an entity for transparency or something)
|
||||
Object = (1 << 26), // Terrible name. This is an object that NPCs should see. Missiles, for example.
|
||||
Killme = (1 << 27), // This entity is marked for death -- will be freed by game DLL
|
||||
Onfire = (1 << 28), // You know...
|
||||
Dissolving = (1 << 29), // We're dissolving!
|
||||
Transragdoll = (1 << 30), // In the process of turning into a client side ragdoll.
|
||||
UnblockableByPlayer = (1 << 31) // pusher that can't be blocked by the player
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* This file is part of CounterStrikeSharp.
|
||||
* CounterStrikeSharp is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* CounterStrikeSharp is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
|
||||
*/
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Entities.Constants
|
||||
{
|
||||
public enum HitGroup
|
||||
{
|
||||
HITGROUP_GENERIC = 0,
|
||||
HITGROUP_HEAD = 1,
|
||||
HITGROUP_CHEST = 2,
|
||||
HITGROUP_STOMACH = 3,
|
||||
HITGROUP_LEFTARM = 4,
|
||||
HITGROUP_RIGHTARM = 5,
|
||||
HITGROUP_LEFTLEG = 6,
|
||||
HITGROUP_RIGHTLEG = 7,
|
||||
HITGROUP_GEAR = 10,
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* This file is part of CounterStrikeSharp.
|
||||
* CounterStrikeSharp is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* CounterStrikeSharp is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
|
||||
*/
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Entities.Constants
|
||||
{
|
||||
public enum MoveType
|
||||
{
|
||||
MOVETYPE_NONE = 0, // never moves
|
||||
MOVETYPE_ISOMETRIC, // For players -- in TF2 commander view, etc.
|
||||
MOVETYPE_WALK, // Player only - moving on the ground
|
||||
MOVETYPE_STEP, // gravity, special edge handling -- monsters use this
|
||||
MOVETYPE_FLY, // No gravity, but still collides with stuff
|
||||
MOVETYPE_FLYGRAVITY, // flies through the air + is affected by gravity
|
||||
MOVETYPE_VPHYSICS, // uses VPHYSICS for simulation
|
||||
MOVETYPE_PUSH, // no clip to world, push and crush
|
||||
MOVETYPE_NOCLIP, // No gravity, no collisions, still do velocity/avelocity
|
||||
MOVETYPE_LADDER, // Used by players only when going onto a ladder
|
||||
MOVETYPE_OBSERVER, // Observer movement, depends on player's observer mode
|
||||
MOVETYPE_CUSTOM, // Allows the entity to describe its own physics
|
||||
|
||||
// should always be defined as the last item in the list
|
||||
MOVETYPE_LAST = MOVETYPE_CUSTOM,
|
||||
|
||||
MOVETYPE_MAX_BITS = 4
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user