mirror of
https://github.com/daffyyyy/CS2-SimpleAdmin.git
synced 2026-02-17 10:31:01 +00:00
Compare commits
373 Commits
1d65d4be78
...
cc54b9e879
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc54b9e879 | ||
|
|
640e618f3b | ||
|
|
23d174c4a5 | ||
|
|
b7371adf26 | ||
|
|
9154748ce6 | ||
|
|
da9830ee05 | ||
|
|
b41ac992c0 | ||
|
|
af0bda8f3a | ||
|
|
e2529cd646 | ||
|
|
9d2cd34845 | ||
|
|
5701455de0 | ||
|
|
b97426313b | ||
|
|
3ab63c05db | ||
|
|
f654d6b085 | ||
|
|
676a18d9b4 | ||
|
|
d75a092047 | ||
|
|
d34ca64970 | ||
|
|
f69f1277f8 | ||
|
|
b6c876d709 | ||
|
|
888d6b0152 | ||
|
|
5d62c743dd | ||
|
|
62b1987fde | ||
|
|
708ae6cb90 | ||
|
|
2d77e86d59 | ||
|
|
babcbc2119 | ||
|
|
64e5f1156e | ||
|
|
8cc0398f6b | ||
|
|
ab14956ae5 | ||
|
|
9163caeb0d | ||
|
|
f2e4b84b29 | ||
|
|
3f1b6b3bf7 | ||
|
|
8af805632a | ||
|
|
8c94a867d3 | ||
|
|
023e1a031b | ||
|
|
1f1c214357 | ||
|
|
2defb2fe14 | ||
|
|
b2ebe136c3 | ||
|
|
5668c0ad7b | ||
|
|
70a62d4b63 | ||
|
|
95818e2742 | ||
|
|
7154843d1d | ||
|
|
c42d2ddeeb | ||
|
|
7a69c5387a | ||
|
|
82b82722a6 | ||
|
|
b38b9a0751 | ||
|
|
5a9367ae89 | ||
|
|
d30ac80a36 | ||
|
|
9820d74095 | ||
|
|
d0207f3d0b | ||
|
|
2bee514e32 | ||
|
|
9d87fd8701 | ||
|
|
6847da2af0 | ||
|
|
757a39f90e | ||
|
|
bd817d6652 | ||
|
|
4206ad18b2 | ||
|
|
774d812723 | ||
|
|
cf5c1d8440 | ||
|
|
efa2a392d6 | ||
|
|
32520c7e84 | ||
|
|
702a0315b8 | ||
|
|
b1ab6e0e0d | ||
|
|
e272449f3c | ||
|
|
05893a90d3 | ||
|
|
a3b8d8cfa7 | ||
|
|
1cf541bb51 | ||
|
|
3fb254bd0a | ||
|
|
a26df7fedd | ||
|
|
446dc9f4f5 | ||
|
|
3a8e0dd1fc | ||
|
|
b90ed05d1b | ||
|
|
6df98fb164 | ||
|
|
100482cb17 | ||
|
|
8f2f95ca23 | ||
|
|
79270acafe | ||
|
|
f95581e7c0 | ||
|
|
af46f0bca1 | ||
|
|
f56e3f6fe9 | ||
|
|
4ef07c8bf7 | ||
|
|
0674b7e492 | ||
|
|
c4cb308147 | ||
|
|
bb0a236f28 | ||
|
|
3bc51330af | ||
|
|
5fdefc9614 | ||
|
|
bcbcb83a35 | ||
|
|
6cf6b1c919 | ||
|
|
3b98f19a7c | ||
|
|
83fdf1dd56 | ||
|
|
6fc8b015ba | ||
|
|
6b5e36087e | ||
|
|
a395a7d9c8 | ||
|
|
d7e46525a5 | ||
|
|
cdd771511b | ||
|
|
8e4724fb3e | ||
|
|
985b1ae61f | ||
|
|
00facafdcb | ||
|
|
962529e445 | ||
|
|
873fed17c9 | ||
|
|
fc2958c84f | ||
|
|
4244104d6e | ||
|
|
d5a6ceacb6 | ||
|
|
14cc9d3b00 | ||
|
|
9fb256d39f | ||
|
|
c25d3c4bda | ||
|
|
ebf9e06bcd | ||
|
|
c72c7231f7 | ||
|
|
9c870b8fc3 | ||
|
|
cf7a87d959 | ||
|
|
9759b34505 | ||
|
|
5a90171842 | ||
|
|
19f8b68c1c | ||
|
|
342d4f717f | ||
|
|
731bc229d3 | ||
|
|
bb8cec7cf8 | ||
|
|
ad4c721a27 | ||
|
|
7efa2cdad3 | ||
|
|
c321502937 | ||
|
|
13ab223c86 | ||
|
|
c73584edae | ||
|
|
aefa6c6355 | ||
|
|
806b5038ca | ||
|
|
b45e112534 | ||
|
|
e1650df72e | ||
|
|
8caa8669ea | ||
|
|
516d9d9014 | ||
|
|
c71391bca4 | ||
|
|
2c66b899d0 | ||
|
|
1cfff8684f | ||
|
|
0bbf1948bf | ||
|
|
b4103bfc25 | ||
|
|
a0b2e59357 | ||
|
|
fb251aadef | ||
|
|
00343b6b66 | ||
|
|
270b36fa26 | ||
|
|
7a8fd066f7 | ||
|
|
90556e66b2 | ||
|
|
143f0b7e1b | ||
|
|
d8a1a1e471 | ||
|
|
3abf246f9b | ||
|
|
d1fdaa4ecf | ||
|
|
89d27e1bd3 | ||
|
|
bd82a981f5 | ||
|
|
052cea5ce3 | ||
|
|
ba2bf90340 | ||
|
|
8f80e13100 | ||
|
|
a377871951 | ||
|
|
8df1e70d5a | ||
|
|
d8e30e02e9 | ||
|
|
b2c56d3fa1 | ||
|
|
5609748d19 | ||
|
|
9d513a92a1 | ||
|
|
3a977f688c | ||
|
|
143dc138f0 | ||
|
|
a8904f2d8a | ||
|
|
39404e52ac | ||
|
|
0cca38b10b | ||
|
|
aac5caa565 | ||
|
|
90c0564f56 | ||
|
|
0f4a2835d2 | ||
|
|
c6126ab2ec | ||
|
|
60c76562f9 | ||
|
|
ac940259f7 | ||
|
|
cce265c6b7 | ||
|
|
525905194c | ||
|
|
2ab2f9c4dc | ||
|
|
da6fb2fc22 | ||
|
|
7d5166cf4b | ||
|
|
a8c4c1f9fa | ||
|
|
3c42acf15e | ||
|
|
f61a5ff6a8 | ||
|
|
229b8d73a3 | ||
|
|
650c115a88 | ||
|
|
4d3cefc495 | ||
|
|
5d58465c74 | ||
|
|
5bf966f9cd | ||
|
|
619bdfbb14 | ||
|
|
f4f669d498 | ||
|
|
8e1a1b2ecf | ||
|
|
64803ebff2 | ||
|
|
285429dc66 | ||
|
|
5b36a5fc62 | ||
|
|
4e898a6509 | ||
|
|
af66802b37 | ||
|
|
6c44281ca5 | ||
|
|
5edddbb70f | ||
|
|
a8a35544c0 | ||
|
|
74852f0651 | ||
|
|
1882b14657 | ||
|
|
c3dd3bed10 | ||
|
|
af73447b9e | ||
|
|
7386a2aae2 | ||
|
|
effe97a210 | ||
|
|
3b6b7fbcab | ||
|
|
9a7a143478 | ||
|
|
fe834d8abc | ||
|
|
eb9519c156 | ||
|
|
8750a66eef | ||
|
|
3317182b9a | ||
|
|
fa5296fec7 | ||
|
|
0a2d19accd | ||
|
|
c2f7b3be08 | ||
|
|
b31bb871a6 | ||
|
|
00819beec5 | ||
|
|
ae8d9db5e4 | ||
|
|
a3ebcccb6f | ||
|
|
dd3ccf4069 | ||
|
|
3413200d8e | ||
|
|
6dccddf929 | ||
|
|
751c97b4cf | ||
|
|
b4c259595e | ||
|
|
d8e8073dd6 | ||
|
|
377049c020 | ||
|
|
74c773aaea | ||
|
|
e2df860b6f | ||
|
|
b1e497ea3f | ||
|
|
953234078c | ||
|
|
bda704e843 | ||
|
|
e4e1023684 | ||
|
|
5d02343268 | ||
|
|
778c93b170 | ||
|
|
9cecc19060 | ||
|
|
f0b5b820e2 | ||
|
|
6a182fff9d | ||
|
|
cd8ee681b2 | ||
|
|
42849c231f | ||
|
|
76914be555 | ||
|
|
aadcaa0e64 | ||
|
|
0b2a520a07 | ||
|
|
79bbe0f4c5 | ||
|
|
01ceb104c5 | ||
|
|
e401fe7c8b | ||
|
|
fbed647699 | ||
|
|
aa95815fbd | ||
|
|
3793385ce4 | ||
|
|
c8c0a3f0ba | ||
|
|
5991f85919 | ||
|
|
a85e5c69ee | ||
|
|
40ad4b995c | ||
|
|
d3062b5f7e | ||
|
|
131030a2cd | ||
|
|
a9f3196441 | ||
|
|
bdb90b0cc6 | ||
|
|
93faad27c1 | ||
|
|
7251516bb4 | ||
|
|
175d6df250 | ||
|
|
7e5dc7b612 | ||
|
|
0572ad7d32 | ||
|
|
86e837931c | ||
|
|
b2f1afd7e7 | ||
|
|
705bacccae | ||
|
|
58086c4009 | ||
|
|
f15061793a | ||
|
|
5035f88da0 | ||
|
|
4e597d73a5 | ||
|
|
13d36dca12 | ||
|
|
9acab82f86 | ||
|
|
66f0ebe08c | ||
|
|
0cd1653185 | ||
|
|
fb827f4f0d | ||
|
|
6848847f21 | ||
|
|
a80de2d7dc | ||
|
|
a266f776a8 | ||
|
|
60ebb698a3 | ||
|
|
5fbd21aec2 | ||
|
|
232f487d01 | ||
|
|
f622701f5e | ||
|
|
65b33c17ea | ||
|
|
4fd268b235 | ||
|
|
bad4289c5c | ||
|
|
fd97e43490 | ||
|
|
a59cf5a7da | ||
|
|
dd7a2f4c16 | ||
|
|
fbf395d327 | ||
|
|
8ca82804a2 | ||
|
|
4b1cb3573b | ||
|
|
5dc35ddb73 | ||
|
|
31592bf1a5 | ||
|
|
a3471e2091 | ||
|
|
dfd9bd5329 | ||
|
|
e028bef5b0 | ||
|
|
67ad1851bf | ||
|
|
e584316a28 | ||
|
|
450a7804c6 | ||
|
|
855f087a7b | ||
|
|
b5600d7e73 | ||
|
|
6640d00442 | ||
|
|
8aee44f709 | ||
|
|
374f6002eb | ||
|
|
99ce2cdf6a | ||
|
|
38020e6509 | ||
|
|
d751bdadbd | ||
|
|
64c806f3d7 | ||
|
|
8c4cb49d02 | ||
|
|
2d41a62b52 | ||
|
|
48baac0a7b | ||
|
|
8d365b670e | ||
|
|
6a82d72244 | ||
|
|
e602eaa445 | ||
|
|
75f18d3c0a | ||
|
|
40611e9126 | ||
|
|
fa8b7a877f | ||
|
|
71af90031e | ||
|
|
07924a6a74 | ||
|
|
f78cd888de | ||
|
|
d173816492 | ||
|
|
dd5e72e873 | ||
|
|
cfe79109a8 | ||
|
|
18122cdc08 | ||
|
|
ac92f1cb4e | ||
|
|
6d6ff53b02 | ||
|
|
675c7bdcee | ||
|
|
c839e69804 | ||
|
|
3930d2d4bd | ||
|
|
6a5165a513 | ||
|
|
5dc14e3301 | ||
|
|
27582b72af | ||
|
|
f95031a3f5 | ||
|
|
7a73212d2e | ||
|
|
270e3bd858 | ||
|
|
b8075ffa8c | ||
|
|
28f6cf63fe | ||
|
|
e040dbced5 | ||
|
|
7e7fc5c885 | ||
|
|
15ab2d9e65 | ||
|
|
2105d80f52 | ||
|
|
d677551463 | ||
|
|
efd9ab5c22 | ||
|
|
e6fb8bc402 | ||
|
|
9712f04e38 | ||
|
|
719caeafef | ||
|
|
22bb7ebac0 | ||
|
|
b813a422a2 | ||
|
|
36e5cd48a5 | ||
|
|
34342c5134 | ||
|
|
5183661c2a | ||
|
|
6c075cdafe | ||
|
|
a56b5f7b3f | ||
|
|
07b79cb91a | ||
|
|
607c38e992 | ||
|
|
dbdefa8168 | ||
|
|
b8048e1500 | ||
|
|
59975bb49d | ||
|
|
454643431a | ||
|
|
6ac370df74 | ||
|
|
f3a1200492 | ||
|
|
90ca6c1ac2 | ||
|
|
29fe968155 | ||
|
|
fdbf1b1a5c | ||
|
|
dcc85b21c3 | ||
|
|
9c293e6201 | ||
|
|
bd0f21b2ad | ||
|
|
13e170a9c4 | ||
|
|
696b8c3877 | ||
|
|
f19c594568 | ||
|
|
90efff5d24 | ||
|
|
2ea03caeed | ||
|
|
d16b92e694 | ||
|
|
0f32daa4c0 | ||
|
|
86378324a7 | ||
|
|
bce6bd4348 | ||
|
|
5ea71f1679 | ||
|
|
53812148b3 | ||
|
|
d496977c76 | ||
|
|
35e6a52ad8 | ||
|
|
68a446a515 | ||
|
|
0a0c0cfbc4 | ||
|
|
39e73763b7 | ||
|
|
ad621b9cb8 | ||
|
|
31bc91e0a4 | ||
|
|
fa508c965a | ||
|
|
d2d1f41e80 | ||
|
|
ffcd623628 | ||
|
|
e4a6c733ab | ||
|
|
6bcb10c5cf |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
ko_fi: daffyy
|
||||
122
.github/workflows/build.yml
vendored
Normal file
122
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
name: Build and Publish
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
paths-ignore:
|
||||
- '**/README.md'
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
paths-ignore:
|
||||
- '**/README.md'
|
||||
|
||||
env:
|
||||
PROJECT_PATH_CS2_SIMPLEADMIN: "CS2-SimpleAdmin/CS2-SimpleAdmin.csproj"
|
||||
PROJECT_NAME_CS2_SIMPLEADMIN: "CS2-SimpleAdmin"
|
||||
PROJECT_PATH_CS2_SIMPLEADMINAPI: "CS2-SimpleAdminApi/CS2-SimpleAdminApi.csproj"
|
||||
PROJECT_NAME_CS2_SIMPLEADMINAPI: "CS2-SimpleAdminApi"
|
||||
PROJECT_PATH_STEALTHMODULE: "Modules/CS2-SimpleAdmin_StealthModule/CS2-SimpleAdmin_StealthModule.csproj"
|
||||
PROJECT_NAME_STEALTHMODULE: "CS2-SimpleAdmin_StealthModule"
|
||||
OUTPUT_PATH: "./counterstrikesharp"
|
||||
TMP_PATH: "./tmp"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions: write-all
|
||||
outputs:
|
||||
build_version: ${{ steps.get_version.outputs.VERSION }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Get Version
|
||||
id: get_version
|
||||
run: echo "VERSION=$(cat CS2-SimpleAdmin/VERSION)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Restore & Build All Projects
|
||||
run: |
|
||||
dotnet restore ${{ env.PROJECT_PATH_CS2_SIMPLEADMIN }}
|
||||
dotnet build ${{ env.PROJECT_PATH_CS2_SIMPLEADMIN }} -c Release -o ${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMIN }}
|
||||
dotnet restore ${{ env.PROJECT_PATH_CS2_SIMPLEADMINAPI }}
|
||||
dotnet build ${{ env.PROJECT_PATH_CS2_SIMPLEADMINAPI }} -c Release -o ${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMINAPI }}
|
||||
dotnet restore ${{ env.PROJECT_PATH_STEALTHMODULE }}
|
||||
dotnet build ${{ env.PROJECT_PATH_STEALTHMODULE }} -c Release -o ${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_STEALTHMODULE }}
|
||||
|
||||
- name: Combine projects
|
||||
run: |
|
||||
mkdir -p ${{ env.OUTPUT_PATH }}/plugins/CS2-SimpleAdmin
|
||||
mkdir -p ${{ env.OUTPUT_PATH }}/plugins/CS2-SimpleAdmin_StealthModule
|
||||
mkdir -p ${{ env.OUTPUT_PATH }}/shared/CS2-SimpleAdminApi
|
||||
|
||||
cp -r ${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMIN }}/* ${{ env.OUTPUT_PATH }}/plugins/CS2-SimpleAdmin/
|
||||
cp -r ${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_STEALTHMODULE }}/* ${{ env.OUTPUT_PATH }}/plugins/CS2-SimpleAdmin_StealthModule
|
||||
cp -r ${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMINAPI }}/* ${{ env.OUTPUT_PATH }}/shared/CS2-SimpleAdminApi/
|
||||
|
||||
- name: Zip Main Build Output
|
||||
run: zip -r CS2-SimpleAdmin-${{ steps.get_version.outputs.VERSION }}.zip ${{ env.OUTPUT_PATH }}
|
||||
|
||||
- name: Extract & Zip StatusBlocker Linux
|
||||
run: |
|
||||
mkdir -p statusblocker-linux &&
|
||||
tar -xzf Modules/CS2-SimpleAdmin_StealthModule/METAMOD\ PLUGIN/StatusBlocker-v*-linux.tar.gz -C statusblocker-linux &&
|
||||
cd statusblocker-linux &&
|
||||
zip -r ../StatusBlocker-linux-${{ steps.get_version.outputs.VERSION }}.zip ./*
|
||||
|
||||
- name: Extract & Zip StatusBlocker Windows
|
||||
run: |
|
||||
mkdir -p statusblocker-windows &&
|
||||
tar -xzf Modules/CS2-SimpleAdmin_StealthModule/METAMOD\ PLUGIN/StatusBlocker-v*-windows.tar.gz -C statusblocker-windows &&
|
||||
cd statusblocker-windows &&
|
||||
zip -r ../StatusBlocker-windows-${{ steps.get_version.outputs.VERSION }}.zip ./*
|
||||
|
||||
- name: Upload all artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: CS2-SimpleAdmin-Build-Artifacts
|
||||
path: |
|
||||
CS2-SimpleAdmin-${{ steps.get_version.outputs.VERSION }}.zip
|
||||
StatusBlocker-linux-${{ steps.get_version.outputs.VERSION }}.zip
|
||||
StatusBlocker-windows-${{ steps.get_version.outputs.VERSION }}.zip
|
||||
|
||||
publish:
|
||||
needs: build
|
||||
if: github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
permissions: write-all
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: CS2-SimpleAdmin-Build-Artifacts
|
||||
path: .
|
||||
- name: Unzip main build artifact
|
||||
run: unzip CS2-SimpleAdmin-${{ needs.build.outputs.build_version }}.zip -d ./counterstrikesharp
|
||||
- name: Publish combined release
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
artifacts: |
|
||||
CS2-SimpleAdmin-${{ needs.build.outputs.build_version }}.zip
|
||||
StatusBlocker-linux-${{ needs.build.outputs.build_version }}.zip
|
||||
StatusBlocker-windows-${{ needs.build.outputs.build_version }}.zip
|
||||
name: "CS2-SimpleAdmin-${{ needs.build.outputs.build_version }}"
|
||||
tag: "build-${{ needs.build.outputs.build_version }}"
|
||||
body: |
|
||||
Place the files in your server as follows:
|
||||
|
||||
- CS2-SimpleAdmin:
|
||||
Place the files inside the addons/counterstrikesharp directory.
|
||||
After the first launch, configure the plugin using the JSON config file at:
|
||||
addons/counterstrikesharp/configs/plugins/CS2-SimpleAdmin/CS2-SimpleAdmin.json
|
||||
|
||||
- StatusBlocker:
|
||||
Place the plugin files directly into the addons directory.
|
||||
This plugin is a Metamod module for the StealthModule and does not require a subfolder.
|
||||
|
||||
Remember to restart or reload your game server after installing and configuring the plugins.
|
||||
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
bin/
|
||||
obj/
|
||||
.vs/
|
||||
.git
|
||||
.vscode/
|
||||
.idea/
|
||||
Modules/CS2-SimpleAdmin_PlayTimeModule
|
||||
CS2-SimpleAdmin.sln.DotSettings.user
|
||||
Modules/CS2-SimpleAdmin_ExampleModule/CS2-SimpleAdmin_ExampleModule.sln.DotSettings.user
|
||||
CS2-SimpleAdmin_BanSoundModule — kopia
|
||||
*.user
|
||||
31
CS2-SimpleAdmin.sln
Normal file
31
CS2-SimpleAdmin.sln
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.8.34309.116
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CS2-SimpleAdmin", "CS2-SimpleAdmin\CS2-SimpleAdmin.csproj", "{CC7C3B4D-26C9-4DE7-B4E1-0864350468D0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CS2-SimpleAdminApi", "CS2-SimpleAdminApi\CS2-SimpleAdminApi.csproj", "{8BEF0C35-7E4E-4BAF-B632-8584FAFCA922}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{CC7C3B4D-26C9-4DE7-B4E1-0864350468D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CC7C3B4D-26C9-4DE7-B4E1-0864350468D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CC7C3B4D-26C9-4DE7-B4E1-0864350468D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CC7C3B4D-26C9-4DE7-B4E1-0864350468D0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8BEF0C35-7E4E-4BAF-B632-8584FAFCA922}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8BEF0C35-7E4E-4BAF-B632-8584FAFCA922}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8BEF0C35-7E4E-4BAF-B632-8584FAFCA922}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8BEF0C35-7E4E-4BAF-B632-8584FAFCA922}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {86114444-059F-4DB8-9A55-E6D1BB76238D}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
BIN
CS2-SimpleAdmin/3rd_party/MenuManagerApi.dll
vendored
Normal file
BIN
CS2-SimpleAdmin/3rd_party/MenuManagerApi.dll
vendored
Normal file
Binary file not shown.
170
CS2-SimpleAdmin/Api/CS2_SimpleAdminApi.cs
Normal file
170
CS2-SimpleAdmin/Api/CS2_SimpleAdminApi.cs
Normal file
@@ -0,0 +1,170 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CS2_SimpleAdmin.Managers;
|
||||
using CS2_SimpleAdminApi;
|
||||
|
||||
namespace CS2_SimpleAdmin.Api;
|
||||
|
||||
public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
||||
{
|
||||
public PlayerInfo GetPlayerInfo(CCSPlayerController player)
|
||||
{
|
||||
if (!player.UserId.HasValue)
|
||||
throw new KeyNotFoundException("Player with specific UserId not found");
|
||||
|
||||
return CS2_SimpleAdmin.PlayersInfo[player.SteamID];
|
||||
}
|
||||
|
||||
public string GetConnectionString() => CS2_SimpleAdmin.Instance.DbConnectionString;
|
||||
public string GetServerAddress() => CS2_SimpleAdmin.IpAddress;
|
||||
public int? GetServerId() => CS2_SimpleAdmin.ServerId;
|
||||
|
||||
public Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>> GetPlayerMuteStatus(CCSPlayerController player)
|
||||
{
|
||||
return PlayerPenaltyManager.GetAllPlayerPenalties(player.Slot);
|
||||
}
|
||||
|
||||
public event Action<PlayerInfo, PlayerInfo?, PenaltyType, string, int, int?, int?>? OnPlayerPenaltied;
|
||||
public event Action<SteamID, PlayerInfo?, PenaltyType, string, int, int?, int?>? OnPlayerPenaltiedAdded;
|
||||
public event Action<string, string?, bool, object>? OnAdminShowActivity;
|
||||
public event Action<int, bool>? OnAdminToggleSilent;
|
||||
|
||||
public void OnPlayerPenaltiedEvent(PlayerInfo player, PlayerInfo? admin, PenaltyType penaltyType, string reason,
|
||||
int duration, int? penaltyId) => OnPlayerPenaltied?.Invoke(player, admin, penaltyType, reason, duration, penaltyId, CS2_SimpleAdmin.ServerId);
|
||||
|
||||
public void OnPlayerPenaltiedAddedEvent(SteamID player, PlayerInfo? admin, PenaltyType penaltyType, string reason,
|
||||
int duration, int? penaltyId) => OnPlayerPenaltiedAdded?.Invoke(player, admin, penaltyType, reason, duration, penaltyId, CS2_SimpleAdmin.ServerId);
|
||||
|
||||
public void OnAdminShowActivityEvent(string messageKey, string? callerName = null, bool dontPublish = false, params object[] messageArgs) => OnAdminShowActivity?.Invoke(messageKey, callerName, dontPublish, messageArgs);
|
||||
|
||||
public void OnAdminToggleSilentEvent(int slot, bool status) => OnAdminToggleSilent?.Invoke(slot, status);
|
||||
|
||||
public void IssuePenalty(CCSPlayerController player, CCSPlayerController? admin, PenaltyType penaltyType, string reason, int duration = -1)
|
||||
{
|
||||
switch (penaltyType)
|
||||
{
|
||||
case PenaltyType.Ban:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.Ban(admin, player, duration, reason);
|
||||
break;
|
||||
}
|
||||
case PenaltyType.Kick:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.Kick(admin, player, reason);
|
||||
break;
|
||||
}
|
||||
case PenaltyType.Gag:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.Gag(admin, player, duration, reason);
|
||||
break;
|
||||
}
|
||||
case PenaltyType.Mute:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.Mute(admin, player, duration, reason);
|
||||
break;
|
||||
}
|
||||
case PenaltyType.Silence:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.Silence(admin, player, duration, reason);
|
||||
break;
|
||||
}
|
||||
case PenaltyType.Warn:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.Warn(admin, player, duration, reason);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(penaltyType), penaltyType, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void IssuePenalty(SteamID steamid, CCSPlayerController? admin, PenaltyType penaltyType, string reason, int duration = -1)
|
||||
{
|
||||
switch (penaltyType)
|
||||
{
|
||||
case PenaltyType.Ban:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.AddBan(admin, steamid, duration, reason);
|
||||
break;
|
||||
}
|
||||
case PenaltyType.Gag:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.AddGag(admin, steamid, duration, reason);
|
||||
break;
|
||||
}
|
||||
case PenaltyType.Mute:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.AddMute(admin, steamid, duration, reason);
|
||||
break;
|
||||
}
|
||||
case PenaltyType.Silence:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.AddSilence(admin, steamid, duration, reason);
|
||||
break;
|
||||
}
|
||||
case PenaltyType.Warn:
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.AddWarn(admin, steamid, duration, reason);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(penaltyType), penaltyType, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void LogCommand(CCSPlayerController? caller, string command)
|
||||
{
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
|
||||
public void LogCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
|
||||
public bool IsAdminSilent(CCSPlayerController player)
|
||||
{
|
||||
return CS2_SimpleAdmin.SilentPlayers.Contains(player.Slot);
|
||||
}
|
||||
|
||||
public HashSet<int> ListSilentAdminsSlots()
|
||||
{
|
||||
return CS2_SimpleAdmin.SilentPlayers;
|
||||
}
|
||||
|
||||
public void RegisterCommand(string name, string? description, CommandInfo.CommandCallback callback)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
throw new ArgumentException("Command name cannot be null or empty.", nameof(name));
|
||||
|
||||
ArgumentNullException.ThrowIfNull(callback);
|
||||
|
||||
var definition = new CommandDefinition(name, description ?? "No description", callback);
|
||||
if (!RegisterCommands._commandDefinitions.TryGetValue(name, out var list))
|
||||
{
|
||||
list = new List<CommandDefinition>();
|
||||
RegisterCommands._commandDefinitions[name] = list;
|
||||
}
|
||||
|
||||
list.Add(definition);
|
||||
}
|
||||
|
||||
public void UnRegisterCommand(string commandName)
|
||||
{
|
||||
var definitions = RegisterCommands._commandDefinitions[commandName];
|
||||
if (definitions.Count == 0)
|
||||
return;
|
||||
|
||||
foreach (var definition in definitions)
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.RemoveCommand(commandName, definition.Callback);
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowAdminActivity(string messageKey, string? callerName = null, bool dontPublish = false, params object[] messageArgs)
|
||||
{
|
||||
Helper.ShowAdminActivity(messageKey, callerName, dontPublish, messageArgs);
|
||||
}
|
||||
}
|
||||
268
CS2-SimpleAdmin/CS2-SimpleAdmin.cs
Normal file
268
CS2-SimpleAdmin/CS2-SimpleAdmin.cs
Normal file
@@ -0,0 +1,268 @@
|
||||
using System.Reflection;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Core.Capabilities;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Commands.Targeting;
|
||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
using CS2_SimpleAdmin.Database;
|
||||
using CS2_SimpleAdmin.Managers;
|
||||
using CS2_SimpleAdminApi;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MySqlConnector;
|
||||
|
||||
namespace CS2_SimpleAdmin;
|
||||
|
||||
[MinimumApiVersion(300)]
|
||||
public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdminConfig>
|
||||
{
|
||||
internal static CS2_SimpleAdmin Instance { get; private set; } = new();
|
||||
|
||||
public override string ModuleName => "CS2-SimpleAdmin" + (Helper.IsDebugBuild ? " (DEBUG)" : " (RELEASE)");
|
||||
public override string ModuleDescription => "Simple admin plugin for Counter-Strike 2 :)";
|
||||
public override string ModuleAuthor => "daffyy & Dliix66";
|
||||
public override string ModuleVersion => "1.7.7-alpha-10";
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
Instance = this;
|
||||
|
||||
if (hotReload)
|
||||
{
|
||||
ServerLoaded = false;
|
||||
_serverLoading = false;
|
||||
|
||||
CacheManager?.Dispose();
|
||||
CacheManager = new CacheManager();
|
||||
|
||||
// OnGameServerSteamAPIActivated();
|
||||
OnMapStart(string.Empty);
|
||||
|
||||
AddTimer(6.0f, () =>
|
||||
{
|
||||
if (DatabaseProvider == null) return;
|
||||
|
||||
PlayersInfo.Clear();
|
||||
CachedPlayers.Clear();
|
||||
BotPlayers.Clear();
|
||||
|
||||
foreach (var player in Utilities.GetPlayers().Where(p => p.IsValid && !p.IsHLTV).ToArray())
|
||||
{
|
||||
if (!player.IsBot)
|
||||
PlayerManager.LoadPlayerData(player, true);
|
||||
else
|
||||
BotPlayers.Add(player);
|
||||
};
|
||||
});
|
||||
|
||||
PlayersTimer?.Kill();
|
||||
PlayersTimer = null;
|
||||
}
|
||||
_cBasePlayerControllerSetPawnFunc = new MemoryFunctionVoid<CBasePlayerController, CCSPlayerPawn, bool, bool>(GameData.GetSignature("CBasePlayerController_SetPawn"));
|
||||
|
||||
SimpleAdminApi = new Api.CS2_SimpleAdminApi();
|
||||
Capabilities.RegisterPluginCapability(ICS2_SimpleAdminApi.PluginCapability, () => SimpleAdminApi);
|
||||
|
||||
PlayersTimer?.Kill();
|
||||
PlayersTimer = null;
|
||||
PlayerManager.CheckPlayersTimer();
|
||||
}
|
||||
|
||||
public override void OnAllPluginsLoaded(bool hotReload)
|
||||
{
|
||||
try
|
||||
{
|
||||
MenuApi = MenuCapability.Get();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Unable to load required plugins ... \n{exception}", ex.Message);
|
||||
Unload(false);
|
||||
}
|
||||
|
||||
AddTimer(6.0f, () => ReloadAdmins(null));
|
||||
RegisterEvents();
|
||||
RegisterCommands.InitializeCommands();
|
||||
|
||||
if (!CoreConfig.UnlockConCommands)
|
||||
{
|
||||
_logger?.LogError(
|
||||
$"⚠️ Warning: 'UnlockConCommands' is disabled in core.json. " +
|
||||
$"Players will not be automatically banned when kicked and will be able " +
|
||||
$"to rejoin the server for 60 seconds. " +
|
||||
$"To enable instant banning, set 'UnlockConCommands': true"
|
||||
);
|
||||
_logger?.LogError(
|
||||
$"⚠️ Warning: 'UnlockConCommands' is disabled in core.json. " +
|
||||
$"Players will not be automatically banned when kicked and will be able " +
|
||||
$"to rejoin the server for 60 seconds. " +
|
||||
$"To enable instant banning, set 'UnlockConCommands': true"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnConfigParsed(CS2_SimpleAdminConfig config)
|
||||
{
|
||||
if (System.Diagnostics.Debugger.IsAttached)
|
||||
Environment.FailFast(":(!");
|
||||
|
||||
Helper.UpdateConfig(config);
|
||||
|
||||
_logger = Logger;
|
||||
Config = config;
|
||||
|
||||
bool missing = false;
|
||||
var cssPath = Path.Combine(Server.GameDirectory, "csgo", "addons", "counterstrikesharp");
|
||||
var pluginsPath = Path.Combine(cssPath, "plugins");
|
||||
var sharedPath = Path.Combine(cssPath, "shared");
|
||||
|
||||
foreach (var plugin in _requiredPlugins)
|
||||
{
|
||||
var pluginDirPath = Path.Combine(pluginsPath, plugin);
|
||||
var pluginDllPath = Path.Combine(pluginDirPath, $"{plugin}.dll");
|
||||
|
||||
if (!Directory.Exists(pluginDirPath))
|
||||
{
|
||||
_logger?.LogError(
|
||||
$"❌ Plugin directory '{plugin}' missing at: {pluginDirPath}"
|
||||
);
|
||||
missing = true;
|
||||
}
|
||||
|
||||
if (!File.Exists(pluginDllPath))
|
||||
{
|
||||
_logger?.LogError(
|
||||
$"❌ Plugin DLL '{plugin}.dll' missing at: {pluginDllPath}"
|
||||
);
|
||||
missing = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var shared in _requiredShared)
|
||||
{
|
||||
var sharedDirPath = Path.Combine(sharedPath, shared);
|
||||
var sharedDllPath = Path.Combine(sharedDirPath, $"{shared}.dll");
|
||||
|
||||
if (!Directory.Exists(sharedDirPath))
|
||||
{
|
||||
_logger?.LogError(
|
||||
$"❌ Shared library directory '{shared}' missing at: {sharedDirPath}"
|
||||
);
|
||||
missing = true;
|
||||
}
|
||||
|
||||
if (!File.Exists(sharedDllPath))
|
||||
{
|
||||
_logger?.LogError(
|
||||
$"❌ Shared library DLL '{shared}.dll' missing at: {sharedDllPath}"
|
||||
);
|
||||
missing = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (missing)
|
||||
Server.ExecuteCommand($"css_plugins unload {ModuleName}");
|
||||
|
||||
Instance = this;
|
||||
|
||||
if (Config.DatabaseConfig.DatabaseType.Contains("mysql", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(config.DatabaseConfig.DatabaseHost) ||
|
||||
string.IsNullOrWhiteSpace(config.DatabaseConfig.DatabaseName) ||
|
||||
string.IsNullOrWhiteSpace(config.DatabaseConfig.DatabaseUser))
|
||||
{
|
||||
throw new Exception("[CS2-SimpleAdmin] You need to setup MySQL credentials in config!");
|
||||
}
|
||||
|
||||
var builder = new MySqlConnectionStringBuilder()
|
||||
{
|
||||
Server = config.DatabaseConfig.DatabaseHost,
|
||||
Database = config.DatabaseConfig.DatabaseName,
|
||||
UserID = config.DatabaseConfig.DatabaseUser,
|
||||
Password = config.DatabaseConfig.DatabasePassword,
|
||||
Port = (uint)config.DatabaseConfig.DatabasePort,
|
||||
SslMode = Enum.TryParse(config.DatabaseConfig.DatabaseSSlMode, true, out MySqlSslMode sslMode)
|
||||
? sslMode
|
||||
: MySqlSslMode.Preferred,
|
||||
Pooling = true,
|
||||
};
|
||||
|
||||
DbConnectionString = builder.ConnectionString;
|
||||
DatabaseProvider = new MySqlDatabaseProvider(DbConnectionString);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(config.DatabaseConfig.SqliteFilePath))
|
||||
{
|
||||
throw new Exception("[CS2-SimpleAdmin] You need to specify SQLite file path in config!");
|
||||
}
|
||||
|
||||
DatabaseProvider = new SqliteDatabaseProvider(ModuleDirectory + "/" + config.DatabaseConfig.SqliteFilePath);
|
||||
}
|
||||
|
||||
var (success, exception) = Task.Run(() => DatabaseProvider.CheckConnectionAsync()).GetAwaiter().GetResult();
|
||||
if (!success)
|
||||
{
|
||||
if (exception != null)
|
||||
Logger.LogError("Problem with database connection! \n{exception}", exception);
|
||||
|
||||
Unload(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Task.Run(() => DatabaseProvider.DatabaseMigrationAsync());
|
||||
|
||||
if (!Directory.Exists(ModuleDirectory + "/data"))
|
||||
{
|
||||
Directory.CreateDirectory(ModuleDirectory + "/data");
|
||||
}
|
||||
|
||||
_localizer = Localizer;
|
||||
|
||||
if (!string.IsNullOrEmpty(Config.Discord.DiscordLogWebhook))
|
||||
DiscordWebhookClientLog = new DiscordManager(Config.Discord.DiscordLogWebhook);
|
||||
|
||||
if (Config.EnableUpdateCheck)
|
||||
Task.Run(async () => await PluginInfo.CheckVersion(ModuleVersion, Logger));
|
||||
|
||||
PermissionManager = new PermissionManager(DatabaseProvider);
|
||||
BanManager = new BanManager(DatabaseProvider);
|
||||
MuteManager = new MuteManager(DatabaseProvider);
|
||||
WarnManager = new WarnManager(DatabaseProvider);
|
||||
}
|
||||
|
||||
private static TargetResult? GetTarget(CommandInfo command, int argument = 1)
|
||||
{
|
||||
var matches = command.GetArgTargetResult(argument);
|
||||
|
||||
if (!matches.Any())
|
||||
{
|
||||
command.ReplyToCommand($"Target {command.GetArg(argument)} not found.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (command.GetArg(argument).StartsWith('@'))
|
||||
return matches;
|
||||
|
||||
if (matches.Count() == 1)
|
||||
return matches;
|
||||
|
||||
command.ReplyToCommand($"Multiple targets found for \"{command.GetArg(argument)}\".");
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void Unload(bool hotReload)
|
||||
{
|
||||
CacheManager?.Dispose();
|
||||
CacheManager = null;
|
||||
PlayersTimer?.Kill();
|
||||
PlayersTimer = null;
|
||||
UnregisterEvents();
|
||||
|
||||
if (hotReload)
|
||||
PlayersInfo.Clear();
|
||||
else
|
||||
Server.ExecuteCommand($"css_plugins unload {ModuleDirectory}");
|
||||
}
|
||||
}
|
||||
158
CS2-SimpleAdmin/CS2-SimpleAdmin.csproj
Normal file
158
CS2-SimpleAdmin/CS2-SimpleAdmin.csproj
Normal file
@@ -0,0 +1,158 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RootNamespace>CS2_SimpleAdmin</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<DebugType>none</DebugType>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
<DebuggerSupport>false</DebuggerSupport>
|
||||
<GenerateDependencyFile>false</GenerateDependencyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.340">
|
||||
<PrivateAssets>none</PrivateAssets>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
<IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Dapper" Version="2.1.66" />
|
||||
<PackageReference Include="MySqlConnector" Version="2.4.0" />
|
||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.119" />
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.3" />
|
||||
<PackageReference Include="ZLinq" Version="1.5.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CS2-SimpleAdminApi\CS2-SimpleAdminApi.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="ClearBuildFiles" AfterTargets="PostBuildEvent">
|
||||
<ItemGroup>
|
||||
<FilesToDelete Include="$(OutDir)Tomlyn.dll" />
|
||||
<FilesToDelete Include="$(OutDir)Serilog*.dll" />
|
||||
<FilesToDelete Include="$(OutDir)CS2-SimpleAdminApi.*" />
|
||||
<FilesToDelete Include="$(OutDir)McMaster*" />
|
||||
<FilesToDelete Include="$(OutDir)Scrutor.dll" />
|
||||
</ItemGroup>
|
||||
<Delete Files="@(FilesToDelete)" />
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="lang\**\*.*" CopyToOutputDirectory="PreserveNewest" />
|
||||
<None Update="Database\Migrations\Mysql\001_CreateTables.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Mysql\002_CreateFlagsTable.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Mysql\003_ChangeColumnsPosition.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Mysql\004_MoveOldFlagsToFlagsTable.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Mysql\005_CreateUnbansTable.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Mysql\006_ServerGroupsFeature.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Mysql\007_ServerGroupsGlobal.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Mysql\008_OnlineTimeInPenalties.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Mysql\009_BanAllUsedIpAddress.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Mysql\010_CreateWarnsTable.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Mysql\011_AddRconColumnToServersTable.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Mysql\012_AddUpdatedAtColumnToSaBansTable.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Mysql\013_AddNameColumnToSaPlayerIpsTable.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Mysql\014_AddIndexesToMutesTable.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Mysql\015_steamidToBigInt.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Sqlite\001_CreateTables.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Sqlite\002_CreateFlagsTable.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Sqlite\003_ChangeColumnsPosition.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Sqlite\004_MoveOldFlagsToFlagsTable.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Sqlite\005_CreateUnbansTable.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Sqlite\006_ServerGroupsFeature.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Sqlite\007_ServerGroupsGlobal.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Sqlite\008_OnlineTimeInPenalties.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Sqlite\009_BanAllUsedIpAddress.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Sqlite\010_CreateWarnsTable.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Sqlite\011_AddRconColumnToServersTable.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Sqlite\012_AddUpdatedAtColumnToSaBansTable.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Sqlite\013_AddNameColumnToSaPlayerIpsTable.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Sqlite\014_AddIndexesToMutesTable.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\Sqlite\015_steamidToBigInt.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Database\Migrations\*.sql" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="admin_help.txt" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="MenuManagerApi">
|
||||
<HintPath>3rd_party\MenuManagerApi.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
260
CS2-SimpleAdmin/Commands/RegisterCommands.cs
Normal file
260
CS2-SimpleAdmin/Commands/RegisterCommands.cs
Normal file
@@ -0,0 +1,260 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CS2_SimpleAdmin;
|
||||
|
||||
public static class RegisterCommands
|
||||
{
|
||||
internal static readonly Dictionary<string, IList<CommandDefinition>> _commandDefinitions =
|
||||
new(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
private delegate void CommandCallback(CCSPlayerController? caller, CommandInfo.CommandCallback callback);
|
||||
|
||||
private static readonly string CommandsPath = Path.Combine(CS2_SimpleAdmin.ConfigDirectory, "Commands.json");
|
||||
private static readonly List<CommandMapping> CommandMappings =
|
||||
[
|
||||
new("css_ban", CS2_SimpleAdmin.Instance.OnBanCommand),
|
||||
new("css_addban", CS2_SimpleAdmin.Instance.OnAddBanCommand),
|
||||
new("css_banip", CS2_SimpleAdmin.Instance.OnBanIpCommand),
|
||||
new("css_unban", CS2_SimpleAdmin.Instance.OnUnbanCommand),
|
||||
new("css_warn", CS2_SimpleAdmin.Instance.OnWarnCommand),
|
||||
new("css_unwarn", CS2_SimpleAdmin.Instance.OnUnwarnCommand),
|
||||
|
||||
new("css_asay", CS2_SimpleAdmin.Instance.OnAdminToAdminSayCommand),
|
||||
new("css_cssay", CS2_SimpleAdmin.Instance.OnAdminCustomSayCommand),
|
||||
new("css_say", CS2_SimpleAdmin.Instance.OnAdminSayCommand),
|
||||
new("css_psay", CS2_SimpleAdmin.Instance.OnAdminPrivateSayCommand),
|
||||
new("css_csay", CS2_SimpleAdmin.Instance.OnAdminCenterSayCommand),
|
||||
new("css_hsay", CS2_SimpleAdmin.Instance.OnAdminHudSayCommand),
|
||||
|
||||
new("css_penalties", CS2_SimpleAdmin.Instance.OnPenaltiesCommand),
|
||||
new("css_admin", CS2_SimpleAdmin.Instance.OnAdminCommand),
|
||||
new("css_adminhelp", CS2_SimpleAdmin.Instance.OnAdminHelpCommand),
|
||||
new("css_addadmin", CS2_SimpleAdmin.Instance.OnAddAdminCommand),
|
||||
new("css_deladmin", CS2_SimpleAdmin.Instance.OnDelAdminCommand),
|
||||
new("css_addgroup", CS2_SimpleAdmin.Instance.OnAddGroup),
|
||||
new("css_delgroup", CS2_SimpleAdmin.Instance.OnDelGroupCommand),
|
||||
new("css_reloadadmins", CS2_SimpleAdmin.Instance.OnRelAdminCommand),
|
||||
new("css_reloadbans", CS2_SimpleAdmin.Instance.OnRelBans),
|
||||
new("css_hide", CS2_SimpleAdmin.Instance.OnHideCommand),
|
||||
new("css_hidecomms", CS2_SimpleAdmin.Instance.OnHideCommsCommand),
|
||||
new("css_who", CS2_SimpleAdmin.Instance.OnWhoCommand),
|
||||
new("css_disconnected", CS2_SimpleAdmin.Instance.OnDisconnectedCommand),
|
||||
new("css_warns", CS2_SimpleAdmin.Instance.OnWarnsCommand),
|
||||
new("css_players", CS2_SimpleAdmin.Instance.OnPlayersCommand),
|
||||
new("css_kick", CS2_SimpleAdmin.Instance.OnKickCommand),
|
||||
new("css_map", CS2_SimpleAdmin.Instance.OnMapCommand),
|
||||
new("css_wsmap", CS2_SimpleAdmin.Instance.OnWorkshopMapCommand),
|
||||
new("css_cvar", CS2_SimpleAdmin.Instance.OnCvarCommand),
|
||||
new("css_rcon", CS2_SimpleAdmin.Instance.OnRconCommand),
|
||||
new("css_rr", CS2_SimpleAdmin.Instance.OnRestartCommand),
|
||||
|
||||
new("css_gag", CS2_SimpleAdmin.Instance.OnGagCommand),
|
||||
new("css_addgag", CS2_SimpleAdmin.Instance.OnAddGagCommand),
|
||||
new("css_ungag", CS2_SimpleAdmin.Instance.OnUngagCommand),
|
||||
new("css_mute", CS2_SimpleAdmin.Instance.OnMuteCommand),
|
||||
new("css_addmute", CS2_SimpleAdmin.Instance.OnAddMuteCommand),
|
||||
new("css_unmute", CS2_SimpleAdmin.Instance.OnUnmuteCommand),
|
||||
new("css_silence", CS2_SimpleAdmin.Instance.OnSilenceCommand),
|
||||
new("css_addsilence", CS2_SimpleAdmin.Instance.OnAddSilenceCommand),
|
||||
new("css_unsilence", CS2_SimpleAdmin.Instance.OnUnsilenceCommand),
|
||||
|
||||
new("css_vote", CS2_SimpleAdmin.Instance.OnVoteCommand),
|
||||
|
||||
new("css_noclip", CS2_SimpleAdmin.Instance.OnNoclipCommand),
|
||||
new("css_freeze", CS2_SimpleAdmin.Instance.OnFreezeCommand),
|
||||
new("css_unfreeze", CS2_SimpleAdmin.Instance.OnUnfreezeCommand),
|
||||
new("css_godmode", CS2_SimpleAdmin.Instance.OnGodCommand),
|
||||
|
||||
new("css_slay", CS2_SimpleAdmin.Instance.OnSlayCommand),
|
||||
new("css_slap", CS2_SimpleAdmin.Instance.OnSlapCommand),
|
||||
new("css_give", CS2_SimpleAdmin.Instance.OnGiveCommand),
|
||||
new("css_strip", CS2_SimpleAdmin.Instance.OnStripCommand),
|
||||
new("css_hp", CS2_SimpleAdmin.Instance.OnHpCommand),
|
||||
new("css_speed", CS2_SimpleAdmin.Instance.OnSpeedCommand),
|
||||
new("css_gravity", CS2_SimpleAdmin.Instance.OnGravityCommand),
|
||||
new("css_resize", CS2_SimpleAdmin.Instance.OnResizeCommand),
|
||||
new("css_money", CS2_SimpleAdmin.Instance.OnMoneyCommand),
|
||||
new("css_team", CS2_SimpleAdmin.Instance.OnTeamCommand),
|
||||
new("css_rename", CS2_SimpleAdmin.Instance.OnRenameCommand),
|
||||
new("css_prename", CS2_SimpleAdmin.Instance.OnPrenameCommand),
|
||||
new("css_respawn", CS2_SimpleAdmin.Instance.OnRespawnCommand),
|
||||
new("css_tp", CS2_SimpleAdmin.Instance.OnGotoCommand),
|
||||
new("css_bring", CS2_SimpleAdmin.Instance.OnBringCommand),
|
||||
new("css_pluginsmanager", CS2_SimpleAdmin.Instance.OnPluginManagerCommand),
|
||||
new("css_adminvoice", CS2_SimpleAdmin.Instance.OnAdminVoiceCommand)
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Initializes command registration.
|
||||
/// If the commands config file does not exist, creates it and then recurses to register commands.
|
||||
/// Otherwise, directly registers commands from the configuration.
|
||||
/// </summary>
|
||||
public static void InitializeCommands()
|
||||
{
|
||||
if (!File.Exists(CommandsPath))
|
||||
{
|
||||
CreateConfig();
|
||||
InitializeCommands();
|
||||
}
|
||||
else
|
||||
{
|
||||
Register();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the default commands configuration JSON file with built-in commands and aliases.
|
||||
/// </summary>
|
||||
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
|
||||
private static void CreateConfig()
|
||||
{
|
||||
var commands = new CommandsConfig
|
||||
{
|
||||
Commands = new Dictionary<string, Command>
|
||||
{
|
||||
{ "css_ban", new Command { Aliases = ["css_ban"] } },
|
||||
{ "css_addban", new Command { Aliases = ["css_addban"] } },
|
||||
{ "css_banip", new Command { Aliases = ["css_banip"] } },
|
||||
{ "css_unban", new Command { Aliases = ["css_unban"] } },
|
||||
{ "css_warn", new Command { Aliases = ["css_warn"] } },
|
||||
{ "css_unwarn", new Command { Aliases = ["css_unwarn"] } },
|
||||
{ "css_asay", new Command { Aliases = ["css_asay"] } },
|
||||
{ "css_cssay", new Command { Aliases = ["css_cssay"] } },
|
||||
{ "css_say", new Command { Aliases = ["css_say"] } },
|
||||
{ "css_psay", new Command { Aliases = ["css_psay"] } },
|
||||
{ "css_csay", new Command { Aliases = ["css_csay"] } },
|
||||
{ "css_hsay", new Command { Aliases = ["css_hsay"] } },
|
||||
{ "css_penalties", new Command { Aliases = ["css_penalties", "css_mypenalties", "css_comms"] } },
|
||||
{ "css_admin", new Command { Aliases = ["css_admin"] } },
|
||||
{ "css_adminhelp", new Command { Aliases = ["css_adminhelp"] } },
|
||||
{ "css_addadmin", new Command { Aliases = ["css_addadmin"] } },
|
||||
{ "css_deladmin", new Command { Aliases = ["css_deladmin"] } },
|
||||
{ "css_addgroup", new Command { Aliases = ["css_addgroup"] } },
|
||||
{ "css_delgroup", new Command { Aliases = ["css_delgroup"] } },
|
||||
{ "css_reloadadmins", new Command { Aliases = ["css_reloadadmins"] } },
|
||||
{ "css_reloadbans", new Command { Aliases = ["css_reloadbans"] } },
|
||||
{ "css_hide", new Command { Aliases = ["css_hide", "css_stealth"] } },
|
||||
{ "css_hidecomms", new Command { Aliases = ["css_hidecomms"] } },
|
||||
{ "css_who", new Command { Aliases = ["css_who"] } },
|
||||
{ "css_disconnected", new Command { Aliases = ["css_disconnected", "css_last"] } },
|
||||
{ "css_warns", new Command { Aliases = ["css_warns"] } },
|
||||
{ "css_players", new Command { Aliases = ["css_players"] } },
|
||||
{ "css_kick", new Command { Aliases = ["css_kick"] } },
|
||||
{ "css_map", new Command { Aliases = ["css_map", "css_changemap"] } },
|
||||
{ "css_wsmap", new Command { Aliases = ["css_wsmap", "css_changewsmap", "css_workshop"] } },
|
||||
{ "css_cvar", new Command { Aliases = ["css_cvar"] } },
|
||||
{ "css_rcon", new Command { Aliases = ["css_rcon"] } },
|
||||
{ "css_rr", new Command { Aliases = ["css_rr", "css_rg", "css_restart", "css_restartgame"] } },
|
||||
{ "css_gag", new Command { Aliases = ["css_gag"] } },
|
||||
{ "css_addgag", new Command { Aliases = ["css_addgag"] } },
|
||||
{ "css_ungag", new Command { Aliases = ["css_ungag"] } },
|
||||
{ "css_mute", new Command { Aliases = ["css_mute"] } },
|
||||
{ "css_addmute", new Command { Aliases = ["css_addmute"] } },
|
||||
{ "css_unmute", new Command { Aliases = ["css_unmute"] } },
|
||||
{ "css_silence", new Command { Aliases = ["css_silence"] } },
|
||||
{ "css_addsilence", new Command { Aliases = ["css_addsilence"] } },
|
||||
{ "css_unsilence", new Command { Aliases = ["css_unsilence"] } },
|
||||
{ "css_vote", new Command { Aliases = ["css_vote"] } },
|
||||
{ "css_noclip", new Command { Aliases = ["css_noclip"] } },
|
||||
{ "css_freeze", new Command { Aliases = ["css_freeze"] } },
|
||||
{ "css_unfreeze", new Command { Aliases = ["css_unfreeze"] } },
|
||||
{ "css_godmode", new Command { Aliases = ["css_godmode"] } },
|
||||
{ "css_slay", new Command { Aliases = ["css_slay"] } },
|
||||
{ "css_slap", new Command { Aliases = ["css_slap"] } },
|
||||
{ "css_give", new Command { Aliases = ["css_give"] } },
|
||||
{ "css_strip", new Command { Aliases = ["css_strip"] } },
|
||||
{ "css_hp", new Command { Aliases = ["css_hp"] } },
|
||||
{ "css_speed", new Command { Aliases = ["css_speed"] } },
|
||||
{ "css_gravity", new Command { Aliases = ["css_gravity"] } },
|
||||
{ "css_resize", new Command { Aliases = ["css_resize", "css_size"] } },
|
||||
{ "css_money", new Command { Aliases = ["css_money"] } },
|
||||
{ "css_team", new Command { Aliases = ["css_team"] } },
|
||||
{ "css_rename", new Command { Aliases = ["css_rename"] } },
|
||||
{ "css_prename", new Command { Aliases = ["css_prename"] } },
|
||||
{ "css_respawn", new Command { Aliases = ["css_respawn"] } },
|
||||
{ "css_tp", new Command { Aliases = ["css_tp", "css_tpto", "css_goto"] } },
|
||||
{ "css_bring", new Command { Aliases = ["css_bring", "css_tphere"] } },
|
||||
{ "css_pluginsmanager", new Command { Aliases = ["css_pluginsmanager", "css_pluginmanager"] } },
|
||||
{ "css_adminvoice", new Command { Aliases = ["css_adminvoice", "css_listenall"] } }
|
||||
}
|
||||
};
|
||||
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(commands, options);
|
||||
File.WriteAllText(CommandsPath, json);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the command configuration JSON file and registers all commands and their aliases with their callbacks.
|
||||
/// Also registers any custom commands previously stored.
|
||||
/// </summary>
|
||||
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
|
||||
private static void Register()
|
||||
{
|
||||
var json = File.ReadAllText(CommandsPath);
|
||||
var commandsConfig = JsonSerializer.Deserialize<CommandsConfig>(json,
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
|
||||
if (commandsConfig?.Commands == null) return;
|
||||
|
||||
foreach (var command in commandsConfig.Commands)
|
||||
{
|
||||
if (command.Value.Aliases == null) continue;
|
||||
|
||||
CS2_SimpleAdmin._logger?.LogInformation(
|
||||
$"Registering command: `{command.Key}` with aliases: `{string.Join(", ", command.Value.Aliases)}`");
|
||||
|
||||
var mapping = CommandMappings.FirstOrDefault(m => m.CommandKey == command.Key);
|
||||
if (mapping == null || command.Value.Aliases.Length == 0) continue;
|
||||
|
||||
foreach (var alias in command.Value.Aliases)
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.AddCommand(alias, "", mapping.Callback);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (name, definitions) in RegisterCommands._commandDefinitions)
|
||||
{
|
||||
foreach (var definition in definitions)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogInformation($"Registering custom command: `{name}`");
|
||||
CS2_SimpleAdmin.Instance.AddCommand(name, definition.Description, definition.Callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the JSON configuration structure for commands.
|
||||
/// </summary>
|
||||
private class CommandsConfig
|
||||
{
|
||||
public Dictionary<string, Command>? Commands { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a command definition containing a list of aliases.
|
||||
/// </summary>
|
||||
private class Command
|
||||
{
|
||||
public string[]? Aliases { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a command key to its respective command callback handler.
|
||||
/// </summary>
|
||||
private class CommandMapping(string commandKey, CommandInfo.CommandCallback callback)
|
||||
{
|
||||
public string CommandKey { get; } = commandKey;
|
||||
public CommandInfo.CommandCallback Callback { get; } = callback;
|
||||
}
|
||||
}
|
||||
580
CS2-SimpleAdmin/Commands/basebans.cs
Normal file
580
CS2-SimpleAdmin/Commands/basebans.cs
Normal file
@@ -0,0 +1,580 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.ValveConstants.Protobuf;
|
||||
using CS2_SimpleAdmin.Managers;
|
||||
using CS2_SimpleAdmin.Menus;
|
||||
using CS2_SimpleAdminApi;
|
||||
|
||||
namespace CS2_SimpleAdmin;
|
||||
|
||||
public partial class CS2_SimpleAdmin
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles the 'ban' command, allowing admins to ban one or more valid connected players.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the ban command, or null for console.</param>
|
||||
/// <param name="command">The command information including arguments.</param>
|
||||
[RequiresPermissions("@css/ban")]
|
||||
[CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnBanCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
if (command.ArgCount < 2)
|
||||
return;
|
||||
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, Connected: PlayerConnectedState.PlayerConnected, IsHLTV: false }).ToList();
|
||||
|
||||
if (playersToTarget.Count > 1 && Config.OtherSettings.DisableDangerousCommands || playersToTarget.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var reason = command.ArgCount >= 3
|
||||
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||
var time = Helper.ParsePenaltyTime(command.GetArg(2));
|
||||
|
||||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
if (!caller.CanTarget(player)) return;
|
||||
|
||||
if (time < 0 && caller != null && caller.IsValid && Config.OtherSettings.ShowBanMenuIfNoTime)
|
||||
{
|
||||
DurationMenu.OpenMenu(caller, $"{_localizer?["sa_ban"] ?? "Ban"}: {player.PlayerName}", player,
|
||||
ManagePlayersMenu.BanMenu);
|
||||
return;
|
||||
}
|
||||
|
||||
Ban(caller, player, time, reason, callerName, BanManager, command);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Core logic to ban a specific player, scheduling database updates, notifications, and kicks.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the ban, or null for console.</param>
|
||||
/// <param name="player">The player to be banned.</param>
|
||||
/// <param name="time">Ban duration in minutes; 0 means permanent.</param>
|
||||
/// <param name="reason">Reason for the ban.</param>
|
||||
/// <param name="callerName">Optional caller name string. If null, defaults to player name or console.</param>
|
||||
/// <param name="banManager">Optional BanManager to handle ban persistence.</param>
|
||||
/// <param name="command">Optional command info object for logging.</param>
|
||||
/// <param name="silent">If true, suppresses command logging.</param>
|
||||
internal void Ban(CCSPlayerController? caller, CCSPlayerController player, int time, string reason, string? callerName = null, BanManager? banManager = null, CommandInfo? command = null, bool silent = false)
|
||||
{
|
||||
if (DatabaseProvider == null || !player.IsValid || !player.UserId.HasValue) return;
|
||||
if (!caller.CanTarget(player)) return;
|
||||
if (!CheckValidBan(caller, time)) return;
|
||||
|
||||
// Set default caller name if not provided
|
||||
callerName = !string.IsNullOrEmpty(caller?.PlayerName)
|
||||
? caller.PlayerName
|
||||
: (_localizer?["sa_console"] ?? "Console");
|
||||
|
||||
// Get player and admin information
|
||||
var playerInfo = PlayersInfo[player.SteamID];
|
||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||
|
||||
// Asynchronously handle banning logic
|
||||
Task.Run(async () =>
|
||||
{
|
||||
int? penaltyId = await BanManager.BanPlayer(playerInfo, adminInfo, reason, time);
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
SimpleAdminApi?.OnPlayerPenaltiedEvent(playerInfo, adminInfo, PenaltyType.Ban, reason, time, penaltyId);
|
||||
});
|
||||
});
|
||||
|
||||
// Determine message keys and arguments based on ban time
|
||||
var (messageKey, activityMessageKey, centerArgs, adminActivityArgs) = time == 0
|
||||
? ("sa_player_ban_message_perm", "sa_admin_ban_message_perm",
|
||||
[reason, "CALLER"],
|
||||
["CALLER", player.PlayerName, reason])
|
||||
: ("sa_player_ban_message_time", "sa_admin_ban_message_time",
|
||||
new object[] { reason, time, "CALLER" },
|
||||
new object[] { "CALLER", player.PlayerName, reason, time });
|
||||
|
||||
// Display center message to the player
|
||||
Helper.DisplayCenterMessage(player, messageKey, callerName, centerArgs);
|
||||
|
||||
// Display admin activity message if necessary
|
||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||
{
|
||||
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||
}
|
||||
|
||||
// Schedule a kick timer
|
||||
if (player.UserId.HasValue)
|
||||
{
|
||||
Helper.KickPlayer(player.UserId.Value, NetworkDisconnectionReason.NETWORK_DISCONNECT_KICKBANADDED, Config.OtherSettings.KickTime);
|
||||
}
|
||||
|
||||
// Execute ban command if necessary
|
||||
if (UnlockedCommands)
|
||||
{
|
||||
Server.ExecuteCommand($"banid 1 {new SteamID(player.SteamID).SteamId3}");
|
||||
}
|
||||
|
||||
if (!silent)
|
||||
{
|
||||
if (command == null)
|
||||
Helper.LogCommand(caller, $"css_ban {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time} {reason}");
|
||||
else
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
|
||||
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Ban, _localizer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a ban for a player by their SteamID, including offline bans.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the ban command.</param>
|
||||
/// <param name="steamid">SteamID of the player to ban.</param>
|
||||
/// <param name="time">Ban duration in minutes (0 means permanent).</param>
|
||||
/// <param name="reason">Reason for banning.</param>
|
||||
/// <param name="banManager">Optional ban manager for database operations.</param>
|
||||
internal void AddBan(CCSPlayerController? caller, SteamID steamid, int time, string reason, BanManager? banManager = null)
|
||||
{
|
||||
// Set default caller name if not provided
|
||||
var callerName = !string.IsNullOrEmpty(caller?.PlayerName)
|
||||
? caller.PlayerName
|
||||
: (_localizer?["sa_console"] ?? "Console");
|
||||
|
||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||
var player = Helper.GetPlayerFromSteamid64(steamid.SteamId64);
|
||||
if (player != null && player.IsValid)
|
||||
{
|
||||
if (!caller.CanTarget(player))
|
||||
return;
|
||||
Ban(caller, player, time, reason, callerName, silent: true);
|
||||
//command.ReplyToCommand($"Banned player {player.PlayerName}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!caller.CanTarget(steamid))
|
||||
return;
|
||||
// Asynchronous ban operation if player is not online or not found
|
||||
Task.Run(async () =>
|
||||
{
|
||||
int? penaltyId = await BanManager.AddBanBySteamid(steamid.SteamId64, adminInfo, reason, time);
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamid, adminInfo, PenaltyType.Ban, reason, time,
|
||||
penaltyId);
|
||||
});
|
||||
});
|
||||
|
||||
Helper.SendDiscordPenaltyMessage(caller, steamid.SteamId64.ToString(), reason, time, PenaltyType.Ban, _localizer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles banning a player by specifying their SteamID via command.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the command, or null if console.</param>
|
||||
/// <param name="command">Command information including arguments (SteamID, time, reason).</param>
|
||||
[RequiresPermissions("@css/ban")]
|
||||
[CommandHelper(minArgs: 1, usage: "<steamid> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnAddBanCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null) return;
|
||||
var callerName = caller?.PlayerName ?? _localizer?["sa_console"] ?? "Console";
|
||||
if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1))) return;
|
||||
if (!Helper.ValidateSteamId(command.GetArg(1), out var steamId) || steamId == null)
|
||||
{
|
||||
command.ReplyToCommand("Invalid SteamID64.");
|
||||
return;
|
||||
}
|
||||
|
||||
var steamid = steamId.SteamId64;
|
||||
var reason = command.ArgCount >= 3
|
||||
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||
|
||||
var time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2)));
|
||||
if (!CheckValidBan(caller, time)) return;
|
||||
|
||||
var adminInfo = caller != null && caller.UserId.HasValue
|
||||
? PlayersInfo[caller.SteamID]
|
||||
: null;
|
||||
|
||||
var player = Helper.GetPlayerFromSteamid64(steamid);
|
||||
if (player != null && player.IsValid)
|
||||
{
|
||||
if (!caller.CanTarget(player))
|
||||
return;
|
||||
|
||||
Ban(caller, player, time, reason, callerName, silent: true);
|
||||
//command.ReplyToCommand($"Banned player {player.PlayerName}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!caller.CanTarget(new SteamID(steamId.SteamId64)))
|
||||
return;
|
||||
|
||||
// Asynchronous ban operation if player is not online or not found
|
||||
Task.Run(async () =>
|
||||
{
|
||||
int? penaltyId = await BanManager.AddBanBySteamid(steamid, adminInfo, reason, time);
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamId, adminInfo, PenaltyType.Ban, reason, time,
|
||||
penaltyId);
|
||||
});
|
||||
});
|
||||
|
||||
Helper.SendDiscordPenaltyMessage(caller, steamid.ToString(), reason, time, PenaltyType.Ban, _localizer);
|
||||
|
||||
command.ReplyToCommand($"Player with steamid {steamid} is not online. Ban has been added offline.");
|
||||
}
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
|
||||
if (UnlockedCommands)
|
||||
Server.ExecuteCommand($"banid 1 {steamId.SteamId3}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles banning a player by their IP address, supporting offline banning if player is not online.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the ban command.</param>
|
||||
/// <param name="command">The command containing the IP, time, and reason arguments.</param>
|
||||
[RequiresPermissions("@css/ban")]
|
||||
[CommandHelper(minArgs: 1, usage: "<ip> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnBanIpCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null) return;
|
||||
var callerName = caller?.PlayerName ?? _localizer?["sa_console"] ?? "Console";
|
||||
if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1))) return;
|
||||
var ipAddress = command.GetArg(1);
|
||||
|
||||
if (!Helper.IsValidIp(ipAddress))
|
||||
{
|
||||
command.ReplyToCommand($"Invalid IP address.");
|
||||
return;
|
||||
}
|
||||
|
||||
var reason = command.ArgCount >= 3
|
||||
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||
|
||||
var time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2)));
|
||||
|
||||
if (!CheckValidBan(caller, time)) return;
|
||||
|
||||
var adminInfo = caller != null && caller.UserId.HasValue
|
||||
? PlayersInfo[caller.SteamID]
|
||||
: null;
|
||||
|
||||
var players = Helper.GetPlayerFromIp(ipAddress);
|
||||
if (players.Count >= 1)
|
||||
{
|
||||
foreach (var player in players)
|
||||
{
|
||||
if (player == null || !player.IsValid) continue;
|
||||
if (!caller.CanTarget(player))
|
||||
return;
|
||||
|
||||
Ban(caller, player, time, reason, callerName, silent: true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Asynchronous ban operation if player is not online or not found
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await BanManager.AddBanByIp(ipAddress, adminInfo, reason, time);
|
||||
});
|
||||
|
||||
command.ReplyToCommand($"Player with ip {ipAddress} is not online. Ban has been added offline.");
|
||||
}
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the ban duration is valid based on the caller's permissions and configured limits.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the ban command.</param>
|
||||
/// <param name="duration">Requested ban duration in minutes.</param>
|
||||
/// <returns>True if ban duration is valid; otherwise, false.</returns>
|
||||
private bool CheckValidBan(CCSPlayerController? caller, int duration)
|
||||
{
|
||||
if (caller == null) return true;
|
||||
|
||||
var canPermBan = AdminManager.PlayerHasPermissions(new SteamID(caller.SteamID), "@css/permban");
|
||||
|
||||
if (duration <= 0 && canPermBan == false)
|
||||
{
|
||||
caller.PrintToChat($"{_localizer!["sa_prefix"]} {_localizer["sa_ban_perm_restricted"]}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (duration <= Config.OtherSettings.MaxBanDuration || canPermBan) return true;
|
||||
|
||||
caller.PrintToChat($"{_localizer!["sa_prefix"]} {_localizer["sa_ban_max_duration_exceeded", Config.OtherSettings.MaxBanDuration]}");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles unbanning players by pattern (steamid, name, or IP).
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the unban command.</param>
|
||||
/// <param name="command">Command containing target pattern and optional reason.</param>
|
||||
[RequiresPermissions("@css/unban")]
|
||||
[CommandHelper(minArgs: 1, usage: "<steamid or name or ip> [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnUnbanCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null) return;
|
||||
var callerSteamId = caller?.SteamID.ToString() ?? _localizer?["sa_console"] ?? "Console";
|
||||
if (command.GetArg(1).Length <= 1)
|
||||
{
|
||||
command.ReplyToCommand($"Too short pattern to search.");
|
||||
return;
|
||||
}
|
||||
|
||||
var pattern = command.GetArg(1);
|
||||
var reason = command.ArgCount >= 2
|
||||
? string.Join(" ", Enumerable.Range(2, command.ArgCount - 2).Select(command.GetArg)).Trim()
|
||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||
Task.Run(async () => await BanManager.UnbanPlayer(pattern, callerSteamId, reason));
|
||||
Helper.LogCommand(caller, command);
|
||||
command.ReplyToCommand($"Unbanned player with pattern {pattern}.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles warning players, supporting multiple targets and warning durations.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the warn command.</param>
|
||||
/// <param name="command">The command containing target, time, and reason parameters.</param>
|
||||
[RequiresPermissions("@css/kick")]
|
||||
[CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnWarnCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null)
|
||||
return;
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
if (command.ArgCount < 2)
|
||||
return;
|
||||
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
var playersToTarget = targets.Players.Where(player => player.IsValid && player.Connected == PlayerConnectedState.PlayerConnected && !player.IsHLTV).ToList();
|
||||
|
||||
if (playersToTarget.Count > 1 && Config.OtherSettings.DisableDangerousCommands || playersToTarget.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2)));
|
||||
var reason = command.ArgCount >= 3
|
||||
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||
|
||||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
if (caller!.CanTarget(player))
|
||||
{
|
||||
Warn(caller, player, time, reason, callerName, command);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Issues a warning penalty to a specific player with optional duration and reason.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the warning.</param>
|
||||
/// <param name="player">The player to warn.</param>
|
||||
/// <param name="time">Duration of the warning in minutes.</param>
|
||||
/// <param name="reason">Reason for the warning.</param>
|
||||
/// <param name="callerName">Optional display name of the caller.</param>
|
||||
/// <param name="command">Optional command info for logging.</param>
|
||||
internal void Warn(CCSPlayerController? caller, CCSPlayerController player, int time, string reason, string? callerName = null, CommandInfo? command = null)
|
||||
{
|
||||
if (DatabaseProvider == null || !player.IsValid || !player.UserId.HasValue) return;
|
||||
if (!caller.CanTarget(player)) return;
|
||||
if (!CheckValidBan(caller, time)) return;
|
||||
|
||||
// Set default caller name if not provided
|
||||
callerName = !string.IsNullOrEmpty(caller?.PlayerName)
|
||||
? caller.PlayerName
|
||||
: (_localizer?["sa_console"] ?? "Console");
|
||||
|
||||
// Freeze player pawn if alive
|
||||
if (player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE)
|
||||
{
|
||||
player.PlayerPawn?.Value?.Freeze();
|
||||
AddTimer(5.0f, () => player.PlayerPawn?.Value?.Unfreeze(), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
||||
}
|
||||
|
||||
// Get player and admin information
|
||||
var playerInfo = PlayersInfo[player.SteamID];
|
||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||
|
||||
// Asynchronously handle warning logic
|
||||
Task.Run(async () =>
|
||||
{
|
||||
int? penaltyId = await WarnManager.WarnPlayer(playerInfo, adminInfo, reason, time);
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
SimpleAdminApi?.OnPlayerPenaltiedEvent(playerInfo, adminInfo, PenaltyType.Warn, reason, time,
|
||||
penaltyId);
|
||||
});
|
||||
|
||||
// Check for warn thresholds and execute punish command if applicable
|
||||
var totalWarns = await WarnManager.GetPlayerWarnsCount(player.SteamID);
|
||||
if (Config.WarnThreshold.Count > 0)
|
||||
{
|
||||
string? punishCommand = null;
|
||||
var lastKey = Config.WarnThreshold.Keys.Max();
|
||||
|
||||
if (totalWarns >= lastKey)
|
||||
punishCommand = Config.WarnThreshold[lastKey];
|
||||
else if (Config.WarnThreshold.TryGetValue(totalWarns, out var value))
|
||||
punishCommand = value;
|
||||
|
||||
if (!string.IsNullOrEmpty(punishCommand))
|
||||
{
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
Server.ExecuteCommand(punishCommand.Replace("USERID", playerInfo.UserId.ToString()).Replace("STEAMID64", playerInfo.SteamId?.ToString()));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Determine message keys and arguments based on warning time
|
||||
var (messageKey, activityMessageKey, centerArgs, adminActivityArgs) = time == 0
|
||||
? ("sa_player_warn_message_perm", "sa_admin_warn_message_perm",
|
||||
new object[] { reason, "CALLER" },
|
||||
new object[] { "CALLER", player.PlayerName, reason })
|
||||
: ("sa_player_warn_message_time", "sa_admin_warn_message_time",
|
||||
[reason, time, "CALLER"],
|
||||
["CALLER", player.PlayerName, reason, time]);
|
||||
|
||||
// Display center message to the playser
|
||||
Helper.DisplayCenterMessage(player, messageKey, callerName, centerArgs);
|
||||
|
||||
// Display admin activity message if necessary
|
||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||
{
|
||||
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||
}
|
||||
|
||||
// Log the warning command
|
||||
if (command == null)
|
||||
Helper.LogCommand(caller, $"css_warn {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time} {reason}");
|
||||
else
|
||||
Helper.LogCommand(caller, command);
|
||||
|
||||
// Send Discord notification for the warning
|
||||
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Warn, _localizer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a warning to a player by their SteamID, including support for offline players.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the warn command.</param>
|
||||
/// <param name="steamid">SteamID of the player to warn.</param>
|
||||
/// <param name="time">Warning duration in minutes.</param>
|
||||
/// <param name="reason">Reason for the warning.</param>
|
||||
/// <param name="warnManager">Optional warn manager instance.</param>
|
||||
internal void AddWarn(CCSPlayerController? caller, SteamID steamid, int time, string reason, WarnManager? warnManager = null)
|
||||
{
|
||||
// Set default caller name if not provided
|
||||
var callerName = !string.IsNullOrEmpty(caller?.PlayerName)
|
||||
? caller.PlayerName
|
||||
: (_localizer?["sa_console"] ?? "Console");
|
||||
|
||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||
|
||||
var player = Helper.GetPlayerFromSteamid64(steamid.SteamId64);
|
||||
|
||||
if (player != null && player.IsValid)
|
||||
{
|
||||
if (!caller.CanTarget(player))
|
||||
return;
|
||||
|
||||
Warn(caller, player, time, reason, callerName);
|
||||
//command.ReplyToCommand($"Banned player {player.PlayerName}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!caller.CanTarget(steamid))
|
||||
return;
|
||||
|
||||
// Asynchronous ban operation if player is not online or not found
|
||||
Task.Run(async () =>
|
||||
{
|
||||
int? penaltyId = await WarnManager.AddWarnBySteamid(steamid.SteamId64, adminInfo, reason, time);
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamid, adminInfo, PenaltyType.Warn, reason, time,
|
||||
penaltyId);
|
||||
});
|
||||
|
||||
// Check for warn thresholds and execute punish command if applicable
|
||||
var totalWarns = await WarnManager.GetPlayerWarnsCount(steamid.SteamId64);
|
||||
if (Config.WarnThreshold.Count > 0)
|
||||
{
|
||||
string? punishCommand = null;
|
||||
var lastKey = Config.WarnThreshold.Keys.Max();
|
||||
|
||||
if (totalWarns >= lastKey)
|
||||
punishCommand = Config.WarnThreshold[lastKey];
|
||||
else if (Config.WarnThreshold.TryGetValue(totalWarns, out var value))
|
||||
punishCommand = value;
|
||||
|
||||
if (!string.IsNullOrEmpty(punishCommand))
|
||||
{
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
Server.ExecuteCommand(punishCommand.Replace("STEAMID64", steamid.SteamId64.ToString()));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Helper.SendDiscordPenaltyMessage(caller, steamid.SteamId64.ToString(), reason, time, PenaltyType.Warn, _localizer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles removing a warning (unwarn) by a pattern string.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the unwarn command.</param>
|
||||
/// <param name="command">The command containing target pattern.</param>
|
||||
[RequiresPermissions("@css/kick")]
|
||||
[CommandHelper(minArgs: 1, usage: "<steamid or name or ip>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnUnwarnCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null) return;
|
||||
|
||||
if (command.GetArg(1).Length <= 1)
|
||||
{
|
||||
command.ReplyToCommand($"Too short pattern to search.");
|
||||
return;
|
||||
}
|
||||
|
||||
var pattern = command.GetArg(1);
|
||||
Task.Run(async () => await WarnManager.UnwarnPlayer(pattern));
|
||||
Helper.LogCommand(caller, command);
|
||||
command.ReplyToCommand($"Unwarned player with pattern {pattern}.");
|
||||
}
|
||||
}
|
||||
150
CS2-SimpleAdmin/Commands/basechat.cs
Normal file
150
CS2-SimpleAdmin/Commands/basechat.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Translations;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using System.Text;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CS2_SimpleAdmin;
|
||||
|
||||
public partial class CS2_SimpleAdmin
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends a chat message only to admins that have chat permission.
|
||||
/// The message is encoded properly to handle UTF-8 characters.
|
||||
/// </summary>
|
||||
/// <param name="caller">The admin player sending the message, or null for console.</param>
|
||||
/// <param name="command">The command input containing the message.</param>
|
||||
[CommandHelper(1, "<message>")]
|
||||
[RequiresPermissions("@css/chat")]
|
||||
public void OnAdminToAdminSayCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
Helper.LogCommand(caller, command);
|
||||
|
||||
var utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
|
||||
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||
|
||||
foreach (var player in Helper.GetValidPlayers()
|
||||
.Where(p => AdminManager.PlayerHasPermissions(new SteamID(p.SteamID), "@css/chat")))
|
||||
{
|
||||
if (_localizer != null)
|
||||
player.PrintToChat(_localizer["sa_adminchat_template_admin",
|
||||
caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName,
|
||||
utf8String]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a custom chat message to all players with color tags processed.
|
||||
/// </summary>
|
||||
/// <param name="caller">The admin or console sending the message.</param>
|
||||
/// <param name="command">The command input containing the message.</param>
|
||||
[CommandHelper(1, "<message>")]
|
||||
[RequiresPermissions("@css/chat")]
|
||||
public void OnAdminCustomSayCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (command.GetCommandString[command.GetCommandString.IndexOf(' ')..].Length == 0) return;
|
||||
|
||||
var utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
|
||||
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
|
||||
foreach (var player in Helper.GetValidPlayers())
|
||||
{
|
||||
player.PrintToChat(utf8String.ReplaceColorTags());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a chat message to all players with localization prefix and color tags handled.
|
||||
/// </summary>
|
||||
/// <param name="caller">The admin or console sending the message.</param>
|
||||
/// <param name="command">The command input containing the message.</param>
|
||||
[CommandHelper(1, "<message>")]
|
||||
[RequiresPermissions("@css/chat")]
|
||||
public void OnAdminSayCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (command.GetCommandString[command.GetCommandString.IndexOf(' ')..].Length == 0) return;
|
||||
|
||||
var utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
|
||||
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
foreach (var player in Helper.GetValidPlayers())
|
||||
{
|
||||
player.SendLocalizedMessage(_localizer,
|
||||
"sa_adminsay_prefix",
|
||||
utf8String.ReplaceColorTags());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a private chat message from the caller to the specified target player(s).
|
||||
/// </summary>
|
||||
/// <param name="caller">The admin or console sending the private message.</param>
|
||||
/// <param name="command">The command input containing target and message.</param>
|
||||
[CommandHelper(2, "<#userid or name> <message>")]
|
||||
[RequiresPermissions("@css/chat")]
|
||||
public void OnAdminPrivateSayCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
||||
|
||||
//Helper.LogCommand(caller, command);
|
||||
|
||||
var range = command.GetArg(0).Length + command.GetArg(1).Length + 2;
|
||||
var message = command.GetCommandString[range..];
|
||||
|
||||
var utf8BytesString = Encoding.UTF8.GetBytes(message);
|
||||
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||
|
||||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
player.PrintToChat($"({callerName}) {utf8String}".ReplaceColorTags());
|
||||
});
|
||||
|
||||
command.ReplyToCommand($" Private message sent!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Broadcasts a center-screen message to all players.
|
||||
/// </summary>
|
||||
/// <param name="caller">The admin or console sending the message.</param>
|
||||
/// <param name="command">The command input containing the message.</param>
|
||||
[CommandHelper(1, "<message>")]
|
||||
[RequiresPermissions("@css/chat")]
|
||||
public void OnAdminCenterSayCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
var utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
|
||||
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
Helper.PrintToCenterAll(utf8String.ReplaceColorTags());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a HUD alert message to all players.
|
||||
/// </summary>
|
||||
/// <param name="caller">The admin or console sending the message.</param>
|
||||
/// <param name="command">The command input containing the message.</param>
|
||||
[CommandHelper(1, "<message>")]
|
||||
[RequiresPermissions("@css/chat")]
|
||||
public void OnAdminHudSayCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
var utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
|
||||
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
|
||||
VirtualFunctions.ClientPrintAll(
|
||||
HudDestination.Alert,
|
||||
utf8String.ReplaceColorTags(),
|
||||
0, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
1279
CS2-SimpleAdmin/Commands/basecommands.cs
Normal file
1279
CS2-SimpleAdmin/Commands/basecommands.cs
Normal file
File diff suppressed because it is too large
Load Diff
973
CS2-SimpleAdmin/Commands/basecomms.cs
Normal file
973
CS2-SimpleAdmin/Commands/basecomms.cs
Normal file
@@ -0,0 +1,973 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CS2_SimpleAdmin.Managers;
|
||||
using CS2_SimpleAdmin.Menus;
|
||||
using CS2_SimpleAdminApi;
|
||||
|
||||
namespace CS2_SimpleAdmin;
|
||||
|
||||
public partial class CS2_SimpleAdmin
|
||||
{
|
||||
/// <summary>
|
||||
/// Processes the 'gag' command, applying a muted penalty to target players with optional time and reason.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the gag command or null for console.</param>
|
||||
/// <param name="command">The command input containing targets, time, and reason.</param>
|
||||
[RequiresPermissions("@css/chat")]
|
||||
[CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnGagCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null) return;
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
||||
|
||||
if (playersToTarget.Count > 1 && Config.OtherSettings.DisableDangerousCommands || playersToTarget.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var reason = command.ArgCount >= 3
|
||||
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||
|
||||
var time = Helper.ParsePenaltyTime(command.GetArg(2));
|
||||
|
||||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
if (!caller!.CanTarget(player)) return;
|
||||
if (time < 0 && caller != null && caller.IsValid && Config.OtherSettings.ShowBanMenuIfNoTime)
|
||||
{
|
||||
DurationMenu.OpenMenu(caller, $"{_localizer?["sa_gag"] ?? "Gag"}: {player.PlayerName}", player,
|
||||
ManagePlayersMenu.GagMenu);
|
||||
return;
|
||||
}
|
||||
|
||||
Gag(caller, player, time, reason, callerName, command);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the gag penalty logic to an individual player, performing permission checks, notification, and logging.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the gag.</param>
|
||||
/// <param name="player">The player to gag.</param>
|
||||
/// <param name="time">Duration of the gag in minutes, 0 is permanent.</param>
|
||||
/// <param name="reason">Reason for the gag.</param>
|
||||
/// <param name="callerName">Optional caller name for notifications.</param>
|
||||
/// <param name="command">Optional command info for logging.</param>
|
||||
/// <param name="silent">If true, suppresses logging notifications.</param>
|
||||
internal void Gag(CCSPlayerController? caller, CCSPlayerController player, int time, string reason, string? callerName = null, CommandInfo? command = null, bool silent = false)
|
||||
{
|
||||
if (DatabaseProvider == null || !player.IsValid || !player.UserId.HasValue) return;
|
||||
if (!caller.CanTarget(player)) return;
|
||||
if (!CheckValidMute(caller, time)) return;
|
||||
|
||||
// Set default caller name if not provided
|
||||
callerName ??= caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
|
||||
// Get player and admin information
|
||||
var playerInfo = PlayersInfo[player.SteamID];
|
||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||
|
||||
// Asynchronously handle gag logic
|
||||
Task.Run(async () =>
|
||||
{
|
||||
int? penaltyId = await MuteManager.MutePlayer(playerInfo, adminInfo, reason, time);
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
SimpleAdminApi?.OnPlayerPenaltiedEvent(playerInfo, adminInfo, PenaltyType.Gag, reason, time,
|
||||
penaltyId);
|
||||
});
|
||||
});
|
||||
|
||||
// Add penalty to the player's penalty manager
|
||||
PlayerPenaltyManager.AddPenalty(player.Slot, PenaltyType.Gag, Time.ActualDateTime().AddMinutes(time), time);
|
||||
|
||||
// Determine message keys and arguments based on gag time (permanent or timed)
|
||||
var (messageKey, activityMessageKey, playerArgs, adminActivityArgs) = time == 0
|
||||
? ("sa_player_gag_message_perm", "sa_admin_gag_message_perm",
|
||||
[reason, "CALLER"],
|
||||
["CALLER", player.PlayerName, reason])
|
||||
: ("sa_player_gag_message_time", "sa_admin_gag_message_time",
|
||||
new object[] { reason, time, "CALLER" },
|
||||
new object[] { "CALLER", player.PlayerName, reason, time });
|
||||
|
||||
// Display center message to the gagged player
|
||||
Helper.DisplayCenterMessage(player, messageKey, callerName, playerArgs);
|
||||
|
||||
// Display admin activity message to other players
|
||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||
{
|
||||
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||
}
|
||||
|
||||
// Increment the player's total gags count
|
||||
PlayersInfo[player.SteamID].TotalGags++;
|
||||
|
||||
// Log the gag command and send Discord notification
|
||||
if (!silent)
|
||||
{
|
||||
if (command == null)
|
||||
Helper.LogCommand(caller, $"css_gag {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time} {reason}");
|
||||
else
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
|
||||
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Gag, _localizer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a gag penalty to a player identified by SteamID, supporting offline players.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the command or null for console.</param>
|
||||
/// <param name="steamid">SteamID of the target player.</param>
|
||||
/// <param name="time">Duration in minutes (0 for permanent).</param>
|
||||
/// <param name="reason">Reason for the gag.</param>
|
||||
internal void AddGag(CCSPlayerController? caller, SteamID steamid, int time, string reason)
|
||||
{
|
||||
// Set default caller name if not provided
|
||||
var callerName = !string.IsNullOrEmpty(caller?.PlayerName)
|
||||
? caller.PlayerName
|
||||
: (_localizer?["sa_console"] ?? "Console");
|
||||
|
||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||
|
||||
var player = Helper.GetPlayerFromSteamid64(steamid.SteamId64);
|
||||
|
||||
if (player != null && player.IsValid)
|
||||
{
|
||||
if (!caller.CanTarget(player))
|
||||
return;
|
||||
|
||||
Gag(caller, player, time, reason, callerName, silent: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!caller.CanTarget(steamid))
|
||||
return;
|
||||
|
||||
// Asynchronous ban operation if player is not online or not found
|
||||
Task.Run(async () =>
|
||||
{
|
||||
int? penaltyId = await MuteManager.AddMuteBySteamid(steamid.SteamId64, adminInfo, reason, time, 3);
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamid, adminInfo, PenaltyType.Gag, reason, time,
|
||||
penaltyId);
|
||||
});
|
||||
});
|
||||
|
||||
Helper.SendDiscordPenaltyMessage(caller, steamid.SteamId64.ToString(), reason, time, PenaltyType.Gag, _localizer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the 'addgag' command, which adds a gag penalty to a player specified by SteamID.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the command or null for console.</param>
|
||||
/// <param name="command">Command input that includes SteamID, optional time, and reason.</param>
|
||||
[RequiresPermissions("@css/chat")]
|
||||
[CommandHelper(minArgs: 1, usage: "<steamid> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnAddGagCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null) return;
|
||||
|
||||
// Set caller name
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
|
||||
// Validate command arguments
|
||||
if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1))) return;
|
||||
|
||||
// Validate and extract SteamID
|
||||
if (!Helper.ValidateSteamId(command.GetArg(1), out var steamId) || steamId == null)
|
||||
{
|
||||
command.ReplyToCommand("Invalid SteamID64.");
|
||||
return;
|
||||
}
|
||||
|
||||
var steamid = steamId.SteamId64;
|
||||
var reason = command.ArgCount >= 3
|
||||
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||
|
||||
var time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2)));
|
||||
if (!CheckValidMute(caller, time)) return;
|
||||
|
||||
// Get player and admin info
|
||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||
|
||||
// Attempt to match player based on SteamID
|
||||
var player = Helper.GetPlayerFromSteamid64(steamid);
|
||||
|
||||
if (player != null && player.IsValid)
|
||||
{
|
||||
// Check if caller can target the player
|
||||
if (!caller.CanTarget(player)) return;
|
||||
|
||||
// Perform the gag for an online player
|
||||
Gag(caller, player, time, reason, callerName, silent: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!caller.CanTarget(new SteamID(steamId.SteamId64)))
|
||||
return;
|
||||
|
||||
// Asynchronous gag operation for offline players
|
||||
Task.Run(async () =>
|
||||
{
|
||||
int? penaltyId = await MuteManager.AddMuteBySteamid(steamid, adminInfo, reason, time);
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamId, adminInfo, PenaltyType.Gag, reason, time,
|
||||
penaltyId);
|
||||
});
|
||||
});
|
||||
|
||||
Helper.SendDiscordPenaltyMessage(caller, steamid.ToString(), reason, time, PenaltyType.Gag, _localizer);
|
||||
|
||||
command.ReplyToCommand($"Player with steamid {steamid} is not online. Gag has been added offline.");
|
||||
}
|
||||
|
||||
// Log the gag command and respond to the command
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles removing a gag penalty ('ungag') of a player, either by SteamID or pattern match.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the ungag command or null for console.</param>
|
||||
/// <param name="command">Command input containing SteamID or player name and optional reason.</param>
|
||||
[RequiresPermissions("@css/chat")]
|
||||
[CommandHelper(minArgs: 1, usage: "<steamid or name> [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnUngagCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null) return;
|
||||
|
||||
var callerSteamId = caller?.SteamID.ToString() ?? _localizer?["sa_console"] ?? "Console";
|
||||
var pattern = command.GetArg(1);
|
||||
var reason = command.ArgCount >= 2
|
||||
? string.Join(" ", Enumerable.Range(2, command.ArgCount - 2).Select(command.GetArg)).Trim()
|
||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||
|
||||
if (pattern.Length <= 1)
|
||||
{
|
||||
command.ReplyToCommand($"Too short pattern to search.");
|
||||
return;
|
||||
}
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
|
||||
// Check if pattern is a valid SteamID64
|
||||
if (Helper.ValidateSteamId(pattern, out var steamId) && steamId != null)
|
||||
{
|
||||
var player = Helper.GetPlayerFromSteamid64(steamId.SteamId64);
|
||||
|
||||
if (player != null && player.IsValid)
|
||||
{
|
||||
PlayerPenaltyManager.RemovePenaltiesByType(player.Slot, PenaltyType.Gag);
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await MuteManager.UnmutePlayer(player.SteamID.ToString(), callerSteamId, reason);
|
||||
});
|
||||
|
||||
command.ReplyToCommand($"Ungaged player {player.PlayerName}.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If not a valid SteamID64, check by player name
|
||||
var nameMatches = Helper.GetPlayerFromName(pattern);
|
||||
var namePlayer = nameMatches.Count == 1 ? nameMatches.FirstOrDefault() : null;
|
||||
|
||||
if (namePlayer != null && namePlayer.IsValid)
|
||||
{
|
||||
PlayerPenaltyManager.RemovePenaltiesByType(namePlayer.Slot, PenaltyType.Gag);
|
||||
|
||||
if (namePlayer.UserId.HasValue && PlayersInfo[namePlayer.SteamID].TotalGags > 0)
|
||||
PlayersInfo[namePlayer.SteamID].TotalGags--;
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await MuteManager.UnmutePlayer(namePlayer.SteamID.ToString(), callerSteamId, reason);
|
||||
});
|
||||
|
||||
command.ReplyToCommand($"Ungaged player {namePlayer.PlayerName}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await MuteManager.UnmutePlayer(pattern, callerSteamId, reason);
|
||||
});
|
||||
|
||||
command.ReplyToCommand($"Ungaged offline player with pattern {pattern}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the 'mute' command, applying a voice mute penalty to target players with optional time and reason.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the mute command or null for console.</param>
|
||||
/// <param name="command">The command input containing targets, time, and reason.</param>
|
||||
[RequiresPermissions("@css/chat")]
|
||||
[CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnMuteCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null) return;
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
||||
|
||||
if (playersToTarget.Count > 1 && Config.OtherSettings.DisableDangerousCommands || playersToTarget.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var reason = command.ArgCount >= 3
|
||||
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||
|
||||
var time = Helper.ParsePenaltyTime(command.GetArg(2));
|
||||
|
||||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
if (!caller!.CanTarget(player)) return;
|
||||
if (time < 0 && caller != null && caller.IsValid && Config.OtherSettings.ShowBanMenuIfNoTime)
|
||||
{
|
||||
DurationMenu.OpenMenu(caller, $"{_localizer?["sa_mute"] ?? "Mute"}: {player.PlayerName}", player,
|
||||
ManagePlayersMenu.MuteMenu);
|
||||
return;
|
||||
}
|
||||
|
||||
Mute(caller, player, time, reason, callerName, command);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the mute penalty logic to an individual player, handling permissions, notifications, logging, and countdown timers.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the mute.</param>
|
||||
/// <param name="player">The player to mute.</param>
|
||||
/// <param name="time">Duration in minutes, 0 indicates permanent mute.</param>
|
||||
/// <param name="reason">Reason for the mute.</param>
|
||||
/// <param name="callerName">Optional caller name for notification messages.</param>
|
||||
/// <param name="command">Optional command info for logging.</param>
|
||||
/// <param name="silent">If true, suppresses some logging.</param>
|
||||
internal void Mute(CCSPlayerController? caller, CCSPlayerController player, int time, string reason, string? callerName = null, CommandInfo? command = null, bool silent = false)
|
||||
{
|
||||
if (DatabaseProvider == null || !player.IsValid || !player.UserId.HasValue) return;
|
||||
if (!caller.CanTarget(player)) return;
|
||||
if (!CheckValidMute(caller, time)) return;
|
||||
|
||||
// Set default caller name if not provided
|
||||
callerName ??= caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
|
||||
// Get player and admin information
|
||||
var playerInfo = PlayersInfo[player.SteamID];
|
||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||
|
||||
// Set player's voice flags to muted
|
||||
player.VoiceFlags = VoiceFlags.Muted;
|
||||
|
||||
// Asynchronously handle mute logic
|
||||
Task.Run(async () =>
|
||||
{
|
||||
int? penaltyId = await MuteManager.MutePlayer(playerInfo, adminInfo, reason, time, 1);
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
SimpleAdminApi?.OnPlayerPenaltiedEvent(playerInfo, adminInfo, PenaltyType.Mute, reason, time,
|
||||
penaltyId);
|
||||
});
|
||||
});
|
||||
|
||||
// Add penalty to the player's penalty manager
|
||||
PlayerPenaltyManager.AddPenalty(player.Slot, PenaltyType.Mute, Time.ActualDateTime().AddMinutes(time), time);
|
||||
|
||||
// Determine message keys and arguments based on mute time (permanent or timed)
|
||||
var (messageKey, activityMessageKey, playerArgs, adminActivityArgs) = time == 0
|
||||
? ("sa_player_mute_message_perm", "sa_admin_mute_message_perm",
|
||||
[reason, "CALLER"],
|
||||
["CALLER", player.PlayerName, reason])
|
||||
: ("sa_player_mute_message_time", "sa_admin_mute_message_time",
|
||||
new object[] { reason, time, "CALLER" },
|
||||
new object[] { "CALLER", player.PlayerName, reason, time });
|
||||
|
||||
// Display center message to the muted player
|
||||
Helper.DisplayCenterMessage(player, messageKey, callerName, playerArgs);
|
||||
|
||||
// Display admin activity message to other players
|
||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||
{
|
||||
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||
}
|
||||
|
||||
// Increment the player's total mutes count
|
||||
PlayersInfo[player.SteamID].TotalMutes++;
|
||||
|
||||
// Log the mute command and send Discord notification
|
||||
if (!silent)
|
||||
{
|
||||
if (command == null)
|
||||
Helper.LogCommand(caller, $"css_mute {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time} {reason}");
|
||||
else
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
|
||||
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Mute, _localizer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the 'addmute' command that adds a mute penalty to a player specified by SteamID.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the command or null for console.</param>
|
||||
/// <param name="command">Command input includes SteamID, optional time, and reason.</param>
|
||||
[RequiresPermissions("@css/chat")]
|
||||
[CommandHelper(minArgs: 1, usage: "<steamid> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnAddMuteCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null) return;
|
||||
|
||||
// Set caller name
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
|
||||
// Validate command arguments
|
||||
if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1))) return;
|
||||
|
||||
// Validate and extract SteamID
|
||||
if (!Helper.ValidateSteamId(command.GetArg(1), out var steamId) || steamId == null)
|
||||
{
|
||||
command.ReplyToCommand("Invalid SteamID64.");
|
||||
return;
|
||||
}
|
||||
|
||||
var steamid = steamId.SteamId64;
|
||||
var reason = command.ArgCount >= 3
|
||||
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||
|
||||
var time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2)));
|
||||
if (!CheckValidMute(caller, time)) return;
|
||||
|
||||
// Get player and admin info
|
||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||
|
||||
// Attempt to match player based on SteamID
|
||||
var player = Helper.GetPlayerFromSteamid64(steamid);
|
||||
|
||||
if (player != null && player.IsValid)
|
||||
{
|
||||
// Check if caller can target the player
|
||||
if (!caller.CanTarget(player)) return;
|
||||
|
||||
// Perform the mute for an online player
|
||||
Mute(caller, player, time, reason, callerName, silent: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!caller.CanTarget(new SteamID(steamId.SteamId64)))
|
||||
return;
|
||||
|
||||
// Asynchronous mute operation for offline players
|
||||
Task.Run(async () =>
|
||||
{
|
||||
int? penaltyId = await MuteManager.AddMuteBySteamid(steamid, adminInfo, reason, time, 1);
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamId, adminInfo, PenaltyType.Mute, reason, time,
|
||||
penaltyId);
|
||||
});
|
||||
});
|
||||
|
||||
Helper.SendDiscordPenaltyMessage(caller, steamid.ToString(), reason, time, PenaltyType.Mute, _localizer);
|
||||
|
||||
command.ReplyToCommand($"Player with steamid {steamid} is not online. Mute has been added offline.");
|
||||
}
|
||||
|
||||
// Log the mute command and respond to the command
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously adds a mute penalty to a player by Steam ID. Handles both online and offline players.
|
||||
/// </summary>
|
||||
/// <param name="caller">The admin/player issuing the mute.</param>
|
||||
/// <param name="steamid">The Steam ID of the player to mute.</param>
|
||||
/// <param name="time">Duration of the mute in minutes.</param>
|
||||
/// <param name="reason">Reason for the mute.</param>
|
||||
/// <param name="muteManager">Optional mute manager instance for handling database ops.</param>
|
||||
internal void AddMute(CCSPlayerController? caller, SteamID steamid, int time, string reason, MuteManager? muteManager = null)
|
||||
{
|
||||
// Set default caller name if not provided
|
||||
var callerName = !string.IsNullOrEmpty(caller?.PlayerName)
|
||||
? caller.PlayerName
|
||||
: (_localizer?["sa_console"] ?? "Console");
|
||||
|
||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||
|
||||
var player = Helper.GetPlayerFromSteamid64(steamid.SteamId64);
|
||||
|
||||
if (player != null && player.IsValid)
|
||||
{
|
||||
if (!caller.CanTarget(player))
|
||||
return;
|
||||
|
||||
Mute(caller, player, time, reason, callerName, silent: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!caller.CanTarget(steamid))
|
||||
return;
|
||||
|
||||
// Asynchronous ban operation if player is not online or not found
|
||||
Task.Run(async () =>
|
||||
{
|
||||
int? penaltyId = await MuteManager.AddMuteBySteamid(steamid.SteamId64, adminInfo, reason, time, 1);
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamid, adminInfo, PenaltyType.Mute, reason, time,
|
||||
penaltyId);
|
||||
});
|
||||
});
|
||||
|
||||
Helper.SendDiscordPenaltyMessage(caller, steamid.SteamId64.ToString(), reason, time, PenaltyType.Mute, _localizer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the unmute command - removes mute penalty from player identified by SteamID or name.
|
||||
/// Can target both online and offline players.
|
||||
/// </summary>
|
||||
/// <param name="caller">The admin/player issuing the unmute.</param>
|
||||
/// <param name="command">The command arguments including target identifier and optional reason.</param>
|
||||
[RequiresPermissions("@css/chat")]
|
||||
[CommandHelper(minArgs: 1, usage: "<steamid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnUnmuteCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null) return;
|
||||
|
||||
var callerSteamId = caller?.SteamID.ToString() ?? _localizer?["sa_console"] ?? "Console";
|
||||
var pattern = command.GetArg(1);
|
||||
var reason = command.ArgCount >= 2
|
||||
? string.Join(" ", Enumerable.Range(2, command.ArgCount - 2).Select(command.GetArg)).Trim()
|
||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||
|
||||
if (pattern.Length <= 1)
|
||||
{
|
||||
command.ReplyToCommand("Too short pattern to search.");
|
||||
return;
|
||||
}
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
|
||||
// Check if pattern is a valid SteamID64
|
||||
if (Helper.ValidateSteamId(pattern, out var steamId) && steamId != null)
|
||||
{
|
||||
var player = Helper.GetPlayerFromSteamid64(steamId.SteamId64);
|
||||
|
||||
if (player != null && player.IsValid)
|
||||
{
|
||||
PlayerPenaltyManager.RemovePenaltiesByType(player.Slot, PenaltyType.Mute);
|
||||
player.VoiceFlags = VoiceFlags.Normal;
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await MuteManager.UnmutePlayer(player.SteamID.ToString(), callerSteamId, reason, 1);
|
||||
});
|
||||
|
||||
command.ReplyToCommand($"Unmuted player {player.PlayerName}.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If not a valid SteamID64, check by player name
|
||||
var nameMatches = Helper.GetPlayerFromName(pattern);
|
||||
var namePlayer = nameMatches.Count == 1 ? nameMatches.FirstOrDefault() : null;
|
||||
|
||||
if (namePlayer != null && namePlayer.IsValid)
|
||||
{
|
||||
PlayerPenaltyManager.RemovePenaltiesByType(namePlayer.Slot, PenaltyType.Mute);
|
||||
namePlayer.VoiceFlags = VoiceFlags.Normal;
|
||||
|
||||
if (namePlayer.UserId.HasValue && PlayersInfo[namePlayer.SteamID].TotalMutes > 0)
|
||||
PlayersInfo[namePlayer.SteamID].TotalMutes--;
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await MuteManager.UnmutePlayer(namePlayer.SteamID.ToString(), callerSteamId, reason, 1);
|
||||
});
|
||||
|
||||
command.ReplyToCommand($"Unmuted player {namePlayer.PlayerName}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await MuteManager.UnmutePlayer(pattern, callerSteamId, reason, 1);
|
||||
});
|
||||
|
||||
command.ReplyToCommand($"Unmuted offline player with pattern {pattern}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Issue a 'silence' penalty to a player - disables voice communication.
|
||||
/// Handles online and offline players, with duration and reason specified.
|
||||
/// </summary>
|
||||
/// <param name="caller">The admin/player issuing the silence.</param>
|
||||
/// <param name="command">Command containing target, duration, and optional reason.</param>
|
||||
[RequiresPermissions("@css/chat")]
|
||||
[CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnSilenceCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null) return;
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
||||
|
||||
if (playersToTarget.Count > 1 && Config.OtherSettings.DisableDangerousCommands || playersToTarget.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var reason = command.ArgCount >= 3
|
||||
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||
|
||||
var time = Helper.ParsePenaltyTime(command.GetArg(2));
|
||||
|
||||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
if (!caller!.CanTarget(player)) return;
|
||||
if (time < 0 && caller != null && caller.IsValid && Config.OtherSettings.ShowBanMenuIfNoTime)
|
||||
{
|
||||
DurationMenu.OpenMenu(caller, $"{_localizer?["sa_silence"] ?? "Silence"}: {player.PlayerName}", player,
|
||||
ManagePlayersMenu.SilenceMenu);
|
||||
return;
|
||||
}
|
||||
|
||||
Silence(caller, player, time, reason, callerName, command);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies silence logical processing for a player - updates database and notifies.
|
||||
/// </summary>
|
||||
/// <param name="caller">Admin/player applying the silence.</param>
|
||||
/// <param name="player">Target player.</param>
|
||||
/// <param name="time">Duration of silence.</param>
|
||||
/// <param name="reason">Reason for silence.</param>
|
||||
/// <param name="callerName">Optional name of silent admin or console.</param>
|
||||
/// <param name="command">Optional command details for logging.</param>
|
||||
/// <param name="silent">If true, suppresses logging notifications.</param>
|
||||
internal void Silence(CCSPlayerController? caller, CCSPlayerController player, int time, string reason, string? callerName = null, CommandInfo? command = null, bool silent = false)
|
||||
{
|
||||
if (DatabaseProvider == null || !player.IsValid || !player.UserId.HasValue) return;
|
||||
if (!caller.CanTarget(player)) return;
|
||||
if (!CheckValidMute(caller, time)) return;
|
||||
|
||||
// Set default caller name if not provided
|
||||
callerName ??= caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
|
||||
// Get player and admin information
|
||||
var playerInfo = PlayersInfo[player.SteamID];
|
||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||
|
||||
// Asynchronously handle silence logic
|
||||
Task.Run(async () =>
|
||||
{
|
||||
int? penaltyId = await MuteManager.MutePlayer(playerInfo, adminInfo, reason, time, 2);
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
SimpleAdminApi?.OnPlayerPenaltiedEvent(playerInfo, adminInfo, PenaltyType.Silence, reason, time,
|
||||
penaltyId);
|
||||
});
|
||||
});
|
||||
|
||||
// Add penalty to the player's penalty manager
|
||||
PlayerPenaltyManager.AddPenalty(player.Slot, PenaltyType.Silence, Time.ActualDateTime().AddMinutes(time), time);
|
||||
player.VoiceFlags = VoiceFlags.Muted;
|
||||
|
||||
// Determine message keys and arguments based on silence time (permanent or timed)
|
||||
var (messageKey, activityMessageKey, playerArgs, adminActivityArgs) = time == 0
|
||||
? ("sa_player_silence_message_perm", "sa_admin_silence_message_perm",
|
||||
[reason, "CALLER"],
|
||||
["CALLER", player.PlayerName, reason])
|
||||
: ("sa_player_silence_message_time", "sa_admin_silence_message_time",
|
||||
new object[] { reason, time, "CALLER" },
|
||||
new object[] { "CALLER", player.PlayerName, reason, time });
|
||||
|
||||
// Display center message to the silenced player
|
||||
Helper.DisplayCenterMessage(player, messageKey, callerName, playerArgs);
|
||||
|
||||
// Display admin activity message to other players
|
||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||
{
|
||||
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||
}
|
||||
|
||||
// Increment the player's total silences count
|
||||
PlayersInfo[player.SteamID].TotalSilences++;
|
||||
|
||||
// Log the silence command and send Discord notification
|
||||
if (!silent)
|
||||
{
|
||||
if (command == null)
|
||||
Helper.LogCommand(caller, $"css_silence {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time} {reason}");
|
||||
else
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
|
||||
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Silence, _localizer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the 'AddSilence' command, applying a silence penalty to a player specified by SteamID,
|
||||
/// with support for offline player penalties.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player/admin issuing the command.</param>
|
||||
/// <param name="command">The command input containing SteamID, optional time, and reason.</param>
|
||||
[RequiresPermissions("@css/chat")]
|
||||
[CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnAddSilenceCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null) return;
|
||||
|
||||
// Set caller name
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
|
||||
// Validate command arguments
|
||||
if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1))) return;
|
||||
|
||||
// Validate and extract SteamID
|
||||
if (!Helper.ValidateSteamId(command.GetArg(1), out var steamId) || steamId == null)
|
||||
{
|
||||
command.ReplyToCommand("Invalid SteamID64.");
|
||||
return;
|
||||
}
|
||||
|
||||
var steamid = steamId.SteamId64;
|
||||
var reason = command.ArgCount >= 3
|
||||
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||
|
||||
var time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2)));
|
||||
if (!CheckValidMute(caller, time)) return;
|
||||
|
||||
// Get player and admin info
|
||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||
|
||||
// Attempt to match player based on SteamID
|
||||
var player = Helper.GetPlayerFromSteamid64(steamid);
|
||||
|
||||
if (player != null && player.IsValid)
|
||||
{
|
||||
// Check if caller can target the player
|
||||
if (!caller.CanTarget(player)) return;
|
||||
|
||||
// Perform the silence for an online player
|
||||
Silence(caller, player, time, reason, callerName, silent: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!caller.CanTarget(new SteamID(steamId.SteamId64)))
|
||||
return;
|
||||
|
||||
// Asynchronous silence operation for offline players
|
||||
Task.Run(async () =>
|
||||
{
|
||||
int? penaltyId = await MuteManager.AddMuteBySteamid(steamid, adminInfo, reason, time, 2);
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamId, adminInfo, PenaltyType.Silence, reason,
|
||||
time, penaltyId);
|
||||
});
|
||||
});
|
||||
|
||||
Helper.SendDiscordPenaltyMessage(caller, steamid.ToString(), reason, time, PenaltyType.Silence, _localizer);
|
||||
|
||||
command.ReplyToCommand($"Player with steamid {steamid} is not online. Silence has been added offline.");
|
||||
}
|
||||
|
||||
// Log the silence command and respond to the command
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a silence penalty to a player by Steam ID. Manages both online and offline player cases.
|
||||
/// </summary>
|
||||
/// <param name="caller">Admin/player initiating the silence.</param>
|
||||
/// <param name="steamid">Steam ID of player.</param>
|
||||
/// <param name="time">Duration of silence.</param>
|
||||
/// <param name="reason">Reason for the penalty.</param>
|
||||
/// <param name="muteManager">Optional mute manager for DB operations.</param>
|
||||
internal void AddSilence(CCSPlayerController? caller, SteamID steamid, int time, string reason, MuteManager? muteManager = null)
|
||||
{
|
||||
// Set default caller name if not provided
|
||||
var callerName = !string.IsNullOrEmpty(caller?.PlayerName)
|
||||
? caller.PlayerName
|
||||
: (_localizer?["sa_console"] ?? "Console");
|
||||
|
||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||
|
||||
var player = Helper.GetPlayerFromSteamid64(steamid.SteamId64);
|
||||
|
||||
if (player != null && player.IsValid)
|
||||
{
|
||||
if (!caller.CanTarget(player))
|
||||
return;
|
||||
|
||||
Silence(caller, player, time, reason, callerName, silent: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!caller.CanTarget(steamid))
|
||||
return;
|
||||
|
||||
// Asynchronous ban operation if player is not online or not found
|
||||
Task.Run(async () =>
|
||||
{
|
||||
int? penaltyId = await MuteManager.AddMuteBySteamid(steamid.SteamId64, adminInfo, reason, time, 2);
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamid, adminInfo, PenaltyType.Silence, reason,
|
||||
time, penaltyId);
|
||||
});
|
||||
});
|
||||
|
||||
Helper.SendDiscordPenaltyMessage(caller, steamid.SteamId64.ToString(), reason, time, PenaltyType.Silence, _localizer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the silence penalty from a player, either by SteamID, name, or offline pattern.
|
||||
/// Resets voice settings and updates notices accordingly.
|
||||
/// </summary>
|
||||
/// <param name="caller">Admin/player issuing the unsilence.</param>
|
||||
/// <param name="command">Command arguments with target pattern and optional reason.</param>
|
||||
[RequiresPermissions("@css/chat")]
|
||||
[CommandHelper(minArgs: 1, usage: "<steamid or name> [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnUnsilenceCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (DatabaseProvider == null) return;
|
||||
|
||||
var callerSteamId = caller?.SteamID.ToString() ?? _localizer?["sa_console"] ?? "Console";
|
||||
var pattern = command.GetArg(1);
|
||||
var reason = command.ArgCount >= 2
|
||||
? string.Join(" ", Enumerable.Range(2, command.ArgCount - 2).Select(command.GetArg)).Trim()
|
||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||
|
||||
if (pattern.Length <= 1)
|
||||
{
|
||||
command.ReplyToCommand("Too short pattern to search.");
|
||||
return;
|
||||
}
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
|
||||
// Check if pattern is a valid SteamID64
|
||||
if (Helper.ValidateSteamId(pattern, out var steamId) && steamId != null)
|
||||
{
|
||||
var player = Helper.GetPlayerFromSteamid64(steamId.SteamId64);
|
||||
|
||||
if (player != null && player.IsValid)
|
||||
{
|
||||
PlayerPenaltyManager.RemovePenaltiesByType(player.Slot, PenaltyType.Silence);
|
||||
|
||||
// Reset voice flags to normal
|
||||
player.VoiceFlags = VoiceFlags.Normal;
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await MuteManager.UnmutePlayer(player.SteamID.ToString(), callerSteamId, reason, 2); // Unmute by type 2 (silence)
|
||||
});
|
||||
|
||||
command.ReplyToCommand($"Unsilenced player {player.PlayerName}.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If not a valid SteamID64, check by player name
|
||||
var nameMatches = Helper.GetPlayerFromName(pattern);
|
||||
var namePlayer = nameMatches.Count == 1 ? nameMatches.FirstOrDefault() : null;
|
||||
|
||||
if (namePlayer != null && namePlayer.IsValid)
|
||||
{
|
||||
PlayerPenaltyManager.RemovePenaltiesByType(namePlayer.Slot, PenaltyType.Silence);
|
||||
|
||||
// Reset voice flags to normal
|
||||
namePlayer.VoiceFlags = VoiceFlags.Normal;
|
||||
|
||||
if (namePlayer.UserId.HasValue && PlayersInfo[namePlayer.SteamID].TotalSilences > 0)
|
||||
PlayersInfo[namePlayer.SteamID].TotalSilences--;
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await MuteManager.UnmutePlayer(namePlayer.SteamID.ToString(), callerSteamId, reason, 2); // Unmute by type 2 (silence)
|
||||
});
|
||||
|
||||
command.ReplyToCommand($"Unsilenced player {namePlayer.PlayerName}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await MuteManager.UnmutePlayer(pattern, callerSteamId, reason, 2); // Unmute by type 2 (silence)
|
||||
});
|
||||
|
||||
command.ReplyToCommand($"Unsilenced offline player with pattern {pattern}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates mute penalty duration based on admin privileges and configured max duration.
|
||||
/// </summary>
|
||||
/// <param name="caller">Admin/player issuing the mute.</param>
|
||||
/// <param name="duration">Requested duration in minutes.</param>
|
||||
/// <returns>True if mute penalty duration is allowed; false otherwise.</returns>
|
||||
private bool CheckValidMute(CCSPlayerController? caller, int duration)
|
||||
{
|
||||
if (caller == null) return true;
|
||||
|
||||
var canPermMute = AdminManager.PlayerHasPermissions(new SteamID(caller.SteamID), "@css/permmute");
|
||||
|
||||
if (duration <= 0 && canPermMute == false)
|
||||
{
|
||||
caller.PrintToChat($"{_localizer!["sa_prefix"]} {_localizer["sa_ban_perm_restricted"]}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (duration <= Config.OtherSettings.MaxMuteDuration || canPermMute) return true;
|
||||
|
||||
caller.PrintToChat($"{_localizer!["sa_prefix"]} {_localizer["sa_ban_max_duration_exceeded", Config.OtherSettings.MaxMuteDuration]}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
98
CS2-SimpleAdmin/Commands/basevotes.cs
Normal file
98
CS2-SimpleAdmin/Commands/basevotes.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Translations;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Menu;
|
||||
|
||||
namespace CS2_SimpleAdmin;
|
||||
|
||||
public partial class CS2_SimpleAdmin
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles the vote command, creates voting menu for players, and collects answers.
|
||||
/// Displays results after timeout and resets voting state.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player/admin who initiated the vote, or null for console.</param>
|
||||
/// <param name="command">Command object containing question and options.</param>
|
||||
[RequiresPermissions("@css/generic")]
|
||||
[CommandHelper(minArgs: 2, usage: "<question> [... options ...]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnVoteCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (command.ArgCount < 2 || _localizer == null)
|
||||
return;
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
|
||||
VoteAnswers.Clear();
|
||||
|
||||
var question = command.GetArg(1);
|
||||
var answersCount = command.ArgCount;
|
||||
|
||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||
{
|
||||
for (var i = 2; i <= answersCount - 1; i++)
|
||||
{
|
||||
VoteAnswers.Add(command.GetArg(i), 0);
|
||||
}
|
||||
|
||||
foreach (var player in Helper.GetValidPlayers())
|
||||
{
|
||||
using (new WithTemporaryCulture(player.GetLanguage()))
|
||||
{
|
||||
IMenu? voteMenu = Helper.CreateMenu(_localizer["sa_admin_vote_menu_title", question]);
|
||||
if (voteMenu == null)
|
||||
return;
|
||||
//ChatMenu voteMenu = new(_localizer!["sa_admin_vote_menu_title", question]);
|
||||
|
||||
for (var i = 2; i <= answersCount - 1; i++)
|
||||
{
|
||||
voteMenu.AddMenuOption(command.GetArg(i), Helper.HandleVotes);
|
||||
}
|
||||
|
||||
voteMenu.PostSelectAction = PostSelectAction.Close;
|
||||
|
||||
Helper.PrintToCenterAll(_localizer["sa_admin_vote_message", caller == null ? _localizer["sa_console"] : caller.PlayerName, question]);
|
||||
|
||||
player.SendLocalizedMessage(_localizer,
|
||||
"sa_admin_vote_message",
|
||||
caller == null ? _localizer["sa_console"] : caller.PlayerName,
|
||||
question);
|
||||
|
||||
voteMenu.Open(player);
|
||||
|
||||
//MenuManager.OpenChatMenu(player, voteMenu);
|
||||
}
|
||||
}
|
||||
|
||||
VoteInProgress = true;
|
||||
}
|
||||
|
||||
if (VoteInProgress)
|
||||
{
|
||||
AddTimer(30, () =>
|
||||
{
|
||||
foreach (var player in Helper.GetValidPlayers())
|
||||
{
|
||||
if (_localizer != null)
|
||||
player.SendLocalizedMessage(_localizer,
|
||||
"sa_admin_vote_message_results",
|
||||
question);
|
||||
}
|
||||
|
||||
foreach (var (key, value) in VoteAnswers)
|
||||
{
|
||||
foreach (var player in Helper.GetValidPlayers())
|
||||
{
|
||||
if (_localizer != null)
|
||||
player.SendLocalizedMessage(_localizer,
|
||||
"sa_admin_vote_message_results_answer",
|
||||
key,
|
||||
value);
|
||||
}
|
||||
}
|
||||
VoteAnswers.Clear();
|
||||
VoteInProgress = false;
|
||||
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
307
CS2-SimpleAdmin/Commands/funcommands.cs
Normal file
307
CS2-SimpleAdmin/Commands/funcommands.cs
Normal file
@@ -0,0 +1,307 @@
|
||||
using System.Globalization;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
|
||||
namespace CS2_SimpleAdmin;
|
||||
|
||||
public partial class CS2_SimpleAdmin
|
||||
{
|
||||
/// <summary>
|
||||
/// Enables or disables no-clip mode for specified player(s).
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the command.</param>
|
||||
/// <param name="command">The command input containing targets.</param>
|
||||
[CommandHelper(1, "<#userid or name>")]
|
||||
[RequiresPermissions("@css/cheats")]
|
||||
public void OnNoclipCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
var playersToTarget = targets.Players.Where(player =>
|
||||
player.IsValid &&
|
||||
player is { IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||
|
||||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
if (caller!.CanTarget(player))
|
||||
{
|
||||
NoClip(caller, player, callerName);
|
||||
}
|
||||
});
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles no-clip mode for a player and shows admin activity messages.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player/admin toggling no-clip.</param>
|
||||
/// <param name="player">The target player whose no-clip state changes.</param>
|
||||
/// <param name="callerName">Optional caller name for messages.</param>
|
||||
/// <param name="command">Optional command info for logging.</param>
|
||||
internal static void NoClip(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
|
||||
{
|
||||
if (!player.IsValid) return;
|
||||
if (!caller.CanTarget(player)) return;
|
||||
|
||||
// Set default caller name if not provided
|
||||
callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||
|
||||
// Toggle no-clip mode for the player
|
||||
player.Pawn.Value?.ToggleNoclip();
|
||||
|
||||
// Determine message keys and arguments for the no-clip notification
|
||||
var (activityMessageKey, adminActivityArgs) =
|
||||
("sa_admin_noclip_message",
|
||||
new object[] { "CALLER", player.PlayerName });
|
||||
|
||||
// Display admin activity message to other players
|
||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||
{
|
||||
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||
}
|
||||
|
||||
// Log the command
|
||||
if (command == null)
|
||||
Helper.LogCommand(caller, $"css_noclip {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables god mode for specified player(s).
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the command.</param>
|
||||
/// <param name="command">The command input containing targets.</param>
|
||||
|
||||
[RequiresPermissions("@css/cheats")]
|
||||
[CommandHelper(minArgs: 1, usage: "<#userid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
public void OnGodCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
|
||||
var playersToTarget = targets.Players.Where(player => player.IsValid && player is {IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||
|
||||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
if (player.Connected != PlayerConnectedState.PlayerConnected)
|
||||
return;
|
||||
|
||||
if (caller!.CanTarget(player))
|
||||
{
|
||||
God(caller, player, command);
|
||||
}
|
||||
});
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles god mode for a player and notifies admins.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player/admin toggling god mode.</param>
|
||||
/// <param name="player">The target player whose god mode changes.</param>
|
||||
/// <param name="command">Optional command info for logging.</param>
|
||||
internal static void God(CCSPlayerController? caller, CCSPlayerController player, CommandInfo? command = null)
|
||||
{
|
||||
if (!caller.CanTarget(player)) return;
|
||||
|
||||
// Set default caller name if not provided
|
||||
var callerName = caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||
|
||||
// Toggle god mode for the player
|
||||
if (!GodPlayers.Add(player.Slot))
|
||||
{
|
||||
GodPlayers.Remove(player.Slot);
|
||||
}
|
||||
|
||||
// Log the command
|
||||
if (command == null)
|
||||
Helper.LogCommand(caller, $"css_god {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
||||
|
||||
// Determine message key and arguments for the god mode notification
|
||||
var (activityMessageKey, adminActivityArgs) =
|
||||
("sa_admin_god_message",
|
||||
new object[] { "CALLER", player.PlayerName });
|
||||
|
||||
// Display admin activity message to other players
|
||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||
{
|
||||
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Freezes target player(s) for an optional specified duration.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the freeze command.</param>
|
||||
/// <param name="command">The command input containing targets and duration.</param>
|
||||
[CommandHelper(1, "<#userid or name> [duration]")]
|
||||
[RequiresPermissions("@css/slay")]
|
||||
public void OnFreezeCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
int.TryParse(command.GetArg(2), out var time);
|
||||
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||
|
||||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
if (caller!.CanTarget(player))
|
||||
{
|
||||
Freeze(caller, player, time, callerName, command);
|
||||
}
|
||||
});
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resizes the target player(s) models to a specified scale.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the resize command.</param>
|
||||
/// <param name="command">The command input containing targets and scale factor.</param>
|
||||
[CommandHelper(1, "<#userid or name> [size]")]
|
||||
[RequiresPermissions("@css/slay")]
|
||||
public void OnResizeCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
float.TryParse(command.GetArg(2), NumberStyles.Float, CultureInfo.InvariantCulture, out var size);
|
||||
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||
|
||||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
if (!caller!.CanTarget(player)) return;
|
||||
|
||||
var sceneNode = player.PlayerPawn.Value!.CBodyComponent?.SceneNode;
|
||||
if (sceneNode == null) return;
|
||||
|
||||
sceneNode.GetSkeletonInstance().Scale = size;
|
||||
player.PlayerPawn.Value.AcceptInput("SetScale", null, null, size.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
Server.NextWorldUpdate(() =>
|
||||
{
|
||||
Utilities.SetStateChanged(player.PlayerPawn.Value, "CBaseEntity", "m_CBodyComponent");
|
||||
});
|
||||
|
||||
var (activityMessageKey, adminActivityArgs) =
|
||||
("sa_admin_resize_message",
|
||||
new object[] { "CALLER", player.PlayerName });
|
||||
|
||||
// Display admin activity message to other players
|
||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||
{
|
||||
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||
}
|
||||
});
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Freezes a single player and optionally schedules automatic unfreeze after a duration.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player/admin freezing the player.</param>
|
||||
/// <param name="player">The player to freeze.</param>
|
||||
/// <param name="time">Duration of freeze in seconds.</param>
|
||||
/// <param name="callerName">Optional name for notifications.</param>
|
||||
/// <param name="command">Optional command info for logging.</param>
|
||||
internal static void Freeze(CCSPlayerController? caller, CCSPlayerController player, int time, string? callerName = null, CommandInfo? command = null)
|
||||
{
|
||||
if (!player.IsValid) return;
|
||||
if (!caller.CanTarget(player)) return;
|
||||
|
||||
// Set default caller name if not provided
|
||||
callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||
|
||||
// Freeze player pawn
|
||||
player.Pawn.Value?.Freeze();
|
||||
|
||||
// Determine message keys and arguments for the freeze notification
|
||||
var (activityMessageKey, adminActivityArgs) =
|
||||
("sa_admin_freeze_message",
|
||||
new object[] { "CALLER", player.PlayerName });
|
||||
|
||||
// Display admin activity message to other players
|
||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||
{
|
||||
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||
}
|
||||
|
||||
// Schedule unfreeze for the player if time is specified
|
||||
if (time > 0)
|
||||
{
|
||||
Instance.AddTimer(time, () => player.Pawn.Value?.Unfreeze(), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
||||
}
|
||||
|
||||
// Log the command and send Discord notification
|
||||
if (command == null)
|
||||
Helper.LogCommand(caller, $"css_freeze {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unfreezes target player(s).
|
||||
/// </summary>
|
||||
/// <param name="caller">The player issuing the unfreeze command.</param>
|
||||
/// <param name="command">The command input with targets.</param>
|
||||
[CommandHelper(1, "<#userid or name>")]
|
||||
[RequiresPermissions("@css/slay")]
|
||||
public void OnUnfreezeCommand(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||
|
||||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
Unfreeze(caller, player, callerName, command);
|
||||
});
|
||||
|
||||
Helper.LogCommand(caller, command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unfreezes a single player and notifies admins.
|
||||
/// </summary>
|
||||
/// <param name="caller">The player/admin unfreezing the player.</param>
|
||||
/// <param name="player">The player to unfreeze.</param>
|
||||
/// <param name="callerName">Optional name for notifications.</param>
|
||||
/// <param name="command">Optional command info for logging.</param>
|
||||
internal static void Unfreeze(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
|
||||
{
|
||||
if (!player.IsValid) return;
|
||||
if (!caller.CanTarget(player)) return;
|
||||
|
||||
// Set default caller name if not provided
|
||||
callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||
|
||||
// Unfreeze player pawn
|
||||
player.Pawn.Value?.Unfreeze();
|
||||
|
||||
// Determine message keys and arguments for the unfreeze notification
|
||||
var (activityMessageKey, adminActivityArgs) =
|
||||
("sa_admin_unfreeze_message",
|
||||
new object[] { "CALLER", player.PlayerName });
|
||||
|
||||
// Display admin activity message to other players
|
||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||
{
|
||||
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||
}
|
||||
|
||||
// Log the command and send Discord notification
|
||||
if (command == null)
|
||||
Helper.LogCommand(caller, $"css_unfreeze {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
||||
}
|
||||
}
|
||||
1140
CS2-SimpleAdmin/Commands/playercommands.cs
Normal file
1140
CS2-SimpleAdmin/Commands/playercommands.cs
Normal file
File diff suppressed because it is too large
Load Diff
325
CS2-SimpleAdmin/Config.cs
Normal file
325
CS2-SimpleAdmin/Config.cs
Normal file
@@ -0,0 +1,325 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace CS2_SimpleAdmin;
|
||||
|
||||
public class DurationItem
|
||||
{
|
||||
[JsonPropertyName("name")]
|
||||
public required string Name { get; set; }
|
||||
|
||||
[JsonPropertyName("duration")]
|
||||
public int Duration { get; set; }
|
||||
}
|
||||
|
||||
public class AdminFlag
|
||||
{
|
||||
[JsonPropertyName("name")]
|
||||
public required string Name { get; set; }
|
||||
|
||||
[JsonPropertyName("flag")]
|
||||
public required string Flag { get; set; }
|
||||
}
|
||||
|
||||
public class DiscordPenaltySetting
|
||||
{
|
||||
[JsonPropertyName("name")]
|
||||
public required string Name { get; set; }
|
||||
|
||||
[JsonPropertyName("value")]
|
||||
public string? Value { get; set; } = "";
|
||||
}
|
||||
|
||||
public class Discord
|
||||
{
|
||||
[JsonPropertyName("DiscordLogWebhook")]
|
||||
public string DiscordLogWebhook { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("DiscordPenaltyBanSettings")]
|
||||
public DiscordPenaltySetting[] DiscordPenaltyBanSettings { get; set; } =
|
||||
[
|
||||
new() { Name = "Color", Value = "" },
|
||||
new() { Name = "Webhook", Value = "" },
|
||||
new() { Name = "ThumbnailUrl", Value = "" },
|
||||
new() { Name = "ImageUrl", Value = "" },
|
||||
new() { Name = "Footer", Value = "" },
|
||||
new() { Name = "Time", Value = "{relative}" },
|
||||
];
|
||||
|
||||
[JsonPropertyName("DiscordPenaltyMuteSettings")]
|
||||
public DiscordPenaltySetting[] DiscordPenaltyMuteSettings { get; set; } =
|
||||
[
|
||||
new() { Name = "Color", Value = "" },
|
||||
new() { Name = "Webhook", Value = "" },
|
||||
new() { Name = "ThumbnailUrl", Value = "" },
|
||||
new() { Name = "ImageUrl", Value = "" },
|
||||
new() { Name = "Footer", Value = "" },
|
||||
new() { Name = "Time", Value = "{relative}" },
|
||||
];
|
||||
|
||||
[JsonPropertyName("DiscordPenaltyGagSettings")]
|
||||
public DiscordPenaltySetting[] DiscordPenaltyGagSettings { get; set; } =
|
||||
[
|
||||
new() { Name = "Color", Value = "" },
|
||||
new() { Name = "Webhook", Value = "" },
|
||||
new() { Name = "ThumbnailUrl", Value = "" },
|
||||
new() { Name = "ImageUrl", Value = "" },
|
||||
new() { Name = "Footer", Value = "" },
|
||||
new() { Name = "Time", Value = "{relative}" },
|
||||
];
|
||||
|
||||
[JsonPropertyName("DiscordPenaltySilenceSettings")]
|
||||
public DiscordPenaltySetting[] DiscordPenaltySilenceSettings { get; set; } =
|
||||
[
|
||||
new() { Name = "Color", Value = "" },
|
||||
new() { Name = "Webhook", Value = "" },
|
||||
new() { Name = "ThumbnailUrl", Value = "" },
|
||||
new() { Name = "ImageUrl", Value = "" },
|
||||
new() { Name = "Footer", Value = "" },
|
||||
new() { Name = "Time", Value = "{relative}" },
|
||||
];
|
||||
|
||||
[JsonPropertyName("DiscordPenaltyWarnSettings")]
|
||||
public DiscordPenaltySetting[] DiscordPenaltyWarnSettings { get; set; } =
|
||||
[
|
||||
new() { Name = "Color", Value = "" },
|
||||
new() { Name = "Webhook", Value = "" },
|
||||
new() { Name = "ThumbnailUrl", Value = "" },
|
||||
new() { Name = "ImageUrl", Value = "" },
|
||||
new() { Name = "Footer", Value = "" },
|
||||
new() { Name = "Time", Value = "{relative}" },
|
||||
];
|
||||
|
||||
[JsonPropertyName("DiscordAssociatedAccountsSettings")]
|
||||
public DiscordPenaltySetting[] DiscordAssociatedAccountsSettings { get; set; } =
|
||||
[
|
||||
new() { Name = "Color", Value = "" },
|
||||
new() { Name = "Webhook", Value = "" },
|
||||
new() { Name = "ThumbnailUrl", Value = "" },
|
||||
new() { Name = "ImageUrl", Value = "" },
|
||||
new() { Name = "Footer", Value = "" },
|
||||
new() { Name = "Time", Value = "{relative}" },
|
||||
];
|
||||
}
|
||||
|
||||
public class CustomServerCommandData
|
||||
{
|
||||
[JsonPropertyName("Flag")]
|
||||
public string Flag { get; set; } = "@css/generic";
|
||||
|
||||
[JsonPropertyName("DisplayName")]
|
||||
public string DisplayName { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("Command")]
|
||||
public string Command { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("ExecuteOnClient")]
|
||||
public bool ExecuteOnClient { get; set; } = false;
|
||||
}
|
||||
|
||||
public class MenuConfig
|
||||
{
|
||||
[JsonPropertyName("MenuType")] public string MenuType { get; set; } = "selectable";
|
||||
|
||||
[JsonPropertyName("Durations")]
|
||||
public DurationItem[] Durations { get; set; } =
|
||||
[
|
||||
new() { Name = "1 minute", Duration = 1 },
|
||||
new() { Name = "5 minutes", Duration = 5 },
|
||||
new() { Name = "15 minutes", Duration = 15 },
|
||||
new() { Name = "1 hour", Duration = 60 },
|
||||
new() { Name = "1 day", Duration = 60 * 24 },
|
||||
new() { Name = "7 days", Duration = 60 * 24 * 7 },
|
||||
new() { Name = "14 days", Duration = 60 * 24 * 14 },
|
||||
new() { Name = "30 days", Duration = 60 * 24 * 30 },
|
||||
new() { Name = "Permanent", Duration = 0 }
|
||||
];
|
||||
|
||||
[JsonPropertyName("BanReasons")]
|
||||
public List<string> BanReasons { get; set; } =
|
||||
[
|
||||
"Hacking",
|
||||
"Voice Abuse",
|
||||
"Chat Abuse",
|
||||
"Admin disrespect",
|
||||
"Other"
|
||||
];
|
||||
|
||||
[JsonPropertyName("KickReasons")]
|
||||
public List<string> KickReasons { get; set; } =
|
||||
[
|
||||
"Voice Abuse",
|
||||
"Chat Abuse",
|
||||
"Admin disrespect",
|
||||
"Other"
|
||||
];
|
||||
|
||||
[JsonPropertyName("WarnReasons")]
|
||||
public List<string> WarnReasons { get; set; } =
|
||||
[
|
||||
"Voice Abuse",
|
||||
"Chat Abuse",
|
||||
"Admin disrespect",
|
||||
"Other"
|
||||
];
|
||||
|
||||
[JsonPropertyName("MuteReasons")]
|
||||
public List<string> MuteReasons { get; set; } =
|
||||
[
|
||||
"Advertising",
|
||||
"Spamming",
|
||||
"Spectator camera abuse",
|
||||
"Hate",
|
||||
"Admin disrespect",
|
||||
"Other"
|
||||
];
|
||||
|
||||
[JsonPropertyName("AdminFlags")]
|
||||
public AdminFlag[] AdminFlags { get; set; } =
|
||||
[
|
||||
new() { Name = "Generic", Flag = "@css/generic" },
|
||||
new() { Name = "Chat", Flag = "@css/chat" },
|
||||
new() { Name = "Change Map", Flag = "@css/changemap" },
|
||||
new() { Name = "Slay", Flag = "@css/slay" },
|
||||
new() { Name = "Kick", Flag = "@css/kick" },
|
||||
new() { Name = "Ban", Flag = "@css/ban" },
|
||||
new() { Name = "Perm Ban", Flag = "@css/permban" },
|
||||
new() { Name = "Unban", Flag = "@css/unban" },
|
||||
new() { Name = "Show IP", Flag = "@css/showip" },
|
||||
new() { Name = "Cvar", Flag = "@css/cvar" },
|
||||
new() { Name = "Rcon", Flag = "@css/rcon" },
|
||||
new() { Name = "Root (all flags)", Flag = "@css/root" }
|
||||
];
|
||||
}
|
||||
|
||||
public class OtherSettings
|
||||
{
|
||||
[JsonPropertyName("ShowActivityType")]
|
||||
public int ShowActivityType { get; set; } = 2;
|
||||
|
||||
[JsonPropertyName("TeamSwitchType")]
|
||||
public int TeamSwitchType { get; set; } = 1;
|
||||
|
||||
[JsonPropertyName("KickTime")]
|
||||
public int KickTime { get; set; } = 5;
|
||||
|
||||
[JsonPropertyName("BanType")]
|
||||
public int BanType { get; set; } = 1;
|
||||
|
||||
[JsonPropertyName("TimeMode")]
|
||||
public int TimeMode { get; set; } = 1;
|
||||
|
||||
[JsonPropertyName("DisableDangerousCommands")]
|
||||
public bool DisableDangerousCommands { get; set; } = true;
|
||||
|
||||
[JsonPropertyName("MaxBanDuration")]
|
||||
public int MaxBanDuration { get; set; } = 60 * 24 * 7;
|
||||
|
||||
[JsonPropertyName("MaxMuteDuration")]
|
||||
public int MaxMuteDuration { get; set; } = 60 * 24 * 7;
|
||||
|
||||
[JsonPropertyName("ExpireOldIpBans")]
|
||||
public int ExpireOldIpBans { get; set; } = 0;
|
||||
|
||||
[JsonPropertyName("ReloadAdminsEveryMapChange")]
|
||||
public bool ReloadAdminsEveryMapChange { get; set; } = false;
|
||||
|
||||
[JsonPropertyName("DisconnectedPlayersHistoryCount")]
|
||||
public int DisconnectedPlayersHistoryCount { get; set; } = 10;
|
||||
|
||||
[JsonPropertyName("NotifyPenaltiesToAdminOnConnect")]
|
||||
public bool NotifyPenaltiesToAdminOnConnect { get; set; } = true;
|
||||
|
||||
[JsonPropertyName("ShowBanMenuIfNoTime")]
|
||||
public bool ShowBanMenuIfNoTime { get; set; } = true;
|
||||
|
||||
[JsonPropertyName("UserMessageGagChatType")]
|
||||
public bool UserMessageGagChatType { get; set; } = false;
|
||||
|
||||
[JsonPropertyName("CheckMultiAccountsByIp")]
|
||||
public bool CheckMultiAccountsByIp { get; set; } = true;
|
||||
|
||||
[JsonPropertyName("AdditionalCommandsToLog")]
|
||||
public List<string> AdditionalCommandsToLog { get; set; } = new();
|
||||
[JsonPropertyName("IgnoredIps")]
|
||||
public List<string> IgnoredIps { get; set; } = new();
|
||||
}
|
||||
|
||||
public class CS2_SimpleAdminConfig : BasePluginConfig
|
||||
{
|
||||
[JsonPropertyName("ConfigVersion")] public override int Version { get; set; } = 25;
|
||||
|
||||
[JsonPropertyName("DatabaseConfig")]
|
||||
public DatabaseConfig DatabaseConfig { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("OtherSettings")]
|
||||
public OtherSettings OtherSettings { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("EnableMetrics")]
|
||||
public bool EnableMetrics { get; set; } = true;
|
||||
|
||||
[JsonPropertyName("EnableUpdateCheck")]
|
||||
public bool EnableUpdateCheck { get; set; } = true;
|
||||
|
||||
[JsonPropertyName("Timezone")]
|
||||
public string Timezone { get; set; } = "UTC";
|
||||
|
||||
[JsonPropertyName("WarnThreshold")]
|
||||
public Dictionary<int, string> WarnThreshold { get; set; } = new()
|
||||
{
|
||||
{ 998, "css_addban STEAMID64 60 \"3/4 Warn\"" },
|
||||
{ 999, "css_ban #USERID 120 \"4/4 Warn\"" },
|
||||
};
|
||||
|
||||
[JsonPropertyName("MultiServerMode")]
|
||||
public bool MultiServerMode { get; set; } = true;
|
||||
|
||||
[JsonPropertyName("Discord")]
|
||||
public Discord Discord { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("DefaultMaps")]
|
||||
public List<string> DefaultMaps { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("WorkshopMaps")]
|
||||
public Dictionary<string, long?> WorkshopMaps { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("CustomServerCommands")]
|
||||
public List<CustomServerCommandData> CustomServerCommands { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("MenuConfig")]
|
||||
public MenuConfig MenuConfigs { get; set; } = new();
|
||||
}
|
||||
|
||||
|
||||
public class DatabaseConfig
|
||||
{
|
||||
[JsonPropertyName("DatabaseType")]
|
||||
public string DatabaseType { get; set; } = "SQLite";
|
||||
|
||||
[JsonPropertyName("SqliteFilePath")]
|
||||
public string SqliteFilePath { get; set; } = "cs2-simpleadmin.sqlite";
|
||||
|
||||
[JsonPropertyName("DatabaseHost")]
|
||||
public string DatabaseHost { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("DatabasePort")]
|
||||
public int DatabasePort { get; set; } = 3306;
|
||||
|
||||
[JsonPropertyName("DatabaseUser")]
|
||||
public string DatabaseUser { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("DatabasePassword")]
|
||||
public string DatabasePassword { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("DatabaseName")]
|
||||
public string DatabaseName { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("DatabaseSSlMode")]
|
||||
public string DatabaseSSlMode { get; set; } = "preferred";
|
||||
}
|
||||
|
||||
public enum DatabaseType
|
||||
{
|
||||
MySQL,
|
||||
SQLite
|
||||
}
|
||||
69
CS2-SimpleAdmin/Database/Database.cs
Normal file
69
CS2-SimpleAdmin/Database/Database.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MySqlConnector;
|
||||
|
||||
namespace CS2_SimpleAdmin.Database;
|
||||
|
||||
public class Database(string dbConnectionString)
|
||||
{
|
||||
public MySqlConnection GetConnection()
|
||||
{
|
||||
try
|
||||
{
|
||||
var connection = new MySqlConnection(dbConnectionString);
|
||||
connection.Open();
|
||||
|
||||
// using var cmd = connection.CreateCommand();
|
||||
// cmd.CommandText = "SET NAMES 'utf8mb4' COLLATE 'utf8mb4_general_ci';";
|
||||
// cmd.ExecuteNonQueryAsync();
|
||||
|
||||
return connection;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogCritical($"Unable to connect to database: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<MySqlConnection> GetConnectionAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var connection = new MySqlConnection(dbConnectionString);
|
||||
await connection.OpenAsync();
|
||||
|
||||
// await using var cmd = connection.CreateCommand();
|
||||
// cmd.CommandText = "SET NAMES 'utf8mb4' COLLATE 'utf8mb4_general_ci';";
|
||||
// await cmd.ExecuteNonQueryAsync();
|
||||
|
||||
return connection;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogCritical($"Unable to connect to database: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// public async Task DatabaseMigration()
|
||||
// {
|
||||
// Migration migrator = new(this);
|
||||
// await migrator.ExecuteMigrationsAsync();
|
||||
// }
|
||||
|
||||
public bool CheckDatabaseConnection(out string? exception)
|
||||
{
|
||||
using var connection = GetConnection();
|
||||
exception = null;
|
||||
|
||||
try
|
||||
{
|
||||
return connection.Ping();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex.Message;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
60
CS2-SimpleAdmin/Database/IDatabaseProvider.cs
Normal file
60
CS2-SimpleAdmin/Database/IDatabaseProvider.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System.Data.Common;
|
||||
|
||||
namespace CS2_SimpleAdmin.Database;
|
||||
|
||||
public interface IDatabaseProvider
|
||||
{
|
||||
Task<DbConnection> CreateConnectionAsync();
|
||||
Task<(bool Success, string? Exception)> CheckConnectionAsync();
|
||||
Task DatabaseMigrationAsync();
|
||||
|
||||
// CacheManager
|
||||
string GetBanSelectQuery(bool multiServer);
|
||||
string GetIpHistoryQuery();
|
||||
string GetBanUpdateQuery(bool multiServer);
|
||||
|
||||
// PermissionManager
|
||||
string GetAdminsQuery();
|
||||
string GetDeleteAdminQuery(bool globalDelete);
|
||||
string GetAddAdminQuery();
|
||||
string GetAddAdminFlagsQuery();
|
||||
string GetUpdateAdminGroupQuery();
|
||||
string GetGroupsQuery();
|
||||
string GetGroupIdByNameQuery();
|
||||
string GetAddGroupQuery();
|
||||
string GetAddGroupFlagsQuery();
|
||||
string GetAddGroupServerQuery();
|
||||
string GetDeleteGroupQuery();
|
||||
string GetDeleteOldAdminsQuery();
|
||||
|
||||
// BanManager
|
||||
string GetAddBanQuery();
|
||||
string GetAddBanBySteamIdQuery();
|
||||
string GetAddBanByIpQuery();
|
||||
string GetUnbanRetrieveBansQuery(bool multiServer);
|
||||
string GetUnbanAdminIdQuery();
|
||||
string GetInsertUnbanQuery(bool includeReason);
|
||||
string GetUpdateBanStatusQuery();
|
||||
string GetExpireBansQuery(bool multiServer);
|
||||
string GetExpireIpBansQuery(bool multiServer);
|
||||
|
||||
// MuteManager
|
||||
string GetAddMuteQuery(bool includePlayerName);
|
||||
string GetIsMutedQuery(bool multiServer, int timeMode);
|
||||
string GetMuteStatsQuery(bool multiServer);
|
||||
string GetUpdateMutePassedQuery(bool multiServer);
|
||||
string GetCheckExpiredMutesQuery(bool multiServer);
|
||||
string GetRetrieveMutesQuery(bool multiServer);
|
||||
string GetUnmuteAdminIdQuery();
|
||||
string GetInsertUnmuteQuery(bool includeReason);
|
||||
string GetUpdateMuteStatusQuery();
|
||||
string GetExpireMutesQuery(bool multiServer, int timeMode);
|
||||
|
||||
// WarnManager
|
||||
string GetAddWarnQuery(bool includePlayerName);
|
||||
string GetPlayerWarnsQuery(bool multiServer, bool active);
|
||||
string GetPlayerWarnsCountQuery(bool multiServer, bool active);
|
||||
string GetUnwarnByIdQuery(bool multiServer);
|
||||
string GetUnwarnLastQuery(bool multiServer);
|
||||
string GetExpireWarnsQuery(bool multiServer);
|
||||
}
|
||||
94
CS2-SimpleAdmin/Database/Migration.cs
Normal file
94
CS2-SimpleAdmin/Database/Migration.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
using System.Data.Common;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CS2_SimpleAdmin.Database;
|
||||
|
||||
public class Migration(string migrationsPath)
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes all migration scripts found in the configured migrations path that have not been applied yet.
|
||||
/// Creates a migration tracking table if it does not exist.
|
||||
/// Applies migration scripts in filename order and logs successes or failures.
|
||||
/// </summary>
|
||||
public async Task ExecuteMigrationsAsync()
|
||||
{
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null) return;
|
||||
var files = Directory.GetFiles(migrationsPath, "*.sql").OrderBy(f => f).ToList();
|
||||
if (files.Count == 0) return;
|
||||
|
||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
|
||||
await using (var cmd = connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = """
|
||||
CREATE TABLE IF NOT EXISTS sa_migrations (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
version TEXT NOT NULL
|
||||
);
|
||||
|
||||
""";
|
||||
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
var lastAppliedVersion = await GetLastAppliedVersionAsync(connection);
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
var version = Path.GetFileNameWithoutExtension(file);
|
||||
if (string.Compare(version, lastAppliedVersion, StringComparison.OrdinalIgnoreCase) <= 0)
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
var sqlScript = await File.ReadAllTextAsync(file);
|
||||
|
||||
await using (var cmdMigration = connection.CreateCommand())
|
||||
{
|
||||
cmdMigration.CommandText = sqlScript;
|
||||
await cmdMigration.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
await UpdateLastAppliedVersionAsync(connection, version);
|
||||
|
||||
CS2_SimpleAdmin._logger?.LogInformation($"Migration \"{version}\" successfully applied.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError(ex, $"Error applying migration \"{version}\".");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the version string of the last applied migration from the database.
|
||||
/// </summary>
|
||||
/// <param name="connection">The open database connection.</param>
|
||||
/// <returns>The version string of the last applied migration, or empty string if none.</returns>
|
||||
private static async Task<string> GetLastAppliedVersionAsync(DbConnection connection)
|
||||
{
|
||||
await using var cmd = connection.CreateCommand();
|
||||
cmd.CommandText = "SELECT version FROM sa_migrations ORDER BY id DESC LIMIT 1;";
|
||||
var result = await cmd.ExecuteScalarAsync();
|
||||
return result?.ToString() ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a record tracking the successful application of a migration version.
|
||||
/// </summary>
|
||||
/// <param name="connection">The open database connection.</param>
|
||||
/// <param name="version">The version string of the migration applied.</param>
|
||||
private static async Task UpdateLastAppliedVersionAsync(DbConnection connection, string version)
|
||||
{
|
||||
await using var cmd = connection.CreateCommand();
|
||||
cmd.CommandText = "INSERT INTO sa_migrations (version) VALUES (@Version);";
|
||||
|
||||
var param = cmd.CreateParameter();
|
||||
param.ParameterName = "@Version";
|
||||
param.Value = version;
|
||||
cmd.Parameters.Add(param);
|
||||
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
CREATE TABLE IF NOT EXISTS `sa_bans` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
`player_name` VARCHAR(128),
|
||||
`player_steamid` VARCHAR(64),
|
||||
`player_ip` VARCHAR(128),
|
||||
`admin_steamid` VARCHAR(64) NOT NULL,
|
||||
`admin_name` VARCHAR(128) NOT NULL,
|
||||
`reason` VARCHAR(255) NOT NULL,
|
||||
`duration` INT NOT NULL,
|
||||
`ends` TIMESTAMP NULL,
|
||||
`created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`server_id` INT NULL,
|
||||
`status` ENUM('ACTIVE', 'UNBANNED', 'EXPIRED', '') NOT NULL DEFAULT 'ACTIVE'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sa_mutes` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`player_name` varchar(128) NULL,
|
||||
`player_steamid` varchar(64) NOT NULL,
|
||||
`admin_steamid` varchar(64) NOT NULL,
|
||||
`admin_name` varchar(128) NOT NULL,
|
||||
`reason` varchar(255) NOT NULL,
|
||||
`duration` int(11) NOT NULL,
|
||||
`ends` timestamp NULL,
|
||||
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`type` enum('GAG','MUTE','SILENCE','') NOT NULL DEFAULT 'GAG',
|
||||
`server_id` INT NULL,
|
||||
`status` enum('ACTIVE','UNMUTED','EXPIRED','') NOT NULL DEFAULT 'ACTIVE',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sa_admins` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`player_name` varchar(128) NOT NULL,
|
||||
`player_steamid` varchar(64) NOT NULL,
|
||||
`flags` TEXT NULL,
|
||||
`immunity` int(11) NOT NULL DEFAULT 0,
|
||||
`server_id` INT NULL,
|
||||
`ends` timestamp NULL,
|
||||
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sa_servers` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`hostname` varchar(128) NOT NULL,
|
||||
`address` varchar(64) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `address` (`address`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
@@ -0,0 +1,9 @@
|
||||
CREATE TABLE IF NOT EXISTS `sa_admins_flags` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`admin_id` int(11) NOT NULL,
|
||||
`flag` varchar(64) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
FOREIGN KEY (`admin_id`) REFERENCES `sa_admins` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
ALTER TABLE `sa_admins` CHANGE `flags` `flags` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL;
|
||||
@@ -0,0 +1,4 @@
|
||||
ALTER TABLE `sa_bans` CHANGE `player_name` `player_name` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL AFTER `id`;
|
||||
ALTER TABLE `sa_mutes` CHANGE `player_name` `player_name` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL AFTER `id`;
|
||||
ALTER TABLE `sa_admins` CHANGE `player_name` `player_name` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL AFTER `id`;
|
||||
ALTER TABLE `sa_servers` CHANGE `hostname` `hostname` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL AFTER `id`;
|
||||
@@ -0,0 +1,36 @@
|
||||
INSERT INTO sa_admins_flags (admin_id, flag)
|
||||
SELECT
|
||||
min_admins.admin_id,
|
||||
TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(sa_admins.flags, ',', numbers.n), ',', -1)) AS flag
|
||||
FROM (
|
||||
SELECT MIN(id) AS admin_id, player_steamid, server_id
|
||||
FROM sa_admins
|
||||
WHERE player_steamid != 'Console'
|
||||
GROUP BY player_steamid, server_id
|
||||
) AS min_admins
|
||||
JOIN sa_admins ON min_admins.player_steamid = sa_admins.player_steamid
|
||||
JOIN (
|
||||
SELECT 1 AS n UNION ALL
|
||||
SELECT 2 UNION ALL
|
||||
SELECT 3 UNION ALL
|
||||
SELECT 4 UNION ALL
|
||||
SELECT 5 UNION ALL
|
||||
SELECT 6 UNION ALL
|
||||
SELECT 7 UNION ALL
|
||||
SELECT 8 UNION ALL
|
||||
SELECT 9 UNION ALL
|
||||
SELECT 10 UNION ALL
|
||||
SELECT 11 UNION ALL
|
||||
SELECT 12 UNION ALL
|
||||
SELECT 13 UNION ALL
|
||||
SELECT 14 UNION ALL
|
||||
SELECT 15 UNION ALL
|
||||
SELECT 16 UNION ALL
|
||||
SELECT 17 UNION ALL
|
||||
SELECT 18 UNION ALL
|
||||
SELECT 19 UNION ALL
|
||||
SELECT 20
|
||||
) AS numbers
|
||||
ON CHAR_LENGTH(sa_admins.flags) - CHAR_LENGTH(REPLACE(sa_admins.flags, ',', '')) >= numbers.n - 1
|
||||
AND (min_admins.server_id = sa_admins.server_id OR (min_admins.server_id IS NULL AND sa_admins.server_id IS NULL))
|
||||
WHERE sa_admins.id IS NOT NULL;
|
||||
@@ -0,0 +1,29 @@
|
||||
CREATE TABLE IF NOT EXISTS `sa_unbans` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`ban_id` int(11) NOT NULL,
|
||||
`admin_id` int(11) NOT NULL DEFAULT 0,
|
||||
`reason` varchar(255) NOT NULL DEFAULT 'Unknown',
|
||||
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sa_unmutes` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`mute_id` int(11) NOT NULL,
|
||||
`admin_id` int(11) NOT NULL DEFAULT 0,
|
||||
`reason` varchar(255) NOT NULL DEFAULT 'Unknown',
|
||||
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
INSERT IGNORE INTO `sa_admins` (`id`, `player_name`, `player_steamid`, `flags`, `immunity`, `server_id`, `ends`, `created`)
|
||||
VALUES (0, 'Console', 'Console', '', '0', NULL, NULL, NOW());
|
||||
|
||||
UPDATE `sa_admins` SET `id` = 0 WHERE `id` = -1;
|
||||
|
||||
ALTER TABLE `sa_bans` ADD `unban_id` INT NULL AFTER `server_id`;
|
||||
ALTER TABLE `sa_mutes` ADD `unmute_id` INT NULL AFTER `server_id`;
|
||||
ALTER TABLE `sa_bans` ADD FOREIGN KEY (`unban_id`) REFERENCES `sa_unbans`(`id`) ON DELETE CASCADE;
|
||||
ALTER TABLE `sa_mutes` ADD FOREIGN KEY (`unmute_id`) REFERENCES `sa_unmutes`(`id`) ON DELETE CASCADE;
|
||||
ALTER TABLE `sa_unbans` ADD FOREIGN KEY (`admin_id`) REFERENCES `sa_admins`(`id`) ON DELETE CASCADE;
|
||||
ALTER TABLE `sa_unmutes` ADD FOREIGN KEY (`admin_id`) REFERENCES `sa_admins`(`id`) ON DELETE CASCADE;
|
||||
@@ -0,0 +1,26 @@
|
||||
CREATE TABLE IF NOT EXISTS `sa_groups` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`immunity` int(11) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sa_groups_flags` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`group_id` int(11) NOT NULL,
|
||||
`flag` varchar(64) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sa_groups_servers` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`group_id` int(11) NOT NULL,
|
||||
`server_id` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
ALTER TABLE `sa_admins` ADD `group_id` INT NULL AFTER `created`;
|
||||
|
||||
ALTER TABLE `sa_groups_flags` ADD FOREIGN KEY (`group_id`) REFERENCES `sa_groups`(`id`) ON DELETE CASCADE;
|
||||
ALTER TABLE `sa_groups_servers` ADD FOREIGN KEY (`group_id`) REFERENCES `sa_groups`(`id`) ON DELETE CASCADE;
|
||||
ALTER TABLE `sa_admins` ADD FOREIGN KEY (`group_id`) REFERENCES `sa_groups`(`id`) ON DELETE SET NULL;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE `sa_groups_servers` CHANGE `server_id` `server_id` INT(11) NULL;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE `sa_mutes` ADD `passed` INT NULL AFTER `duration`;
|
||||
@@ -0,0 +1,8 @@
|
||||
CREATE TABLE IF NOT EXISTS `sa_players_ips` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`steamid` bigint(20) NOT NULL,
|
||||
`address` varchar(64) NOT NULL,
|
||||
`used_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `steamid` (`steamid`,`address`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
@@ -0,0 +1,14 @@
|
||||
CREATE TABLE IF NOT EXISTS `sa_warns` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`player_name` varchar(128) DEFAULT NULL,
|
||||
`player_steamid` varchar(64) NOT NULL,
|
||||
`admin_steamid` varchar(64) NOT NULL,
|
||||
`admin_name` varchar(128) NOT NULL,
|
||||
`reason` varchar(255) NOT NULL,
|
||||
`duration` int(11) NOT NULL,
|
||||
`ends` timestamp NOT NULL,
|
||||
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`server_id` int(11) DEFAULT NULL,
|
||||
`status` enum('ACTIVE','EXPIRED','') NOT NULL DEFAULT 'ACTIVE',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE `sa_servers` ADD `rcon_password` varchar(128) NULL AFTER `hostname`;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE `sa_bans` ADD COLUMN `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER `status`;
|
||||
@@ -0,0 +1,13 @@
|
||||
DELETE FROM `sa_players_ips`
|
||||
WHERE `id` NOT IN (
|
||||
SELECT * FROM (
|
||||
SELECT MIN(`id`)
|
||||
FROM `sa_players_ips`
|
||||
GROUP BY `steamid`
|
||||
) AS `keep_ids`
|
||||
);
|
||||
DELETE FROM sa_players_ips WHERE INET_ATON(address) IS NULL AND address IS NOT NULL;
|
||||
UPDATE `sa_players_ips` SET `address` = INET_ATON(address);
|
||||
ALTER TABLE `sa_players_ips` CHANGE `address` `address` INT UNSIGNED NOT NULL;
|
||||
ALTER TABLE `sa_players_ips` ADD INDEX (used_at DESC);
|
||||
ALTER TABLE `sa_players_ips` ADD `name` VARCHAR(64) NULL DEFAULT NULL AFTER `steamid`;
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE sa_mutes ADD INDEX (player_steamid, status, ends);
|
||||
ALTER TABLE sa_mutes ADD INDEX(player_steamid, status, server_id, duration);
|
||||
ALTER TABLE sa_mutes ADD INDEX(player_steamid, type);
|
||||
@@ -0,0 +1,23 @@
|
||||
ALTER TABLE `sa_bans` CHANGE `player_steamid` `player_steamid` BIGINT NULL DEFAULT NULL;
|
||||
UPDATE `sa_bans`
|
||||
SET admin_steamid = '0'
|
||||
WHERE admin_steamid NOT REGEXP '^[0-9]+$';
|
||||
ALTER TABLE `sa_bans` CHANGE `admin_steamid` `admin_steamid` BIGINT NOT NULL;
|
||||
|
||||
ALTER TABLE `sa_mutes` CHANGE `player_steamid` `player_steamid` BIGINT NULL DEFAULT NULL;
|
||||
UPDATE `sa_mutes`
|
||||
SET admin_steamid = '0'
|
||||
WHERE admin_steamid NOT REGEXP '^[0-9]+$';
|
||||
ALTER TABLE `sa_mutes` CHANGE `admin_steamid` `admin_steamid` BIGINT NOT NULL;
|
||||
|
||||
ALTER TABLE `sa_warns` CHANGE `player_steamid` `player_steamid` BIGINT NULL DEFAULT NULL;
|
||||
UPDATE `sa_warns`
|
||||
SET admin_steamid = '0'
|
||||
WHERE admin_steamid NOT REGEXP '^[0-9]+$';
|
||||
ALTER TABLE `sa_warns` CHANGE `admin_steamid` `admin_steamid` BIGINT NOT NULL;
|
||||
|
||||
UPDATE `sa_admins`
|
||||
SET player_steamid = '0'
|
||||
WHERE player_steamid NOT REGEXP '^[0-9]+$';
|
||||
ALTER TABLE `sa_admins` CHANGE `player_steamid` `player_steamid` BIGINT NULL DEFAULT NULL;
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
CREATE TABLE IF NOT EXISTS `sa_bans` (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`player_name` VARCHAR(128),
|
||||
`player_steamid` VARCHAR(64),
|
||||
`player_ip` VARCHAR(128),
|
||||
`admin_steamid` VARCHAR(64) NOT NULL,
|
||||
`admin_name` VARCHAR(128) NOT NULL,
|
||||
`reason` VARCHAR(255) NOT NULL,
|
||||
`duration` INTEGER NOT NULL,
|
||||
`ends` TIMESTAMP NULL,
|
||||
`created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`server_id` INTEGER NULL,
|
||||
`status` TEXT NOT NULL DEFAULT 'ACTIVE'
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sa_mutes` (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`player_name` VARCHAR(128) NULL,
|
||||
`player_steamid` VARCHAR(64) NOT NULL,
|
||||
`admin_steamid` VARCHAR(64) NOT NULL,
|
||||
`admin_name` VARCHAR(128) NOT NULL,
|
||||
`reason` VARCHAR(255) NOT NULL,
|
||||
`duration` INTEGER NOT NULL,
|
||||
`ends` TIMESTAMP NULL,
|
||||
`created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`type` TEXT NOT NULL DEFAULT 'GAG',
|
||||
`server_id` INTEGER NULL,
|
||||
`status` TEXT NOT NULL DEFAULT 'ACTIVE'
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sa_admins` (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`player_name` VARCHAR(128) NOT NULL,
|
||||
`player_steamid` VARCHAR(64) NOT NULL,
|
||||
`flags` TEXT NULL,
|
||||
`immunity` INTEGER NOT NULL DEFAULT 0,
|
||||
`server_id` INTEGER NULL,
|
||||
`ends` TIMESTAMP NULL,
|
||||
`created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sa_servers` (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`hostname` VARCHAR(128) NOT NULL,
|
||||
`address` VARCHAR(64) NOT NULL,
|
||||
UNIQUE (`address`)
|
||||
);
|
||||
@@ -0,0 +1,6 @@
|
||||
CREATE TABLE IF NOT EXISTS `sa_admins_flags` (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`admin_id` INTEGER NOT NULL,
|
||||
`flag` VARCHAR(64) NOT NULL,
|
||||
FOREIGN KEY (`admin_id`) REFERENCES `sa_admins` (`id`) ON DELETE CASCADE
|
||||
);
|
||||
@@ -0,0 +1,46 @@
|
||||
INSERT INTO sa_admins_flags (admin_id, flag)
|
||||
WITH RECURSIVE
|
||||
min_admins AS (
|
||||
SELECT MIN(id) AS admin_id, player_steamid, server_id
|
||||
FROM sa_admins
|
||||
WHERE player_steamid != 'Console'
|
||||
GROUP BY player_steamid, server_id
|
||||
),
|
||||
split_flags AS (
|
||||
SELECT
|
||||
ma.admin_id,
|
||||
sa.flags,
|
||||
1 AS pos,
|
||||
CASE
|
||||
WHEN INSTR(sa.flags || ',', ',') = 0 THEN sa.flags
|
||||
ELSE SUBSTR(sa.flags, 1, INSTR(sa.flags || ',', ',') - 1)
|
||||
END AS flag,
|
||||
CASE
|
||||
WHEN INSTR(sa.flags || ',', ',') = 0 THEN ''
|
||||
ELSE SUBSTR(sa.flags, INSTR(sa.flags || ',', ',') + 1)
|
||||
END AS remaining
|
||||
FROM min_admins ma
|
||||
JOIN sa_admins sa ON ma.player_steamid = sa.player_steamid
|
||||
AND (ma.server_id = sa.server_id OR (ma.server_id IS NULL AND sa.server_id IS NULL))
|
||||
WHERE sa.flags IS NOT NULL AND sa.flags != ''
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
admin_id,
|
||||
flags,
|
||||
pos + 1,
|
||||
CASE
|
||||
WHEN INSTR(remaining || ',', ',') = 0 THEN remaining
|
||||
ELSE SUBSTR(remaining, 1, INSTR(remaining || ',', ',') - 1)
|
||||
END AS flag,
|
||||
CASE
|
||||
WHEN INSTR(remaining || ',', ',') = 0 THEN ''
|
||||
ELSE SUBSTR(remaining, INSTR(remaining || ',', ',') + 1)
|
||||
END AS remaining
|
||||
FROM split_flags
|
||||
WHERE remaining != ''
|
||||
)
|
||||
SELECT admin_id, TRIM(flag)
|
||||
FROM split_flags
|
||||
WHERE flag IS NOT NULL AND TRIM(flag) != '';
|
||||
@@ -0,0 +1,23 @@
|
||||
CREATE TABLE IF NOT EXISTS `sa_unbans` (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`ban_id` INTEGER NOT NULL,
|
||||
`admin_id` INTEGER NOT NULL DEFAULT 0,
|
||||
`reason` VARCHAR(255) NOT NULL DEFAULT 'Unknown',
|
||||
`date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sa_unmutes` (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`mute_id` INTEGER NOT NULL,
|
||||
`admin_id` INTEGER NOT NULL DEFAULT 0,
|
||||
`reason` VARCHAR(255) NOT NULL DEFAULT 'Unknown',
|
||||
`date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
INSERT OR IGNORE INTO `sa_admins` (`id`, `player_name`, `player_steamid`, `flags`, `immunity`, `server_id`, `ends`, `created`)
|
||||
VALUES (0, 'Console', 'Console', '', '0', NULL, NULL, CURRENT_TIMESTAMP);
|
||||
|
||||
UPDATE `sa_admins` SET `id` = 0 WHERE `id` = -1;
|
||||
|
||||
ALTER TABLE `sa_bans` ADD `unban_id` INTEGER NULL;
|
||||
ALTER TABLE `sa_mutes` ADD `unmute_id` INTEGER NULL;
|
||||
@@ -0,0 +1,21 @@
|
||||
CREATE TABLE IF NOT EXISTS `sa_groups` (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`immunity` INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sa_groups_flags` (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`group_id` INTEGER NOT NULL,
|
||||
`flag` VARCHAR(64) NOT NULL,
|
||||
FOREIGN KEY (`group_id`) REFERENCES `sa_groups` (`id`) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sa_groups_servers` (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`group_id` INTEGER NOT NULL,
|
||||
`server_id` INTEGER NULL,
|
||||
FOREIGN KEY (`group_id`) REFERENCES `sa_groups` (`id`) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE `sa_admins` ADD `group_id` INTEGER NULL;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE `sa_mutes` ADD `passed` INTEGER NULL;
|
||||
@@ -0,0 +1,7 @@
|
||||
CREATE TABLE IF NOT EXISTS `sa_players_ips` (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`steamid` INTEGER NOT NULL,
|
||||
`address` VARCHAR(64) NOT NULL,
|
||||
`used_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE (`steamid`, `address`)
|
||||
);
|
||||
@@ -0,0 +1,13 @@
|
||||
CREATE TABLE IF NOT EXISTS `sa_warns` (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`player_name` VARCHAR(128) DEFAULT NULL,
|
||||
`player_steamid` VARCHAR(64) NOT NULL,
|
||||
`admin_steamid` VARCHAR(64) NOT NULL,
|
||||
`admin_name` VARCHAR(128) NOT NULL,
|
||||
`reason` VARCHAR(255) NOT NULL,
|
||||
`duration` INTEGER NOT NULL,
|
||||
`ends` TIMESTAMP NOT NULL,
|
||||
`created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`server_id` INTEGER DEFAULT NULL,
|
||||
`status` TEXT NOT NULL DEFAULT 'ACTIVE'
|
||||
);
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE `sa_servers` ADD `rcon_password` VARCHAR(128) NULL;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE `sa_bans` ADD COLUMN `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||
@@ -0,0 +1,9 @@
|
||||
DELETE FROM `sa_players_ips`
|
||||
WHERE `id` NOT IN (
|
||||
SELECT MIN(`id`)
|
||||
FROM `sa_players_ips`
|
||||
GROUP BY `steamid`
|
||||
);
|
||||
|
||||
ALTER TABLE `sa_players_ips` ADD `name` VARCHAR(64) NULL DEFAULT NULL;
|
||||
CREATE INDEX IF NOT EXISTS `idx_sa_players_ips_used_at` ON `sa_players_ips` (`used_at` DESC);
|
||||
@@ -0,0 +1,3 @@
|
||||
CREATE INDEX IF NOT EXISTS `idx_sa_mutes_steamid_status_ends` ON `sa_mutes` (`player_steamid`, `status`, `ends`);
|
||||
CREATE INDEX IF NOT EXISTS `idx_sa_mutes_steamid_status_server_duration` ON `sa_mutes` (`player_steamid`, `status`, `server_id`, `duration`);
|
||||
CREATE INDEX IF NOT EXISTS `idx_sa_mutes_steamid_type` ON `sa_mutes` (`player_steamid`, `type`);
|
||||
@@ -0,0 +1,15 @@
|
||||
UPDATE `sa_bans`
|
||||
SET admin_steamid = '0'
|
||||
WHERE admin_steamid NOT GLOB '[0-9]*';
|
||||
|
||||
UPDATE `sa_mutes`
|
||||
SET admin_steamid = '0'
|
||||
WHERE admin_steamid NOT GLOB '[0-9]*';
|
||||
|
||||
UPDATE `sa_warns`
|
||||
SET admin_steamid = '0'
|
||||
WHERE admin_steamid NOT GLOB '[0-9]*';
|
||||
|
||||
UPDATE `sa_admins`
|
||||
SET player_steamid = '0'
|
||||
WHERE player_steamid NOT GLOB '[0-9]*';
|
||||
384
CS2-SimpleAdmin/Database/MysqlDatabaseProvider.cs
Normal file
384
CS2-SimpleAdmin/Database/MysqlDatabaseProvider.cs
Normal file
@@ -0,0 +1,384 @@
|
||||
using System.Data.Common;
|
||||
using MySqlConnector;
|
||||
|
||||
namespace CS2_SimpleAdmin.Database;
|
||||
|
||||
public class MySqlDatabaseProvider(string connectionString) : IDatabaseProvider
|
||||
{
|
||||
public async Task<DbConnection> CreateConnectionAsync()
|
||||
{
|
||||
var connection = new MySqlConnection(connectionString);
|
||||
await connection.OpenAsync();
|
||||
return connection;
|
||||
}
|
||||
|
||||
public async Task<(bool Success, string? Exception)> CheckConnectionAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var conn = await CreateConnectionAsync();
|
||||
return (true, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (false, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public Task DatabaseMigrationAsync()
|
||||
{
|
||||
var migration = new Migration(CS2_SimpleAdmin.Instance.ModuleDirectory + "/Database/Migrations/Mysql");
|
||||
return migration.ExecuteMigrationsAsync();
|
||||
}
|
||||
|
||||
public string GetBanSelectQuery(bool multiServer)
|
||||
{
|
||||
return multiServer ? """
|
||||
SELECT
|
||||
id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM sa_bans
|
||||
""" : """
|
||||
SELECT
|
||||
id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM sa_bans
|
||||
WHERE server_id = @serverId
|
||||
""";
|
||||
}
|
||||
|
||||
public static string GetBanUpdatedSelectQuery(bool multiServer)
|
||||
{
|
||||
return multiServer ? """
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM `sa_bans` WHERE updated_at > @lastUpdate OR created > @lastUpdate ORDER BY updated_at DESC
|
||||
""" : """
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM `sa_bans` WHERE (updated_at > @lastUpdate OR created > @lastUpdate) AND server_id = @serverId ORDER BY updated_at DESC
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetIpHistoryQuery()
|
||||
{
|
||||
return "SELECT steamid, name, address, used_at FROM sa_players_ips ORDER BY used_at DESC";
|
||||
}
|
||||
|
||||
public string GetBanUpdateQuery(bool multiServer)
|
||||
{
|
||||
return multiServer ? """
|
||||
UPDATE sa_bans
|
||||
SET
|
||||
player_ip = COALESCE(player_ip, @PlayerIP),
|
||||
player_name = COALESCE(player_name, @PlayerName)
|
||||
WHERE
|
||||
(player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
AND status = 'ACTIVE'
|
||||
AND (duration = 0 OR ends > @CurrentTime)
|
||||
""" : """
|
||||
UPDATE sa_bans
|
||||
SET
|
||||
player_ip = COALESCE(player_ip, @PlayerIP),
|
||||
player_name = COALESCE(player_name, @PlayerName)
|
||||
WHERE
|
||||
(player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
AND status = 'ACTIVE'
|
||||
AND (duration = 0 OR ends > @CurrentTime) AND server_id = @ServerId
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetAdminsQuery()
|
||||
{
|
||||
return """
|
||||
SELECT sa_admins.player_steamid, sa_admins.player_name, sa_admins_flags.flag, sa_admins.immunity, sa_admins.ends
|
||||
FROM sa_admins_flags
|
||||
JOIN sa_admins ON sa_admins_flags.admin_id = sa_admins.id
|
||||
WHERE (sa_admins.ends IS NULL OR sa_admins.ends > @CurrentTime)
|
||||
AND (sa_admins.server_id IS NULL OR sa_admins.server_id = @serverid)
|
||||
ORDER BY sa_admins.player_steamid
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetDeleteAdminQuery(bool globalDelete) =>
|
||||
globalDelete
|
||||
? "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID"
|
||||
: "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID AND server_id = @ServerId";
|
||||
|
||||
public string GetAddAdminQuery() =>
|
||||
"INSERT INTO sa_admins (player_steamid, player_name, immunity, ends, created, server_id) " +
|
||||
"VALUES (@playerSteamId, @playerName, @immunity, @ends, @created, @serverid); SELECT LAST_INSERT_ID();";
|
||||
|
||||
public string GetGroupsQuery()
|
||||
{
|
||||
return """
|
||||
SELECT g.group_id, sg.name AS group_name, sg.immunity, f.flag
|
||||
FROM sa_groups_flags f
|
||||
JOIN sa_groups_servers g ON f.group_id = g.group_id
|
||||
JOIN sa_groups sg ON sg.id = g.group_id
|
||||
WHERE (g.server_id = @serverid OR server_id IS NULL)
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetAddAdminFlagsQuery() =>
|
||||
"INSERT INTO sa_admins_flags (admin_id, flag) VALUES (@adminId, @flag);";
|
||||
|
||||
public string GetUpdateAdminGroupQuery() =>
|
||||
"UPDATE sa_admins SET group_id = @groupId WHERE id = @adminId;";
|
||||
|
||||
public string GetAddGroupQuery() =>
|
||||
"INSERT INTO sa_groups (name, immunity) VALUES (@groupName, @immunity); SELECT LAST_INSERT_ID();";
|
||||
|
||||
public string GetGroupIdByNameQuery() =>
|
||||
"""
|
||||
SELECT sgs.group_id
|
||||
FROM sa_groups_servers sgs
|
||||
JOIN sa_groups sg ON sgs.group_id = sg.id
|
||||
WHERE sg.name = @groupName
|
||||
ORDER BY (sgs.server_id = @serverId) DESC, sgs.server_id ASC
|
||||
LIMIT 1;
|
||||
""";
|
||||
public string GetAddGroupFlagsQuery() =>
|
||||
"INSERT INTO sa_groups_flags (group_id, flag) VALUES (@groupId, @flag);";
|
||||
|
||||
public string GetAddGroupServerQuery() =>
|
||||
"INSERT INTO sa_groups_servers (group_id, server_id) VALUES (@groupId, @server_id);";
|
||||
|
||||
public string GetDeleteGroupQuery() =>
|
||||
"DELETE FROM sa_groups WHERE name = @groupName;";
|
||||
|
||||
public string GetDeleteOldAdminsQuery() =>
|
||||
"DELETE FROM sa_admins WHERE ends IS NOT NULL AND ends <= @CurrentTime;";
|
||||
|
||||
public string GetAddBanQuery()
|
||||
{
|
||||
return """
|
||||
INSERT INTO `sa_bans`
|
||||
(`player_steamid`, `player_name`, `player_ip`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `server_id`)
|
||||
VALUES
|
||||
(@playerSteamid, @playerName, @playerIp, @adminSteamid, @adminName, @banReason, @duration, @ends, @created, @serverid);
|
||||
SELECT LAST_INSERT_ID();
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetAddBanBySteamIdQuery()
|
||||
{
|
||||
return """
|
||||
INSERT INTO `sa_bans`
|
||||
(`player_steamid`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `server_id`)
|
||||
VALUES
|
||||
(@playerSteamid, @adminSteamid, @adminName, @banReason, @duration, @ends, @created, @serverid);
|
||||
SELECT LAST_INSERT_ID();
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetAddBanByIpQuery()
|
||||
{
|
||||
return """
|
||||
INSERT INTO `sa_bans`
|
||||
(`player_ip`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `server_id`)
|
||||
VALUES
|
||||
(@playerIp, @adminSteamid, @adminName, @banReason, @duration, @ends, @created, @serverid);
|
||||
""";
|
||||
}
|
||||
|
||||
public string GetUnbanRetrieveBansQuery(bool multiServer)
|
||||
{
|
||||
return multiServer
|
||||
? "SELECT id FROM sa_bans WHERE (player_steamid = @pattern OR player_name = @pattern OR player_ip = @pattern) AND status = 'ACTIVE'"
|
||||
: "SELECT id FROM sa_bans WHERE (player_steamid = @pattern OR player_name = @pattern OR player_ip = @pattern) AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetUnbanAdminIdQuery()
|
||||
{
|
||||
return "SELECT id FROM sa_admins WHERE player_steamid = @adminSteamId";
|
||||
}
|
||||
|
||||
public string GetInsertUnbanQuery(bool includeReason)
|
||||
{
|
||||
return includeReason
|
||||
? "INSERT INTO sa_unbans (ban_id, admin_id, reason) VALUES (@banId, @adminId, @reason); SELECT LAST_INSERT_ID();"
|
||||
: "INSERT INTO sa_unbans (ban_id, admin_id) VALUES (@banId, @adminId); SELECT LAST_INSERT_ID();";
|
||||
}
|
||||
|
||||
public string GetUpdateBanStatusQuery()
|
||||
{
|
||||
return "UPDATE sa_bans SET status = 'UNBANNED', unban_id = @unbanId WHERE id = @banId";
|
||||
}
|
||||
|
||||
public string GetExpireBansQuery(bool multiServer)
|
||||
{
|
||||
return multiServer
|
||||
? "UPDATE sa_bans SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @currentTime"
|
||||
: "UPDATE sa_bans SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @currentTime AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetExpireIpBansQuery(bool multiServer)
|
||||
{
|
||||
return multiServer
|
||||
? "UPDATE sa_bans SET player_ip = NULL WHERE status = 'ACTIVE' AND ends <= @ipBansTime"
|
||||
: "UPDATE sa_bans SET player_ip = NULL WHERE status = 'ACTIVE' AND ends <= @ipBansTime AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetAddMuteQuery(bool includePlayerName) =>
|
||||
includePlayerName
|
||||
? """
|
||||
INSERT INTO `sa_mutes`
|
||||
(`player_steamid`, `player_name`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `type`, `server_id`)
|
||||
VALUES (@playerSteamid, @playerName, @adminSteamid, @adminName, @muteReason, @duration, @ends, @created, @type, @serverid);
|
||||
SELECT LAST_INSERT_ID();
|
||||
"""
|
||||
: """
|
||||
INSERT INTO `sa_mutes`
|
||||
(`player_steamid`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `type`, `server_id`)
|
||||
VALUES (@playerSteamid, @adminSteamid, @adminName, @muteReason, @duration, @ends, @created, @type, @serverid);
|
||||
SELECT LAST_INSERT_ID();
|
||||
""";
|
||||
|
||||
public string GetIsMutedQuery(bool multiServer, int timeMode) =>
|
||||
multiServer
|
||||
? (timeMode == 1
|
||||
? "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR ends > @CurrentTime)"
|
||||
: "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR duration > COALESCE(passed, 0))")
|
||||
: (timeMode == 1
|
||||
? "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR ends > @CurrentTime) AND server_id = @serverid"
|
||||
: "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR duration > COALESCE(passed, 0)) AND server_id = @serverid");
|
||||
|
||||
public string GetMuteStatsQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? """
|
||||
SELECT
|
||||
COUNT(CASE WHEN type = 'MUTE' THEN 1 END) AS TotalMutes,
|
||||
COUNT(CASE WHEN type = 'GAG' THEN 1 END) AS TotalGags,
|
||||
COUNT(CASE WHEN type = 'SILENCE' THEN 1 END) AS TotalSilences
|
||||
FROM sa_mutes
|
||||
WHERE player_steamid = @PlayerSteamID;
|
||||
"""
|
||||
: """
|
||||
SELECT
|
||||
COUNT(CASE WHEN type = 'MUTE' THEN 1 END) AS TotalMutes,
|
||||
COUNT(CASE WHEN type = 'GAG' THEN 1 END) AS TotalGags,
|
||||
COUNT(CASE WHEN type = 'SILENCE' THEN 1 END) AS TotalSilences
|
||||
FROM sa_mutes
|
||||
WHERE player_steamid = @PlayerSteamID AND server_id = @ServerId;
|
||||
""";
|
||||
|
||||
public string GetUpdateMutePassedQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "UPDATE `sa_mutes` SET passed = COALESCE(passed, 0) + 1 WHERE (player_steamid = @PlayerSteamID) AND duration > 0 AND status = 'ACTIVE'"
|
||||
: "UPDATE `sa_mutes` SET passed = COALESCE(passed, 0) + 1 WHERE (player_steamid = @PlayerSteamID) AND duration > 0 AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
|
||||
public string GetCheckExpiredMutesQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "SELECT * FROM `sa_mutes` WHERE player_steamid = @PlayerSteamID AND passed >= duration AND duration > 0 AND status = 'ACTIVE'"
|
||||
: "SELECT * FROM `sa_mutes` WHERE player_steamid = @PlayerSteamID AND passed >= duration AND duration > 0 AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
|
||||
public string GetRetrieveMutesQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "SELECT id FROM sa_mutes WHERE (player_steamid = @pattern OR player_name = @pattern) AND type = @muteType AND status = 'ACTIVE'"
|
||||
: "SELECT id FROM sa_mutes WHERE (player_steamid = @pattern OR player_name = @pattern) AND type = @muteType AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
|
||||
public string GetUnmuteAdminIdQuery() =>
|
||||
"SELECT id FROM sa_admins WHERE player_steamid = @adminSteamId";
|
||||
|
||||
public string GetInsertUnmuteQuery(bool includeReason) =>
|
||||
includeReason
|
||||
? "INSERT INTO sa_unmutes (mute_id, admin_id, reason) VALUES (@muteId, @adminId, @reason); SELECT LAST_INSERT_ID();"
|
||||
: "INSERT INTO sa_unmutes (mute_id, admin_id) VALUES (@muteId, @adminId); SELECT LAST_INSERT_ID();";
|
||||
|
||||
public string GetUpdateMuteStatusQuery() =>
|
||||
"UPDATE sa_mutes SET status = 'UNMUTED', unmute_id = @unmuteId WHERE id = @muteId";
|
||||
|
||||
public string GetExpireMutesQuery(bool multiServer, int timeMode) =>
|
||||
multiServer
|
||||
? (timeMode == 1
|
||||
? "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime"
|
||||
: "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND `passed` >= `duration`")
|
||||
: (timeMode == 1
|
||||
? "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime AND server_id = @serverid"
|
||||
: "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND `passed` >= `duration` AND server_id = @serverid");
|
||||
|
||||
|
||||
public string GetAddWarnQuery(bool includePlayerName) =>
|
||||
includePlayerName
|
||||
? """
|
||||
INSERT INTO `sa_warns`
|
||||
(`player_steamid`, `player_name`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `server_id`)
|
||||
VALUES
|
||||
(@playerSteamid, @playerName, @adminSteamid, @adminName, @warnReason, @duration, @ends, @created, @serverid);
|
||||
SELECT LAST_INSERT_ID();
|
||||
"""
|
||||
: """
|
||||
INSERT INTO `sa_warns`
|
||||
(`player_steamid`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `server_id`)
|
||||
VALUES
|
||||
(@playerSteamid, @adminSteamid, @adminName, @warnReason, @duration, @ends, @created, @serverid);
|
||||
SELECT LAST_INSERT_ID();
|
||||
""";
|
||||
|
||||
public string GetPlayerWarnsQuery(bool multiServer, bool active) =>
|
||||
multiServer
|
||||
? active
|
||||
? "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' ORDER BY id DESC"
|
||||
: "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID ORDER BY id DESC"
|
||||
: active
|
||||
? "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid AND status = 'ACTIVE' ORDER BY id DESC"
|
||||
: "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid ORDER BY id DESC";
|
||||
|
||||
public string GetPlayerWarnsCountQuery(bool multiServer, bool active) =>
|
||||
multiServer
|
||||
? active
|
||||
? "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE'"
|
||||
: "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID"
|
||||
: active
|
||||
? "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid AND status = 'ACTIVE'"
|
||||
: "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid";
|
||||
|
||||
public string GetUnwarnByIdQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND player_steamid = @steamid AND id = @warnId"
|
||||
: "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND player_steamid = @steamid AND id = @warnId AND server_id = @serverid";
|
||||
|
||||
public string GetUnwarnLastQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? """
|
||||
UPDATE sa_warns
|
||||
JOIN (
|
||||
SELECT MAX(id) AS max_id
|
||||
FROM sa_warns
|
||||
WHERE player_steamid = @steamid AND status = 'ACTIVE'
|
||||
) AS subquery ON sa_warns.id = subquery.max_id
|
||||
SET sa_warns.status = 'EXPIRED'
|
||||
WHERE sa_warns.status = 'ACTIVE' AND sa_warns.player_steamid = @steamid;
|
||||
"""
|
||||
: """
|
||||
UPDATE sa_warns
|
||||
JOIN (
|
||||
SELECT MAX(id) AS max_id
|
||||
FROM sa_warns
|
||||
WHERE player_steamid = @steamid AND status = 'ACTIVE' AND server_id = @serverid
|
||||
) AS subquery ON sa_warns.id = subquery.max_id
|
||||
SET sa_warns.status = 'EXPIRED'
|
||||
WHERE sa_warns.status = 'ACTIVE' AND sa_warns.player_steamid = @steamid AND sa_warns.server_id = @serverid;
|
||||
""";
|
||||
|
||||
public string GetExpireWarnsQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime"
|
||||
: "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime AND server_id = @serverid";
|
||||
|
||||
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
366
CS2-SimpleAdmin/Database/SqliteDatabaseProvider.cs
Normal file
366
CS2-SimpleAdmin/Database/SqliteDatabaseProvider.cs
Normal file
@@ -0,0 +1,366 @@
|
||||
using System.Data.Common;
|
||||
using System.Data.SQLite;
|
||||
|
||||
namespace CS2_SimpleAdmin.Database;
|
||||
|
||||
public class SqliteDatabaseProvider(string filePath) : IDatabaseProvider
|
||||
{
|
||||
private readonly string _connectionString = $"Data Source={filePath}";
|
||||
|
||||
public async Task<DbConnection> CreateConnectionAsync()
|
||||
{
|
||||
var conn = new SQLiteConnection(_connectionString);
|
||||
await conn.OpenAsync();
|
||||
return conn;
|
||||
}
|
||||
|
||||
public async Task<(bool Success, string? Exception)> CheckConnectionAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var conn = await CreateConnectionAsync();
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = "SELECT 1";
|
||||
await cmd.ExecuteScalarAsync();
|
||||
return (true, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (false, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public Task DatabaseMigrationAsync()
|
||||
{
|
||||
var migration = new Migration(CS2_SimpleAdmin.Instance.ModuleDirectory + "/Database/Migrations/Sqlite");
|
||||
return migration.ExecuteMigrationsAsync();
|
||||
}
|
||||
|
||||
public string GetBanSelectQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? """
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM sa_bans
|
||||
"""
|
||||
: """
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM sa_bans
|
||||
WHERE server_id = @serverId
|
||||
""";
|
||||
|
||||
public static string GetBanUpdatedSelectQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? """
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM sa_bans
|
||||
WHERE updated_at > @lastUpdate OR created > @lastUpdate
|
||||
ORDER BY updated_at DESC
|
||||
"""
|
||||
: """
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM sa_bans
|
||||
WHERE (updated_at > @lastUpdate OR created > @lastUpdate)
|
||||
AND server_id = @serverId
|
||||
ORDER BY updated_at DESC
|
||||
""";
|
||||
|
||||
public string GetIpHistoryQuery() =>
|
||||
"SELECT steamid, name, address, used_at FROM sa_players_ips ORDER BY used_at DESC";
|
||||
|
||||
public string GetBanUpdateQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? """
|
||||
UPDATE sa_bans
|
||||
SET player_ip = COALESCE(player_ip, @PlayerIP),
|
||||
player_name = COALESCE(player_name, @PlayerName)
|
||||
WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
AND status = 'ACTIVE'
|
||||
AND (duration = 0 OR ends > @CurrentTime)
|
||||
"""
|
||||
: """
|
||||
UPDATE sa_bans
|
||||
SET player_ip = COALESCE(player_ip, @PlayerIP),
|
||||
player_name = COALESCE(player_name, @PlayerName)
|
||||
WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
AND status = 'ACTIVE'
|
||||
AND (duration = 0 OR ends > @CurrentTime)
|
||||
AND server_id = @ServerId
|
||||
""";
|
||||
|
||||
public string GetAddBanQuery() =>
|
||||
"""
|
||||
INSERT INTO sa_bans
|
||||
(player_steamid, player_name, player_ip, admin_steamid, admin_name, reason, duration, ends, created, server_id)
|
||||
VALUES
|
||||
(@playerSteamid, @playerName, @playerIp, @adminSteamid, @adminName, @banReason, @duration, @ends, @created, @serverid);
|
||||
SELECT last_insert_rowid();
|
||||
""";
|
||||
|
||||
public string GetAddBanBySteamIdQuery() =>
|
||||
"""
|
||||
INSERT INTO sa_bans
|
||||
(player_steamid, admin_steamid, admin_name, reason, duration, ends, created, server_id)
|
||||
VALUES
|
||||
(@playerSteamid, @adminSteamid, @adminName, @banReason, @duration, @ends, @created, @serverid);
|
||||
SELECT last_insert_rowid();
|
||||
""";
|
||||
|
||||
public string GetAddBanByIpQuery() =>
|
||||
"""
|
||||
INSERT INTO sa_bans
|
||||
(player_ip, admin_steamid, admin_name, reason, duration, ends, created, server_id)
|
||||
VALUES
|
||||
(@playerIp, @adminSteamid, @adminName, @banReason, @duration, @ends, @created, @serverid);
|
||||
SELECT last_insert_rowid();
|
||||
""";
|
||||
|
||||
public string GetUnbanRetrieveBansQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "SELECT id FROM sa_bans WHERE (player_steamid = @pattern OR player_name = @pattern OR player_ip = @pattern) AND status = 'ACTIVE'"
|
||||
: "SELECT id FROM sa_bans WHERE (player_steamid = @pattern OR player_name = @pattern OR player_ip = @pattern) AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
|
||||
public string GetUnbanAdminIdQuery() =>
|
||||
"SELECT id FROM sa_admins WHERE player_steamid = @adminSteamId";
|
||||
|
||||
public string GetInsertUnbanQuery(bool includeReason) =>
|
||||
includeReason
|
||||
? "INSERT INTO sa_unbans (ban_id, admin_id, reason) VALUES (@banId, @adminId, @reason); SELECT last_insert_rowid();"
|
||||
: "INSERT INTO sa_unbans (ban_id, admin_id) VALUES (@banId, @adminId); SELECT last_insert_rowid();";
|
||||
|
||||
public string GetUpdateBanStatusQuery() =>
|
||||
"UPDATE sa_bans SET status = 'UNBANNED', unban_id = @unbanId WHERE id = @banId";
|
||||
|
||||
public string GetExpireBansQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "UPDATE sa_bans SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @currentTime"
|
||||
: "UPDATE sa_bans SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @currentTime AND server_id = @serverid";
|
||||
|
||||
public string GetExpireIpBansQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "UPDATE sa_bans SET player_ip = NULL WHERE status = 'ACTIVE' AND ends <= @ipBansTime"
|
||||
: "UPDATE sa_bans SET player_ip = NULL WHERE status = 'ACTIVE' AND ends <= @ipBansTime AND server_id = @serverid";
|
||||
|
||||
public string GetAdminsQuery() =>
|
||||
"""
|
||||
SELECT sa_admins.player_steamid, sa_admins.player_name, sa_admins_flags.flag, sa_admins.immunity, sa_admins.ends
|
||||
FROM sa_admins_flags
|
||||
JOIN sa_admins ON sa_admins_flags.admin_id = sa_admins.id
|
||||
WHERE (sa_admins.ends IS NULL OR sa_admins.ends > @CurrentTime)
|
||||
AND (sa_admins.server_id IS NULL OR sa_admins.server_id = @serverid)
|
||||
ORDER BY sa_admins.player_steamid
|
||||
""";
|
||||
|
||||
public string GetDeleteAdminQuery(bool globalDelete) =>
|
||||
globalDelete
|
||||
? "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID"
|
||||
: "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID AND server_id = @ServerId";
|
||||
|
||||
public string GetAddAdminQuery() =>
|
||||
"""
|
||||
INSERT INTO sa_admins (player_steamid, player_name, immunity, ends, created, server_id)
|
||||
VALUES (@playerSteamId, @playerName, @immunity, @ends, @created, @serverid);
|
||||
SELECT last_insert_rowid();
|
||||
""";
|
||||
|
||||
public string GetGroupsQuery() =>
|
||||
"""
|
||||
SELECT g.group_id, sg.name AS group_name, sg.immunity, f.flag
|
||||
FROM sa_groups_flags f
|
||||
JOIN sa_groups_servers g ON f.group_id = g.group_id
|
||||
JOIN sa_groups sg ON sg.id = g.group_id
|
||||
WHERE (g.server_id = @serverid OR server_id IS NULL)
|
||||
""";
|
||||
|
||||
public string GetAddAdminFlagsQuery() =>
|
||||
"INSERT INTO sa_admins_flags (admin_id, flag) VALUES (@adminId, @flag);";
|
||||
|
||||
public string GetUpdateAdminGroupQuery() =>
|
||||
"UPDATE sa_admins SET group_id = @groupId WHERE id = @adminId;";
|
||||
|
||||
public string GetAddGroupQuery() =>
|
||||
"""
|
||||
INSERT INTO sa_groups (name, immunity) VALUES (@groupName, @immunity);
|
||||
SELECT last_insert_rowid();
|
||||
""";
|
||||
|
||||
public string GetGroupIdByNameQuery() =>
|
||||
"""
|
||||
SELECT sgs.group_id
|
||||
FROM sa_groups_servers sgs
|
||||
JOIN sa_groups sg ON sgs.group_id = sg.id
|
||||
WHERE sg.name = @groupName
|
||||
ORDER BY (sgs.server_id = @serverId) DESC, sgs.server_id ASC
|
||||
LIMIT 1;
|
||||
""";
|
||||
|
||||
public string GetAddGroupFlagsQuery() =>
|
||||
"INSERT INTO sa_groups_flags (group_id, flag) VALUES (@groupId, @flag);";
|
||||
|
||||
public string GetAddGroupServerQuery() =>
|
||||
"INSERT INTO sa_groups_servers (group_id, server_id) VALUES (@groupId, @server_id);";
|
||||
|
||||
public string GetDeleteGroupQuery() =>
|
||||
"DELETE FROM sa_groups WHERE name = @groupName;";
|
||||
|
||||
public string GetDeleteOldAdminsQuery() =>
|
||||
"DELETE FROM sa_admins WHERE ends IS NOT NULL AND ends <= @CurrentTime;";
|
||||
|
||||
public string GetAddMuteQuery(bool includePlayerName) =>
|
||||
includePlayerName
|
||||
? """
|
||||
INSERT INTO sa_mutes
|
||||
(player_steamid, player_name, admin_steamid, admin_name, reason, duration, ends, created, type, server_id)
|
||||
VALUES (@playerSteamid, @playerName, @adminSteamid, @adminName, @muteReason, @duration, @ends, @created, @type, @serverid);
|
||||
SELECT last_insert_rowid();
|
||||
"""
|
||||
: """
|
||||
INSERT INTO sa_mutes
|
||||
(player_steamid, admin_steamid, admin_name, reason, duration, ends, created, type, server_id)
|
||||
VALUES (@playerSteamid, @adminSteamid, @adminName, @muteReason, @duration, @ends, @created, @type, @serverid);
|
||||
SELECT last_insert_rowid();
|
||||
""";
|
||||
|
||||
public string GetIsMutedQuery(bool multiServer, int timeMode) =>
|
||||
multiServer
|
||||
? (timeMode == 1
|
||||
? "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR ends > @CurrentTime)"
|
||||
: "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR duration > COALESCE(passed, 0))")
|
||||
: (timeMode == 1
|
||||
? "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR ends > @CurrentTime) AND server_id = @serverid"
|
||||
: "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR duration > COALESCE(passed, 0)) AND server_id = @serverid");
|
||||
|
||||
public string GetMuteStatsQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? """
|
||||
SELECT
|
||||
COUNT(CASE WHEN type = 'MUTE' THEN 1 END) AS TotalMutes,
|
||||
COUNT(CASE WHEN type = 'GAG' THEN 1 END) AS TotalGags,
|
||||
COUNT(CASE WHEN type = 'SILENCE' THEN 1 END) AS TotalSilences
|
||||
FROM sa_mutes
|
||||
WHERE player_steamid = @PlayerSteamID;
|
||||
"""
|
||||
: """
|
||||
SELECT
|
||||
COUNT(CASE WHEN type = 'MUTE' THEN 1 END) AS TotalMutes,
|
||||
COUNT(CASE WHEN type = 'GAG' THEN 1 END) AS TotalGags,
|
||||
COUNT(CASE WHEN type = 'SILENCE' THEN 1 END) AS TotalSilences
|
||||
FROM sa_mutes
|
||||
WHERE player_steamid = @PlayerSteamID AND server_id = @ServerId;
|
||||
""";
|
||||
|
||||
public string GetUpdateMutePassedQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "UPDATE sa_mutes SET passed = COALESCE(passed, 0) + 1 WHERE (player_steamid = @PlayerSteamID) AND duration > 0 AND status = 'ACTIVE'"
|
||||
: "UPDATE sa_mutes SET passed = COALESCE(passed, 0) + 1 WHERE (player_steamid = @PlayerSteamID) AND duration > 0 AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
|
||||
public string GetCheckExpiredMutesQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND passed >= duration AND duration > 0 AND status = 'ACTIVE'"
|
||||
: "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND passed >= duration AND duration > 0 AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
|
||||
public string GetRetrieveMutesQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "SELECT id FROM sa_mutes WHERE (player_steamid = @pattern OR player_name = @pattern) AND type = @muteType AND status = 'ACTIVE'"
|
||||
: "SELECT id FROM sa_mutes WHERE (player_steamid = @pattern OR player_name = @pattern) AND type = @muteType AND status = 'ACTIVE' AND server_id = @serverid";
|
||||
|
||||
public string GetUnmuteAdminIdQuery() =>
|
||||
"SELECT id FROM sa_admins WHERE player_steamid = @adminSteamId";
|
||||
|
||||
public string GetInsertUnmuteQuery(bool includeReason) =>
|
||||
includeReason
|
||||
? "INSERT INTO sa_unmutes (mute_id, admin_id, reason) VALUES (@muteId, @adminId, @reason); SELECT last_insert_rowid();"
|
||||
: "INSERT INTO sa_unmutes (mute_id, admin_id) VALUES (@muteId, @adminId); SELECT last_insert_rowid();";
|
||||
|
||||
public string GetUpdateMuteStatusQuery() =>
|
||||
"UPDATE sa_mutes SET status = 'UNMUTED', unmute_id = @unmuteId WHERE id = @muteId";
|
||||
|
||||
public string GetExpireMutesQuery(bool multiServer, int timeMode) =>
|
||||
multiServer
|
||||
? (timeMode == 1
|
||||
? "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @CurrentTime"
|
||||
: "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND passed >= duration")
|
||||
: (timeMode == 1
|
||||
? "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @CurrentTime AND server_id = @serverid"
|
||||
: "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND passed >= duration AND server_id = @serverid");
|
||||
|
||||
public string GetAddWarnQuery(bool includePlayerName) =>
|
||||
includePlayerName
|
||||
? """
|
||||
INSERT INTO sa_warns
|
||||
(player_steamid, player_name, admin_steamid, admin_name, reason, duration, ends, created, server_id)
|
||||
VALUES
|
||||
(@playerSteamid, @playerName, @adminSteamid, @adminName, @warnReason, @duration, @ends, @created, @serverid);
|
||||
SELECT last_insert_rowid();
|
||||
"""
|
||||
: """
|
||||
INSERT INTO sa_warns
|
||||
(player_steamid, admin_steamid, admin_name, reason, duration, ends, created, server_id)
|
||||
VALUES
|
||||
(@playerSteamid, @adminSteamid, @adminName, @warnReason, @duration, @ends, @created, @serverid);
|
||||
SELECT last_insert_rowid();
|
||||
""";
|
||||
|
||||
public string GetPlayerWarnsQuery(bool multiServer, bool active) =>
|
||||
multiServer
|
||||
? active
|
||||
? "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' ORDER BY id DESC"
|
||||
: "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID ORDER BY id DESC"
|
||||
: active
|
||||
? "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid AND status = 'ACTIVE' ORDER BY id DESC"
|
||||
: "SELECT * FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid ORDER BY id DESC";
|
||||
|
||||
public string GetPlayerWarnsCountQuery(bool multiServer, bool active) =>
|
||||
multiServer
|
||||
? active
|
||||
? "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE'"
|
||||
: "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID"
|
||||
: active
|
||||
? "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid AND status = 'ACTIVE'"
|
||||
: "SELECT COUNT(*) FROM sa_warns WHERE player_steamid = @PlayerSteamID AND server_id = @serverid";
|
||||
|
||||
public string GetUnwarnByIdQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND player_steamid = @steamid AND id = @warnId"
|
||||
: "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND player_steamid = @steamid AND id = @warnId AND server_id = @serverid";
|
||||
|
||||
public string GetUnwarnLastQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? """
|
||||
UPDATE sa_warns
|
||||
SET status = 'EXPIRED'
|
||||
WHERE status = 'ACTIVE'
|
||||
AND player_steamid = @steamid
|
||||
ORDER BY id DESC
|
||||
LIMIT 1
|
||||
"""
|
||||
: """
|
||||
UPDATE sa_warns
|
||||
SET status = 'EXPIRED'
|
||||
WHERE status = 'ACTIVE'
|
||||
AND player_steamid = @steamid
|
||||
AND server_id = @serverid
|
||||
ORDER BY id DESC
|
||||
LIMIT 1
|
||||
""";
|
||||
|
||||
public string GetExpireWarnsQuery(bool multiServer) =>
|
||||
multiServer
|
||||
? "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @CurrentTime"
|
||||
: "UPDATE sa_warns SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND duration > 0 AND ends <= @CurrentTime AND server_id = @serverid";
|
||||
}
|
||||
560
CS2-SimpleAdmin/Events.cs
Normal file
560
CS2-SimpleAdmin/Events.cs
Normal file
@@ -0,0 +1,560 @@
|
||||
using System.Numerics;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes.Registration;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CS2_SimpleAdmin.Managers;
|
||||
using CS2_SimpleAdmin.Models;
|
||||
using CS2_SimpleAdminApi;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Text;
|
||||
using CounterStrikeSharp.API.Core.Translations;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.UserMessages;
|
||||
|
||||
namespace CS2_SimpleAdmin;
|
||||
|
||||
public partial class CS2_SimpleAdmin
|
||||
{
|
||||
private bool _serverLoading;
|
||||
|
||||
private void RegisterEvents()
|
||||
{
|
||||
RegisterListener<Listeners.OnMapStart>(OnMapStart);
|
||||
// RegisterListener<Listeners.OnClientConnect>(OnClientConnect);
|
||||
RegisterListener<Listeners.OnClientConnect>(OnClientConnect);
|
||||
RegisterListener<Listeners.OnClientConnected>(OnClientConnected);
|
||||
RegisterListener<Listeners.OnGameServerSteamAPIActivated>(OnGameServerSteamAPIActivated);
|
||||
if (Config.OtherSettings.UserMessageGagChatType)
|
||||
HookUserMessage(118, HookUmChat);
|
||||
|
||||
AddCommandListener(null, ComamndListenerHandler);
|
||||
// AddCommandListener("callvote", OnCommandCallVote);
|
||||
// AddCommandListener("say", OnCommandSay);
|
||||
// AddCommandListener("say_team", OnCommandTeamSay);
|
||||
}
|
||||
|
||||
private void UnregisterEvents()
|
||||
{
|
||||
RemoveListener<Listeners.OnMapStart>(OnMapStart);
|
||||
RemoveListener<Listeners.OnClientConnect>(OnClientConnect);
|
||||
RemoveListener<Listeners.OnClientConnected>(OnClientConnected);
|
||||
RemoveListener<Listeners.OnGameServerSteamAPIActivated>(OnGameServerSteamAPIActivated);
|
||||
if (Config.OtherSettings.UserMessageGagChatType)
|
||||
UnhookUserMessage(118, HookUmChat);
|
||||
|
||||
RemoveCommandListener(null!, ComamndListenerHandler, HookMode.Pre);
|
||||
// AddCommandListener("callvote", OnCommandCallVote);
|
||||
// AddCommandListener("say", OnCommandSay);
|
||||
// AddCommandListener("say_team", OnCommandTeamSay);
|
||||
}
|
||||
|
||||
|
||||
// private HookResult OnCommandCallVote(CCSPlayerController? caller, CommandInfo info)
|
||||
// {
|
||||
// var voteType = info.GetArg(1).ToLower();
|
||||
//
|
||||
// if (voteType != "kick")
|
||||
// return HookResult.Continue;
|
||||
//
|
||||
// var target = int.TryParse(info.GetArg(2), out var userId)
|
||||
// ? Utilities.GetPlayerFromUserid(userId)
|
||||
// : null;
|
||||
//
|
||||
// if (target == null || !target.IsValid || target.Connected != PlayerConnectedState.PlayerConnected)
|
||||
// return HookResult.Continue;
|
||||
//
|
||||
// return !AdminManager.CanPlayerTarget(caller, target) ? HookResult.Stop : HookResult.Continue;
|
||||
// }
|
||||
|
||||
private void OnGameServerSteamAPIActivated()
|
||||
{
|
||||
if (ServerLoaded || _serverLoading)
|
||||
return;
|
||||
|
||||
_serverLoading = true;
|
||||
new ServerManager().LoadServerData();
|
||||
}
|
||||
|
||||
[GameEventHandler(HookMode.Pre)]
|
||||
public HookResult OnClientDisconnect(EventPlayerDisconnect @event, GameEventInfo info)
|
||||
{
|
||||
if (@event.Reason is 149 or 6)
|
||||
info.DontBroadcast = true;
|
||||
|
||||
var player = @event.Userid;
|
||||
|
||||
#if DEBUG
|
||||
Logger.LogCritical("[OnClientDisconnect] Before");
|
||||
#endif
|
||||
|
||||
if (player == null || !player.IsValid || player.IsHLTV)
|
||||
return HookResult.Continue;
|
||||
|
||||
BotPlayers.Remove(player);
|
||||
CachedPlayers.Remove(player);
|
||||
|
||||
SilentPlayers.Remove(player.Slot);
|
||||
GodPlayers.Remove(player.Slot);
|
||||
SpeedPlayers.Remove(player);
|
||||
GravityPlayers.Remove(player);
|
||||
|
||||
if (player.IsBot)
|
||||
return HookResult.Continue;
|
||||
|
||||
#if DEBUG
|
||||
Logger.LogCritical("[OnClientDisconnect] After Check");
|
||||
#endif
|
||||
try
|
||||
{
|
||||
if (DisconnectedPlayers.Count >= Config.OtherSettings.DisconnectedPlayersHistoryCount)
|
||||
DisconnectedPlayers.RemoveAt(0);
|
||||
|
||||
var steamId = new SteamID(player.SteamID);
|
||||
var disconnectedPlayer = DisconnectedPlayers.FirstOrDefault(p => p.SteamId == steamId);
|
||||
|
||||
if (disconnectedPlayer != null)
|
||||
{
|
||||
disconnectedPlayer.Name = player.PlayerName;
|
||||
disconnectedPlayer.IpAddress = player.IpAddress?.Split(":")[0];
|
||||
disconnectedPlayer.DisconnectTime = Time.ActualDateTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
DisconnectedPlayers.Add(new DisconnectedPlayer(steamId, player.PlayerName,
|
||||
player.IpAddress?.Split(":")[0], Time.ActualDateTime()));
|
||||
}
|
||||
|
||||
PlayerPenaltyManager.RemoveAllPenalties(player.Slot);
|
||||
|
||||
if (player.UserId.HasValue)
|
||||
PlayersInfo.TryRemove(player.SteamID, out _);
|
||||
|
||||
if (!PermissionManager.AdminCache.TryGetValue(steamId, out var data)
|
||||
|| !(data.ExpirationTime <= Time.ActualDateTime()))
|
||||
{
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
AdminManager.RemovePlayerPermissions(steamId, PermissionManager.AdminCache[steamId].Flags.ToArray());
|
||||
AdminManager.RemovePlayerFromGroup(steamId, true, PermissionManager.AdminCache[steamId].Flags.ToArray());
|
||||
var adminData = AdminManager.GetPlayerAdminData(steamId);
|
||||
|
||||
if (adminData == null || data.Flags.ToList().Count != 0 && adminData.Groups.ToList().Count != 0)
|
||||
return HookResult.Continue;
|
||||
|
||||
AdminManager.ClearPlayerPermissions(steamId);
|
||||
AdminManager.RemovePlayerAdminData(steamId);
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"An error occurred in OnClientDisconnect: {ex.Message}");
|
||||
return HookResult.Continue;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClientConnect(int playerslot, string name, string ipAddress)
|
||||
{
|
||||
#if DEBUG
|
||||
Logger.LogCritical("[OnClientConnect]");
|
||||
#endif
|
||||
|
||||
var player = Utilities.GetPlayerFromSlot(playerslot);
|
||||
if (player == null || !player.IsValid || player.IsBot)
|
||||
return;
|
||||
|
||||
PlayerManager.LoadPlayerData(player);
|
||||
}
|
||||
|
||||
private void OnClientConnected(int playerslot)
|
||||
{
|
||||
#if DEBUG
|
||||
Logger.LogCritical("[OnClientConnected]");
|
||||
#endif
|
||||
|
||||
var player = Utilities.GetPlayerFromSlot(playerslot);
|
||||
if (player == null || !player.IsValid || player.IsBot)
|
||||
return;
|
||||
|
||||
PlayerManager.LoadPlayerData(player);
|
||||
}
|
||||
|
||||
// private void OnClientConnect(int playerslot, string name, string ipaddress)
|
||||
// {
|
||||
// #if DEBUG
|
||||
// Logger.LogCritical("[OnClientConnect]");
|
||||
// #endif
|
||||
// if (Config.OtherSettings.BanType == 0)
|
||||
// return;
|
||||
//
|
||||
// if (Instance.CacheManager != null && !Instance.CacheManager.IsPlayerBanned(null, ipaddress.Split(":")[0]))
|
||||
// return;
|
||||
//
|
||||
// var testPlayer = Utilities.GetPlayerFromSlot(playerslot);
|
||||
// if (testPlayer == null)
|
||||
// return;
|
||||
// Logger.LogInformation($"Gracz {testPlayer.PlayerName} ({testPlayer.SteamID.ToString()}) Czas: {DateTime.Now}");
|
||||
//
|
||||
// Server.NextFrame((() =>
|
||||
// {
|
||||
// var player = Utilities.GetPlayerFromSlot(playerslot);
|
||||
// if (player == null || !player.IsValid || player.IsBot)
|
||||
// return;
|
||||
//
|
||||
// Helper.KickPlayer(player, NetworkDisconnectionReason.NETWORK_DISCONNECT_REJECT_BANNED);
|
||||
// }));
|
||||
//
|
||||
// // Server.NextFrame(() =>
|
||||
// // {
|
||||
// // var player = Utilities.GetPlayerFromSlot(playerslot);
|
||||
// //
|
||||
// // if (player == null || !player.IsValid || player.IsBot)
|
||||
// // return;
|
||||
// //
|
||||
// // new PlayerManager().LoadPlayerData(player);
|
||||
// // });
|
||||
// }
|
||||
|
||||
[GameEventHandler]
|
||||
public HookResult OnPlayerFullConnect(EventPlayerConnectFull @event, GameEventInfo info)
|
||||
{
|
||||
#if DEBUG
|
||||
Logger.LogCritical("[OnPlayerFullConnect]");
|
||||
#endif
|
||||
|
||||
var player = @event.Userid;
|
||||
|
||||
if (player == null || !player.IsValid)
|
||||
return HookResult.Continue;
|
||||
|
||||
if (player is { IsBot: true, IsHLTV: false })
|
||||
{
|
||||
BotPlayers.Add(player);
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
PlayerManager.LoadPlayerData(player, true);
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
[GameEventHandler]
|
||||
public HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info)
|
||||
{
|
||||
#if DEBUG
|
||||
Logger.LogCritical("[OnRoundStart]");
|
||||
#endif
|
||||
|
||||
GodPlayers.Clear();
|
||||
SpeedPlayers.Clear();
|
||||
GravityPlayers.Clear();
|
||||
|
||||
foreach (var player in PlayersInfo.Values)
|
||||
{
|
||||
player.DiePosition = null;
|
||||
}
|
||||
|
||||
AddTimer(0.41f, () =>
|
||||
{
|
||||
foreach (var list in RenamedPlayers)
|
||||
{
|
||||
var player = Utilities.GetPlayerFromSteamId(list.Key);
|
||||
|
||||
if (player == null || !player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected)
|
||||
continue;
|
||||
|
||||
if (player.PlayerName.Equals(list.Value))
|
||||
continue;
|
||||
|
||||
player.Rename(list.Value);
|
||||
}
|
||||
});
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
private HookResult HookUmChat(UserMessage um)
|
||||
{
|
||||
var author = Utilities.GetPlayerFromIndex(um.ReadInt("entityindex"));
|
||||
if (author == null || !author.IsValid || author.IsBot)
|
||||
return HookResult.Continue;
|
||||
|
||||
if (!PlayerPenaltyManager.IsPenalized(author.Slot, PenaltyType.Gag, out DateTime? endDateTime) &&
|
||||
!PlayerPenaltyManager.IsPenalized(author.Slot, PenaltyType.Silence, out endDateTime))
|
||||
return HookResult.Continue;
|
||||
|
||||
if (_localizer == null || endDateTime == null)
|
||||
return HookResult.Continue;
|
||||
|
||||
var message = um.ReadString("param2");
|
||||
var triggers = CoreConfig.PublicChatTrigger.Concat(CoreConfig.SilentChatTrigger);
|
||||
if (!triggers.Any(trigger => message.StartsWith(trigger))) return HookResult.Stop;
|
||||
|
||||
for (var i = um.Recipients.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (um.Recipients[i] != author)
|
||||
{
|
||||
um.Recipients.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
return HookResult.Continue;
|
||||
|
||||
// author.SendLocalizedMessage(_localizer, "sa_player_penalty_chat_active", endDateTime.Value.ToString("g", author.GetLanguage()));
|
||||
}
|
||||
|
||||
private HookResult ComamndListenerHandler(CCSPlayerController? player, CommandInfo info)
|
||||
{
|
||||
if (player == null || !player.IsValid || player.IsBot)
|
||||
return HookResult.Continue;
|
||||
|
||||
var command = info.GetArg(0).ToLower();
|
||||
|
||||
if (Config.OtherSettings.AdditionalCommandsToLog.Contains(command))
|
||||
Helper.LogCommand(player, info);
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case "css_admins_reload":
|
||||
AddTimer(1.0f, () => ReloadAdmins(null));
|
||||
return HookResult.Continue;
|
||||
case "callvote":
|
||||
{
|
||||
var voteType = info.GetArg(1).ToLower();
|
||||
|
||||
if (voteType != "kick")
|
||||
return HookResult.Continue;
|
||||
|
||||
var target = int.TryParse(info.GetArg(2), out var userId)
|
||||
? Utilities.GetPlayerFromUserid(userId)
|
||||
: null;
|
||||
|
||||
if (target == null || !target.IsValid || target.Connected != PlayerConnectedState.PlayerConnected)
|
||||
return HookResult.Continue;
|
||||
|
||||
return !player.CanTarget(target) ? HookResult.Stop : HookResult.Continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!command.Contains("say"))
|
||||
return HookResult.Continue;
|
||||
|
||||
if (info.GetArg(1).Length == 0)
|
||||
return HookResult.Stop;
|
||||
|
||||
var triggers = CoreConfig.PublicChatTrigger.Concat(CoreConfig.SilentChatTrigger);
|
||||
if (triggers.Any(trigger => info.GetArg(1).StartsWith(trigger)))
|
||||
{
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
// if (!Config.OtherSettings.UserMessageGagChatType)
|
||||
// {
|
||||
if (PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag, out DateTime? endDateTime) ||
|
||||
PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence, out endDateTime))
|
||||
{
|
||||
if (_localizer != null && endDateTime is not null)
|
||||
player.SendLocalizedMessage(_localizer, "sa_player_penalty_chat_active", endDateTime.Value.ToString("g", player.GetLanguage()));
|
||||
return HookResult.Stop;
|
||||
}
|
||||
// }
|
||||
|
||||
if (AdminManager.PlayerHasPermissions(new SteamID(player.SteamID), "@css/chat") && command == "say" && info.GetArg(1).StartsWith($"@"))
|
||||
{
|
||||
player.ExecuteClientCommandFromServer($"css_say {info.GetArg(1).Remove(0, 1)}");
|
||||
return HookResult.Stop;
|
||||
}
|
||||
|
||||
if (command != "say_team" || !info.GetArg(1).StartsWith($"@")) return HookResult.Continue;
|
||||
|
||||
StringBuilder sb = new();
|
||||
if (AdminManager.PlayerHasPermissions(new SteamID(player.SteamID), "@css/chat"))
|
||||
{
|
||||
sb.Append(_localizer!["sa_adminchat_template_admin", player.PlayerName, info.GetArg(1).Remove(0, 1)]);
|
||||
foreach (var p in Utilities.GetPlayers().Where(p => p.IsValid && p is { IsBot: false, IsHLTV: false } && AdminManager.PlayerHasPermissions(new SteamID(p.SteamID), "@css/chat")))
|
||||
{
|
||||
p.PrintToChat(sb.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(_localizer!["sa_adminchat_template_player", player.PlayerName, info.GetArg(1).Remove(0, 1)]);
|
||||
player.PrintToChat(sb.ToString());
|
||||
foreach (var p in Utilities.GetPlayers().Where(p => p is { IsValid: true, IsBot: false, IsHLTV: false } && AdminManager.PlayerHasPermissions(new SteamID(p.SteamID), "@css/chat")))
|
||||
{
|
||||
p.PrintToChat(sb.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
return HookResult.Stop;
|
||||
}
|
||||
|
||||
/*public HookResult OnCommandSay(CCSPlayerController? player, CommandInfo info)
|
||||
{
|
||||
if (player == null || !player.IsValid || player.IsBot)
|
||||
return HookResult.Continue;
|
||||
|
||||
if (info.GetArg(1).StartsWith($"/")
|
||||
|| info.GetArg(1).StartsWith($"!"))
|
||||
return HookResult.Continue;
|
||||
|
||||
if (info.GetArg(1).Length == 0)
|
||||
return HookResult.Handled;
|
||||
|
||||
if (PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag) || PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence))
|
||||
return HookResult.Handled;
|
||||
|
||||
return HookResult.Continue;
|
||||
}*/
|
||||
|
||||
public HookResult OnCommandTeamSay(CCSPlayerController? player, CommandInfo info)
|
||||
{
|
||||
if (player == null || !player.IsValid || player.IsBot)
|
||||
return HookResult.Continue;
|
||||
|
||||
if (info.GetArg(1).StartsWith($"/")
|
||||
|| info.GetArg(1).StartsWith($"!"))
|
||||
return HookResult.Continue;
|
||||
|
||||
if (info.GetArg(1).Length == 0)
|
||||
return HookResult.Handled;
|
||||
|
||||
if (PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag, out _) || PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence, out _))
|
||||
return HookResult.Stop;
|
||||
|
||||
if (!info.GetArg(1).StartsWith($"@")) return HookResult.Continue;
|
||||
|
||||
StringBuilder sb = new();
|
||||
|
||||
if (AdminManager.PlayerHasPermissions(new SteamID(player.SteamID), "@css/chat"))
|
||||
{
|
||||
sb.Append(_localizer!["sa_adminchat_template_admin", player.PlayerName, info.GetArg(1).Remove(0, 1)]);
|
||||
foreach (var p in Utilities.GetPlayers().Where(p => p.IsValid && p is { IsBot: false, IsHLTV: false } && AdminManager.PlayerHasPermissions(new SteamID(p.SteamID), "@css/chat")))
|
||||
{
|
||||
p.PrintToChat(sb.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(_localizer!["sa_adminchat_template_player", player.PlayerName, info.GetArg(1).Remove(0, 1)]);
|
||||
player.PrintToChat(sb.ToString());
|
||||
foreach (var p in Utilities.GetPlayers().Where(p => p is { IsValid: true, IsBot: false, IsHLTV: false } && AdminManager.PlayerHasPermissions(new SteamID(p.SteamID), "@css/chat")))
|
||||
{
|
||||
p.PrintToChat(sb.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
return HookResult.Handled;
|
||||
}
|
||||
|
||||
private void OnMapStart(string mapName)
|
||||
{
|
||||
if (!ServerLoaded || ServerId == null)
|
||||
AddTimer(2.0f, OnGameServerSteamAPIActivated);
|
||||
|
||||
if (Config.OtherSettings.ReloadAdminsEveryMapChange && ServerLoaded && ServerId != null)
|
||||
AddTimer(5.0f, () => ReloadAdmins(null));
|
||||
|
||||
AddTimer(1.0f, ServerManager.CheckHibernationStatus);
|
||||
|
||||
// AddTimer(34, () =>
|
||||
// {
|
||||
// if (!ServerLoaded)
|
||||
// OnGameServerSteamAPIActivated();
|
||||
// });
|
||||
|
||||
GodPlayers.Clear();
|
||||
SilentPlayers.Clear();
|
||||
SpeedPlayers.Clear();
|
||||
GravityPlayers.Clear();
|
||||
|
||||
PlayerPenaltyManager.RemoveAllPenalties();
|
||||
}
|
||||
|
||||
[GameEventHandler]
|
||||
public HookResult OnPlayerHurt(EventPlayerHurt @event, GameEventInfo info)
|
||||
{
|
||||
var player = @event.Userid;
|
||||
|
||||
if (player is null || @event.Attacker is null || player.PlayerPawn?.Value?.LifeState != (int)LifeState_t.LIFE_ALIVE || player.PlayerPawn.Value == null)
|
||||
return HookResult.Continue;
|
||||
|
||||
if (!GodPlayers.Contains(player.Slot)) return HookResult.Continue;
|
||||
|
||||
player.PlayerPawn.Value.Health = player.PlayerPawn.Value.MaxHealth;
|
||||
player.PlayerPawn.Value.ArmorValue = 100;
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
[GameEventHandler]
|
||||
public HookResult OnPlayerDeath(EventPlayerDeath @event, GameEventInfo info)
|
||||
{
|
||||
var player = @event.Userid;
|
||||
|
||||
if (player?.UserId == null || !player.IsValid || player.IsHLTV || player.Connected != PlayerConnectedState.PlayerConnected)
|
||||
return HookResult.Continue;
|
||||
|
||||
SpeedPlayers.Remove(player);
|
||||
GravityPlayers.Remove(player);
|
||||
|
||||
if (!PlayersInfo.ContainsKey(player.SteamID) || @event.Attacker == null)
|
||||
return HookResult.Continue;
|
||||
|
||||
var playerPosition = player.PlayerPawn.Value?.AbsOrigin;
|
||||
var playerRotation = player.PlayerPawn.Value?.AbsRotation;
|
||||
PlayersInfo[player.SteamID].DiePosition = new DiePosition(
|
||||
new Vector3(
|
||||
playerPosition?.X ?? 0,
|
||||
playerPosition?.Y ?? 0,
|
||||
playerPosition?.Z ?? 0
|
||||
),
|
||||
new Vector3(
|
||||
playerRotation?.X ?? 0,
|
||||
playerRotation?.Y ?? 0,
|
||||
playerRotation?.Z ?? 0
|
||||
)
|
||||
);
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
[GameEventHandler(HookMode.Pre)]
|
||||
public HookResult OnPlayerTeam(EventPlayerTeam @event, GameEventInfo info)
|
||||
{
|
||||
var player = @event.Userid;
|
||||
if (player == null || !player.IsValid || player.IsBot)
|
||||
return HookResult.Continue;
|
||||
|
||||
if (!SilentPlayers.Contains(player.Slot))
|
||||
return HookResult.Continue;
|
||||
|
||||
if (@event is { Oldteam: <= 1, Team: >= 1 })
|
||||
{
|
||||
SilentPlayers.Remove(player.Slot);
|
||||
SimpleAdminApi?.OnAdminToggleSilentEvent(player.Slot, false);
|
||||
}
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
[GameEventHandler]
|
||||
public HookResult OnPlayerInfo(EventPlayerInfo @event, GameEventInfo _)
|
||||
{
|
||||
var player = @event.Userid;
|
||||
|
||||
if (player is null || !player.IsValid || player.IsBot)
|
||||
return HookResult.Continue;
|
||||
|
||||
if (!RenamedPlayers.TryGetValue(player.SteamID, out var name)) return HookResult.Continue;
|
||||
|
||||
if (player.PlayerName.Equals(name))
|
||||
return HookResult.Continue;
|
||||
|
||||
player.Rename(name);
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
}
|
||||
12
CS2-SimpleAdmin/Extensions/EnumerableExtensions.cs
Normal file
12
CS2-SimpleAdmin/Extensions/EnumerableExtensions.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace CS2_SimpleAdmin;
|
||||
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
public static IEnumerable<IEnumerable<T>> ChunkBy<T>(this IEnumerable<T> source, int chunkSize)
|
||||
{
|
||||
return source
|
||||
.Select((x, i) => new { Index = i, Value = x })
|
||||
.GroupBy(x => x.Index / chunkSize)
|
||||
.Select(x => x.Select(v => v.Value));
|
||||
}
|
||||
}
|
||||
376
CS2-SimpleAdmin/Extensions/PlayerExtensions.cs
Normal file
376
CS2-SimpleAdmin/Extensions/PlayerExtensions.cs
Normal file
@@ -0,0 +1,376 @@
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Translations;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using System.Text;
|
||||
using CounterStrikeSharp.API.Modules.UserMessages;
|
||||
using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
|
||||
|
||||
namespace CS2_SimpleAdmin;
|
||||
|
||||
public static class PlayerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Slaps the player pawn by applying optional damage and adding a random velocity knockback.
|
||||
/// </summary>
|
||||
/// <param name="pawn">The player pawn to slap.</param>
|
||||
/// <param name="damage">The amount of damage to apply (default is 0).</param>
|
||||
public static void Slap(this CBasePlayerPawn pawn, int damage = 0)
|
||||
{
|
||||
PerformSlap(pawn, damage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints a localized chat message to the player with a prefix.
|
||||
/// </summary>
|
||||
/// <param name="controller">The player controller to send the message to.</param>
|
||||
/// <param name="message">The message string.</param>
|
||||
public static void Print(this CCSPlayerController controller, string message = "")
|
||||
{
|
||||
StringBuilder _message = new(CS2_SimpleAdmin._localizer!["sa_prefix"]);
|
||||
_message.Append(message);
|
||||
controller.PrintToChat(_message.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the player controller can target another player controller, respecting admin permissions and immunity.
|
||||
/// </summary>
|
||||
/// <param name="controller">The player controller who wants to target.</param>
|
||||
/// <param name="target">The player controller being targeted.</param>
|
||||
/// <returns>True if targeting is allowed, false otherwise.</returns>
|
||||
public static bool CanTarget(this CCSPlayerController? controller, CCSPlayerController? target)
|
||||
{
|
||||
if (controller is null || target is null) return true;
|
||||
if (target.IsBot) return true;
|
||||
|
||||
return AdminManager.CanPlayerTarget(controller, target) ||
|
||||
AdminManager.CanPlayerTarget(new SteamID(controller.SteamID),
|
||||
new SteamID(target.SteamID)) ||
|
||||
AdminManager.GetPlayerImmunity(controller) >= AdminManager.GetPlayerImmunity(target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the controller can target a player by SteamID, considering targeting permissions and immunities.
|
||||
/// </summary>
|
||||
/// <param name="controller">The attacker player controller.</param>
|
||||
/// <param name="steamId">The SteamID of the target player.</param>
|
||||
/// <returns>True if targeting is permitted, false otherwise.</returns>
|
||||
public static bool CanTarget(this CCSPlayerController? controller, SteamID steamId)
|
||||
{
|
||||
if (controller is null) return true;
|
||||
|
||||
return AdminManager.CanPlayerTarget(new SteamID(controller.SteamID), steamId) ||
|
||||
AdminManager.GetPlayerImmunity(controller) >= AdminManager.GetPlayerImmunity(steamId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the movement speed modifier of the player controller.
|
||||
/// </summary>
|
||||
/// <param name="controller">The player controller.</param>
|
||||
/// <param name="speed">The speed modifier value.</param>
|
||||
public static void SetSpeed(this CCSPlayerController? controller, float speed)
|
||||
{
|
||||
var playerPawnValue = controller?.PlayerPawn.Value;
|
||||
if (playerPawnValue == null) return;
|
||||
|
||||
playerPawnValue.VelocityModifier = speed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the gravity scale for the player controller.
|
||||
/// </summary>
|
||||
/// <param name="controller">The player controller.</param>
|
||||
/// <param name="gravity">The gravity scale.</param>
|
||||
public static void SetGravity(this CCSPlayerController? controller, float gravity)
|
||||
{
|
||||
var playerPawnValue = controller?.PlayerPawn.Value;
|
||||
if (playerPawnValue == null) return;
|
||||
|
||||
playerPawnValue.ActualGravityScale = gravity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the player's in-game money amount.
|
||||
/// </summary>
|
||||
/// <param name="controller">The player controller.</param>
|
||||
/// <param name="money">The amount of money to set.</param>
|
||||
public static void SetMoney(this CCSPlayerController? controller, int money)
|
||||
{
|
||||
var moneyServices = controller?.InGameMoneyServices;
|
||||
if (moneyServices == null) return;
|
||||
|
||||
moneyServices.Account = money;
|
||||
|
||||
if (controller != null) Utilities.SetStateChanged(controller, "CCSPlayerController", "m_pInGameMoneyServices");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the player's health points.
|
||||
/// </summary>
|
||||
/// <param name="controller">The player controller.</param>
|
||||
/// <param name="health">The health value, default is 100.</param>
|
||||
public static void SetHp(this CCSPlayerController? controller, int health = 100)
|
||||
{
|
||||
if (controller == null) return;
|
||||
if (health <= 0 || controller.PlayerPawn.Value == null || controller.PlayerPawn?.Value?.LifeState != (int)LifeState_t.LIFE_ALIVE) return;
|
||||
|
||||
controller.PlayerPawn.Value.Health = health;
|
||||
|
||||
if (health > 100)
|
||||
{
|
||||
controller.PlayerPawn.Value.MaxHealth = health;
|
||||
}
|
||||
|
||||
Utilities.SetStateChanged(controller.PlayerPawn.Value, "CBaseEntity", "m_iHealth");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Buries the player pawn by moving it down by a depth offset.
|
||||
/// </summary>
|
||||
/// <param name="pawn">The player pawn to bury.</param>
|
||||
/// <param name="depth">The depth offset (default 10 units).</param>
|
||||
public static void Bury(this CBasePlayerPawn pawn, float depth = 10f)
|
||||
{
|
||||
var newPos = new Vector3(pawn.AbsOrigin!.X, pawn.AbsOrigin.Y,
|
||||
pawn.AbsOrigin!.Z - depth);
|
||||
var newRotation = new Vector3(pawn.AbsRotation!.X, pawn.AbsRotation.Y, pawn.AbsRotation.Z);
|
||||
var newVelocity = new Vector3(pawn.AbsVelocity.X, pawn.AbsVelocity.Y, pawn.AbsVelocity.Z);
|
||||
|
||||
pawn.Teleport(newPos, newRotation, newVelocity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unburies the player pawn by moving it up by a depth offset.
|
||||
/// </summary>
|
||||
/// <param name="pawn">The player pawn to unbury.</param>
|
||||
/// <param name="depth">The depth offset (default 15 units).</param>
|
||||
public static void Unbury(this CBasePlayerPawn pawn, float depth = 15f)
|
||||
{
|
||||
var newPos = new Vector3(pawn.AbsOrigin!.X, pawn.AbsOrigin.Y,
|
||||
pawn.AbsOrigin!.Z + depth);
|
||||
var newRotation = new Vector3(pawn.AbsRotation!.X, pawn.AbsRotation.Y, pawn.AbsRotation.Z);
|
||||
var newVelocity = new Vector3(pawn.AbsVelocity.X, pawn.AbsVelocity.Y, pawn.AbsVelocity.Z);
|
||||
|
||||
pawn.Teleport(newPos, newRotation, newVelocity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Freezes the player pawn, disabling movement.
|
||||
/// </summary>
|
||||
/// <param name="pawn">The player pawn to freeze.</param>
|
||||
public static void Freeze(this CBasePlayerPawn pawn)
|
||||
{
|
||||
pawn.MoveType = MoveType_t.MOVETYPE_INVALID;
|
||||
Schema.SetSchemaValue(pawn.Handle, "CBaseEntity", "m_nActualMoveType", 11); // invalid
|
||||
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_MoveType");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unfreezes the player pawn, enabling movement.
|
||||
/// </summary>
|
||||
/// <param name="pawn">The player pawn to unfreeze.</param>
|
||||
public static void Unfreeze(this CBasePlayerPawn pawn)
|
||||
{
|
||||
pawn.MoveType = MoveType_t.MOVETYPE_WALK;
|
||||
Schema.SetSchemaValue(pawn.Handle, "CBaseEntity", "m_nActualMoveType", 2); // walk
|
||||
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_MoveType");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the player's color tint to specified RGBA values.
|
||||
/// </summary>
|
||||
/// <param name="pawn">The pawn to colorize.</param>
|
||||
/// <param name="r">Red component (0-255).</param>
|
||||
/// <param name="g">Green component (0-255).</param>
|
||||
/// <param name="b">Blue component (0-255).</param>
|
||||
/// <param name="a">Alpha (transparency) component (0-255).</param>
|
||||
public static void Colorize(this CBasePlayerPawn pawn, int r = 255, int g = 255, int b = 255, int a = 255)
|
||||
{
|
||||
pawn.Render = Color.FromArgb(a, r, g, b);
|
||||
Utilities.SetStateChanged(pawn, "CBaseModelEntity", "m_clrRender");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles noclip mode for the player pawn.
|
||||
/// </summary>
|
||||
/// <param name="pawn">The player pawn.</param>
|
||||
public static void ToggleNoclip(this CBasePlayerPawn pawn)
|
||||
{
|
||||
if (pawn.MoveType == MoveType_t.MOVETYPE_NOCLIP)
|
||||
{
|
||||
pawn.MoveType = MoveType_t.MOVETYPE_WALK;
|
||||
Schema.SetSchemaValue(pawn.Handle, "CBaseEntity", "m_nActualMoveType", 2); // walk
|
||||
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_MoveType");
|
||||
}
|
||||
else
|
||||
{
|
||||
pawn.MoveType = MoveType_t.MOVETYPE_NOCLIP;
|
||||
Schema.SetSchemaValue(pawn.Handle, "CBaseEntity", "m_nActualMoveType", 8); // noclip
|
||||
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_MoveType");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renames the player controller to a new name, with fallback to a localized "Unknown".
|
||||
/// </summary>
|
||||
/// <param name="controller">The player controller to rename.</param>
|
||||
/// <param name="newName">The new name to assign.</param>
|
||||
public static void Rename(this CCSPlayerController? controller, string newName = "Unknown")
|
||||
{
|
||||
newName ??= CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
if (controller != null)
|
||||
{
|
||||
var playerName = new SchemaString<CBasePlayerController>(controller, "m_iszPlayerName");
|
||||
playerName.Set(newName + " ");
|
||||
|
||||
CS2_SimpleAdmin.Instance.AddTimer(0.25f, () =>
|
||||
{
|
||||
Utilities.SetStateChanged(controller, "CCSPlayerController", "m_szClan");
|
||||
Utilities.SetStateChanged(controller, "CBasePlayerController", "m_iszPlayerName");
|
||||
});
|
||||
|
||||
CS2_SimpleAdmin.Instance.AddTimer(0.3f, () =>
|
||||
{
|
||||
playerName.Set(newName);
|
||||
});
|
||||
}
|
||||
|
||||
CS2_SimpleAdmin.Instance.AddTimer(0.4f, () =>
|
||||
{
|
||||
if (controller != null) Utilities.SetStateChanged(controller, "CBasePlayerController", "m_iszPlayerName");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Teleports a player controller to the position, rotation, and velocity of another player controller.
|
||||
/// </summary>
|
||||
/// <param name="controller">The controller to teleport.</param>
|
||||
/// <param name="target">The target controller whose position to copy.</param>
|
||||
public static void TeleportPlayer(this CCSPlayerController? controller, CCSPlayerController? target)
|
||||
{
|
||||
if (controller?.PlayerPawn.Value == null && target?.PlayerPawn.Value == null)
|
||||
return;
|
||||
|
||||
if (
|
||||
controller?.PlayerPawn.Value is { AbsOrigin: not null, AbsRotation: not null } &&
|
||||
target?.PlayerPawn.Value is { AbsOrigin: not null, AbsRotation: not null }
|
||||
)
|
||||
{
|
||||
controller.PlayerPawn.Value.Teleport(
|
||||
target.PlayerPawn.Value.AbsOrigin,
|
||||
target.PlayerPawn.Value.AbsRotation,
|
||||
target.PlayerPawn.Value.AbsVelocity
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a slap effect to the given player pawn, optionally inflicting damage and adding velocity knockback.
|
||||
/// </summary>
|
||||
/// <param name="pawn">The player pawn to slap.</param>
|
||||
/// <param name="damage">The amount of damage to deal (default is 0).</param>
|
||||
private static void PerformSlap(CBasePlayerPawn pawn, int damage = 0)
|
||||
{
|
||||
if (pawn.LifeState != (int)LifeState_t.LIFE_ALIVE)
|
||||
return;
|
||||
|
||||
var controller = pawn.Controller.Value?.As<CCSPlayerController>();
|
||||
|
||||
/* Teleport in a random direction - thank you, Mani!*/
|
||||
/* Thank you AM & al!*/
|
||||
var random = new Random();
|
||||
var vel = new Vector3(pawn.AbsVelocity.X, pawn.AbsVelocity.Y, pawn.AbsVelocity.Z);
|
||||
|
||||
vel.X += (random.Next(180) + 50) * (random.Next(2) == 1 ? -1 : 1);
|
||||
vel.Y += (random.Next(180) + 50) * (random.Next(2) == 1 ? -1 : 1);
|
||||
vel.Z += random.Next(200) + 100;
|
||||
|
||||
pawn.AbsVelocity.X = vel.X;
|
||||
pawn.AbsVelocity.Y = vel.Y;
|
||||
pawn.AbsVelocity.Z = vel.Z;
|
||||
|
||||
if (controller != null && controller.IsValid)
|
||||
{
|
||||
var shakeMessage = UserMessage.FromPartialName("Shake");
|
||||
shakeMessage.SetFloat("duration", 1);
|
||||
shakeMessage.SetFloat("amplitude", 10);
|
||||
shakeMessage.SetFloat("frequency", 1f);
|
||||
shakeMessage.SetInt("command", 0);
|
||||
shakeMessage.Recipients.Add(controller);
|
||||
shakeMessage.Send();
|
||||
}
|
||||
|
||||
if (damage <= 0)
|
||||
return;
|
||||
|
||||
pawn.Health -= damage;
|
||||
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iHealth");
|
||||
|
||||
if (pawn.Health <= 0)
|
||||
pawn.CommitSuicide(true, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a localized chat message to the player controller.
|
||||
/// The message is retrieved from the specified localizer using the given message key and optional formatting arguments.
|
||||
/// Each line of the message is prefixed with a localized prefix string.
|
||||
/// The message respects the player's configured language for proper localization.
|
||||
/// </summary>
|
||||
/// <param name="controller">The target player controller to receive the message.</param>
|
||||
/// <param name="localizer">The string localizer used for localization.</param>
|
||||
/// <param name="messageKey">The key identifying the localized message.</param>
|
||||
/// <param name="messageArgs">Optional arguments to format the localized message.</param>
|
||||
public static void SendLocalizedMessage(this CCSPlayerController? controller, IStringLocalizer? localizer,
|
||||
string messageKey, params object[] messageArgs)
|
||||
{
|
||||
if (controller == null || localizer == null) return;
|
||||
|
||||
using (new WithTemporaryCulture(controller.GetLanguage()))
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.Append(localizer[messageKey, messageArgs]);
|
||||
|
||||
foreach (var part in Helper.SeparateLines(sb.ToString()))
|
||||
{
|
||||
var lineWithPrefix = localizer["sa_prefix"] + part.Trim();
|
||||
controller.PrintToChat(lineWithPrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a localized chat message to the player controller, centered horizontally on the player's screen.
|
||||
/// The message is retrieved from the specified localizer using the given message key and optional formatting arguments.
|
||||
/// Each line of the message is centered and prefixed with a localized prefix string.
|
||||
/// The message respects the player's configured language for localization.
|
||||
/// </summary>
|
||||
/// <param name="controller">The target player controller to receive the message.</param>
|
||||
/// <param name="localizer">The string localizer used for localization.</param>
|
||||
/// <param name="messageKey">The key identifying the localized message.</param>
|
||||
/// <param name="messageArgs">Optional arguments to format the localized message.</param>
|
||||
public static void SendLocalizedMessageCenter(this CCSPlayerController? controller, IStringLocalizer? localizer,
|
||||
string messageKey, params object[] messageArgs)
|
||||
{
|
||||
if (controller == null || localizer == null) return;
|
||||
|
||||
using (new WithTemporaryCulture(controller.GetLanguage()))
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.Append(localizer[messageKey, messageArgs]);
|
||||
|
||||
foreach (var part in Helper.SeparateLines(sb.ToString()))
|
||||
{
|
||||
string _part;
|
||||
_part = Helper.CenterMessage(part);
|
||||
var lineWithPrefix = localizer["sa_prefix"] + _part;
|
||||
controller.PrintToChat(lineWithPrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1068
CS2-SimpleAdmin/Helper.cs
Normal file
1068
CS2-SimpleAdmin/Helper.cs
Normal file
File diff suppressed because it is too large
Load Diff
442
CS2-SimpleAdmin/Managers/BanManager.cs
Normal file
442
CS2-SimpleAdmin/Managers/BanManager.cs
Normal file
@@ -0,0 +1,442 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.ValveConstants.Protobuf;
|
||||
using CS2_SimpleAdminApi;
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MySqlConnector;
|
||||
using System.Text;
|
||||
using CS2_SimpleAdmin.Database;
|
||||
|
||||
namespace CS2_SimpleAdmin.Managers;
|
||||
|
||||
internal class BanManager(IDatabaseProvider? databaseProvider)
|
||||
{
|
||||
/// <summary>
|
||||
/// Bans an online player and inserts the ban record into the database.
|
||||
/// </summary>
|
||||
/// <param name="player">The player to be banned (must be currently online).</param>
|
||||
/// <param name="issuer">The admin issuing the ban. Can be null if issued from console.</param>
|
||||
/// <param name="reason">The reason for the ban.</param>
|
||||
/// <param name="time">Ban duration in minutes. If 0, the ban is permanent.</param>
|
||||
/// <returns>The newly created ban ID if successful, otherwise null.</returns>
|
||||
public async Task<int?> BanPlayer(PlayerInfo player, PlayerInfo? issuer, string reason, int time = 0)
|
||||
{
|
||||
if (databaseProvider == null) return null;
|
||||
DateTime now = Time.ActualDateTime();
|
||||
DateTime futureTime = now.AddMinutes(time);
|
||||
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
try
|
||||
{
|
||||
var sql = databaseProvider.GetAddBanQuery();
|
||||
|
||||
var banId = await connection.ExecuteScalarAsync<int?>(sql, new
|
||||
{
|
||||
playerSteamid = player.SteamId.SteamId64,
|
||||
playerName = player.Name,
|
||||
playerIp = CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 1 ? player.IpAddress : null,
|
||||
adminSteamid = issuer?.SteamId.SteamId64 ?? 0,
|
||||
adminName = issuer?.Name ?? CS2_SimpleAdmin._localizer?["sa_console"] ?? "Console",
|
||||
banReason = reason,
|
||||
duration = time,
|
||||
ends = futureTime,
|
||||
created = now,
|
||||
serverid = CS2_SimpleAdmin.ServerId
|
||||
});
|
||||
|
||||
return banId;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError(ex, ex.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a ban for an offline player identified by their SteamID.
|
||||
/// </summary>
|
||||
/// <param name="playerSteamId">The SteamID64 of the player to ban.</param>
|
||||
/// <param name="issuer">The admin issuing the ban. Can be null if issued from console.</param>
|
||||
/// <param name="reason">The reason for the ban.</param>
|
||||
/// <param name="time">Ban duration in minutes. If 0, the ban is permanent.</param>
|
||||
/// <returns>The ID of the newly created ban if successful, otherwise null.</returns>
|
||||
public async Task<int?> AddBanBySteamid(ulong playerSteamId, PlayerInfo? issuer, string reason, int time = 0)
|
||||
{
|
||||
if (databaseProvider == null) return null;
|
||||
|
||||
DateTime now = Time.ActualDateTime();
|
||||
DateTime futureTime = now.AddMinutes(time);
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetAddBanBySteamIdQuery();
|
||||
var banId = await connection.ExecuteScalarAsync<int?>(sql, new
|
||||
{
|
||||
playerSteamid = playerSteamId,
|
||||
adminSteamid = issuer?.SteamId.SteamId64 ?? 0,
|
||||
adminName = issuer?.Name ?? CS2_SimpleAdmin._localizer?["sa_console"] ?? "Console",
|
||||
banReason = reason,
|
||||
duration = time,
|
||||
ends = futureTime,
|
||||
created = now,
|
||||
serverid = CS2_SimpleAdmin.ServerId
|
||||
});
|
||||
|
||||
return banId;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError(ex, ex.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a ban for an offline player identified by their IP address.
|
||||
/// </summary>
|
||||
/// <param name="playerIp">The IP address of the player to ban.</param>
|
||||
/// <param name="issuer">The admin issuing the ban. Can be null if issued from console.</param>
|
||||
/// <param name="reason">The reason for the ban.</param>
|
||||
/// <param name="time">Ban duration in minutes. If 0, the ban is permanent.</param>
|
||||
public async Task AddBanByIp(string playerIp, PlayerInfo? issuer, string reason, int time = 0)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
|
||||
if (string.IsNullOrEmpty(playerIp)) return;
|
||||
|
||||
DateTime now = Time.ActualDateTime();
|
||||
DateTime futureTime = now.AddMinutes(time);
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var sql = databaseProvider.GetAddBanByIpQuery();
|
||||
await connection.ExecuteAsync(sql, new
|
||||
{
|
||||
playerIp,
|
||||
adminSteamid = issuer?.SteamId.SteamId64 ?? 0,
|
||||
adminName = issuer?.Name ?? CS2_SimpleAdmin._localizer?["sa_console"] ?? "Console",
|
||||
banReason = reason,
|
||||
duration = time,
|
||||
ends = futureTime,
|
||||
created = now,
|
||||
serverid = CS2_SimpleAdmin.ServerId
|
||||
});
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// public async Task<bool> IsPlayerBanned(PlayerInfo player)
|
||||
// {
|
||||
// if (database == null) return false;
|
||||
//
|
||||
// if (player.IpAddress == null)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// #if DEBUG
|
||||
// if (CS2_SimpleAdmin._logger != null)
|
||||
// CS2_SimpleAdmin._logger.LogCritical($"IsPlayerBanned for {player.Name}");
|
||||
// #endif
|
||||
//
|
||||
// int banCount;
|
||||
//
|
||||
// DateTime currentTime = Time.ActualDateTime();
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// string sql;
|
||||
//
|
||||
// if (CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp && !CS2_SimpleAdmin.Instance.Config.OtherSettings.IgnoredIps.Contains(player.IpAddress))
|
||||
// {
|
||||
// sql = CS2_SimpleAdmin.Instance.Config.MultiServerMode ? """
|
||||
// SELECT COALESCE((
|
||||
// SELECT COUNT(*)
|
||||
// FROM sa_bans
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime)
|
||||
// ), 0)
|
||||
// +
|
||||
// COALESCE((
|
||||
// SELECT COUNT(*)
|
||||
// FROM sa_bans
|
||||
// JOIN sa_players_ips ON sa_bans.player_steamid = sa_players_ips.steamid
|
||||
// WHERE sa_bans.status = 'ACTIVE'
|
||||
// AND sa_players_ips.address = @PlayerIP
|
||||
// AND NOT EXISTS (
|
||||
// SELECT 1
|
||||
// FROM sa_bans
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime)
|
||||
// )
|
||||
// ), 0) AS TotalBanCount;
|
||||
// """ : """
|
||||
// SELECT COALESCE((
|
||||
// SELECT COUNT(*)
|
||||
// FROM sa_bans
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime)
|
||||
// AND server_id = @ServerId
|
||||
// ), 0)
|
||||
// +
|
||||
// COALESCE((
|
||||
// SELECT COUNT(*)
|
||||
// FROM sa_bans
|
||||
// JOIN sa_players_ips ON sa_bans.player_steamid = sa_players_ips.steamid
|
||||
// WHERE sa_bans.status = 'ACTIVE'
|
||||
// AND sa_players_ips.address = @PlayerIP
|
||||
// AND NOT EXISTS (
|
||||
// SELECT 1
|
||||
// FROM sa_bans
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime)
|
||||
// AND server_id = @ServerId
|
||||
// )
|
||||
// ), 0) AS TotalBanCount;
|
||||
// """;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// sql = CS2_SimpleAdmin.Instance.Config.MultiServerMode ? """
|
||||
// UPDATE sa_bans
|
||||
// SET player_ip = CASE WHEN player_ip IS NULL THEN @PlayerIP ELSE player_ip END,
|
||||
// player_name = CASE WHEN player_name IS NULL THEN @PlayerName ELSE player_name END
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime);
|
||||
//
|
||||
// SELECT COUNT(*) FROM sa_bans
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime);
|
||||
// """ : """
|
||||
// UPDATE sa_bans
|
||||
// SET player_ip = CASE WHEN player_ip IS NULL THEN @PlayerIP ELSE player_ip END,
|
||||
// player_name = CASE WHEN player_name IS NULL THEN @PlayerName ELSE player_name END
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime) AND server_id = @ServerId;
|
||||
//
|
||||
// SELECT COUNT(*) FROM sa_bans
|
||||
// WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
// AND status = 'ACTIVE'
|
||||
// AND (duration = 0 OR ends > @CurrentTime) AND server_id = @ServerId;
|
||||
// """;
|
||||
// }
|
||||
//
|
||||
// await using var connection = await database.GetConnectionAsync();
|
||||
//
|
||||
// var parameters = new
|
||||
// {
|
||||
// PlayerSteamID = player.SteamId.SteamId64,
|
||||
// PlayerIP = CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0 ||
|
||||
// string.IsNullOrEmpty(player.IpAddress) ||
|
||||
// CS2_SimpleAdmin.Instance.Config.OtherSettings.IgnoredIps.Contains(player.IpAddress)
|
||||
// ? null
|
||||
// : player.IpAddress,
|
||||
// PlayerName = !string.IsNullOrEmpty(player.Name) ? player.Name : string.Empty,
|
||||
// CurrentTime = currentTime,
|
||||
// CS2_SimpleAdmin.ServerId
|
||||
// };
|
||||
//
|
||||
// banCount = await connection.ExecuteScalarAsync<int>(sql, parameters);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// CS2_SimpleAdmin._logger?.LogError("Unable to check ban status for {PlayerName} ({ExceptionMessage})",
|
||||
// player.Name, ex.Message);
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// return banCount > 0;
|
||||
// }
|
||||
//
|
||||
// public async Task<int> GetPlayerBans(PlayerInfo player)
|
||||
// {
|
||||
// if (database == null) return 0;
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// string sql;
|
||||
//
|
||||
// sql = CS2_SimpleAdmin.Instance.Config.MultiServerMode
|
||||
// ? "SELECT COUNT(*) FROM sa_bans WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)"
|
||||
// : "SELECT COUNT(*) FROM sa_bans WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP) AND server_id = @serverid";
|
||||
//
|
||||
// int banCount;
|
||||
//
|
||||
// await using var connection = await database.GetConnectionAsync();
|
||||
//
|
||||
// if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType > 0 && !string.IsNullOrEmpty(player.IpAddress))
|
||||
// {
|
||||
// banCount = await connection.ExecuteScalarAsync<int>(sql,
|
||||
// new
|
||||
// {
|
||||
// PlayerSteamID = player.SteamId.SteamId64,
|
||||
// PlayerIP = player.IpAddress,
|
||||
// serverid = CS2_SimpleAdmin.ServerId
|
||||
// });
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// banCount = await connection.ExecuteScalarAsync<int>(sql,
|
||||
// new
|
||||
// {
|
||||
// PlayerSteamID = player.SteamId.SteamId64,
|
||||
// PlayerIP = DBNull.Value,
|
||||
// serverid = CS2_SimpleAdmin.ServerId
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// return banCount;
|
||||
// }
|
||||
// catch { }
|
||||
//
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Unbans a player based on a pattern match of SteamID or IP address.
|
||||
/// </summary>
|
||||
/// <param name="playerPattern">Pattern to match against player identifiers (e.g., partial SteamID).</param>
|
||||
/// <param name="adminSteamId">SteamID64 of the admin performing the unban.</param>
|
||||
/// <param name="reason">Optional reason for the unban. If null or empty, the unban reason is not stored.</param>
|
||||
public async Task UnbanPlayer(string playerPattern, string adminSteamId, string reason)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
|
||||
if (playerPattern is not { Length: > 1 })
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sqlRetrieveBans = databaseProvider.GetUnbanRetrieveBansQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
|
||||
var bans = await connection.QueryAsync(sqlRetrieveBans, new { pattern = playerPattern, serverid = CS2_SimpleAdmin.ServerId });
|
||||
var bansList = bans as dynamic[] ?? bans.ToArray();
|
||||
if (bansList.Length == 0)
|
||||
return;
|
||||
|
||||
var sqlAdminId = databaseProvider.GetUnbanAdminIdQuery();
|
||||
var adminId = await connection.ExecuteScalarAsync<int?>(sqlAdminId, new { adminSteamId }) ?? 0;
|
||||
|
||||
foreach (var ban in bansList)
|
||||
{
|
||||
int banId = ban.id;
|
||||
|
||||
var sqlInsertUnban = databaseProvider.GetInsertUnbanQuery(reason != null);
|
||||
var unbanId = await connection.ExecuteScalarAsync<int>(sqlInsertUnban, new { banId, adminId, reason });
|
||||
|
||||
var sqlUpdateBan = databaseProvider.GetUpdateBanStatusQuery();
|
||||
await connection.ExecuteAsync(sqlUpdateBan, new { unbanId, banId });
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// public async Task CheckOnlinePlayers(List<(string? IpAddress, ulong SteamID, int? UserId, int Slot)> players)
|
||||
// {
|
||||
// if (database == null) return;
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// await using var connection = await database.GetConnectionAsync();
|
||||
// bool checkIpBans = CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType > 0;
|
||||
//
|
||||
// var filteredPlayers = players.Where(p => p.UserId.HasValue).ToList();
|
||||
//
|
||||
// var steamIds = filteredPlayers.Select(p => p.SteamID).Distinct().ToList();
|
||||
// var ipAddresses = filteredPlayers
|
||||
// .Where(p => !string.IsNullOrEmpty(p.IpAddress))
|
||||
// .Select(p => p.IpAddress)
|
||||
// .Distinct()
|
||||
// .ToList();
|
||||
//
|
||||
// var sql = new StringBuilder();
|
||||
// sql.Append("SELECT `player_steamid`, `player_ip` FROM `sa_bans` WHERE `status` = 'ACTIVE' ");
|
||||
//
|
||||
// if (CS2_SimpleAdmin.Instance.Config.MultiServerMode)
|
||||
// {
|
||||
// sql.Append("AND (player_steamid IN @SteamIDs ");
|
||||
// if (checkIpBans && ipAddresses.Count != 0)
|
||||
// {
|
||||
// sql.Append("OR player_ip IN @IpAddresses");
|
||||
// }
|
||||
// sql.Append(')');
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// sql.Append("AND server_id = @ServerId AND (player_steamid IN @SteamIDs ");
|
||||
// if (checkIpBans && ipAddresses.Count != 0)
|
||||
// {
|
||||
// sql.Append("OR player_ip IN @IpAddresses");
|
||||
// }
|
||||
// sql.Append(')');
|
||||
// }
|
||||
//
|
||||
// var bannedPlayers = await connection.QueryAsync<(ulong PlayerSteamID, string PlayerIP)>(
|
||||
// sql.ToString(),
|
||||
// new
|
||||
// {
|
||||
// SteamIDs = steamIds,
|
||||
// IpAddresses = checkIpBans ? ipAddresses : [],
|
||||
// CS2_SimpleAdmin.ServerId
|
||||
// });
|
||||
//
|
||||
// var valueTuples = bannedPlayers.ToList();
|
||||
// var bannedSteamIds = valueTuples.Select(b => b.PlayerSteamID).ToHashSet();
|
||||
// var bannedIps = valueTuples.Select(b => b.PlayerIP).ToHashSet();
|
||||
//
|
||||
// foreach (var player in filteredPlayers.Where(player => bannedSteamIds.Contains(player.SteamID) ||
|
||||
// (checkIpBans && bannedIps.Contains(player.IpAddress ?? ""))))
|
||||
// {
|
||||
// if (!player.UserId.HasValue || CS2_SimpleAdmin.PlayersInfo[player.SteamID].WaitingForKick) continue;
|
||||
//
|
||||
// await Server.NextWorldUpdateAsync(() =>
|
||||
// {
|
||||
// Helper.KickPlayer(player.UserId.Value, NetworkDisconnectionReason.NETWORK_DISCONNECT_KICKBANADDED);
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// CS2_SimpleAdmin._logger?.LogError($"Error checking online players: {ex.Message}");
|
||||
// }
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Expires all bans that have passed their end time, including optional cleanup of old IP bans.
|
||||
/// </summary>
|
||||
public async Task ExpireOldBans()
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
var currentTime = Time.ActualDateTime();
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetExpireBansQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
await connection.ExecuteAsync(sql, new { currentTime, serverid = CS2_SimpleAdmin.ServerId });
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.ExpireOldIpBans > 0)
|
||||
{
|
||||
var ipBansTime = currentTime.AddDays(-CS2_SimpleAdmin.Instance.Config.OtherSettings.ExpireOldIpBans);
|
||||
sql = databaseProvider.GetExpireIpBansQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
await connection.ExecuteAsync(sql, new { ipBansTime, CS2_SimpleAdmin.ServerId });
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogCritical("Unable to remove expired bans");
|
||||
}
|
||||
}
|
||||
}
|
||||
686
CS2-SimpleAdmin/Managers/CacheManager.cs
Normal file
686
CS2-SimpleAdmin/Managers/CacheManager.cs
Normal file
@@ -0,0 +1,686 @@
|
||||
using System.Collections.Concurrent;
|
||||
using CS2_SimpleAdmin.Database;
|
||||
using CS2_SimpleAdmin.Models;
|
||||
using Dapper;
|
||||
using ZLinq;
|
||||
|
||||
namespace CS2_SimpleAdmin.Managers;
|
||||
|
||||
internal class CacheManager: IDisposable
|
||||
{
|
||||
private readonly ConcurrentDictionary<int, BanRecord> _banCache = [];
|
||||
private readonly ConcurrentDictionary<ulong, List<BanRecord>> _steamIdIndex = [];
|
||||
private readonly ConcurrentDictionary<uint, List<BanRecord>> _ipIndex = [];
|
||||
|
||||
private readonly ConcurrentDictionary<ulong, HashSet<IpRecord>> _playerIpsCache = [];
|
||||
private HashSet<uint> _cachedIgnoredIps = [];
|
||||
|
||||
private DateTime _lastUpdateTime = DateTime.MinValue;
|
||||
private bool _isInitialized;
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes and builds the ban and IP cache from the database. Loads bans, player IP history, and config settings.
|
||||
/// </summary>
|
||||
/// <returns>Asynchronous task representing the initialization process.</returns>
|
||||
public async Task InitializeCacheAsync()
|
||||
{
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null) return;
|
||||
if (!CS2_SimpleAdmin.ServerLoaded) return;
|
||||
if (_isInitialized) return;
|
||||
|
||||
try
|
||||
{
|
||||
Clear();
|
||||
_cachedIgnoredIps = CS2_SimpleAdmin.Instance.Config.OtherSettings.IgnoredIps
|
||||
.AsValueEnumerable()
|
||||
.Select(IpHelper.IpToUint)
|
||||
.ToHashSet();
|
||||
|
||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
List<BanRecord> bans;
|
||||
|
||||
if (CS2_SimpleAdmin.Instance.Config.MultiServerMode)
|
||||
{
|
||||
bans = (await connection.QueryAsync<BanRecord>(
|
||||
"""
|
||||
SELECT
|
||||
id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM sa_bans
|
||||
""")).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
bans = (await connection.QueryAsync<BanRecord>(
|
||||
"""
|
||||
SELECT
|
||||
id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM sa_bans
|
||||
WHERE server_id = @serverId
|
||||
""", new {serverId = CS2_SimpleAdmin.ServerId})).ToList();
|
||||
}
|
||||
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp)
|
||||
{
|
||||
var ipHistory =
|
||||
(await connection.QueryAsync<(ulong steamid, string? name, uint address, DateTime used_at)>(
|
||||
"SELECT steamid, name, address, used_at FROM sa_players_ips ORDER BY used_at DESC")).ToList();
|
||||
|
||||
foreach (var group in ipHistory.AsValueEnumerable().GroupBy(x => x.steamid))
|
||||
{
|
||||
var ipSet = group
|
||||
.GroupBy(x => x.address)
|
||||
.Select(g =>
|
||||
{
|
||||
var latest = g.MaxBy(x => x.used_at);
|
||||
return new IpRecord(
|
||||
g.Key,
|
||||
latest.used_at,
|
||||
string.IsNullOrEmpty(latest.name)
|
||||
? CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown"
|
||||
: latest.name
|
||||
);
|
||||
})
|
||||
.ToHashSet(new IpRecordComparer());
|
||||
|
||||
_playerIpsCache.AddOrUpdate(
|
||||
group.Key,
|
||||
_ => ipSet,
|
||||
(_, existingSet) =>
|
||||
{
|
||||
foreach (var ip in ipSet)
|
||||
{
|
||||
existingSet.Remove(ip);
|
||||
existingSet.Add(ip);
|
||||
}
|
||||
return existingSet;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var ban in bans.AsValueEnumerable())
|
||||
_banCache.TryAdd(ban.Id, ban);
|
||||
|
||||
RebuildIndexes();
|
||||
|
||||
_lastUpdateTime = Time.ActualDateTime().AddSeconds(-1);
|
||||
_isInitialized = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all cached data and reinitializes the cache from the database.
|
||||
/// </summary>
|
||||
/// <returns>Asynchronous task representing the reinitialization process.</returns>
|
||||
public async Task ForceReInitializeCacheAsync()
|
||||
{
|
||||
_isInitialized = false;
|
||||
|
||||
_banCache.Clear();
|
||||
_playerIpsCache.Clear();
|
||||
_cachedIgnoredIps = [];
|
||||
_lastUpdateTime = DateTime.MinValue;
|
||||
|
||||
await InitializeCacheAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the in-memory cache with updated or new data from the database since the last update time.
|
||||
/// Also updates multi-account IP history if enabled.
|
||||
/// </summary>
|
||||
/// <returns>Asynchronous task representing the refresh operation.</returns>
|
||||
public async Task RefreshCacheAsync()
|
||||
{
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null) return;
|
||||
if (!_isInitialized) return;
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
IEnumerable<BanRecord> updatedBans;
|
||||
|
||||
var allIds = (await connection.QueryAsync<int>("SELECT id FROM sa_bans")).ToHashSet();
|
||||
|
||||
if (CS2_SimpleAdmin.Instance.Config.MultiServerMode)
|
||||
{
|
||||
updatedBans = (await connection.QueryAsync<BanRecord>(
|
||||
"""
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM `sa_bans` WHERE updated_at > @lastUpdate OR created > @lastUpdate ORDER BY updated_at DESC
|
||||
""",
|
||||
new { lastUpdate = _lastUpdateTime }
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
updatedBans = (await connection.QueryAsync<BanRecord>(
|
||||
"""
|
||||
SELECT id AS Id,
|
||||
player_name AS PlayerName,
|
||||
player_steamid AS PlayerSteamId,
|
||||
player_ip AS PlayerIp,
|
||||
status AS Status
|
||||
FROM `sa_bans` WHERE (updated_at > @lastUpdate OR created > @lastUpdate) AND server_id = @serverId ORDER BY updated_at DESC
|
||||
""",
|
||||
new { lastUpdate = _lastUpdateTime, serverId = CS2_SimpleAdmin.ServerId }
|
||||
));
|
||||
}
|
||||
|
||||
foreach (var id in _banCache.Keys)
|
||||
{
|
||||
if (allIds.Contains(id) || !_banCache.TryRemove(id, out var ban)) continue;
|
||||
|
||||
if (ban.PlayerSteamId != null &&
|
||||
_steamIdIndex.TryGetValue(ban.PlayerSteamId.Value, out var steamBans))
|
||||
{
|
||||
steamBans.RemoveAll(b => b.Id == id);
|
||||
if (steamBans.Count == 0)
|
||||
_steamIdIndex.TryRemove(ban.PlayerSteamId.Value, out _);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ban.PlayerIp) ||
|
||||
!IpHelper.TryConvertIpToUint(ban.PlayerIp, out var ipUInt) ||
|
||||
!_ipIndex.TryGetValue(ipUInt, out var ipBans)) continue;
|
||||
{
|
||||
ipBans.RemoveAll(b => b.Id == id);
|
||||
if (ipBans.Count == 0)
|
||||
_ipIndex.TryRemove(ipUInt, out _);
|
||||
}
|
||||
}
|
||||
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp)
|
||||
{
|
||||
var ipHistory = (await connection.QueryAsync<(ulong steamid, string? name, uint address, DateTime used_at)>(
|
||||
"SELECT steamid, name, address, used_at FROM sa_players_ips WHERE used_at >= @lastUpdate ORDER BY used_at DESC LIMIT 300",
|
||||
new { lastUpdate = _lastUpdateTime }));
|
||||
|
||||
foreach (var group in ipHistory.AsValueEnumerable().GroupBy(x => x.steamid))
|
||||
{
|
||||
var ipSet = new HashSet<IpRecord>(
|
||||
group
|
||||
.GroupBy(x => x.address)
|
||||
.Select(g =>
|
||||
{
|
||||
var latest = g.MaxBy(x => x.used_at);
|
||||
return new IpRecord(
|
||||
g.Key,
|
||||
latest.used_at,
|
||||
!string.IsNullOrEmpty(latest.name)
|
||||
? latest.name
|
||||
: CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown"
|
||||
);
|
||||
}),
|
||||
new IpRecordComparer()
|
||||
);
|
||||
|
||||
_playerIpsCache.AddOrUpdate(
|
||||
group.Key,
|
||||
_ => ipSet,
|
||||
(_, existingSet) =>
|
||||
{
|
||||
foreach (var newEntry in ipSet)
|
||||
{
|
||||
existingSet.Remove(newEntry);
|
||||
existingSet.Add(newEntry);
|
||||
}
|
||||
|
||||
return existingSet;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var ban in updatedBans)
|
||||
{
|
||||
_banCache.AddOrUpdate(ban.Id, ban, (_, _) => ban);
|
||||
}
|
||||
|
||||
RebuildIndexes();
|
||||
_lastUpdateTime = Time.ActualDateTime().AddSeconds(-1);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebuilds the internal indexes for fast lookup of active bans by Steam ID and IP address.
|
||||
/// Clears and repopulates both indexes based on the current in-memory ban cache.
|
||||
/// </summary>
|
||||
private void RebuildIndexes()
|
||||
{
|
||||
_steamIdIndex.Clear();
|
||||
_ipIndex.Clear();
|
||||
|
||||
foreach (var ban in _banCache.Values)
|
||||
{
|
||||
if (ban.StatusEnum != BanStatus.ACTIVE)
|
||||
continue;
|
||||
|
||||
if (ban.PlayerSteamId != null)
|
||||
{
|
||||
var steamId = ban.PlayerSteamId;
|
||||
_steamIdIndex.AddOrUpdate(
|
||||
steamId.Value,
|
||||
key => [ban],
|
||||
(key, list) =>
|
||||
{
|
||||
list.Add(ban);
|
||||
return list;
|
||||
});
|
||||
}
|
||||
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0) continue;
|
||||
|
||||
if (ban.PlayerIp != null &&
|
||||
IpHelper.TryConvertIpToUint(ban.PlayerIp, out var ipUInt))
|
||||
{
|
||||
_ipIndex.AddOrUpdate(
|
||||
ipUInt,
|
||||
key => [ban],
|
||||
(key, list) =>
|
||||
{
|
||||
list.Add(ban);
|
||||
return list;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all ban records currently stored in the cache.
|
||||
/// </summary>
|
||||
/// <returns>List of all <see cref="BanRecord"/> objects.</returns>
|
||||
public List<BanRecord> GetAllBans() => _banCache.Values.ToList();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves only active ban records from the cache.
|
||||
/// </summary>
|
||||
/// <returns>List of active <see cref="BanRecord"/> objects.</returns>
|
||||
public List<BanRecord> GetActiveBans() => _banCache.Values.Where(b => b.StatusEnum == BanStatus.ACTIVE).ToList();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all ban records for a specific player by their Steam ID.
|
||||
/// </summary>
|
||||
/// <param name="steamId">64-bit Steam ID of the player.</param>
|
||||
/// <returns>List of <see cref="BanRecord"/> objects associated with the Steam ID.</returns>
|
||||
public List<BanRecord> GetPlayerBansBySteamId(ulong steamId) => _steamIdIndex.TryGetValue(steamId, out var bans) ? bans : [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets all known Steam accounts that have used the specified IP address.
|
||||
/// </summary>
|
||||
/// <param name="ipAddress">The IP address to search for, in string format.</param>
|
||||
/// <returns>
|
||||
/// List of tuples containing the Steam ID, last used time, and player name for each matching entry.
|
||||
/// </returns>
|
||||
public List<(ulong SteamId, DateTime UsedAt, string PlayerName)> GetAccountsByIp(string ipAddress)
|
||||
{
|
||||
var ipAsUint = IpHelper.IpToUint(ipAddress);
|
||||
var results = new List<(ulong, DateTime, string)>();
|
||||
var comparer = _playerIpsCache.Comparer;
|
||||
|
||||
foreach (var (steamId, ipSet) in _playerIpsCache)
|
||||
{
|
||||
if (!ipSet.TryGetValue(new IpRecord(ipAsUint, Time.ActualDateTime(), "Unknown"), out var actualEntry)) continue;
|
||||
results.Add((steamId, actualEntry.UsedAt, actualEntry.PlayerName));
|
||||
|
||||
foreach (var entry in ipSet)
|
||||
{
|
||||
if (entry.Ip == ipAsUint && !Equals(entry, actualEntry))
|
||||
{
|
||||
results.Add((steamId, entry.UsedAt, entry.PlayerName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// public IEnumerable<(ulong SteamId, DateTime UsedAt, string PlayerName)> GetAccountsByIp(string ipAddress)
|
||||
// {
|
||||
// var ipAsUint = IpHelper.IpToUint(ipAddress);
|
||||
//
|
||||
// return _playerIpsCache.SelectMany(kvp => kvp.Value
|
||||
// .Where(entry => entry.Ip == ipAsUint)
|
||||
// .Select(entry => (kvp.Key, entry.UsedAt, entry.PlayerName)));
|
||||
// }
|
||||
|
||||
private bool IsIpBanned(string ipAddress)
|
||||
{
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0) return false;
|
||||
var ipUInt = IpHelper.IpToUint(ipAddress);
|
||||
return !_cachedIgnoredIps.Contains(ipUInt) && _ipIndex.ContainsKey(ipUInt);
|
||||
}
|
||||
|
||||
// public bool IsPlayerBanned(ulong? steamId, string? ipAddress)
|
||||
// {
|
||||
// if (steamId != null && _steamIdIndex.ContainsKey(steamId.Value))
|
||||
// return true;
|
||||
//
|
||||
// if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0)
|
||||
// return false;
|
||||
//
|
||||
// if (string.IsNullOrEmpty(ipAddress) || !IpHelper.TryConvertIpToUint(ipAddress, out var ipUInt))
|
||||
// return false;
|
||||
//
|
||||
// return !_cachedIgnoredIps.Contains(ipUInt) && _ipIndex.ContainsKey(ipUInt);
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a player is currently banned by Steam ID or IP address.
|
||||
/// If a partial ban record is found, updates it with the latest player information.
|
||||
/// </summary>
|
||||
/// <param name="playerName">Name of the player attempting to connect.</param>
|
||||
/// <param name="steamId">Optional 64-bit Steam ID of the player.</param>
|
||||
/// <param name="ipAddress">Optional IP address of the player.</param>
|
||||
/// <returns>True if the player is banned, otherwise false.</returns>
|
||||
public bool IsPlayerBanned(string playerName, ulong? steamId, string? ipAddress)
|
||||
{
|
||||
BanRecord? record;
|
||||
if (steamId.HasValue && _steamIdIndex.TryGetValue(steamId.Value, out var steamRecords))
|
||||
{
|
||||
record = steamRecords.FirstOrDefault(r => r.StatusEnum == BanStatus.ACTIVE);
|
||||
if (record != null)
|
||||
{
|
||||
if ((string.IsNullOrEmpty(record.PlayerIp) && !string.IsNullOrEmpty(ipAddress)) ||
|
||||
(!record.PlayerSteamId.HasValue))
|
||||
{
|
||||
_ = Task.Run(() => UpdatePlayerData(playerName, steamId, ipAddress));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0)
|
||||
return false;
|
||||
|
||||
if (string.IsNullOrEmpty(ipAddress) ||
|
||||
!IpHelper.TryConvertIpToUint(ipAddress, out var ipUInt) ||
|
||||
_cachedIgnoredIps.Contains(ipUInt) ||
|
||||
!_ipIndex.TryGetValue(ipUInt, out var ipRecords)) return false;
|
||||
|
||||
record = ipRecords.FirstOrDefault(r => r.StatusEnum == BanStatus.ACTIVE);
|
||||
if (record == null) return false;
|
||||
if ((string.IsNullOrEmpty(record.PlayerIp) && !string.IsNullOrEmpty(ipAddress)) ||
|
||||
(!record.PlayerSteamId.HasValue && steamId.HasValue))
|
||||
{
|
||||
_ = Task.Run(() => UpdatePlayerData(playerName, steamId, ipAddress));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// public bool IsPlayerOrAnyIpBanned(ulong steamId, string? ipAddress)
|
||||
// {
|
||||
// if (_steamIdIndex.ContainsKey(steamId))
|
||||
// return true;
|
||||
//
|
||||
// if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0)
|
||||
// return false;
|
||||
//
|
||||
// if (!_playerIpsCache.TryGetValue(steamId, out var ipData))
|
||||
// return false;
|
||||
//
|
||||
// // var now = Time.ActualDateTime();
|
||||
// var cutoff = Time.ActualDateTime().AddDays(-CS2_SimpleAdmin.Instance.Config.OtherSettings.ExpireOldIpBans);
|
||||
// var unknownName = CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown";
|
||||
//
|
||||
// if (ipAddress != null && IpHelper.TryConvertIpToUint(ipAddress, out var ipAsUint))
|
||||
// {
|
||||
// if (!_cachedIgnoredIps.Contains(ipAsUint))
|
||||
// {
|
||||
// ipData.Add(new IpRecord(
|
||||
// ipAsUint,
|
||||
// Time.ActualDateTime().AddSeconds(-2),
|
||||
// unknownName
|
||||
// ));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // foreach (var ipRecord in ipData)
|
||||
// // {
|
||||
// // // Skip if too old or in ignored list
|
||||
// // if (ipRecord.UsedAt < cutoff || _cachedIgnoredIps.Contains(ipRecord.Ip))
|
||||
// // continue;
|
||||
// //
|
||||
// // // Check if IP is banned
|
||||
// // if (_ipIndex.ContainsKey(ipRecord.Ip))
|
||||
// // return true;
|
||||
// // }
|
||||
//
|
||||
// foreach (var ipRecord in ipData)
|
||||
// {
|
||||
// if (ipRecord.UsedAt < cutoff || _cachedIgnoredIps.Contains(ipRecord.Ip))
|
||||
// continue;
|
||||
//
|
||||
// if (!_ipIndex.TryGetValue(ipRecord.Ip, out var banRecords)) continue;
|
||||
//
|
||||
// var activeBan = banRecords.FirstOrDefault(r => r.StatusEnum == BanStatus.ACTIVE);
|
||||
// if (activeBan == null) continue;
|
||||
//
|
||||
// if (!string.IsNullOrEmpty(activeBan.PlayerName) && activeBan.PlayerSteamId.HasValue) return true;
|
||||
//
|
||||
// _ = Task.Run(() => UpdatePlayerData(
|
||||
// activeBan.PlayerName,
|
||||
// steamId,
|
||||
// ipAddress
|
||||
// ));
|
||||
//
|
||||
// if (string.IsNullOrEmpty(activeBan.PlayerName) && !string.IsNullOrEmpty(unknownName))
|
||||
// activeBan.PlayerName = unknownName;
|
||||
//
|
||||
// activeBan.PlayerSteamId ??= steamId;
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// return false;
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the player or any IP previously associated with them is currently banned.
|
||||
/// Also updates ban records with missing player info if found.
|
||||
/// </summary>
|
||||
/// <param name="playerName">Current player name.</param>
|
||||
/// <param name="steamId">64-bit Steam ID of the player.</param>
|
||||
/// <param name="ipAddress">Current IP address of the player (optional).</param>
|
||||
/// <returns>True if the player or their known IPs are banned, otherwise false.</returns>
|
||||
public bool IsPlayerOrAnyIpBanned(string playerName, ulong steamId, string? ipAddress)
|
||||
{
|
||||
if (_steamIdIndex.TryGetValue(steamId, out var steamBans))
|
||||
{
|
||||
var activeBan = steamBans.FirstOrDefault(b => b.StatusEnum == BanStatus.ACTIVE);
|
||||
if (activeBan != null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(activeBan.PlayerName) || string.IsNullOrEmpty(activeBan.PlayerIp))
|
||||
_ = Task.Run(() => UpdatePlayerData(playerName, steamId, ipAddress));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0)
|
||||
return false;
|
||||
|
||||
if (!_playerIpsCache.TryGetValue(steamId, out var ipData))
|
||||
return false;
|
||||
|
||||
var cutoff = Time.ActualDateTime().AddDays(-CS2_SimpleAdmin.Instance.Config.OtherSettings.ExpireOldIpBans);
|
||||
var unknownName = CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
if (ipAddress != null && IpHelper.TryConvertIpToUint(ipAddress, out var ipAsUint))
|
||||
{
|
||||
if (!_cachedIgnoredIps.Contains(ipAsUint))
|
||||
{
|
||||
ipData.Add(new IpRecord(ipAsUint, Time.ActualDateTime().AddSeconds(-2), unknownName));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var ipRecord in ipData)
|
||||
{
|
||||
if (ipRecord.UsedAt < cutoff || _cachedIgnoredIps.Contains(ipRecord.Ip))
|
||||
continue;
|
||||
|
||||
if (!_ipIndex.TryGetValue(ipRecord.Ip, out var banRecords))
|
||||
continue;
|
||||
|
||||
var activeBan = banRecords.FirstOrDefault(r => r.StatusEnum == BanStatus.ACTIVE);
|
||||
if (activeBan == null)
|
||||
continue;
|
||||
|
||||
if (string.IsNullOrEmpty(activeBan.PlayerName))
|
||||
activeBan.PlayerName = unknownName;
|
||||
|
||||
activeBan.PlayerSteamId ??= steamId;
|
||||
|
||||
_ = Task.Run(() => UpdatePlayerData(playerName, steamId, ipAddress));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given IP address is known (previously recorded) for the specified Steam ID.
|
||||
/// </summary>
|
||||
/// <param name="steamId">64-bit Steam ID of the player.</param>
|
||||
/// <param name="ipAddress">IP address to check.</param>
|
||||
/// <returns>True if the IP is recorded for the player, otherwise false.</returns>
|
||||
public bool HasIpForPlayer(ulong steamId, string ipAddress)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ipAddress))
|
||||
return false;
|
||||
|
||||
if (!IpHelper.TryConvertIpToUint(ipAddress, out var ipUint))
|
||||
return false;
|
||||
|
||||
return _playerIpsCache.TryGetValue(steamId, out var ipData) &&
|
||||
ipData.Contains(new IpRecord(ipUint, default, null!));
|
||||
}
|
||||
|
||||
// public bool HasIpForPlayer(ulong steamId, string ipAddress)
|
||||
// {
|
||||
// if (string.IsNullOrWhiteSpace(ipAddress))
|
||||
// return false;
|
||||
//
|
||||
// return _playerIpsCache.TryGetValue(steamId, out var ipData)
|
||||
// && ipData.Any(x => x.Ip == IpHelper.IpToUint(ipAddress));
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Updates existing active ban records in the database with the latest known player name and IP address.
|
||||
/// Also updates in-memory cache to reflect these changes.
|
||||
/// </summary>
|
||||
/// <param name="playerName">Current player name.</param>
|
||||
/// <param name="steamId">Optional Steam ID of the player.</param>
|
||||
/// <param name="ipAddress">Optional IP address of the player.</param>
|
||||
/// <returns>Asynchronous task representing the update operation.</returns>
|
||||
private async Task UpdatePlayerData(string? playerName, ulong? steamId, string? ipAddress)
|
||||
{
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null)
|
||||
return;
|
||||
|
||||
var baseSql = """
|
||||
UPDATE sa_bans
|
||||
SET
|
||||
player_ip = COALESCE(player_ip, @PlayerIP),
|
||||
player_name = COALESCE(player_name, @PlayerName)
|
||||
WHERE
|
||||
(player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)
|
||||
AND status = 'ACTIVE'
|
||||
AND (duration = 0 OR ends > @CurrentTime)
|
||||
""";
|
||||
|
||||
if (!CS2_SimpleAdmin.Instance.Config.MultiServerMode)
|
||||
{
|
||||
baseSql += " AND server_id = @ServerId;";
|
||||
}
|
||||
|
||||
var parameters = new
|
||||
{
|
||||
PlayerSteamID = steamId,
|
||||
PlayerIP = CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0
|
||||
|| string.IsNullOrEmpty(ipAddress)
|
||||
|| CS2_SimpleAdmin.Instance.Config.OtherSettings.IgnoredIps.Contains(ipAddress)
|
||||
? null
|
||||
: ipAddress,
|
||||
PlayerName = string.IsNullOrEmpty(playerName) ? string.Empty : playerName,
|
||||
CurrentTime = Time.ActualDateTime(),
|
||||
CS2_SimpleAdmin.ServerId
|
||||
};
|
||||
|
||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
await connection.ExecuteAsync(baseSql, parameters);
|
||||
|
||||
if (steamId.HasValue && _steamIdIndex.TryGetValue(steamId.Value, out var steamRecords))
|
||||
{
|
||||
foreach (var rec in steamRecords.Where(r => r.StatusEnum == BanStatus.ACTIVE))
|
||||
{
|
||||
if (string.IsNullOrEmpty(rec.PlayerIp) && !string.IsNullOrEmpty(ipAddress))
|
||||
rec.PlayerIp = ipAddress;
|
||||
|
||||
if (string.IsNullOrEmpty(rec.PlayerName) && !string.IsNullOrEmpty(playerName))
|
||||
rec.PlayerName = playerName;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ipAddress) && IpHelper.TryConvertIpToUint(ipAddress, out var ipUInt)
|
||||
&& _ipIndex.TryGetValue(ipUInt, out var ipRecords))
|
||||
{
|
||||
foreach (var rec in ipRecords.Where(r => r.StatusEnum == BanStatus.ACTIVE))
|
||||
{
|
||||
if (!rec.PlayerSteamId.HasValue && steamId.HasValue)
|
||||
rec.PlayerSteamId = steamId;
|
||||
|
||||
if (string.IsNullOrEmpty(rec.PlayerName) && !string.IsNullOrEmpty(playerName))
|
||||
rec.PlayerName = playerName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Clear()
|
||||
{
|
||||
_steamIdIndex.Clear();
|
||||
_ipIndex.Clear();
|
||||
|
||||
_banCache.Clear();
|
||||
_playerIpsCache.Clear();
|
||||
_cachedIgnoredIps.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears and disposes of all cached data and marks the object as disposed.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
Clear();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public class IpRecordComparer : IEqualityComparer<IpRecord>
|
||||
{
|
||||
public bool Equals(IpRecord x, IpRecord y)
|
||||
=> x.Ip == y.Ip;
|
||||
|
||||
public int GetHashCode(IpRecord obj)
|
||||
=> obj.Ip.GetHashCode();
|
||||
}
|
||||
167
CS2-SimpleAdmin/Managers/DiscordManager.cs
Normal file
167
CS2-SimpleAdmin/Managers/DiscordManager.cs
Normal file
@@ -0,0 +1,167 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CS2_SimpleAdmin.Managers;
|
||||
|
||||
public class DiscordManager(string webhookUrl)
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Sends a plain text message asynchronously to the configured Discord webhook URL.
|
||||
/// </summary>
|
||||
/// <param name="message">The text message to send to Discord.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
|
||||
public async Task SendMessageAsync(string message)
|
||||
{
|
||||
var client = CS2_SimpleAdmin.HttpClient;
|
||||
var payload = new
|
||||
{
|
||||
content = message
|
||||
};
|
||||
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = false
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(payload, options);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
try
|
||||
{
|
||||
var response = await client.PostAsync(webhookUrl, content);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError(
|
||||
$"Failed to send discord message. Status Code: {response.StatusCode}, Reason: {response.ReasonPhrase}");
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError($"Error sending discord message: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends an embed message asynchronously to the configured Discord webhook URL.
|
||||
/// </summary>
|
||||
/// <param name="embed">The embed object containing rich content to send.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
|
||||
public async Task SendEmbedAsync(Embed embed)
|
||||
{
|
||||
var httpClient = CS2_SimpleAdmin.HttpClient;
|
||||
|
||||
var payload = new
|
||||
{
|
||||
embeds = new[]
|
||||
{
|
||||
new
|
||||
{
|
||||
color = embed.Color,
|
||||
title = !string.IsNullOrEmpty(embed.Title) ? embed.Title : null,
|
||||
description = !string.IsNullOrEmpty(embed.Description) ? embed.Description : null,
|
||||
thumbnail = !string.IsNullOrEmpty(embed.ThumbnailUrl) ? new { url = embed.ThumbnailUrl } : null,
|
||||
image = !string.IsNullOrEmpty(embed.ImageUrl) ? new { url = embed.ImageUrl } : null,
|
||||
footer = !string.IsNullOrEmpty(embed.Footer?.Text) ? new { text = embed.Footer.Text, icon_url = embed.Footer.IconUrl } : null,
|
||||
timestamp = embed.Timestamp,
|
||||
fields = embed.Fields.Count > 0 ? embed.Fields.Select(field => new
|
||||
{
|
||||
name = field.Name,
|
||||
value = field.Value,
|
||||
inline = field.Inline
|
||||
}).ToArray() : null
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = false
|
||||
};
|
||||
|
||||
var jsonPayload = JsonSerializer.Serialize(payload, options);
|
||||
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await httpClient.PostAsync(webhookUrl, content);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var errorMessage = await response.Content.ReadAsStringAsync();
|
||||
CS2_SimpleAdmin._logger?.LogError($"Failed to send embed: {response.StatusCode} - {errorMessage}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a hexadecimal color string (e.g. "#FF0000") to its integer representation.
|
||||
/// </summary>
|
||||
/// <param name="hex">The hexadecimal color string, optionally starting with '#'.</param>
|
||||
/// <returns>An integer representing the color.</returns>
|
||||
public static int ColorFromHex(string hex)
|
||||
{
|
||||
if (hex.StartsWith($"#"))
|
||||
{
|
||||
hex = hex[1..];
|
||||
}
|
||||
|
||||
return int.Parse(hex, System.Globalization.NumberStyles.HexNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Discord embed message containing rich content such as title, description, fields, and images.
|
||||
/// </summary>
|
||||
public class Embed
|
||||
{
|
||||
public int Color { get; init; }
|
||||
public string? Title { get; init; }
|
||||
public string? Description { get; init; }
|
||||
public string? ImageUrl { get; init; }
|
||||
public string? ThumbnailUrl { get; init; }
|
||||
public Footer? Footer { get; init; }
|
||||
public string? Timestamp { get; init; }
|
||||
|
||||
public List<EmbedField> Fields { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Adds a field to the embed message.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the field.</param>
|
||||
/// <param name="value">The value or content of the field.</param>
|
||||
/// <param name="inline">Whether the field should be displayed inline with other fields.</param>
|
||||
public void AddField(string name, string value, bool inline)
|
||||
{
|
||||
var field = new EmbedField
|
||||
{
|
||||
Name = name,
|
||||
Value = value,
|
||||
Inline = inline
|
||||
};
|
||||
|
||||
Fields.Add(field);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the footer section of a Discord embed message, including optional text and icon URL.
|
||||
/// </summary>
|
||||
public class Footer
|
||||
{
|
||||
public string? Text { get; init; }
|
||||
public string? IconUrl { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a field inside a Discord embed message.
|
||||
/// </summary>
|
||||
public class EmbedField
|
||||
{
|
||||
public string? Name { get; init; }
|
||||
public string? Value { get; init; }
|
||||
public bool Inline { get; init; }
|
||||
}
|
||||
|
||||
300
CS2-SimpleAdmin/Managers/MuteManager.cs
Normal file
300
CS2-SimpleAdmin/Managers/MuteManager.cs
Normal file
@@ -0,0 +1,300 @@
|
||||
using CS2_SimpleAdmin.Database;
|
||||
using CS2_SimpleAdminApi;
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CS2_SimpleAdmin.Managers;
|
||||
|
||||
internal class MuteManager(IDatabaseProvider? databaseProvider)
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a mute entry for a specified player with detailed information.
|
||||
/// </summary>
|
||||
/// <param name="player">Player to be muted.</param>
|
||||
/// <param name="issuer">Admin issuing the mute; null if issued from console.</param>
|
||||
/// <param name="reason">Reason for muting the player.</param>
|
||||
/// <param name="time">Duration of the mute in minutes. Zero means permanent mute.</param>
|
||||
/// <param name="type">Mute type: 0 = GAG, 1 = MUTE, 2 = SILENCE.</param>
|
||||
/// <returns>Mute ID if successfully added, otherwise null.</returns>
|
||||
public async Task<int?> MutePlayer(PlayerInfo player, PlayerInfo? issuer, string reason, int time = 0, int type = 0)
|
||||
{
|
||||
if (databaseProvider == null) return null;
|
||||
|
||||
var now = Time.ActualDateTime();
|
||||
var futureTime = now.AddMinutes(time);
|
||||
|
||||
var muteType = type switch
|
||||
{
|
||||
1 => "MUTE",
|
||||
2 => "SILENCE",
|
||||
_ => "GAG"
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetAddMuteQuery(true);
|
||||
|
||||
var muteId = await connection.ExecuteScalarAsync<int?>(sql, new
|
||||
{
|
||||
playerSteamid = player.SteamId.SteamId64,
|
||||
playerName = player.Name,
|
||||
adminSteamid = issuer?.SteamId.SteamId64 ?? 0,
|
||||
adminName = issuer?.Name ?? CS2_SimpleAdmin._localizer?["sa_console"] ?? "Console",
|
||||
muteReason = reason,
|
||||
duration = time,
|
||||
ends = futureTime,
|
||||
created = now,
|
||||
type = muteType,
|
||||
serverid = CS2_SimpleAdmin.ServerId
|
||||
});
|
||||
|
||||
return muteId;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError(ex.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a mute entry for a offline player identified by their SteamID.
|
||||
/// </summary>
|
||||
/// <param name="playerSteamId">SteamID64 of the player to mute.</param>
|
||||
/// <param name="issuer">Admin issuing the mute; can be null if from console.</param>
|
||||
/// <param name="reason">Reason for the mute.</param>
|
||||
/// <param name="time">Mute duration in minutes; 0 for permanent.</param>
|
||||
/// <param name="type">Mute type: 0 = GAG, 1 = MUTE, 2 = SILENCE.</param>
|
||||
/// <returns>Mute ID if successful, otherwise null.</returns>
|
||||
public async Task<int?> AddMuteBySteamid(ulong playerSteamId, PlayerInfo? issuer, string reason, int time = 0, int type = 0)
|
||||
{
|
||||
if (databaseProvider == null) return null;
|
||||
|
||||
var now = Time.ActualDateTime();
|
||||
var futureTime = now.AddMinutes(time);
|
||||
|
||||
var muteType = type switch
|
||||
{
|
||||
1 => "MUTE",
|
||||
2 => "SILENCE",
|
||||
_ => "GAG"
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetAddMuteQuery(false);
|
||||
|
||||
var muteId = await connection.ExecuteScalarAsync<int?>(sql, new
|
||||
{
|
||||
playerSteamid = playerSteamId,
|
||||
adminSteamid = issuer?.SteamId.SteamId64 ?? 0,
|
||||
adminName = issuer?.Name ?? CS2_SimpleAdmin._localizer?["sa_console"] ?? "Console",
|
||||
muteReason = reason,
|
||||
duration = time,
|
||||
ends = futureTime,
|
||||
created = now,
|
||||
type = muteType,
|
||||
serverid = CS2_SimpleAdmin.ServerId
|
||||
});
|
||||
|
||||
return muteId;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a player with the given SteamID currently has any active mutes.
|
||||
/// </summary>
|
||||
/// <param name="steamId">SteamID64 of the player to check.</param>
|
||||
/// <returns>List of active mute records; empty list if none or on error.</returns>
|
||||
public async Task<List<dynamic>> IsPlayerMuted(string steamId)
|
||||
{
|
||||
if (databaseProvider == null) return [];
|
||||
|
||||
if (string.IsNullOrEmpty(steamId))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
if (CS2_SimpleAdmin._logger != null)
|
||||
CS2_SimpleAdmin._logger.LogCritical($"IsPlayerMuted for {steamId}");
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var currentTime = Time.ActualDateTime();
|
||||
|
||||
var sql = databaseProvider.GetIsMutedQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode, CS2_SimpleAdmin.Instance.Config.OtherSettings.TimeMode);
|
||||
|
||||
var parameters = new { PlayerSteamID = steamId, CurrentTime = currentTime, serverid = CS2_SimpleAdmin.ServerId };
|
||||
var activeMutes = (await connection.QueryAsync(sql, parameters)).ToList();
|
||||
return activeMutes;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves counts of total mutes, gags, and silences for a given player.
|
||||
/// </summary>
|
||||
/// <param name="playerInfo">Information about the player.</param>
|
||||
/// <returns>
|
||||
/// Tuple containing total mutes, total gags, and total silences respectively.
|
||||
/// Returns zeros if no data or on error.
|
||||
/// </returns>
|
||||
public async Task<(int TotalMutes, int TotalGags, int TotalSilences)> GetPlayerMutes(PlayerInfo playerInfo)
|
||||
{
|
||||
if (databaseProvider == null) return (0,0,0);
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var sql = databaseProvider.GetRetrieveMutesQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
var result = await connection.QuerySingleAsync<(int TotalMutes, int TotalGags, int TotalSilences)>(sql, new
|
||||
{
|
||||
PlayerSteamID = playerInfo.SteamId.SteamId64,
|
||||
CS2_SimpleAdmin.ServerId
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return (0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a batch of online players to update their mute status and remove expired penalties.
|
||||
/// </summary>
|
||||
/// <param name="players">List of tuples containing player SteamID, optional UserID, and slot index.</param>
|
||||
/// <returns>Task representing the asynchronous operation.</returns>
|
||||
public async Task CheckOnlineModeMutes(List<(ulong SteamID, int? UserId, int Slot)> players)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
const int batchSize = 20;
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var sql = databaseProvider.GetUpdateMutePassedQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
|
||||
for (var i = 0; i < players.Count; i += batchSize)
|
||||
{
|
||||
var batch = players.Skip(i).Take(batchSize);
|
||||
var parametersList = new List<object>();
|
||||
|
||||
foreach (var (steamId, _, _) in batch)
|
||||
{
|
||||
parametersList.Add(new { PlayerSteamID = steamId, serverid = CS2_SimpleAdmin.ServerId });
|
||||
}
|
||||
|
||||
await connection.ExecuteAsync(sql, parametersList);
|
||||
}
|
||||
|
||||
sql = databaseProvider.GetCheckExpiredMutesQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
|
||||
foreach (var (steamId, _, slot) in players)
|
||||
{
|
||||
var muteRecords = await connection.QueryAsync(sql, new { PlayerSteamID = steamId, serverid = CS2_SimpleAdmin.ServerId });
|
||||
|
||||
foreach (var muteRecord in muteRecords)
|
||||
{
|
||||
DateTime endDateTime = muteRecord.ends;
|
||||
PlayerPenaltyManager.RemovePenaltiesByDateTime(slot, endDateTime);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes active mutes for players matching the specified pattern.
|
||||
/// </summary>
|
||||
/// <param name="playerPattern">Pattern to match player names or identifiers.</param>
|
||||
/// <param name="adminSteamId">SteamID64 of the admin performing the unmute.</param>
|
||||
/// <param name="reason">Reason for unmuting the player(s).</param>
|
||||
/// <param name="type">Mute type to remove: 0 = GAG, 1 = MUTE, 2 = SILENCE.</param>
|
||||
/// <returns>Task representing the asynchronous operation.</returns>
|
||||
public async Task UnmutePlayer(string playerPattern, string adminSteamId, string reason, int type = 0)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
|
||||
if (playerPattern.Length <= 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var muteType = type switch
|
||||
{
|
||||
1 => "MUTE",
|
||||
2 => "SILENCE",
|
||||
_ => "GAG"
|
||||
};
|
||||
|
||||
var sqlRetrieveMutes =
|
||||
databaseProvider.GetRetrieveMutesQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
var mutes = await connection.QueryAsync(sqlRetrieveMutes, new { pattern = playerPattern, muteType, serverid = CS2_SimpleAdmin.ServerId });
|
||||
|
||||
var mutesList = mutes as dynamic[] ?? mutes.ToArray();
|
||||
if (mutesList.Length == 0)
|
||||
return;
|
||||
|
||||
var sqlAdmin = databaseProvider.GetUnmuteAdminIdQuery();
|
||||
var sqlInsertUnmute = databaseProvider.GetInsertUnmuteQuery(string.IsNullOrEmpty(reason));
|
||||
|
||||
var sqlAdminId = await connection.ExecuteScalarAsync<int?>(sqlAdmin, new { adminSteamId });
|
||||
var adminId = sqlAdminId ?? 0;
|
||||
|
||||
foreach (var mute in mutesList)
|
||||
{
|
||||
int muteId = mute.id;
|
||||
|
||||
int? unmuteId =
|
||||
await connection.ExecuteScalarAsync<int>(sqlInsertUnmute, new { muteId, adminId, reason });
|
||||
|
||||
var sqlUpdateMute = databaseProvider.GetUpdateMuteStatusQuery();
|
||||
await connection.ExecuteAsync(sqlUpdateMute, new { unmuteId, muteId });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expires all old mutes that have passed their duration according to current time.
|
||||
/// </summary>
|
||||
/// <returns>Task representing the asynchronous expiration operation.</returns>
|
||||
public async Task ExpireOldMutes()
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetExpireMutesQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode, CS2_SimpleAdmin.Instance.Config.OtherSettings.TimeMode);
|
||||
await connection.ExecuteAsync(sql, new { CurrentTime = Time.ActualDateTime(), serverid = CS2_SimpleAdmin.ServerId });
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogCritical("Unable to remove expired mutes");
|
||||
}
|
||||
}
|
||||
}
|
||||
668
CS2-SimpleAdmin/Managers/PermissionManager.cs
Normal file
668
CS2-SimpleAdmin/Managers/PermissionManager.cs
Normal file
@@ -0,0 +1,668 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text.Json;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using CS2_SimpleAdmin.Database;
|
||||
|
||||
namespace CS2_SimpleAdmin.Managers;
|
||||
|
||||
public class PermissionManager(IDatabaseProvider? databaseProvider)
|
||||
{
|
||||
// Unused for now
|
||||
//public static readonly ConcurrentDictionary<string, ConcurrentBag<string>> _adminCache = new ConcurrentDictionary<string, ConcurrentBag<string>>();
|
||||
// public static readonly ConcurrentDictionary<SteamID, DateTime?> AdminCache = new();
|
||||
public static readonly ConcurrentDictionary<SteamID, (DateTime? ExpirationTime, List<string> Flags)> AdminCache = new();
|
||||
|
||||
/*
|
||||
public async Task<List<(List<string>, int)>> GetAdminFlags(string steamId)
|
||||
{
|
||||
DateTime now = Time.ActualDateTime();
|
||||
|
||||
await using MySqlConnection connection = await _database.GetConnectionAsync();
|
||||
|
||||
string sql = "SELECT flags, immunity, ends FROM sa_admins WHERE player_steamid = @PlayerSteamID AND (ends IS NULL OR ends > @CurrentTime) AND (server_id IS NULL OR server_id = @serverid)";
|
||||
List<dynamic>? activeFlags = (await connection.QueryAsync(sql, new { PlayerSteamID = steamId, CurrentTime = now, serverid = CS2_SimpleAdmin.ServerId }))?.ToList();
|
||||
|
||||
if (activeFlags == null)
|
||||
{
|
||||
return new List<(List<string>, int)>();
|
||||
}
|
||||
|
||||
List<(List<string>, int)> filteredFlagsWithImmunity = [];
|
||||
|
||||
foreach (dynamic flags in activeFlags)
|
||||
{
|
||||
if (flags is not IDictionary<string, object> flagsDict)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!flagsDict.TryGetValue("flags", out var flagsValueObj) || !flagsDict.TryGetValue("immunity", out var immunityValueObj))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(flagsValueObj is string flagsValue) || !int.TryParse(immunityValueObj.ToString(), out var immunityValue))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//Console.WriteLine($"Flags: {flagsValue}, Immunity: {immunityValue}");
|
||||
|
||||
filteredFlagsWithImmunity.Add((flagsValue.Split(',').ToList(), immunityValue));
|
||||
}
|
||||
|
||||
return filteredFlagsWithImmunity;
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all players' flags and associated data asynchronously.
|
||||
/// </summary>
|
||||
/// <returns>A list of tuples containing player SteamID, name, flags, immunity, and expiration time.</returns>
|
||||
private async Task<List<(ulong, string ,List<string>, int, DateTime?)>> GetAllPlayersFlags()
|
||||
{
|
||||
if (databaseProvider == null)
|
||||
return new List<(ulong, string, List<string>, int, DateTime?)>();
|
||||
|
||||
var now = Time.ActualDateTime();
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetAdminsQuery();
|
||||
var admins = (await connection.QueryAsync(sql, new { CurrentTime = now, serverid = CS2_SimpleAdmin.ServerId })).ToList();
|
||||
|
||||
var groupedPlayers = admins
|
||||
.GroupBy(r => new { playerSteamId = r.player_steamid, playerName = r.player_name, r.immunity, r.ends })
|
||||
.Select(g =>
|
||||
{
|
||||
ulong steamId = g.Key.playerSteamId switch
|
||||
{
|
||||
long l => (ulong)l,
|
||||
int i => (ulong)i,
|
||||
string s when ulong.TryParse(s, out var parsed) => parsed,
|
||||
_ => 0UL
|
||||
};
|
||||
|
||||
int immunity = g.Key.immunity switch
|
||||
{
|
||||
int i => i,
|
||||
string s when int.TryParse(s, out var parsed) => parsed,
|
||||
_ => 0
|
||||
};
|
||||
|
||||
DateTime? ends = g.Key.ends as DateTime?;
|
||||
|
||||
string playerName = g.Key.playerName as string ?? string.Empty;
|
||||
|
||||
// tutaj zakładamy, że Dapper zwraca już string (nie dynamic)
|
||||
var flags = g.Select(r => r.flag as string ?? string.Empty)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
return (steamId, playerName, flags, immunity, ends);
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return groupedPlayers;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError("Unable to load admins from database! {exception}", ex.Message);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
public async Task<Dictionary<int, Tuple<List<string>, List<Tuple<string, DateTime?>>, int>>> GetAllGroupsFlags()
|
||||
{
|
||||
try
|
||||
{
|
||||
await using MySqlConnection connection = await _database.GetConnectionAsync();
|
||||
|
||||
string sql = "SELECT group_id FROM sa_groups_servers WHERE server_id = @serverid";
|
||||
var groupIds = connection.Query<int>(sql, new { serverid = CS2_SimpleAdmin.ServerId }).ToList();
|
||||
|
||||
sql = @"
|
||||
SELECT g.group_id, f.flag
|
||||
FROM sa_groups_flags f
|
||||
JOIN sa_groups_servers g ON f.group_id = g.group_id
|
||||
WHERE g.server_id = @serverid";
|
||||
|
||||
var groupFlagData = connection.Query(sql, new { serverid = CS2_SimpleAdmin.ServerId }).ToList();
|
||||
|
||||
if (groupIds.Count == 0 || groupFlagData.Count == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var groupInfoDictionary = new Dictionary<int, Tuple<List<string>, List<Tuple<string, DateTime?>>, int>>();
|
||||
|
||||
foreach (var groupId in groupIds)
|
||||
{
|
||||
groupInfoDictionary[groupId] = new Tuple<List<string>, List<Tuple<string, DateTime?>>, int>([], [], 0);
|
||||
}
|
||||
|
||||
foreach (var row in groupFlagData)
|
||||
{
|
||||
var groupId = (int)row.group_id;
|
||||
var flag = (string)row.flag;
|
||||
|
||||
groupInfoDictionary[groupId].Item1.Add(flag);
|
||||
}
|
||||
|
||||
sql = @"
|
||||
SELECT a.group_id, a.player_steamid, a.ends, g.immunity, g.name
|
||||
FROM sa_admins a
|
||||
JOIN sa_groups g ON a.group_id = g.id
|
||||
WHERE a.group_id IN @groupIds";
|
||||
|
||||
var playerData = (await connection.QueryAsync(sql, new { groupIds })).ToList();
|
||||
|
||||
foreach (var row in playerData)
|
||||
{
|
||||
var groupId = (int)row.group_id;
|
||||
var playerSteamid = (string)row.player_steamid;
|
||||
var ends = row.ends as DateTime?;
|
||||
var immunity = (int)row.immunity;
|
||||
|
||||
groupInfoDictionary[groupId].Item2.Add(new Tuple<string, DateTime?>(playerSteamid, ends));
|
||||
groupInfoDictionary[groupId] = new Tuple<List<string>, List<Tuple<string, DateTime?>>, int>(groupInfoDictionary[groupId].Item1, groupInfoDictionary[groupId].Item2, immunity);
|
||||
}
|
||||
|
||||
return groupInfoDictionary;
|
||||
}
|
||||
catch { }
|
||||
|
||||
return [];
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all groups' data including flags and immunity asynchronously.
|
||||
/// </summary>
|
||||
/// <returns>A dictionary with group names as keys and tuples of flags and immunity as values.</returns>
|
||||
private async Task<Dictionary<string, (List<string>, int)>> GetAllGroupsData()
|
||||
{
|
||||
if (databaseProvider == null) return [];
|
||||
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
;
|
||||
try
|
||||
{
|
||||
// var sql = "SELECT group_id FROM sa_groups_servers WHERE (server_id = @serverid OR server_id IS NULL)";
|
||||
// var groupDataSql = connection.Query<int>(sql, new { serverid = CS2_SimpleAdmin.ServerId }).ToList();
|
||||
|
||||
var sql = databaseProvider.GetGroupsQuery();
|
||||
var groupData = connection.Query(sql, new { serverid = CS2_SimpleAdmin.ServerId }).ToList();
|
||||
if (groupData.Count == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var groupInfoDictionary = new Dictionary<string, (List<string>, int)>();
|
||||
foreach (var row in groupData)
|
||||
{
|
||||
var groupName = (string)row.group_name;
|
||||
var flag = (string)row.flag;
|
||||
var immunity = (int)row.immunity;
|
||||
|
||||
// Check if the group name already exists in the dictionary
|
||||
if (!groupInfoDictionary.TryGetValue(groupName, out (List<string>, int) value))
|
||||
{
|
||||
value = ([], immunity);
|
||||
// If it doesn't exist, add a new entry with an empty list of flags and immunity
|
||||
groupInfoDictionary[groupName] = value;
|
||||
}
|
||||
|
||||
value.Item1.Add(flag);
|
||||
}
|
||||
|
||||
return groupInfoDictionary;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError("Unable to load groups from database! {exception}", ex.Message);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a JSON file containing groups data asynchronously.
|
||||
/// </summary>
|
||||
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
|
||||
public async Task CrateGroupsJsonFile()
|
||||
{
|
||||
var groupsData = await GetAllGroupsData();
|
||||
var jsonData = new Dictionary<string, object>();
|
||||
|
||||
foreach (var kvp in groupsData)
|
||||
{
|
||||
var groupData = new Dictionary<string, object>
|
||||
{
|
||||
["flags"] = kvp.Value.Item1,
|
||||
["immunity"] = kvp.Value.Item2
|
||||
};
|
||||
|
||||
jsonData[kvp.Key] = groupData;
|
||||
}
|
||||
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(jsonData, options);
|
||||
var filePath = Path.Combine(CS2_SimpleAdmin.Instance.ModuleDirectory, "data", "groups.json");
|
||||
await File.WriteAllTextAsync(filePath, json);
|
||||
}
|
||||
|
||||
/*
|
||||
public async Task GiveAllGroupsFlags()
|
||||
{
|
||||
Dictionary<int, Tuple<List<string>, List<Tuple<string, DateTime?>>, int>> groupFlags = await GetAllGroupsFlags();
|
||||
|
||||
foreach (var kvp in groupFlags)
|
||||
{
|
||||
var flags = kvp.Value.Item1;
|
||||
var players = kvp.Value.Item2;
|
||||
int immunity = kvp.Value.Item3;
|
||||
|
||||
foreach (var playerTuple in players)
|
||||
{
|
||||
var steamIdStr = playerTuple.Item1;
|
||||
var ends = playerTuple.Item2;
|
||||
|
||||
if (!string.IsNullOrEmpty(steamIdStr) && SteamID.TryParse(steamIdStr, out var steamId) && steamId != null)
|
||||
{
|
||||
if (!_adminCache.ContainsKey(steamId))
|
||||
{
|
||||
_adminCache.TryAdd(steamId, ends);
|
||||
}
|
||||
|
||||
Helper.GivePlayerFlags(steamId, flags, (uint)immunity);
|
||||
// Often need to call 2 times
|
||||
Helper.GivePlayerFlags(steamId, flags, (uint)immunity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
/*
|
||||
public async Task GiveAllFlags()
|
||||
{
|
||||
List<(string, string, List<string>, int, DateTime?)> allPlayers = await GetAllPlayersFlags();
|
||||
|
||||
foreach (var record in allPlayers)
|
||||
{
|
||||
string steamIdStr = record.Item1;
|
||||
List<string> flags = record.Item2;
|
||||
int immunity = record.Item3;
|
||||
|
||||
DateTime? ends = record.Item4;
|
||||
|
||||
if (!string.IsNullOrEmpty(steamIdStr) && SteamID.TryParse(steamIdStr, out var steamId) && steamId != null)
|
||||
{
|
||||
if (!_adminCache.ContainsKey(steamId))
|
||||
{
|
||||
_adminCache.TryAdd(steamId, ends);
|
||||
//_adminCacheTimestamps.Add(steamId, ends);
|
||||
}
|
||||
|
||||
Helper.GivePlayerFlags(steamId, flags, (uint)immunity);
|
||||
// Often need to call 2 times
|
||||
Helper.GivePlayerFlags(steamId, flags, (uint)immunity);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Creates a JSON file containing admins data asynchronously.
|
||||
/// </summary>
|
||||
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
|
||||
public async Task CreateAdminsJsonFile()
|
||||
{
|
||||
List<(ulong identity, string name, List<string> flags, int immunity, DateTime? ends)> allPlayers = await GetAllPlayersFlags();
|
||||
var validPlayers = allPlayers
|
||||
.Where(player => SteamID.TryParse(player.identity.ToString(), out _))
|
||||
.ToList();
|
||||
|
||||
// foreach (var player in allPlayers)
|
||||
// {
|
||||
// var (steamId, name, flags, immunity, ends) = player;
|
||||
//
|
||||
// Console.WriteLine($"Player SteamID: {steamId}");
|
||||
// Console.WriteLine($"Player Name: {name}");
|
||||
// Console.WriteLine($"Flags: {string.Join(", ", flags)}");
|
||||
// Console.WriteLine($"Immunity: {immunity}");
|
||||
// Console.WriteLine($"Ends: {(ends.HasValue ? ends.Value.ToString("yyyy-MM-dd HH:mm:ss") : "Never")}");
|
||||
// Console.WriteLine();
|
||||
// }
|
||||
|
||||
var jsonData = validPlayers
|
||||
.GroupBy(player => player.name) // Group by player name
|
||||
.ToDictionary(
|
||||
group => group.Key, // Use the player name as key
|
||||
object (group) =>
|
||||
{
|
||||
// Consolidate data for players with same name
|
||||
var consolidatedData = group.Aggregate(
|
||||
new
|
||||
{
|
||||
identity = string.Empty,
|
||||
immunity = 0,
|
||||
flags = new List<string>(),
|
||||
groups = new List<string>()
|
||||
},
|
||||
(acc, player) =>
|
||||
{
|
||||
// Merge identities
|
||||
if (string.IsNullOrEmpty(acc.identity) && !string.IsNullOrEmpty(player.identity.ToString()))
|
||||
{
|
||||
acc = acc with { identity = player.identity.ToString() };
|
||||
}
|
||||
|
||||
// Combine immunities by maximum value
|
||||
acc = acc with { immunity = Math.Max(acc.immunity, player.immunity) };
|
||||
|
||||
// Combine flags and groups
|
||||
acc = acc with
|
||||
{
|
||||
flags = acc.flags.Concat(player.flags.Where(flag => flag.StartsWith($"@"))).Distinct().ToList(),
|
||||
groups = acc.groups.Concat(player.flags.Where(flag => flag.StartsWith($"#"))).Distinct().ToList()
|
||||
};
|
||||
|
||||
return acc;
|
||||
});
|
||||
|
||||
Server.NextWorldUpdate(() =>
|
||||
{
|
||||
var keysToRemove = new List<SteamID>();
|
||||
|
||||
foreach (var steamId in AdminCache.Keys.ToList())
|
||||
{
|
||||
var data = AdminManager.GetPlayerAdminData(steamId);
|
||||
if (data != null)
|
||||
{
|
||||
var flagsArray = AdminCache[steamId].Flags.ToArray();
|
||||
AdminManager.RemovePlayerPermissions(steamId, flagsArray);
|
||||
AdminManager.RemovePlayerFromGroup(steamId, true, flagsArray);
|
||||
}
|
||||
|
||||
keysToRemove.Add(steamId);
|
||||
}
|
||||
|
||||
foreach (var steamId in keysToRemove)
|
||||
{
|
||||
if (!AdminCache.TryRemove(steamId, out _)) continue;
|
||||
|
||||
var data = AdminManager.GetPlayerAdminData(steamId);
|
||||
if (data == null) continue;
|
||||
if (data.Flags.Count != 0 && data.Groups.Count != 0) continue;
|
||||
|
||||
AdminManager.ClearPlayerPermissions(steamId);
|
||||
AdminManager.RemovePlayerAdminData(steamId);
|
||||
}
|
||||
|
||||
foreach (var player in group)
|
||||
{
|
||||
if (SteamID.TryParse(player.identity.ToString(), out var steamId) && steamId != null)
|
||||
{
|
||||
AdminCache.TryAdd(steamId, (player.ends, player.flags));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Server.NextFrameAsync(() =>
|
||||
// {
|
||||
// for (var index = 0; index < AdminCache.Keys.ToList().Count; index++)
|
||||
// {
|
||||
// var steamId = AdminCache.Keys.ToList()[index];
|
||||
//
|
||||
// var data = AdminManager.GetPlayerAdminData(steamId);
|
||||
// if (data != null)
|
||||
// {
|
||||
// AdminManager.RemovePlayerPermissions(steamId, AdminCache[steamId].Flags.ToArray());
|
||||
// AdminManager.RemovePlayerFromGroup(steamId, true, AdminCache[steamId].Flags.ToArray());
|
||||
// }
|
||||
//
|
||||
// if (!AdminCache.TryRemove(steamId, out _)) continue;
|
||||
//
|
||||
// if (data == null) continue;
|
||||
// if (data.Flags.ToList().Count != 0 && data.Groups.ToList().Count != 0)
|
||||
// continue;
|
||||
//
|
||||
// AdminManager.ClearPlayerPermissions(steamId);
|
||||
// AdminManager.RemovePlayerAdminData(steamId);
|
||||
// }
|
||||
//
|
||||
// foreach (var player in group)
|
||||
// {
|
||||
// SteamID.TryParse(player.identity, out var steamId);
|
||||
// if (steamId == null) continue;
|
||||
// AdminCache.TryAdd(steamId, (player.ends, player.flags));
|
||||
// }
|
||||
// });
|
||||
|
||||
return consolidatedData;
|
||||
});
|
||||
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(jsonData, options);
|
||||
var filePath = Path.Combine(CS2_SimpleAdmin.Instance.ModuleDirectory, "data", "admins.json");
|
||||
|
||||
await File.WriteAllTextAsync(filePath, json);
|
||||
|
||||
//await File.WriteAllTextAsync(CS2_SimpleAdmin.Instance.ModuleDirectory + "/data/admins.json", json);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes an admin by their SteamID from the database asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="playerSteamId">The SteamID of the admin to delete.</param>
|
||||
/// <param name="globalDelete">Whether to delete the admin globally or only for the current server.</param>
|
||||
public async Task DeleteAdminBySteamId(string playerSteamId, bool globalDelete = false)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
if (string.IsNullOrEmpty(playerSteamId)) return;
|
||||
|
||||
//_adminCache.TryRemove(playerSteamId, out _);
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetDeleteAdminQuery(globalDelete);
|
||||
await connection.ExecuteAsync(sql, new { PlayerSteamID = playerSteamId, CS2_SimpleAdmin.ServerId });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new admin with specified details asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="playerSteamId">SteamID of the admin.</param>
|
||||
/// <param name="playerName">Name of the admin.</param>
|
||||
/// <param name="flagsList">List of flags assigned to the admin.</param>
|
||||
/// <param name="immunity">Immunity level.</param>
|
||||
/// <param name="time">Duration in minutes for admin expiration; 0 means permanent.</param>
|
||||
/// <param name="globalAdmin">Whether the admin is global or server-specific.</param>
|
||||
public async Task AddAdminBySteamId(string playerSteamId, string playerName, List<string> flagsList, int immunity = 0, int time = 0, bool globalAdmin = false)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
|
||||
if (string.IsNullOrEmpty(playerSteamId) || flagsList.Count == 0) return;
|
||||
|
||||
var now = Time.ActualDateTime();
|
||||
DateTime? futureTime;
|
||||
|
||||
if (time != 0)
|
||||
futureTime = now.AddMinutes(time);
|
||||
else
|
||||
futureTime = null;
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
// Insert admin into sa_admins table
|
||||
var insertAdminSql = databaseProvider.GetAddAdminQuery();
|
||||
var adminId = await connection.ExecuteScalarAsync<int>(insertAdminSql, new
|
||||
{
|
||||
playerSteamId,
|
||||
playerName,
|
||||
immunity,
|
||||
ends = futureTime,
|
||||
created = now,
|
||||
serverid = globalAdmin ? null : CS2_SimpleAdmin.ServerId
|
||||
});
|
||||
|
||||
// Insert flags into sa_admins_flags table
|
||||
foreach (var flag in flagsList)
|
||||
{
|
||||
// if (flag.StartsWith($"#"))
|
||||
// {
|
||||
// // const string sql = "SELECT id FROM `sa_groups` WHERE name = @groupName";
|
||||
// // var groupId = await connection.QuerySingleOrDefaultAsync<int?>(sql, new { groupName = flag });
|
||||
//
|
||||
// var sql = databaseProvider.GetGroupIdByNameQuery();
|
||||
// var groupId = await connection.QuerySingleOrDefaultAsync<int?>(sql, new { groupName = flag, CS2_SimpleAdmin.ServerId });
|
||||
//
|
||||
// if (groupId != null)
|
||||
// {
|
||||
// var updateAdminGroup = "UPDATE `sa_admins` SET group_id = @groupId WHERE id = @adminId";
|
||||
// await connection.ExecuteAsync(updateAdminGroup, new
|
||||
// {
|
||||
// groupId,
|
||||
// adminId
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
var insertFlagsSql = databaseProvider.GetAddAdminFlagsQuery();
|
||||
await connection.ExecuteAsync(insertFlagsSql, new
|
||||
{
|
||||
adminId,
|
||||
flag
|
||||
});
|
||||
}
|
||||
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.ReloadAdmins(null);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new group with flags and immunity asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="groupName">Name of the group.</param>
|
||||
/// <param name="flagsList">List of flags assigned to the group.</param>
|
||||
/// <param name="immunity">Immunity level of the group.</param>
|
||||
/// <param name="globalGroup">Whether the group is global or server-specific.</param>
|
||||
public async Task AddGroup(string groupName, List<string> flagsList, int immunity = 0, bool globalGroup = false)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
|
||||
if (string.IsNullOrEmpty(groupName) || flagsList.Count == 0) return;
|
||||
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
try
|
||||
{
|
||||
// Insert group into sa_groups table
|
||||
var insertGroup = databaseProvider.GetAddGroupQuery();
|
||||
var groupId = await connection.ExecuteScalarAsync<int>(insertGroup, new
|
||||
{
|
||||
groupName,
|
||||
immunity
|
||||
});
|
||||
|
||||
// Insert flags into sa_groups_flags table
|
||||
foreach (var flag in flagsList)
|
||||
{
|
||||
var insertFlagsSql = databaseProvider.GetAddGroupFlagsQuery();
|
||||
|
||||
await connection.ExecuteAsync(insertFlagsSql, new
|
||||
{
|
||||
groupId,
|
||||
flag
|
||||
});
|
||||
}
|
||||
|
||||
var insertGroupServer = databaseProvider.GetAddGroupServerQuery();
|
||||
await connection.ExecuteAsync(insertGroupServer, new { groupId, server_id = globalGroup ? null : CS2_SimpleAdmin.ServerId });
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.ReloadAdmins(null);
|
||||
});
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError("Problem with loading admins: {exception}", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a group by name asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="groupName">Name of the group to delete.</param>
|
||||
public async Task DeleteGroup(string groupName)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
|
||||
if (string.IsNullOrEmpty(groupName)) return;
|
||||
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
try
|
||||
{
|
||||
var sql = databaseProvider.GetDeleteGroupQuery();
|
||||
await connection.ExecuteAsync(sql, new { groupName });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes admins whose permissions have expired asynchronously.
|
||||
/// </summary>
|
||||
public async Task DeleteOldAdmins()
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var sql = databaseProvider.GetDeleteOldAdminsQuery();
|
||||
await connection.ExecuteAsync(sql, new { CurrentTime = Time.ActualDateTime() });
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogCritical("Unable to remove expired admins");
|
||||
}
|
||||
}
|
||||
}
|
||||
450
CS2-SimpleAdmin/Managers/PlayerManager.cs
Normal file
450
CS2-SimpleAdmin/Managers/PlayerManager.cs
Normal file
@@ -0,0 +1,450 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Timers;
|
||||
using CounterStrikeSharp.API.ValveConstants.Protobuf;
|
||||
using CS2_SimpleAdminApi;
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ZLinq;
|
||||
|
||||
namespace CS2_SimpleAdmin.Managers;
|
||||
|
||||
internal class PlayerManager
|
||||
{
|
||||
private readonly SemaphoreSlim _loadPlayerSemaphore = new(5);
|
||||
private readonly CS2_SimpleAdminConfig _config = CS2_SimpleAdmin.Instance.Config;
|
||||
|
||||
/// <summary>
|
||||
/// Loads and initializes player data when a client connects.
|
||||
/// </summary>
|
||||
/// <param name="player">The <see cref="CCSPlayerController"/> instance representing the connecting player.</param>
|
||||
/// <param name="fullConnect">
|
||||
/// Determines whether to perform a full synchronization of player data.
|
||||
/// If true, full checks (bans, IP history, penalties, warns, mutes) will be loaded and applied.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// This method validates the player's identity, checks for bans, updates the IP history table,
|
||||
/// loads penalties (mutes/gags/warns), and optionally notifies admin players about the connecting player's penalties.
|
||||
/// </remarks>
|
||||
public void LoadPlayerData(CCSPlayerController player, bool fullConnect = false)
|
||||
{
|
||||
if (!player.UserId.HasValue)
|
||||
{
|
||||
Helper.KickPlayer(player, NetworkDisconnectionReason.NETWORK_DISCONNECT_REJECT_INVALIDCONNECTION);
|
||||
return;
|
||||
}
|
||||
|
||||
var userId = player.UserId.Value;
|
||||
var slot = player.Slot;
|
||||
var steamId = player.SteamID;
|
||||
var playerName = !string.IsNullOrEmpty(player.PlayerName)
|
||||
? player.PlayerName
|
||||
: CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown";
|
||||
var ipAddress = player.IpAddress?.Split(":")[0];
|
||||
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null || CS2_SimpleAdmin.Instance.CacheManager == null) return;
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await _loadPlayerSemaphore.WaitAsync();
|
||||
|
||||
if (!CS2_SimpleAdmin.PlayersInfo.ContainsKey(steamId))
|
||||
{
|
||||
var isBanned = CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType switch
|
||||
{
|
||||
0 => CS2_SimpleAdmin.Instance.CacheManager.IsPlayerBanned(playerName, steamId, null),
|
||||
_ => CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp
|
||||
? CS2_SimpleAdmin.Instance.CacheManager.IsPlayerOrAnyIpBanned(playerName, steamId,
|
||||
ipAddress)
|
||||
: CS2_SimpleAdmin.Instance.CacheManager.IsPlayerBanned(playerName, steamId, ipAddress)
|
||||
};
|
||||
|
||||
// CS2_SimpleAdmin._logger?.LogInformation($"Player {playerName} ({steamId} - {ipAddress}) is banned? {isBanned.ToString()}");
|
||||
|
||||
if (isBanned)
|
||||
{
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
// CS2_SimpleAdmin._logger?.LogInformation($"Kicking {playerName}");
|
||||
Helper.KickPlayer(userId, NetworkDisconnectionReason.NETWORK_DISCONNECT_REJECT_BANNED);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (fullConnect)
|
||||
{
|
||||
var playerInfo = new PlayerInfo(userId, slot, new SteamID(steamId), playerName, ipAddress);
|
||||
CS2_SimpleAdmin.PlayersInfo[steamId] = playerInfo;
|
||||
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
if (!CS2_SimpleAdmin.CachedPlayers.Contains(player))
|
||||
CS2_SimpleAdmin.CachedPlayers.Add(player);
|
||||
});
|
||||
|
||||
if (_config.OtherSettings.CheckMultiAccountsByIp && ipAddress != null &&
|
||||
CS2_SimpleAdmin.PlayersInfo[steamId] != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
|
||||
if (CS2_SimpleAdmin.Instance.CacheManager.HasIpForPlayer(
|
||||
steamId, ipAddress))
|
||||
{
|
||||
const string updateQuery = """
|
||||
UPDATE `sa_players_ips`
|
||||
SET used_at = CURRENT_TIMESTAMP,
|
||||
name = @playerName
|
||||
WHERE steamid = @SteamID AND address = @IPAddress;
|
||||
""";
|
||||
await connection.ExecuteAsync(updateQuery, new
|
||||
{
|
||||
playerName,
|
||||
SteamID = CS2_SimpleAdmin.PlayersInfo[steamId].SteamId.SteamId64,
|
||||
IPAddress = IpHelper.IpToUint(ipAddress)
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
const string selectQuery =
|
||||
"SELECT COUNT(*) FROM `sa_players_ips` WHERE steamid = @SteamID AND address = @IPAddress;";
|
||||
var recordExists = await connection.ExecuteScalarAsync<int>(selectQuery, new
|
||||
{
|
||||
SteamID = CS2_SimpleAdmin.PlayersInfo[steamId].SteamId.SteamId64,
|
||||
IPAddress = IpHelper.IpToUint(ipAddress)
|
||||
});
|
||||
|
||||
if (recordExists > 0)
|
||||
{
|
||||
const string updateQuery = """
|
||||
UPDATE `sa_players_ips`
|
||||
SET used_at = CURRENT_TIMESTAMP,
|
||||
name = @playerName
|
||||
WHERE steamid = @SteamID AND address = @IPAddress;
|
||||
""";
|
||||
await connection.ExecuteAsync(updateQuery, new
|
||||
{
|
||||
playerName,
|
||||
SteamID = CS2_SimpleAdmin.PlayersInfo[steamId].SteamId.SteamId64,
|
||||
IPAddress = IpHelper.IpToUint(ipAddress)
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
const string insertQuery = """
|
||||
INSERT INTO `sa_players_ips` (steamid, name, address, used_at)
|
||||
VALUES (@SteamID, @playerName, @IPAddress, CURRENT_TIMESTAMP);
|
||||
""";
|
||||
await connection.ExecuteAsync(insertQuery, new
|
||||
{
|
||||
SteamID = CS2_SimpleAdmin.PlayersInfo[steamId].SteamId.SteamId64,
|
||||
playerName,
|
||||
IPAddress = IpHelper.IpToUint(ipAddress)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError(
|
||||
$"Unable to save ip address for {playerInfo.Name} ({ipAddress}) {ex.Message}");
|
||||
}
|
||||
|
||||
playerInfo.AccountsAssociated =
|
||||
CS2_SimpleAdmin.Instance.CacheManager?.GetAccountsByIp(ipAddress).AsValueEnumerable()
|
||||
.Select(x => (x.SteamId, x.PlayerName)).ToList() ?? [];
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// var isBanned = CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0
|
||||
// ? CS2_SimpleAdmin.Instance.CacheManager.IsPlayerBanned(
|
||||
// CS2_SimpleAdmin.PlayersInfo[userId].SteamId.SteamId64.ToString(), null)
|
||||
// : CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp
|
||||
// ? CS2_SimpleAdmin.Instance.CacheManager.IsPlayerOrAnyIpBanned(CS2_SimpleAdmin
|
||||
// .PlayersInfo[userId].SteamId.SteamId64)
|
||||
// : CS2_SimpleAdmin.Instance.CacheManager.IsPlayerBanned(CS2_SimpleAdmin.PlayersInfo[userId].SteamId.SteamId64.ToString(), ipAddress);
|
||||
|
||||
if (CS2_SimpleAdmin.PlayersInfo.TryGetValue(steamId, out PlayerInfo? value)) // Temp skip
|
||||
{
|
||||
var warns = await CS2_SimpleAdmin.Instance.WarnManager.GetPlayerWarns(value, false);
|
||||
var (totalMutes, totalGags, totalSilences) =
|
||||
await CS2_SimpleAdmin.Instance.MuteManager.GetPlayerMutes(value);
|
||||
value.TotalBans = CS2_SimpleAdmin.Instance.CacheManager
|
||||
?.GetPlayerBansBySteamId(value.SteamId.SteamId64)
|
||||
.Count ?? 0;
|
||||
value.TotalMutes = totalMutes;
|
||||
value.TotalGags = totalGags;
|
||||
value.TotalSilences = totalSilences;
|
||||
value.TotalWarns = warns.Count;
|
||||
|
||||
var activeMutes =
|
||||
await CS2_SimpleAdmin.Instance.MuteManager.IsPlayerMuted(value.SteamId.SteamId64
|
||||
.ToString());
|
||||
|
||||
if (activeMutes.Count > 0)
|
||||
{
|
||||
foreach (var mute in activeMutes)
|
||||
{
|
||||
string muteType = mute.type;
|
||||
DateTime ends = mute.ends;
|
||||
int duration = mute.duration;
|
||||
switch (muteType)
|
||||
{
|
||||
// Apply mute penalty based on mute type
|
||||
case "GAG":
|
||||
PlayerPenaltyManager.AddPenalty(
|
||||
CS2_SimpleAdmin.PlayersInfo[steamId].Slot,
|
||||
PenaltyType.Gag, ends, duration);
|
||||
// if (CS2_SimpleAdmin._localizer != null)
|
||||
// mutesList[PenaltyType.Gag].Add(CS2_SimpleAdmin._localizer["sa_player_penalty_info_active_gag", ends.ToLocalTime().ToString(CultureInfo.CurrentCulture)]);
|
||||
break;
|
||||
case "MUTE":
|
||||
PlayerPenaltyManager.AddPenalty(
|
||||
CS2_SimpleAdmin.PlayersInfo[steamId].Slot,
|
||||
PenaltyType.Mute, ends, duration);
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
player.VoiceFlags = VoiceFlags.Muted;
|
||||
});
|
||||
// if (CS2_SimpleAdmin._localizer != null)
|
||||
// mutesList[PenaltyType.Mute].Add(CS2_SimpleAdmin._localizer["sa_player_penalty_info_active_mute", ends.ToLocalTime().ToString(CultureInfo.InvariantCulture)]);
|
||||
break;
|
||||
default:
|
||||
PlayerPenaltyManager.AddPenalty(
|
||||
CS2_SimpleAdmin.PlayersInfo[steamId].Slot,
|
||||
PenaltyType.Silence, ends, duration);
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
player.VoiceFlags = VoiceFlags.Muted;
|
||||
});
|
||||
// if (CS2_SimpleAdmin._localizer != null)
|
||||
// mutesList[PenaltyType.Silence].Add(CS2_SimpleAdmin._localizer["sa_player_penalty_info_active_silence", ends.ToLocalTime().ToString(CultureInfo.CurrentCulture)]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.NotifyPenaltiesToAdminOnConnect)
|
||||
{
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
foreach (var admin in Helper.GetValidPlayers()
|
||||
.Where(p => (AdminManager.PlayerHasPermissions(
|
||||
new SteamID(p.SteamID),
|
||||
"@css/kick") ||
|
||||
AdminManager.PlayerHasPermissions(
|
||||
new SteamID(p.SteamID),
|
||||
"@css/ban")) &&
|
||||
p.Connected == PlayerConnectedState.PlayerConnected &&
|
||||
!CS2_SimpleAdmin.AdminDisabledJoinComms
|
||||
.Contains(p.SteamID)))
|
||||
{
|
||||
if (CS2_SimpleAdmin._localizer == null || admin == player) continue;
|
||||
admin.SendLocalizedMessage(CS2_SimpleAdmin._localizer,
|
||||
"sa_admin_penalty_info",
|
||||
player.PlayerName,
|
||||
CS2_SimpleAdmin.PlayersInfo[steamId].TotalBans,
|
||||
CS2_SimpleAdmin.PlayersInfo[steamId].TotalGags,
|
||||
CS2_SimpleAdmin.PlayersInfo[steamId].TotalMutes,
|
||||
CS2_SimpleAdmin.PlayersInfo[steamId].TotalSilences,
|
||||
CS2_SimpleAdmin.PlayersInfo[steamId].TotalWarns
|
||||
);
|
||||
|
||||
if (CS2_SimpleAdmin.PlayersInfo[steamId].AccountsAssociated.Count >= 2)
|
||||
{
|
||||
var associatedAcccountsChunks =
|
||||
CS2_SimpleAdmin.PlayersInfo[steamId].AccountsAssociated.ChunkBy(5)
|
||||
.ToList();
|
||||
foreach (var chunk in associatedAcccountsChunks)
|
||||
{
|
||||
admin.SendLocalizedMessage(CS2_SimpleAdmin._localizer,
|
||||
"sa_admin_associated_accounts",
|
||||
player.PlayerName,
|
||||
string.Join(", ",
|
||||
chunk.Select(a => $"{a.PlayerName} ({a.SteamId})"))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError("Error processing player connection: {exception}",
|
||||
ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_loadPlayerSemaphore.Release();
|
||||
}
|
||||
});
|
||||
if (CS2_SimpleAdmin.RenamedPlayers.TryGetValue(player.SteamID, out var name))
|
||||
{
|
||||
player.Rename(name);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Periodically checks the status of online players and applies timers for speed, gravity,
|
||||
/// and penalty expiration validation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method registers two repeating timers:
|
||||
/// <list type="bullet">
|
||||
/// <item><description>One short-interval timer to update speed/gravity modifications applied to players.</description></item>
|
||||
/// <item><description>
|
||||
/// One long-interval timer (default 61 seconds) to expire bans, mutes, warns, refresh caches,
|
||||
/// and remove outdated penalties from connected players.
|
||||
/// </description></item>
|
||||
/// </list>
|
||||
/// Additionally, banned players still online are kicked, and admins may be updated about mute statuses based on the configured time mode.
|
||||
/// </remarks>
|
||||
public void CheckPlayersTimer()
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.AddTimer(0.12f, () =>
|
||||
{
|
||||
if (CS2_SimpleAdmin.SpeedPlayers.Count > 0)
|
||||
{
|
||||
foreach (var (player, speed) in CS2_SimpleAdmin.SpeedPlayers)
|
||||
{
|
||||
if (player is { IsValid: true, Connected: PlayerConnectedState.PlayerConnected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE })
|
||||
{
|
||||
player.SetSpeed(speed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CS2_SimpleAdmin.GravityPlayers.Count > 0)
|
||||
{
|
||||
foreach (var (player, gravity) in CS2_SimpleAdmin.GravityPlayers)
|
||||
{
|
||||
if (player is { IsValid: true, Connected: PlayerConnectedState.PlayerConnected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE })
|
||||
{
|
||||
player.SetGravity(gravity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, TimerFlags.REPEAT);
|
||||
|
||||
CS2_SimpleAdmin.Instance.PlayersTimer = CS2_SimpleAdmin.Instance.AddTimer(61.0f, () =>
|
||||
{
|
||||
#if DEBUG
|
||||
CS2_SimpleAdmin._logger?.LogCritical("[OnMapStart] Expired check");
|
||||
#endif
|
||||
if (CS2_SimpleAdmin.DatabaseProvider == null)
|
||||
return;
|
||||
|
||||
var tempPlayers = Helper.GetValidPlayers()
|
||||
.Select(p => new
|
||||
{
|
||||
p.PlayerName, p.SteamID, p.IpAddress, p.UserId, p.Slot,
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var pluginInstance = CS2_SimpleAdmin.Instance;
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var expireTasks = new[]
|
||||
{
|
||||
pluginInstance.BanManager.ExpireOldBans(),
|
||||
pluginInstance.MuteManager.ExpireOldMutes(),
|
||||
pluginInstance.WarnManager.ExpireOldWarns(),
|
||||
pluginInstance.CacheManager?.RefreshCacheAsync() ?? Task.CompletedTask,
|
||||
pluginInstance.PermissionManager.DeleteOldAdmins()
|
||||
};
|
||||
|
||||
await Task.WhenAll(expireTasks);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError($"Error processing players timer tasks: {ex.Message}");
|
||||
|
||||
if (ex is AggregateException aggregate)
|
||||
{
|
||||
foreach (var inner in aggregate.InnerExceptions)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError($"Inner exception: {inner.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pluginInstance.CacheManager == null)
|
||||
return;
|
||||
|
||||
var bannedPlayers = tempPlayers.AsValueEnumerable()
|
||||
.Where(player =>
|
||||
{
|
||||
var playerName = player.PlayerName;
|
||||
var steamId = player.SteamID;
|
||||
var ip = player.IpAddress?.Split(':')[0];
|
||||
|
||||
return CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType switch
|
||||
{
|
||||
0 => pluginInstance.CacheManager.IsPlayerBanned(playerName, steamId, null),
|
||||
_ => CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp
|
||||
? pluginInstance.CacheManager.IsPlayerOrAnyIpBanned(playerName, steamId, ip)
|
||||
: pluginInstance.CacheManager.IsPlayerBanned(playerName, steamId, ip)
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
if (bannedPlayers.Count > 0)
|
||||
{
|
||||
foreach (var player in bannedPlayers)
|
||||
{
|
||||
if (!player.UserId.HasValue) continue;
|
||||
await Server.NextWorldUpdateAsync(() => Helper.KickPlayer((int)player.UserId, NetworkDisconnectionReason.NETWORK_DISCONNECT_REJECT_BANNED));
|
||||
}
|
||||
}
|
||||
|
||||
if (_config.OtherSettings.TimeMode == 0)
|
||||
{
|
||||
var onlinePlayers = tempPlayers.AsValueEnumerable().Select(player => (player.SteamID, player.UserId, player.Slot)).ToList();
|
||||
if (tempPlayers.Count == 0 || onlinePlayers.Count == 0) return;
|
||||
await pluginInstance.MuteManager.CheckOnlineModeMutes(onlinePlayers);
|
||||
}
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
var players = Helper.GetValidPlayers();
|
||||
var penalizedSlots = players
|
||||
.Where(player => PlayerPenaltyManager.IsSlotInPenalties(player.Slot))
|
||||
.Select(player => new
|
||||
{
|
||||
Player = player,
|
||||
IsMuted = PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Mute, out _),
|
||||
IsSilenced = PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence, out _),
|
||||
IsGagged = PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag, out _)
|
||||
});
|
||||
|
||||
foreach (var entry in penalizedSlots)
|
||||
{
|
||||
if (!entry.IsMuted && !entry.IsSilenced)
|
||||
{
|
||||
entry.Player.VoiceFlags = VoiceFlags.Normal;
|
||||
}
|
||||
}
|
||||
|
||||
PlayerPenaltyManager.RemoveExpiredPenalties();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError($"Unable to remove old penalties: {ex.Message}");
|
||||
}
|
||||
}, TimerFlags.REPEAT);
|
||||
}
|
||||
}
|
||||
258
CS2-SimpleAdmin/Managers/PlayerPenaltyManager.cs
Normal file
258
CS2-SimpleAdmin/Managers/PlayerPenaltyManager.cs
Normal file
@@ -0,0 +1,258 @@
|
||||
using CS2_SimpleAdminApi;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace CS2_SimpleAdmin.Managers;
|
||||
|
||||
public static class PlayerPenaltyManager
|
||||
{
|
||||
private static readonly ConcurrentDictionary<int, Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>>> Penalties =
|
||||
new();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a penalty for a specific player slot and penalty type.
|
||||
/// </summary>
|
||||
/// <param name="slot">The player slot where the penalty should be applied.</param>
|
||||
/// <param name="penaltyType">The type of penalty to apply (e.g. gag, mute, silence).</param>
|
||||
/// <param name="endDateTime">The validity expiration date/time of the penalty.</param>
|
||||
/// <param name="durationInMinutes">The duration of the penalty in minutes (0 for permanent).</param>
|
||||
public static void AddPenalty(int slot, PenaltyType penaltyType, DateTime endDateTime, int durationInMinutes)
|
||||
{
|
||||
Penalties.AddOrUpdate(slot,
|
||||
(_) =>
|
||||
{
|
||||
var dict = new Dictionary<PenaltyType, List<(DateTime, int, bool)>>
|
||||
{
|
||||
[penaltyType] = [(endDateTime, durationInMinutes, false)]
|
||||
};
|
||||
return dict;
|
||||
},
|
||||
(_, existingDict) =>
|
||||
{
|
||||
if (!existingDict.TryGetValue(penaltyType, out var value))
|
||||
{
|
||||
value = new List<(DateTime, int, bool)>();
|
||||
existingDict[penaltyType] = value;
|
||||
}
|
||||
|
||||
value.Add((endDateTime, durationInMinutes, false));
|
||||
return existingDict;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a player is currently penalized with the given penalty type.
|
||||
/// </summary>
|
||||
/// <param name="slot">The player slot to check.</param>
|
||||
/// <param name="penaltyType">The penalty type to check.</param>
|
||||
/// <param name="endDateTime">The out-parameter returning the end datetime of the penalty if active.</param>
|
||||
/// <returns>True if the player has an active penalty, false otherwise.</returns>
|
||||
public static bool IsPenalized(int slot, PenaltyType penaltyType, out DateTime? endDateTime)
|
||||
{
|
||||
endDateTime = null;
|
||||
|
||||
if (!Penalties.TryGetValue(slot, out var penaltyDict) ||
|
||||
!penaltyDict.TryGetValue(penaltyType, out var penaltiesList)) return false;
|
||||
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.TimeMode == 0)
|
||||
{
|
||||
if (penaltiesList.Count == 0) return false;
|
||||
|
||||
endDateTime = penaltiesList.First().EndDateTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
var now = Time.ActualDateTime();
|
||||
|
||||
// Check if any active penalties exist
|
||||
foreach (var penalty in penaltiesList.ToList())
|
||||
{
|
||||
// Check if the penalty is still active
|
||||
if (penalty.Duration > 0 && now >= penalty.EndDateTime)
|
||||
{
|
||||
penaltiesList.Remove(penalty); // Remove expired penalty
|
||||
if (penaltiesList.Count == 0)
|
||||
{
|
||||
penaltyDict.Remove(penaltyType); // Remove penalty type if no more penalties exist
|
||||
}
|
||||
}
|
||||
else if (penalty.Duration == 0 || now < penalty.EndDateTime)
|
||||
{
|
||||
// Set endDateTime to the end time of this active penalty
|
||||
endDateTime = penalty.EndDateTime;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Return false if no active penalties are found
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all penalties for a player of a specific penalty type.
|
||||
/// </summary>
|
||||
/// <param name="slot">The player slot.</param>
|
||||
/// <param name="penaltyType">The penalty type to retrieve.</param>
|
||||
/// <returns>A list of penalties if found, otherwise an empty list.</returns>
|
||||
public static List<(DateTime EndDateTime, int Duration, bool Passed)> GetPlayerPenalties(int slot, PenaltyType penaltyType)
|
||||
{
|
||||
if (Penalties.TryGetValue(slot, out var penaltyDict) &&
|
||||
penaltyDict.TryGetValue(penaltyType, out var penaltiesList))
|
||||
{
|
||||
return penaltiesList;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all penalties for a player across multiple penalty types.
|
||||
/// </summary>
|
||||
/// <param name="slot">The player slot.</param>
|
||||
/// <param name="penaltyType">A list of penalty types to retrieve.</param>
|
||||
/// <returns>A combined list of penalties of all requested types.</returns>
|
||||
public static List<(DateTime EndDateTime, int Duration, bool Passed)> GetPlayerPenalties(int slot, List<PenaltyType> penaltyType)
|
||||
{
|
||||
List<(DateTime EndDateTime, int Duration, bool Passed)> result = [];
|
||||
|
||||
if (Penalties.TryGetValue(slot, out var penaltyDict))
|
||||
{
|
||||
foreach (var type in penaltyType)
|
||||
{
|
||||
if (penaltyDict.TryGetValue(type, out var penaltiesList))
|
||||
{
|
||||
result.AddRange(penaltiesList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all penalties for a player across all penalty types.
|
||||
/// </summary>
|
||||
/// <param name="slot">The player slot.</param>
|
||||
/// <returns>A dictionary with penalty types as keys and lists of penalties as values.</returns>
|
||||
public static Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>> GetAllPlayerPenalties(int slot)
|
||||
{
|
||||
// Check if the player has any penalties in the dictionary
|
||||
return Penalties.TryGetValue(slot, out var penaltyDict) ?
|
||||
// Return all penalty types and their respective penalties for the player
|
||||
penaltyDict :
|
||||
// If the player has no penalties, return an empty dictionary
|
||||
new Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a given slot has any penalties assigned.
|
||||
/// </summary>
|
||||
/// <param name="slot">The player slot.</param>
|
||||
/// <returns>True if the player has any penalties, false otherwise.</returns>
|
||||
public static bool IsSlotInPenalties(int slot)
|
||||
{
|
||||
return Penalties.ContainsKey(slot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all penalties assigned to a specific player slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">The player slot.</param>
|
||||
public static void RemoveAllPenalties(int slot)
|
||||
{
|
||||
if (Penalties.ContainsKey(slot))
|
||||
{
|
||||
Penalties.TryRemove(slot, out _);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all penalties for all players.
|
||||
/// </summary>
|
||||
public static void RemoveAllPenalties()
|
||||
{
|
||||
Penalties.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all penalties of a specific type from a player.
|
||||
/// </summary>
|
||||
/// <param name="slot">The player slot.</param>
|
||||
/// <param name="penaltyType">The penalty type to remove.</param>
|
||||
public static void RemovePenaltiesByType(int slot, PenaltyType penaltyType)
|
||||
{
|
||||
if (Penalties.TryGetValue(slot, out var penaltyDict) &&
|
||||
penaltyDict.ContainsKey(penaltyType))
|
||||
{
|
||||
penaltyDict.Remove(penaltyType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks penalties with a specific end datetime as "passed" for a player.
|
||||
/// </summary>
|
||||
/// <param name="slot">The player slot.</param>
|
||||
/// <param name="dateTime">The end datetime of penalties to mark as passed.</param>
|
||||
public static void RemovePenaltiesByDateTime(int slot, DateTime dateTime)
|
||||
{
|
||||
if (!Penalties.TryGetValue(slot, out var penaltyDict)) return;
|
||||
|
||||
foreach (var penaltiesList in penaltyDict.Values)
|
||||
{
|
||||
for (var i = 0; i < penaltiesList.Count; i++)
|
||||
{
|
||||
if (penaltiesList[i].EndDateTime != dateTime) continue;
|
||||
// Create a copy of the penalty
|
||||
var penalty = penaltiesList[i];
|
||||
|
||||
// Update the end datetime of the copied penalty to the current datetime
|
||||
penalty.Passed = true;
|
||||
|
||||
// Replace the original penalty with the modified one
|
||||
penaltiesList[i] = penalty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes or expires penalties automatically across all players based on their duration or "passed" flag.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If <c>TimeMode == 0</c>, penalties are considered passed manually and are removed if flagged as such.
|
||||
/// Otherwise, expired penalties are removed based on the current datetime compared with their end time.
|
||||
/// </remarks>
|
||||
public static void RemoveExpiredPenalties()
|
||||
{
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.TimeMode == 0)
|
||||
{
|
||||
foreach (var (playerSlot, penaltyDict) in Penalties.ToList()) // Use ToList to avoid modification while iterating
|
||||
{
|
||||
// Remove expired penalties for the player
|
||||
foreach (var penaltiesList in penaltyDict.Values)
|
||||
{
|
||||
penaltiesList.RemoveAll(p => p is { Duration: > 0, Passed: true });
|
||||
}
|
||||
|
||||
// Remove player slot if no penalties left
|
||||
if (penaltyDict.Count == 0)
|
||||
{
|
||||
Penalties.TryRemove(playerSlot, out _);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var now = Time.ActualDateTime();
|
||||
foreach (var (playerSlot, penaltyDict) in Penalties.ToList()) // Use ToList to avoid modification while iterating
|
||||
{
|
||||
foreach (var penaltiesList in penaltyDict.Values)
|
||||
{
|
||||
penaltiesList.RemoveAll(p => p.Duration > 0 && now >= p.EndDateTime);
|
||||
}
|
||||
|
||||
if (penaltyDict.Count == 0)
|
||||
{
|
||||
Penalties.TryRemove(playerSlot, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
123
CS2-SimpleAdmin/Managers/ServerManager.cs
Normal file
123
CS2-SimpleAdmin/Managers/ServerManager.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CS2_SimpleAdmin.Managers;
|
||||
|
||||
public class ServerManager
|
||||
{
|
||||
private int _getIpTryCount;
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the server setting <c>sv_hibernate_when_empty</c> is enabled.
|
||||
/// Logs an error if this setting is true, since it prevents the plugin from working properly.
|
||||
/// </summary>
|
||||
public static void CheckHibernationStatus()
|
||||
{
|
||||
var convar = ConVar.Find("sv_hibernate_when_empty");
|
||||
if (convar == null || !convar.GetPrimitiveValue<bool>())
|
||||
return;
|
||||
|
||||
CS2_SimpleAdmin._logger?.LogError("Detected setting \"sv_hibernate_when_empty true\", set false to make plugin work properly");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initiates the asynchronous process to load server data such as IP address, port, hostname, and RCON password.
|
||||
/// Handles retry attempts if IP address is not immediately available.
|
||||
/// Updates or inserts the server record in the database accordingly.
|
||||
/// After loading, triggers admin reload and cache initialization.
|
||||
/// Also optionally sends plugin usage metrics if enabled in configuration.
|
||||
/// </summary>
|
||||
public void LoadServerData()
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.AddTimer(2.0f, () =>
|
||||
{
|
||||
if (CS2_SimpleAdmin.ServerLoaded || CS2_SimpleAdmin.DatabaseProvider == null) return;
|
||||
|
||||
if (_getIpTryCount > 32 && Helper.GetServerIp().StartsWith("0.0.0.0") || string.IsNullOrEmpty(Helper.GetServerIp()))
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError("Unable to load server data - can't fetch ip address!");
|
||||
return;
|
||||
}
|
||||
|
||||
var ipAddress = ConVar.Find("ip")?.StringValue;
|
||||
if (string.IsNullOrEmpty(ipAddress) || ipAddress.StartsWith("0.0.0"))
|
||||
{
|
||||
ipAddress = Helper.GetServerIp();
|
||||
|
||||
if (_getIpTryCount <= 32 && (string.IsNullOrEmpty(ipAddress) || ipAddress.StartsWith("0.0.0")))
|
||||
{
|
||||
_getIpTryCount++;
|
||||
|
||||
LoadServerData();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var address = $"{ipAddress}:{ConVar.Find("hostport")?.GetPrimitiveValue<int>()}";
|
||||
var hostname = ConVar.Find("hostname")?.StringValue ?? CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown";
|
||||
var rconPassword = ConVar.Find("rcon_password")?.StringValue ?? "";
|
||||
CS2_SimpleAdmin.IpAddress = address;
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
int? serverId = await connection.ExecuteScalarAsync<int?>(
|
||||
"SELECT id FROM sa_servers WHERE address = @address",
|
||||
new { address });
|
||||
|
||||
if (serverId == null)
|
||||
{
|
||||
await connection.ExecuteAsync(
|
||||
"INSERT INTO sa_servers (address, hostname, rcon_password) VALUES (@address, @hostname, @rconPassword)",
|
||||
new { address, hostname, rconPassword });
|
||||
|
||||
serverId = await connection.ExecuteScalarAsync<int>(
|
||||
"SELECT id FROM sa_servers WHERE address = @address",
|
||||
new { address });
|
||||
}
|
||||
else
|
||||
{
|
||||
await connection.ExecuteAsync(
|
||||
"UPDATE sa_servers SET hostname = @hostname, rcon_password = @rconPassword WHERE address = @address",
|
||||
new { address, hostname, rconPassword });
|
||||
}
|
||||
|
||||
CS2_SimpleAdmin.ServerId = serverId;
|
||||
CS2_SimpleAdmin._logger?.LogInformation("Loaded server with ip {ip}", ipAddress);
|
||||
|
||||
if (CS2_SimpleAdmin.ServerId != null)
|
||||
{
|
||||
await Server.NextWorldUpdateAsync(() => CS2_SimpleAdmin.Instance.ReloadAdmins(null));
|
||||
}
|
||||
|
||||
CS2_SimpleAdmin.ServerLoaded = true;
|
||||
if (CS2_SimpleAdmin.Instance.CacheManager != null)
|
||||
await CS2_SimpleAdmin.Instance.CacheManager.InitializeCacheAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogCritical("Unable to create or get server_id: " + ex.Message);
|
||||
}
|
||||
|
||||
if (CS2_SimpleAdmin.Instance.Config.EnableMetrics)
|
||||
{
|
||||
var queryString = $"?address={address}&hostname={hostname}";
|
||||
var client = CS2_SimpleAdmin.HttpClient;
|
||||
|
||||
try
|
||||
{
|
||||
await client.GetAsync($"https://api.daffyy.dev/index.php{queryString}");
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogWarning($"Unable to make metrics call: {ex.Message}");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
206
CS2-SimpleAdmin/Managers/WarnManager.cs
Normal file
206
CS2-SimpleAdmin/Managers/WarnManager.cs
Normal file
@@ -0,0 +1,206 @@
|
||||
using CS2_SimpleAdmin.Database;
|
||||
using CS2_SimpleAdminApi;
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CS2_SimpleAdmin.Managers;
|
||||
|
||||
internal class WarnManager(IDatabaseProvider? databaseProvider)
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a warning to a player with an optional issuer and reason.
|
||||
/// </summary>
|
||||
/// <param name="player">The player who is being warned.</param>
|
||||
/// <param name="issuer">The player issuing the warning; null indicates console or system.</param>
|
||||
/// <param name="reason">The reason for the warning.</param>
|
||||
/// <param name="time">Optional duration of the warning in minutes (0 means permanent).</param>
|
||||
/// <returns>The identifier of the inserted warning, or null if the operation failed.</returns>
|
||||
public async Task<int?> WarnPlayer(PlayerInfo player, PlayerInfo? issuer, string reason, int time = 0)
|
||||
{
|
||||
if (databaseProvider == null) return null;
|
||||
|
||||
var now = Time.ActualDateTime();
|
||||
var futureTime = now.AddMinutes(time);
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetAddWarnQuery(true);
|
||||
|
||||
var warnId = await connection.ExecuteScalarAsync<int?>(sql, new
|
||||
{
|
||||
playerSteamid = player.SteamId.SteamId64,
|
||||
playerName = player.Name,
|
||||
adminSteamid = issuer?.SteamId.SteamId64 ?? 0,
|
||||
adminName = issuer?.Name ?? CS2_SimpleAdmin._localizer?["sa_console"] ?? "Console",
|
||||
muteReason = reason,
|
||||
duration = time,
|
||||
ends = futureTime,
|
||||
created = now,
|
||||
serverid = CS2_SimpleAdmin.ServerId
|
||||
});
|
||||
|
||||
return warnId;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a warning to a player identified by SteamID with optional issuer and reason.
|
||||
/// </summary>
|
||||
/// <param name="playerSteamId">The SteamID64 of the player being warned.</param>
|
||||
/// <param name="issuer">The player issuing the warning; null indicates console or system.</param>
|
||||
/// <param name="reason">The reason for the warning.</param>
|
||||
/// <param name="time">Optional duration of the warning in minutes (0 means permanent).</param>
|
||||
/// <returns>The identifier of the inserted warning, or null if the operation failed.</returns>
|
||||
public async Task<int?> AddWarnBySteamid(ulong playerSteamId, PlayerInfo? issuer, string reason, int time = 0)
|
||||
{
|
||||
if (databaseProvider == null) return null;
|
||||
|
||||
var now = Time.ActualDateTime();
|
||||
var futureTime = now.AddMinutes(time);
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
var sql = databaseProvider.GetAddWarnQuery(false);
|
||||
|
||||
var warnId = await connection.ExecuteScalarAsync<int?>(sql, new
|
||||
{
|
||||
playerSteamid = playerSteamId,
|
||||
adminSteamid = issuer?.SteamId.SteamId64 ?? 0,
|
||||
adminName = issuer?.Name ?? CS2_SimpleAdmin._localizer?["sa_console"] ?? "Console",
|
||||
muteReason = reason,
|
||||
duration = time,
|
||||
ends = futureTime,
|
||||
created = now,
|
||||
serverid = CS2_SimpleAdmin.ServerId
|
||||
});
|
||||
|
||||
return warnId;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of warnings for a specific player.
|
||||
/// </summary>
|
||||
/// <param name="player">The player whose warnings to retrieve.</param>
|
||||
/// <param name="active">If true, returns only active (non-expired) warnings; otherwise returns all warnings.</param>
|
||||
/// <returns>A list of dynamic objects representing warnings, or an empty list if none found or on failure.</returns>
|
||||
public async Task<List<dynamic>> GetPlayerWarns(PlayerInfo player, bool active = true)
|
||||
{
|
||||
if (databaseProvider == null) return [];
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var sql = databaseProvider.GetPlayerWarnsQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode, active);
|
||||
var parameters = new { PlayerSteamID = player.SteamId.SteamId64, serverid = CS2_SimpleAdmin.ServerId };
|
||||
var warns = await connection.QueryAsync<dynamic>(sql, parameters);
|
||||
|
||||
return warns.ToList();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the count of warnings for a player specified by SteamID.
|
||||
/// </summary>
|
||||
/// <param name="steamId">The SteamID64 of the player.</param>
|
||||
/// <param name="active">If true, counts only active (non-expired) warnings; otherwise counts all warnings.</param>
|
||||
/// <returns>The count of warnings as an integer, or 0 if none found or on failure.</returns>
|
||||
public async Task<int> GetPlayerWarnsCount(ulong steamId, bool active = true)
|
||||
{
|
||||
if (databaseProvider == null) return 0;
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var sql = databaseProvider.GetPlayerWarnsCountQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode, active);
|
||||
var warnsCount = await connection.ExecuteScalarAsync<int>(sql, new { PlayerSteamID = steamId, serverid = CS2_SimpleAdmin.ServerId });
|
||||
return warnsCount;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a specific warning by its identifier from a player's record.
|
||||
/// </summary>
|
||||
/// <param name="player">The player whose warning will be removed.</param>
|
||||
/// <param name="warnId">The identifier of the warning to remove.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
public async Task UnwarnPlayer(PlayerInfo player, int warnId)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var sql = databaseProvider.GetUnwarnByIdQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
await connection.ExecuteAsync(sql, new { steamid = player.SteamId.SteamId64, warnId, serverid = CS2_SimpleAdmin.ServerId });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogCritical($"Unable to remove warn + {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the most recent warning matching a player pattern (usually SteamID string).
|
||||
/// </summary>
|
||||
/// <param name="playerPattern">The pattern identifying the player whose last warning should be removed.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
public async Task UnwarnPlayer(string playerPattern)
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var sql = databaseProvider.GetUnwarnLastQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
await connection.ExecuteAsync(sql, new { steamid = playerPattern, serverid = CS2_SimpleAdmin.ServerId });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogCritical("Unable to remove last warn {exception}", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expires old warnings based on the current time, removing or marking them as inactive.
|
||||
/// </summary>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
public async Task ExpireOldWarns()
|
||||
{
|
||||
if (databaseProvider == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await databaseProvider.CreateConnectionAsync();
|
||||
|
||||
var sql = databaseProvider.GetExpireWarnsQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
await connection.ExecuteAsync(sql, new { CurrentTime = Time.ActualDateTime(), serverid = CS2_SimpleAdmin.ServerId });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogCritical($"Unable to remove expired warns + {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
70
CS2-SimpleAdmin/Menus/AdminMenu.cs
Normal file
70
CS2-SimpleAdmin/Menus/AdminMenu.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Menu;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
public static class AdminMenu
|
||||
{
|
||||
public static IMenu? CreateMenu(string title, Action<CCSPlayerController>? backAction = null)
|
||||
{
|
||||
return Helper.CreateMenu(title, backAction);
|
||||
// return CS2_SimpleAdmin.Instance.Config.UseChatMenu ? new ChatMenu(title) : new CenterHtmlMenu(title, CS2_SimpleAdmin.Instance);
|
||||
}
|
||||
|
||||
public static void OpenMenu(CCSPlayerController player, IMenu menu)
|
||||
{
|
||||
menu.Open(player);
|
||||
// switch (menu)
|
||||
// {
|
||||
// case CenterHtmlMenu centerHtmlMenu:
|
||||
// MenuManager.OpenCenterHtmlMenu(CS2_SimpleAdmin.Instance, player, centerHtmlMenu);
|
||||
// break;
|
||||
// case ChatMenu chatMenu:
|
||||
// MenuManager.OpenChatMenu(player, chatMenu);
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
|
||||
public static void OpenMenu(CCSPlayerController admin)
|
||||
{
|
||||
if (admin.IsValid == false)
|
||||
return;
|
||||
|
||||
var localizer = CS2_SimpleAdmin._localizer;
|
||||
if (AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/generic") == false)
|
||||
{
|
||||
admin.PrintToChat(localizer?["sa_prefix"] ??
|
||||
"[SimpleAdmin] " +
|
||||
(localizer?["sa_no_permission"] ?? "You do not have permissions to use this command")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = CreateMenu(localizer?["sa_title"] ?? "SimpleAdmin");
|
||||
List<ChatMenuOptionData> options =
|
||||
[
|
||||
new(localizer?["sa_menu_players_manage"] ?? "Players Manage", () => ManagePlayersMenu.OpenMenu(admin)),
|
||||
new(localizer?["sa_menu_server_manage"] ?? "Server Manage", () => ManageServerMenu.OpenMenu(admin)),
|
||||
new(localizer?["sa_menu_fun_commands"] ?? "Fun Commands", () => FunActionsMenu.OpenMenu(admin)),
|
||||
];
|
||||
|
||||
var customCommands = CS2_SimpleAdmin.Instance.Config.CustomServerCommands;
|
||||
if (customCommands.Count > 0)
|
||||
{
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_menu_custom_commands"] ?? "Custom Commands", () => CustomCommandsMenu.OpenMenu(admin)));
|
||||
}
|
||||
|
||||
if (AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/root"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_menu_admins_manage"] ?? "Admins Manage", () => ManageAdminsMenu.OpenMenu(admin)));
|
||||
|
||||
foreach (var menuOptionData in options)
|
||||
{
|
||||
var menuName = menuOptionData.Name;
|
||||
menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action.Invoke(); }, menuOptionData.Disabled);
|
||||
}
|
||||
|
||||
if (menu != null) OpenMenu(admin, menu);
|
||||
}
|
||||
}
|
||||
8
CS2-SimpleAdmin/Menus/ChatMenuOptionData.cs
Normal file
8
CS2-SimpleAdmin/Menus/ChatMenuOptionData.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
public class ChatMenuOptionData(string name, Action action, bool disabled = false)
|
||||
{
|
||||
public readonly string Name = name;
|
||||
public readonly Action Action = action;
|
||||
public readonly bool Disabled = disabled;
|
||||
}
|
||||
51
CS2-SimpleAdmin/Menus/CustomCommandsMenu.cs
Normal file
51
CS2-SimpleAdmin/Menus/CustomCommandsMenu.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
public static class CustomCommandsMenu
|
||||
{
|
||||
public static void OpenMenu(CCSPlayerController admin)
|
||||
{
|
||||
if (admin.IsValid == false)
|
||||
return;
|
||||
|
||||
var localizer = CS2_SimpleAdmin._localizer;
|
||||
if (AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/generic") == false)
|
||||
{
|
||||
admin.PrintToChat(localizer?["sa_prefix"] ??
|
||||
"[SimpleAdmin] " +
|
||||
(localizer?["sa_no_permission"] ?? "You do not have permissions to use this command")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = AdminMenu.CreateMenu(localizer?["sa_menu_custom_commands"] ?? "Custom Commands");
|
||||
List<ChatMenuOptionData> options = [];
|
||||
|
||||
var customCommands = CS2_SimpleAdmin.Instance.Config.CustomServerCommands;
|
||||
options.AddRange(from customCommand in customCommands
|
||||
where !string.IsNullOrEmpty(customCommand.DisplayName) && !string.IsNullOrEmpty(customCommand.Command)
|
||||
let hasRights = AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), customCommand.Flag)
|
||||
where hasRights
|
||||
select new ChatMenuOptionData(customCommand.DisplayName, () =>
|
||||
{
|
||||
Helper.TryLogCommandOnDiscord(admin, customCommand.Command);
|
||||
|
||||
if (customCommand.ExecuteOnClient)
|
||||
admin.ExecuteClientCommandFromServer(customCommand.Command);
|
||||
else
|
||||
Server.ExecuteCommand(customCommand.Command);
|
||||
}));
|
||||
|
||||
foreach (var menuOptionData in options)
|
||||
{
|
||||
var menuName = menuOptionData.Name;
|
||||
menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action(); }, menuOptionData.Disabled);
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
}
|
||||
32
CS2-SimpleAdmin/Menus/DurationMenu.cs
Normal file
32
CS2-SimpleAdmin/Menus/DurationMenu.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CS2_SimpleAdmin.Models;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
public static class DurationMenu
|
||||
{
|
||||
public static void OpenMenu(CCSPlayerController admin, string menuName, CCSPlayerController player, Action<CCSPlayerController, CCSPlayerController, int> onSelectAction)
|
||||
{
|
||||
var menu = AdminMenu.CreateMenu(menuName);
|
||||
|
||||
foreach (var durationItem in CS2_SimpleAdmin.Instance.Config.MenuConfigs.Durations)
|
||||
{
|
||||
menu?.AddMenuOption(durationItem.Name, (_, _) => { onSelectAction(admin, player, durationItem.Duration); });
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
public static void OpenMenu(CCSPlayerController admin, string menuName, DisconnectedPlayer player, Action<CCSPlayerController, DisconnectedPlayer, int> onSelectAction)
|
||||
{
|
||||
var menu = AdminMenu.CreateMenu(menuName);
|
||||
|
||||
foreach (var durationItem in CS2_SimpleAdmin.Instance.Config.MenuConfigs.Durations)
|
||||
{
|
||||
menu?.AddMenuOption(durationItem.Name, (_, _) => { onSelectAction(admin, player, durationItem.Duration); });
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
}
|
||||
267
CS2-SimpleAdmin/Menus/FunActionsMenu.cs
Normal file
267
CS2-SimpleAdmin/Menus/FunActionsMenu.cs
Normal file
@@ -0,0 +1,267 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
public static class FunActionsMenu
|
||||
{
|
||||
private static Dictionary<int, CsItem>? _weaponsCache;
|
||||
|
||||
private static Dictionary<int, CsItem> GetWeaponsCache
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_weaponsCache != null) return _weaponsCache;
|
||||
|
||||
var weaponsArray = Enum.GetValues(typeof(CsItem));
|
||||
|
||||
// avoid duplicates in the menu
|
||||
_weaponsCache = new Dictionary<int, CsItem>();
|
||||
foreach (CsItem item in weaponsArray)
|
||||
{
|
||||
if (item == CsItem.Tablet)
|
||||
continue;
|
||||
|
||||
_weaponsCache[(int)item] = item;
|
||||
}
|
||||
|
||||
return _weaponsCache;
|
||||
}
|
||||
}
|
||||
|
||||
public static void OpenMenu(CCSPlayerController admin)
|
||||
{
|
||||
if (admin.IsValid == false)
|
||||
return;
|
||||
|
||||
var localizer = CS2_SimpleAdmin._localizer;
|
||||
if (AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/generic") == false)
|
||||
{
|
||||
admin.PrintToChat(localizer?["sa_prefix"] ??
|
||||
"[SimpleAdmin] " +
|
||||
(localizer?["sa_no_permission"] ?? "You do not have permissions to use this command")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = AdminMenu.CreateMenu(localizer?["sa_menu_fun_commands"] ?? "Fun Commands");
|
||||
List<ChatMenuOptionData> options = [];
|
||||
|
||||
//var hasCheats = AdminManager.PlayerHasPermissions(admin, "@css/cheats");
|
||||
//var hasSlay = AdminManager.PlayerHasPermissions(admin, "@css/slay");
|
||||
|
||||
// options added in order
|
||||
|
||||
if (AdminManager.CommandIsOverriden("css_god")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_god"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/cheats"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_godmode"] ?? "God Mode", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_godmode"] ?? "God Mode", GodMode)));
|
||||
if (AdminManager.CommandIsOverriden("css_noclip")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_noclip"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/cheats"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_noclip"] ?? "No Clip", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_noclip"] ?? "No Clip", NoClip)));
|
||||
if (AdminManager.CommandIsOverriden("css_respawn")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_respawn"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/cheats"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_respawn"] ?? "Respawn", () => PlayersMenu.OpenDeadMenu(admin, localizer?["sa_respawn"] ?? "Respawn", Respawn)));
|
||||
if (AdminManager.CommandIsOverriden("css_give")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_give"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/cheats"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_give_weapon"] ?? "Give Weapon", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_give_weapon"] ?? "Give Weapon", GiveWeaponMenu)));
|
||||
|
||||
if (AdminManager.CommandIsOverriden("css_strip")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_strip"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_strip_weapons"] ?? "Strip Weapons", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_strip_weapons"] ?? "Strip Weapons", StripWeapons)));
|
||||
if (AdminManager.CommandIsOverriden("css_freeze")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_freeze"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_freeze"] ?? "Freeze", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_freeze"] ?? "Freeze", Freeze)));
|
||||
if (AdminManager.CommandIsOverriden("css_hp")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_hp"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_set_hp"] ?? "Set Hp", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_set_hp"] ?? "Set Hp", SetHpMenu)));
|
||||
if (AdminManager.CommandIsOverriden("css_speed")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_speed"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_set_speed"] ?? "Set Speed", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_set_speed"] ?? "Set Speed", SetSpeedMenu)));
|
||||
if (AdminManager.CommandIsOverriden("css_gravity")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_gravity"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_set_gravity"] ?? "Set Gravity", () => PlayersMenu.OpenAliveMenu(admin, localizer?["sa_set_gravity"] ?? "Set Gravity", SetGravityMenu)));
|
||||
if (AdminManager.CommandIsOverriden("css_money")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_money"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_set_money"] ?? "Set Money", () => PlayersMenu.OpenMenu(admin, localizer?["sa_set_money"] ?? "Set Money", SetMoneyMenu)));
|
||||
|
||||
foreach (var menuOptionData in options)
|
||||
{
|
||||
var menuName = menuOptionData.Name;
|
||||
menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action(); }, menuOptionData.Disabled);
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void GodMode(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
CS2_SimpleAdmin.God(admin, player);
|
||||
}
|
||||
|
||||
private static void NoClip(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
CS2_SimpleAdmin.NoClip(admin, player);
|
||||
}
|
||||
|
||||
private static void Respawn(CCSPlayerController? admin, CCSPlayerController player)
|
||||
{
|
||||
CS2_SimpleAdmin.Respawn(admin, player);
|
||||
}
|
||||
|
||||
private static void GiveWeaponMenu(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_give_weapon"] ?? "Give Weapon"}: {player.PlayerName}");
|
||||
|
||||
foreach (var weapon in GetWeaponsCache)
|
||||
{
|
||||
menu?.AddMenuOption(weapon.Value.ToString(), (_, _) => { GiveWeapon(admin, player, weapon.Value); });
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void GiveWeapon(CCSPlayerController admin, CCSPlayerController player, CsItem weaponValue)
|
||||
{
|
||||
CS2_SimpleAdmin.GiveWeapon(admin, player, weaponValue);
|
||||
}
|
||||
|
||||
private static void StripWeapons(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
CS2_SimpleAdmin.StripWeapons(admin, player);
|
||||
}
|
||||
|
||||
private static void Freeze(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
if (!(player.PlayerPawn.Value?.IsValid ?? false))
|
||||
return;
|
||||
|
||||
if (player.PlayerPawn.Value.MoveType != MoveType_t.MOVETYPE_INVALID)
|
||||
CS2_SimpleAdmin.Freeze(admin, player, -1);
|
||||
else
|
||||
CS2_SimpleAdmin.Unfreeze(admin, player);
|
||||
}
|
||||
|
||||
private static void SetHpMenu(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
var hpArray = new[]
|
||||
{
|
||||
new Tuple<string, int>("1", 1),
|
||||
new Tuple<string, int>("10", 10),
|
||||
new Tuple<string, int>("25", 25),
|
||||
new Tuple<string, int>("50", 50),
|
||||
new Tuple<string, int>("100", 100),
|
||||
new Tuple<string, int>("200", 200),
|
||||
new Tuple<string, int>("500", 500),
|
||||
new Tuple<string, int>("999", 999)
|
||||
};
|
||||
|
||||
var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_set_hp"] ?? "Set Hp"}: {player.PlayerName}");
|
||||
|
||||
foreach (var (optionName, value) in hpArray)
|
||||
{
|
||||
menu?.AddMenuOption(optionName, (_, _) => { SetHp(admin, player, value); });
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void SetHp(CCSPlayerController admin, CCSPlayerController player, int hp)
|
||||
{
|
||||
CS2_SimpleAdmin.SetHp(admin, player, hp);
|
||||
}
|
||||
|
||||
private static void SetSpeedMenu(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
var speedArray = new[]
|
||||
{
|
||||
new Tuple<string, float>("0.1", .1f),
|
||||
new Tuple<string, float>("0.25", .25f),
|
||||
new Tuple<string, float>("0.5", .5f),
|
||||
new Tuple<string, float>("0.75", .75f),
|
||||
new Tuple<string, float>("1", 1),
|
||||
new Tuple<string, float>("2", 2),
|
||||
new Tuple<string, float>("3", 3),
|
||||
new Tuple<string, float>("4", 4)
|
||||
};
|
||||
|
||||
var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_set_speed"] ?? "Set Speed"}: {player.PlayerName}");
|
||||
|
||||
foreach (var (optionName, value) in speedArray)
|
||||
{
|
||||
menu?.AddMenuOption(optionName, (_, _) => { SetSpeed(admin, player, value); });
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void SetSpeed(CCSPlayerController admin, CCSPlayerController player, float speed)
|
||||
{
|
||||
CS2_SimpleAdmin.SetSpeed(admin, player, speed);
|
||||
}
|
||||
|
||||
private static void SetGravityMenu(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
var gravityArray = new[]
|
||||
{
|
||||
new Tuple<string, float>("0.1", .1f),
|
||||
new Tuple<string, float>("0.25", .25f),
|
||||
new Tuple<string, float>("0.5", .5f),
|
||||
new Tuple<string, float>("0.75", .75f),
|
||||
new Tuple<string, float>("1", 1),
|
||||
new Tuple<string, float>("2", 2)
|
||||
};
|
||||
|
||||
var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_set_gravity"] ?? "Set Gravity"}: {player.PlayerName}");
|
||||
|
||||
foreach (var (optionName, value) in gravityArray)
|
||||
{
|
||||
menu?.AddMenuOption(optionName, (_, _) => { SetGravity(admin, player, value); });
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void SetGravity(CCSPlayerController admin, CCSPlayerController player, float gravity)
|
||||
{
|
||||
CS2_SimpleAdmin.SetGravity(admin, player, gravity);
|
||||
}
|
||||
|
||||
private static void SetMoneyMenu(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
var moneyArray = new[]
|
||||
{
|
||||
new Tuple<string, int>("$0", 0),
|
||||
new Tuple<string, int>("$1000", 1000),
|
||||
new Tuple<string, int>("$2500", 2500),
|
||||
new Tuple<string, int>("$5000", 5000),
|
||||
new Tuple<string, int>("$10000", 10000),
|
||||
new Tuple<string, int>("$16000", 16000)
|
||||
};
|
||||
|
||||
var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_set_money"] ?? "Set Money"}: {player.PlayerName}");
|
||||
|
||||
foreach (var (optionName, value) in moneyArray)
|
||||
{
|
||||
menu?.AddMenuOption(optionName, (_, _) => { SetMoney(admin, player, value); });
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void SetMoney(CCSPlayerController admin, CCSPlayerController player, int money)
|
||||
{
|
||||
CS2_SimpleAdmin.SetMoney(admin, player, money);
|
||||
}
|
||||
}
|
||||
72
CS2-SimpleAdmin/Menus/ManageAdminsMenu.cs
Normal file
72
CS2-SimpleAdmin/Menus/ManageAdminsMenu.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
public static class ManageAdminsMenu
|
||||
{
|
||||
public static void OpenMenu(CCSPlayerController admin)
|
||||
{
|
||||
if (admin.IsValid == false)
|
||||
return;
|
||||
|
||||
var localizer = CS2_SimpleAdmin._localizer;
|
||||
if (AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/root") == false)
|
||||
{
|
||||
admin.PrintToChat(localizer?["sa_prefix"] ??
|
||||
"[SimpleAdmin] " +
|
||||
(localizer?["sa_no_permission"] ?? "You do not have permissions to use this command")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = AdminMenu.CreateMenu(localizer?["sa_menu_admins_manage"] ?? "Admins Manage");
|
||||
List<ChatMenuOptionData> options =
|
||||
[
|
||||
new(localizer?["sa_admin_add"] ?? "Add Admin",
|
||||
() => PlayersMenu.OpenRealPlayersMenu(admin, localizer?["sa_admin_add"] ?? "Add Admin", AddAdminMenu)),
|
||||
new(localizer?["sa_admin_remove"] ?? "Remove Admin",
|
||||
() => PlayersMenu.OpenAdminPlayersMenu(admin, localizer?["sa_admin_remove"] ?? "Remove Admin", RemoveAdmin,
|
||||
player => player != admin && admin.CanTarget(player))),
|
||||
new(localizer?["sa_admin_reload"] ?? "Reload Admins", () => ReloadAdmins(admin))
|
||||
];
|
||||
|
||||
foreach (var menuOptionData in options)
|
||||
{
|
||||
var menuName = menuOptionData.Name;
|
||||
menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action.Invoke(); }, menuOptionData.Disabled);
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void AddAdminMenu(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_admin_add"] ?? "Add Admin"}: {player.PlayerName}");
|
||||
|
||||
foreach (var adminFlag in CS2_SimpleAdmin.Instance.Config.MenuConfigs.AdminFlags)
|
||||
{
|
||||
var disabled = AdminManager.PlayerHasPermissions(player, adminFlag.Flag);
|
||||
menu?.AddMenuOption(adminFlag.Name, (_, _) => { AddAdmin(admin, player, adminFlag.Flag); }, disabled);
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void AddAdmin(CCSPlayerController admin, CCSPlayerController player, string flag)
|
||||
{
|
||||
// TODO: Change default immunity?
|
||||
CS2_SimpleAdmin.AddAdmin(admin, player.SteamID.ToString(), player.PlayerName, flag, 10);
|
||||
}
|
||||
|
||||
private static void RemoveAdmin(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.RemoveAdmin(admin, player.SteamID.ToString());
|
||||
}
|
||||
|
||||
private static void ReloadAdmins(CCSPlayerController admin)
|
||||
{
|
||||
CS2_SimpleAdmin.Instance.ReloadAdmins(admin);
|
||||
}
|
||||
}
|
||||
336
CS2-SimpleAdmin/Menus/ManagePlayersMenu.cs
Normal file
336
CS2-SimpleAdmin/Menus/ManagePlayersMenu.cs
Normal file
@@ -0,0 +1,336 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using CS2_SimpleAdminApi;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
public static class ManagePlayersMenu
|
||||
{
|
||||
public static void OpenMenu(CCSPlayerController admin)
|
||||
{
|
||||
if (admin.IsValid == false)
|
||||
return;
|
||||
|
||||
var localizer = CS2_SimpleAdmin._localizer;
|
||||
if (AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/generic") == false)
|
||||
{
|
||||
admin.PrintToChat(localizer?["sa_prefix"] ??
|
||||
"[SimpleAdmin] " +
|
||||
(localizer?["sa_no_permission"] ?? "You do not have permissions to use this command")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = AdminMenu.CreateMenu(localizer?["sa_menu_players_manage"] ?? "Manage Players");
|
||||
List<ChatMenuOptionData> options = [];
|
||||
|
||||
// permissions
|
||||
var hasSlay = AdminManager.CommandIsOverriden("css_slay") ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_slay")) : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/slay");
|
||||
var hasKick = AdminManager.CommandIsOverriden("css_kick") ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_kick")) : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/kick");
|
||||
var hasBan = AdminManager.CommandIsOverriden("css_ban") ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_ban")) : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/ban");
|
||||
var hasChat = AdminManager.CommandIsOverriden("css_gag") ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_gag")) : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/chat");
|
||||
|
||||
// TODO: Localize options
|
||||
// options added in order
|
||||
|
||||
if (hasSlay)
|
||||
{
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_slap"] ?? "Slap", () => PlayersMenu.OpenMenu(admin, localizer?["sa_slap"] ?? "Slap", SlapMenu)));
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_slay"] ?? "Slay", () => PlayersMenu.OpenMenu(admin, localizer?["sa_slay"] ?? "Slay", Slay)));
|
||||
}
|
||||
|
||||
if (hasKick)
|
||||
{
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_kick"] ?? "Kick", () => PlayersMenu.OpenMenu(admin, localizer?["sa_kick"] ?? "Kick", KickMenu)));
|
||||
}
|
||||
|
||||
if (AdminManager.CommandIsOverriden("css_warn")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_warn"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/kick"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_warn"] ?? "Warn", () => PlayersMenu.OpenRealPlayersMenu(admin, localizer?["sa_warn"] ?? "Warn", (admin, player) => DurationMenu.OpenMenu(admin, $"{localizer?["sa_warn"] ?? "Warn"}: {player.PlayerName}", player, WarnMenu))));
|
||||
|
||||
if (hasBan)
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_ban"] ?? "Ban", () => PlayersMenu.OpenRealPlayersMenu(admin, localizer?["sa_ban"] ?? "Ban", (admin, player) => DurationMenu.OpenMenu(admin, $"{localizer?["sa_ban"] ?? "Ban"}: {player.PlayerName}", player, BanMenu))));
|
||||
|
||||
if (hasChat)
|
||||
{
|
||||
if (AdminManager.CommandIsOverriden("css_gag")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_gag"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/chat"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_gag"] ?? "Gag", () => PlayersMenu.OpenRealPlayersMenu(admin, localizer?["sa_gag"] ?? "Gag", (admin, player) => DurationMenu.OpenMenu(admin, $"{localizer?["sa_gag"] ?? "Gag"}: {player.PlayerName}", player, GagMenu))));
|
||||
if (AdminManager.CommandIsOverriden("css_mute")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_mute"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/chat"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_mute"] ?? "Mute", () => PlayersMenu.OpenRealPlayersMenu(admin, localizer?["sa_mute"] ?? "Mute", (admin, player) => DurationMenu.OpenMenu(admin, $"{localizer?["sa_mute"] ?? "Mute"}: {player.PlayerName}", player, MuteMenu))));
|
||||
if (AdminManager.CommandIsOverriden("css_silence")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_silence"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/chat"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_silence"] ?? "Silence", () => PlayersMenu.OpenRealPlayersMenu(admin, localizer?["sa_silence"] ?? "Silence", (admin, player) => DurationMenu.OpenMenu(admin, $"{localizer?["sa_silence"] ?? "Silence"}: {player.PlayerName}", player, SilenceMenu))));
|
||||
}
|
||||
|
||||
if (AdminManager.CommandIsOverriden("css_team")
|
||||
? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_team"))
|
||||
: AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/kick"))
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_team_force"] ?? "Force Team", () => PlayersMenu.OpenMenu(admin, localizer?["sa_team_force"] ?? "Force Team", ForceTeamMenu)));
|
||||
|
||||
foreach (var menuOptionData in options)
|
||||
{
|
||||
var menuName = menuOptionData.Name;
|
||||
menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action.Invoke(); }, menuOptionData.Disabled);
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void SlapMenu(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_slap"] ?? "Slap"}: {player.PlayerName}");
|
||||
List<ChatMenuOptionData> options =
|
||||
[
|
||||
// options added in order
|
||||
new("0 hp", () => ApplySlapAndKeepMenu(admin, player, 0)),
|
||||
new("1 hp", () => ApplySlapAndKeepMenu(admin, player, 1)),
|
||||
new("5 hp", () => ApplySlapAndKeepMenu(admin, player, 5)),
|
||||
new("10 hp", () => ApplySlapAndKeepMenu(admin, player, 10)),
|
||||
new("50 hp", () => ApplySlapAndKeepMenu(admin, player, 50)),
|
||||
new("100 hp", () => ApplySlapAndKeepMenu(admin, player, 100)),
|
||||
];
|
||||
|
||||
foreach (var menuOptionData in options)
|
||||
{
|
||||
var menuName = menuOptionData.Name;
|
||||
menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action.Invoke(); }, menuOptionData.Disabled);
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void ApplySlapAndKeepMenu(CCSPlayerController admin, CCSPlayerController player, int damage)
|
||||
{
|
||||
if (player is not { IsValid: true }) return;
|
||||
|
||||
CS2_SimpleAdmin.Slap(admin, player, damage);
|
||||
SlapMenu(admin, player);
|
||||
}
|
||||
|
||||
private static void Slay(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
if (player is not { IsValid: true }) return;
|
||||
|
||||
CS2_SimpleAdmin.Slay(admin, player);
|
||||
}
|
||||
|
||||
private static void KickMenu(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
ReasonMenu.OpenMenu(admin, PenaltyType.Kick,
|
||||
$"{CS2_SimpleAdmin._localizer?["sa_kick"] ?? "Kick"}: {player.PlayerName}", player, (_, _, reason) =>
|
||||
{
|
||||
if (player is { IsValid: true })
|
||||
Kick(admin, player, reason);
|
||||
});
|
||||
|
||||
// var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_kick"] ?? "Kick"}: {player?.PlayerName}");
|
||||
//
|
||||
// foreach (var option in CS2_SimpleAdmin.Instance.Config.MenuConfigs.KickReasons)
|
||||
// {
|
||||
// menu?.AddMenuOption(option, (_, _) =>
|
||||
// {
|
||||
// if (player is { IsValid: true })
|
||||
// Kick(admin, player, option);
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void Kick(CCSPlayerController admin, CCSPlayerController player, string? reason)
|
||||
{
|
||||
if (player is not { IsValid: true }) return;
|
||||
|
||||
CS2_SimpleAdmin.Instance.Kick(admin, player, reason, admin.PlayerName);
|
||||
}
|
||||
|
||||
internal static void BanMenu(CCSPlayerController admin, CCSPlayerController player, int duration)
|
||||
{
|
||||
ReasonMenu.OpenMenu(admin, PenaltyType.Ban,
|
||||
$"{CS2_SimpleAdmin._localizer?["sa_ban"] ?? "Ban"}: {player.PlayerName}", player, (_, _, reason) =>
|
||||
{
|
||||
if (player is { IsValid: true })
|
||||
Ban(admin, player, duration, reason);
|
||||
|
||||
CS2_SimpleAdmin.MenuApi?.CloseMenu(admin);
|
||||
});
|
||||
|
||||
// var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_ban"] ?? "Ban"}: {player?.PlayerName}");
|
||||
//
|
||||
// foreach (var option in CS2_SimpleAdmin.Instance.Config.MenuConfigs.BanReasons)
|
||||
// {
|
||||
// menu?.AddMenuOption(option, (_, _) =>
|
||||
// {
|
||||
// if (player is { IsValid: true })
|
||||
// Ban(admin, player, duration, option);
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void Ban(CCSPlayerController admin, CCSPlayerController player, int duration, string reason)
|
||||
{
|
||||
if (player is not { IsValid: true }) return;
|
||||
|
||||
CS2_SimpleAdmin.Instance.Ban(admin, player, duration, reason, admin.PlayerName);
|
||||
}
|
||||
|
||||
private static void WarnMenu(CCSPlayerController admin, CCSPlayerController player, int duration)
|
||||
{
|
||||
ReasonMenu.OpenMenu(admin, PenaltyType.Warn,
|
||||
$"{CS2_SimpleAdmin._localizer?["sa_warn"] ?? "Warn"}: {player.PlayerName}", player, (_, _, reason) =>
|
||||
{
|
||||
if (player is { IsValid: true })
|
||||
Warn(admin, player, duration, reason);
|
||||
});
|
||||
|
||||
// var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_warn"] ?? "Warn"}: {player?.PlayerName}");
|
||||
//
|
||||
// foreach (var option in CS2_SimpleAdmin.Instance.Config.MenuConfigs.WarnReasons)
|
||||
// {
|
||||
// menu?.AddMenuOption(option, (_, _) =>
|
||||
// {
|
||||
// if (player is { IsValid: true })
|
||||
// Warn(admin, player, duration, option);
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void Warn(CCSPlayerController admin, CCSPlayerController player, int duration, string reason)
|
||||
{
|
||||
if (player is not { IsValid: true }) return;
|
||||
|
||||
CS2_SimpleAdmin.Instance.Warn(admin, player, duration, reason, admin.PlayerName);
|
||||
}
|
||||
|
||||
internal static void GagMenu(CCSPlayerController admin, CCSPlayerController player, int duration)
|
||||
{
|
||||
ReasonMenu.OpenMenu(admin, PenaltyType.Gag,
|
||||
$"{CS2_SimpleAdmin._localizer?["sa_gag"] ?? "Gag"}: {player.PlayerName}", player, (_, _, reason) =>
|
||||
{
|
||||
if (player is { IsValid: true })
|
||||
Gag(admin, player, duration, reason);
|
||||
});
|
||||
|
||||
// var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_gag"] ?? "Gag"}: {player?.PlayerName}");
|
||||
//
|
||||
// foreach (var option in CS2_SimpleAdmin.Instance.Config.MenuConfigs.MuteReasons)
|
||||
// {
|
||||
// menu?.AddMenuOption(option, (_, _) =>
|
||||
// {
|
||||
// if (player is { IsValid: true })
|
||||
// Gag(admin, player, duration, option);
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void Gag(CCSPlayerController admin, CCSPlayerController player, int duration, string reason)
|
||||
{
|
||||
if (player is not { IsValid: true }) return;
|
||||
|
||||
CS2_SimpleAdmin.Instance.Gag(admin, player, duration, reason);
|
||||
}
|
||||
|
||||
internal static void MuteMenu(CCSPlayerController admin, CCSPlayerController player, int duration)
|
||||
{
|
||||
ReasonMenu.OpenMenu(admin, PenaltyType.Mute,
|
||||
$"{CS2_SimpleAdmin._localizer?["sa_mute"] ?? "mute"}: {player.PlayerName}", player, (_, _, reason) =>
|
||||
{
|
||||
if (player is { IsValid: true })
|
||||
Mute(admin, player, duration, reason);
|
||||
});
|
||||
|
||||
// // TODO: Localize and make options in config?
|
||||
// var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_mute"] ?? "Mute"}: {player?.PlayerName}");
|
||||
//
|
||||
// foreach (var option in CS2_SimpleAdmin.Instance.Config.MenuConfigs.MuteReasons)
|
||||
// {
|
||||
// menu?.AddMenuOption(option, (_, _) =>
|
||||
// {
|
||||
// if (player is { IsValid: true })
|
||||
// Mute(admin, player, duration, option);
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void Mute(CCSPlayerController admin, CCSPlayerController player, int duration, string reason)
|
||||
{
|
||||
if (player is not { IsValid: true }) return;
|
||||
|
||||
CS2_SimpleAdmin.Instance.Mute(admin, player, duration, reason);
|
||||
}
|
||||
|
||||
internal static void SilenceMenu(CCSPlayerController admin, CCSPlayerController player, int duration)
|
||||
{
|
||||
ReasonMenu.OpenMenu(admin, PenaltyType.Silence,
|
||||
$"{CS2_SimpleAdmin._localizer?["sa_silence"] ?? "Silence"}: {player.PlayerName}", player, (_, _, reason) =>
|
||||
{
|
||||
if (player is { IsValid: true })
|
||||
Silence(admin, player, duration, reason);
|
||||
});
|
||||
|
||||
// // TODO: Localize and make options in config?
|
||||
// var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_silence"] ?? "Silence"}: {player?.PlayerName}");
|
||||
//
|
||||
// foreach (var option in CS2_SimpleAdmin.Instance.Config.MenuConfigs.MuteReasons)
|
||||
// {
|
||||
// menu?.AddMenuOption(option, (_, _) =>
|
||||
// {
|
||||
// if (player is { IsValid: true })
|
||||
// Silence(admin, player, duration, option);
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void Silence(CCSPlayerController admin, CCSPlayerController player, int duration, string reason)
|
||||
{
|
||||
if (player is not { IsValid: true }) return;
|
||||
|
||||
CS2_SimpleAdmin.Instance.Silence(admin, player, duration, reason);
|
||||
}
|
||||
|
||||
private static void ForceTeamMenu(CCSPlayerController admin, CCSPlayerController player)
|
||||
{
|
||||
// TODO: Localize
|
||||
var menu = AdminMenu.CreateMenu($"{CS2_SimpleAdmin._localizer?["sa_team_force"] ?? "Force Team"} {player.PlayerName}");
|
||||
List<ChatMenuOptionData> options =
|
||||
[
|
||||
new(CS2_SimpleAdmin._localizer?["sa_team_ct"] ?? "CT", () => ForceTeam(admin, player, "ct", CsTeam.CounterTerrorist)),
|
||||
new(CS2_SimpleAdmin._localizer?["sa_team_t"] ?? "T", () => ForceTeam(admin, player, "t", CsTeam.Terrorist)),
|
||||
new(CS2_SimpleAdmin._localizer?["sa_team_swap"] ?? "Swap", () => ForceTeam(admin, player, "swap", CsTeam.Spectator)),
|
||||
new(CS2_SimpleAdmin._localizer?["sa_team_spec"] ?? "Spec", () => ForceTeam(admin, player, "spec", CsTeam.Spectator)),
|
||||
];
|
||||
|
||||
foreach (var menuOptionData in options)
|
||||
{
|
||||
var menuName = menuOptionData.Name;
|
||||
menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action.Invoke(); }, menuOptionData.Disabled);
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void ForceTeam(CCSPlayerController admin, CCSPlayerController player, string teamName, CsTeam teamNum)
|
||||
{
|
||||
if (player is not { IsValid: true }) return;
|
||||
|
||||
CS2_SimpleAdmin.ChangeTeam(admin, player, teamName, teamNum, true);
|
||||
}
|
||||
}
|
||||
83
CS2-SimpleAdmin/Menus/ManageServerMenu.cs
Normal file
83
CS2-SimpleAdmin/Menus/ManageServerMenu.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
public static class ManageServerMenu
|
||||
{
|
||||
public static void OpenMenu(CCSPlayerController admin)
|
||||
{
|
||||
if (admin.IsValid == false)
|
||||
return;
|
||||
|
||||
var localizer = CS2_SimpleAdmin._localizer;
|
||||
if (AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/generic") == false)
|
||||
{
|
||||
admin.PrintToChat(localizer?["sa_prefix"] ??
|
||||
"[SimpleAdmin] " +
|
||||
(localizer?["sa_no_permission"] ?? "You do not have permissions to use this command")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = AdminMenu.CreateMenu(localizer?["sa_menu_server_manage"] ?? "Server Manage");
|
||||
List<ChatMenuOptionData> options = [];
|
||||
|
||||
// permissions
|
||||
var hasMap = AdminManager.CommandIsOverriden("css_map") ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_map")) : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/changemap");
|
||||
var hasPlugins = AdminManager.CommandIsOverriden("css_pluginsmanager") ? AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), AdminManager.GetPermissionOverrides("css_pluginsmanager")) : AdminManager.PlayerHasPermissions(new SteamID(admin.SteamID), "@css/root");
|
||||
|
||||
//bool hasMap = AdminManager.PlayerHasPermissions(admin, "@css/changemap");
|
||||
|
||||
// options added in order
|
||||
|
||||
if (hasPlugins)
|
||||
{
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_menu_pluginsmanager_title"] ?? "Manage Plugins", () => admin.ExecuteClientCommandFromServer("css_pluginsmanager")));
|
||||
}
|
||||
|
||||
if (hasMap)
|
||||
{
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_changemap"] ?? "Change Map", () => ChangeMapMenu(admin)));
|
||||
}
|
||||
|
||||
options.Add(new ChatMenuOptionData(localizer?["sa_restart_game"] ?? "Restart Game", () => CS2_SimpleAdmin.RestartGame(admin)));
|
||||
|
||||
foreach (var menuOptionData in options)
|
||||
{
|
||||
var menuName = menuOptionData.Name;
|
||||
menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action.Invoke(); }, menuOptionData.Disabled);
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void ChangeMapMenu(CCSPlayerController admin)
|
||||
{
|
||||
var menu = AdminMenu.CreateMenu(CS2_SimpleAdmin._localizer?["sa_changemap"] ?? "Change Map");
|
||||
List<ChatMenuOptionData> options = [];
|
||||
|
||||
var maps = CS2_SimpleAdmin.Instance.Config.DefaultMaps;
|
||||
options.AddRange(maps.Select(map => new ChatMenuOptionData(map, () => ExecuteChangeMap(admin, map, false))));
|
||||
|
||||
var wsMaps = CS2_SimpleAdmin.Instance.Config.WorkshopMaps;
|
||||
options.AddRange(wsMaps.Select(map => new ChatMenuOptionData($"{map.Key} (WS)", () => ExecuteChangeMap(admin, map.Value?.ToString() ?? map.Key, true))));
|
||||
|
||||
foreach (var menuOptionData in options)
|
||||
{
|
||||
var menuName = menuOptionData.Name;
|
||||
menu?.AddMenuOption(menuName, (_, _) => { menuOptionData.Action.Invoke(); }, menuOptionData.Disabled);
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
private static void ExecuteChangeMap(CCSPlayerController admin, string mapName, bool workshop)
|
||||
{
|
||||
if (workshop)
|
||||
CS2_SimpleAdmin.Instance.ChangeWorkshopMap(admin, mapName);
|
||||
else
|
||||
CS2_SimpleAdmin.Instance.ChangeMap(admin, mapName);
|
||||
}
|
||||
}
|
||||
55
CS2-SimpleAdmin/Menus/PlayersMenu.cs
Normal file
55
CS2-SimpleAdmin/Menus/PlayersMenu.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using System.Web;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
public static class PlayersMenu
|
||||
{
|
||||
public static void OpenRealPlayersMenu(CCSPlayerController admin, string menuName, Action<CCSPlayerController, CCSPlayerController> onSelectAction, Func<CCSPlayerController, bool>? enableFilter = null)
|
||||
{
|
||||
OpenMenu(admin, menuName, onSelectAction, p => p.IsBot == false);
|
||||
}
|
||||
|
||||
public static void OpenAdminPlayersMenu(CCSPlayerController admin, string menuName, Action<CCSPlayerController, CCSPlayerController> onSelectAction, Func<CCSPlayerController?, bool>? enableFilter = null)
|
||||
{
|
||||
OpenMenu(admin, menuName, onSelectAction, p => AdminManager.GetPlayerAdminData(p)?.Flags.Count > 0);
|
||||
}
|
||||
|
||||
public static void OpenAliveMenu(CCSPlayerController admin, string menuName, Action<CCSPlayerController, CCSPlayerController> onSelectAction, Func<CCSPlayerController, bool>? enableFilter = null)
|
||||
{
|
||||
OpenMenu(admin, menuName, onSelectAction, p => p.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE);
|
||||
}
|
||||
|
||||
public static void OpenDeadMenu(CCSPlayerController admin, string menuName, Action<CCSPlayerController?, CCSPlayerController> onSelectAction, Func<CCSPlayerController, bool>? enableFilter = null)
|
||||
{
|
||||
OpenMenu(admin, menuName, onSelectAction, p => p.PlayerPawn?.Value?.LifeState != (int)LifeState_t.LIFE_ALIVE);
|
||||
}
|
||||
|
||||
public static void OpenMenu(CCSPlayerController admin, string menuName, Action<CCSPlayerController, CCSPlayerController> onSelectAction, Func<CCSPlayerController, bool>? enableFilter = null)
|
||||
{
|
||||
var menu = AdminMenu.CreateMenu(menuName);
|
||||
|
||||
var players = Helper.GetValidPlayersWithBots();
|
||||
|
||||
foreach (var player in players)
|
||||
{
|
||||
var playerName = player != null && player.PlayerName.Length > 26 ? player.PlayerName[..26] : player?.PlayerName;
|
||||
|
||||
var optionName = HttpUtility.HtmlEncode(playerName);
|
||||
if (player != null && enableFilter != null && enableFilter(player) == false)
|
||||
continue;
|
||||
|
||||
var enabled = admin.CanTarget(player);
|
||||
|
||||
if (optionName != null)
|
||||
menu?.AddMenuOption(optionName, (_, _) =>
|
||||
{
|
||||
if (player != null) onSelectAction.Invoke(admin, player);
|
||||
},
|
||||
enabled == false);
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
}
|
||||
52
CS2-SimpleAdmin/Menus/ReasonMenu.cs
Normal file
52
CS2-SimpleAdmin/Menus/ReasonMenu.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CS2_SimpleAdmin.Models;
|
||||
using CS2_SimpleAdminApi;
|
||||
|
||||
namespace CS2_SimpleAdmin.Menus;
|
||||
|
||||
public static class ReasonMenu
|
||||
{
|
||||
public static void OpenMenu(CCSPlayerController admin, PenaltyType penaltyType, string menuName, CCSPlayerController player, Action<CCSPlayerController, CCSPlayerController, string> onSelectAction)
|
||||
{
|
||||
var menu = AdminMenu.CreateMenu(menuName);
|
||||
|
||||
var reasons = penaltyType switch
|
||||
{
|
||||
PenaltyType.Ban => CS2_SimpleAdmin.Instance.Config.MenuConfigs.BanReasons,
|
||||
PenaltyType.Kick => CS2_SimpleAdmin.Instance.Config.MenuConfigs.KickReasons,
|
||||
PenaltyType.Mute => CS2_SimpleAdmin.Instance.Config.MenuConfigs.MuteReasons,
|
||||
PenaltyType.Warn => CS2_SimpleAdmin.Instance.Config.MenuConfigs.WarnReasons,
|
||||
PenaltyType.Gag => CS2_SimpleAdmin.Instance.Config.MenuConfigs.MuteReasons,
|
||||
PenaltyType.Silence => CS2_SimpleAdmin.Instance.Config.MenuConfigs.MuteReasons,
|
||||
_ => CS2_SimpleAdmin.Instance.Config.MenuConfigs.BanReasons
|
||||
};
|
||||
|
||||
foreach (var reason in reasons)
|
||||
{
|
||||
menu?.AddMenuOption(reason, (_, _) => onSelectAction(admin, player, reason));
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
|
||||
public static void OpenMenu(CCSPlayerController admin, PenaltyType penaltyType, string menuName, DisconnectedPlayer player, Action<CCSPlayerController, DisconnectedPlayer, string> onSelectAction)
|
||||
{
|
||||
var menu = AdminMenu.CreateMenu(menuName);
|
||||
|
||||
var reasons = penaltyType switch
|
||||
{
|
||||
PenaltyType.Ban => CS2_SimpleAdmin.Instance.Config.MenuConfigs.BanReasons,
|
||||
PenaltyType.Kick => CS2_SimpleAdmin.Instance.Config.MenuConfigs.KickReasons,
|
||||
PenaltyType.Mute => CS2_SimpleAdmin.Instance.Config.MenuConfigs.MuteReasons,
|
||||
PenaltyType.Warn => CS2_SimpleAdmin.Instance.Config.MenuConfigs.WarnReasons,
|
||||
_ => CS2_SimpleAdmin.Instance.Config.MenuConfigs.BanReasons
|
||||
};
|
||||
|
||||
foreach (var reason in reasons)
|
||||
{
|
||||
menu?.AddMenuOption(reason, (_, _) => onSelectAction(admin, player, reason));
|
||||
}
|
||||
|
||||
if (menu != null) AdminMenu.OpenMenu(admin, menu);
|
||||
}
|
||||
}
|
||||
39
CS2-SimpleAdmin/Models/BanRecord.cs
Normal file
39
CS2-SimpleAdmin/Models/BanRecord.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace CS2_SimpleAdmin.Models;
|
||||
|
||||
public enum BanStatus
|
||||
{
|
||||
[Description("ACTIVE")] ACTIVE,
|
||||
[Description("UNBANNED")] UNBANNED,
|
||||
[Description("EXPIRED")] EXPIRED,
|
||||
[Description("")] UNKNOWN
|
||||
}
|
||||
|
||||
public record BanRecord
|
||||
{
|
||||
[Column("id")]
|
||||
public int Id { get; init; }
|
||||
|
||||
[Column("player_name")]
|
||||
public string? PlayerName { get; set; }
|
||||
|
||||
[Column("player_steamid")]
|
||||
public ulong? PlayerSteamId { get; set; }
|
||||
|
||||
[Column("player_ip")]
|
||||
public string? PlayerIp { get; set; }
|
||||
|
||||
[Column("status")]
|
||||
public required string Status { get; init; }
|
||||
|
||||
[NotMapped]
|
||||
public BanStatus StatusEnum => Status.ToUpper() switch
|
||||
{
|
||||
"ACTIVE" => BanStatus.ACTIVE,
|
||||
"UNBANNED" => BanStatus.UNBANNED,
|
||||
"EXPIRED" => BanStatus.EXPIRED,
|
||||
_ => BanStatus.UNKNOWN
|
||||
};
|
||||
}
|
||||
15
CS2-SimpleAdmin/Models/DisconnectedPlayer.cs
Normal file
15
CS2-SimpleAdmin/Models/DisconnectedPlayer.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CS2_SimpleAdmin.Models;
|
||||
|
||||
public class DisconnectedPlayer(
|
||||
SteamID steamId,
|
||||
string name,
|
||||
string? ipAddress,
|
||||
DateTime disconnectTime)
|
||||
{
|
||||
public SteamID SteamId { get; } = steamId;
|
||||
public string Name { get; set; } = name;
|
||||
public string? IpAddress { get; set; } = ipAddress;
|
||||
public DateTime DisconnectTime = disconnectTime;
|
||||
}
|
||||
3
CS2-SimpleAdmin/Models/IpRecord.cs
Normal file
3
CS2-SimpleAdmin/Models/IpRecord.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace CS2_SimpleAdmin.Models;
|
||||
|
||||
public readonly record struct IpRecord(uint Ip, DateTime UsedAt, string PlayerName);
|
||||
12
CS2-SimpleAdmin/Models/PlayerDto.cs
Normal file
12
CS2-SimpleAdmin/Models/PlayerDto.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace CS2_SimpleAdmin.Models;
|
||||
|
||||
public record PlayerStats(int Score, int Kills, int Deaths, int MVPs);
|
||||
public record PlayerDto(
|
||||
int UserId,
|
||||
string Name,
|
||||
string SteamId,
|
||||
string IpAddress,
|
||||
uint Ping,
|
||||
bool IsAdmin,
|
||||
PlayerStats Stats
|
||||
);
|
||||
1
CS2-SimpleAdmin/VERSION
Normal file
1
CS2-SimpleAdmin/VERSION
Normal file
@@ -0,0 +1 @@
|
||||
1.7.7-alpha-10
|
||||
87
CS2-SimpleAdmin/Variables.cs
Normal file
87
CS2-SimpleAdmin/Variables.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Capabilities;
|
||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
using CS2_SimpleAdmin.Models;
|
||||
using CS2_SimpleAdminApi;
|
||||
using MenuManager;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Concurrent;
|
||||
using CS2_SimpleAdmin.Database;
|
||||
using CS2_SimpleAdmin.Managers;
|
||||
using Timer = CounterStrikeSharp.API.Modules.Timers.Timer;
|
||||
|
||||
namespace CS2_SimpleAdmin;
|
||||
|
||||
public partial class CS2_SimpleAdmin
|
||||
{
|
||||
// Config
|
||||
public CS2_SimpleAdminConfig Config { get; set; } = new();
|
||||
|
||||
// HttpClient
|
||||
internal static readonly HttpClient HttpClient = new();
|
||||
|
||||
// Paths
|
||||
internal static readonly string ConfigDirectory =
|
||||
Path.Combine(Application.RootDirectory, "configs/plugins/CS2-SimpleAdmin");
|
||||
|
||||
// Localization
|
||||
public static IStringLocalizer? _localizer;
|
||||
|
||||
// Voting System
|
||||
public static readonly Dictionary<string, int> VoteAnswers = [];
|
||||
public static bool VoteInProgress;
|
||||
|
||||
// Command and Server Settings
|
||||
public static readonly bool UnlockedCommands = CoreConfig.UnlockConCommands;
|
||||
internal static string IpAddress = string.Empty;
|
||||
internal static bool ServerLoaded;
|
||||
internal static int? ServerId = null;
|
||||
internal static readonly HashSet<ulong> AdminDisabledJoinComms = [];
|
||||
|
||||
// Player Management
|
||||
private static readonly HashSet<int> GodPlayers = [];
|
||||
internal static readonly HashSet<int> SilentPlayers = [];
|
||||
internal static readonly Dictionary<ulong, string> RenamedPlayers = [];
|
||||
internal static readonly ConcurrentDictionary<ulong, PlayerInfo> PlayersInfo = [];
|
||||
internal static readonly List<CCSPlayerController> CachedPlayers = [];
|
||||
internal static readonly List<CCSPlayerController> BotPlayers = [];
|
||||
private static readonly List<DisconnectedPlayer> DisconnectedPlayers = [];
|
||||
|
||||
// Discord Integration
|
||||
internal static DiscordManager? DiscordWebhookClientLog;
|
||||
|
||||
// Database Settings
|
||||
internal string DbConnectionString = string.Empty;
|
||||
// internal static Database.Database? Database;
|
||||
internal static IDatabaseProvider? DatabaseProvider;
|
||||
|
||||
// Logger
|
||||
internal static ILogger? _logger;
|
||||
|
||||
// Memory Function (Game-related)
|
||||
private static MemoryFunctionVoid<CBasePlayerController, CCSPlayerPawn, bool, bool>?
|
||||
_cBasePlayerControllerSetPawnFunc;
|
||||
|
||||
// Menu API and Capabilities
|
||||
internal static IMenuApi? MenuApi;
|
||||
private static readonly PluginCapability<IMenuApi> MenuCapability = new("menu:nfcore");
|
||||
|
||||
// Shared API
|
||||
internal static Api.CS2_SimpleAdminApi? SimpleAdminApi { get; private set; }
|
||||
|
||||
// Managers
|
||||
internal PermissionManager PermissionManager = new(DatabaseProvider);
|
||||
internal BanManager BanManager = new(DatabaseProvider);
|
||||
internal MuteManager MuteManager = new(DatabaseProvider);
|
||||
internal WarnManager WarnManager = new(DatabaseProvider);
|
||||
internal CacheManager? CacheManager = new();
|
||||
private static readonly PlayerManager PlayerManager = new();
|
||||
|
||||
// Timers
|
||||
internal Timer? PlayersTimer = null;
|
||||
|
||||
// Funny list
|
||||
private readonly List<string> _requiredPlugins = ["MenuManagerCore", "PlayerSettings"];
|
||||
private readonly List<string> _requiredShared = ["MenuManagerApi", "PlayerSettingsApi", "AnyBaseLib", "CS2-SimpleAdminApi"];
|
||||
}
|
||||
39
CS2-SimpleAdmin/admin_help.txt
Normal file
39
CS2-SimpleAdmin/admin_help.txt
Normal file
@@ -0,0 +1,39 @@
|
||||
{GREEN}[ CS2-SimpleAdmin HELP ]{DEFAULT}
|
||||
- css_who <#userid or name> - Display informations about player
|
||||
- css_players - Display player list
|
||||
- css_ban <#userid or name> [time in minutes/0 perm] [reason] - Ban player
|
||||
- css_addban <steamid> [time in minutes/0 perm] [reason] - Ban player via steamid64
|
||||
- css_banip <ip> [time in minutes/0 perm] [reason] - Ban player via IP address
|
||||
- css_unban <steamid or name or ip> - Unban player
|
||||
- css_kick <#userid or name> [reason] - Kick player
|
||||
- css_gag <#userid or name> [time in minutes/0 perm] [reason] - Gag player
|
||||
- css_addgag <steamid> [time in minutes/0 perm] [reason] - Gag player via steamid64
|
||||
- css_unmute <steamid or name> - Ungag player
|
||||
- css_mute <#userid or name> [time in minutes/0 perm] [reason] - Mute player
|
||||
- css_addmute <steamid> [time in minutes/0 perm] [reason] - Mute player via steamid64
|
||||
- css_give <#userid or name> <weapon> - Give player a weapon
|
||||
- css_strip <#userid or name> <weapon> - Takes all of the player weapons
|
||||
- css_hp <#userid or name> [health] - Set player health
|
||||
- css_speed <#userid or name> [speed] - Set player speed
|
||||
- css_gravity <#userid or name> [gravity] - Set player gravity
|
||||
- css_money <#userid or name> [money] - Set player money
|
||||
- css_god <#userid or name> - Toggle player godmode
|
||||
- css_slay <#userid or name> - Kill player
|
||||
- css_slap <#userid or name> [damage] - Slap player
|
||||
- css_vote <'Question?'> ['Answer1'] ['Answer2'] ... - Create vote
|
||||
- css_map <mapname> - Change map
|
||||
- css_wsmap <name or id> - Change workshop map
|
||||
- css_asay <message> - Say message to all admins
|
||||
- css_say <message> - Say message as admin in chat
|
||||
- css_psay <#userid or name> <message> - Sends private message to player
|
||||
- css_csay <message> - Say message as admin in center
|
||||
- css_hsay <message> - Say message as admin in hud
|
||||
- css_noclip <#userid or name> - Toggle noclip for player
|
||||
- css_freeze <#userid or name> [duration] - Freeze player
|
||||
- css_unfreeze <#userid or name> - Unfreeze player
|
||||
- css_respawn <#userid or name> - Respawn player
|
||||
- css_cvar <cvar> <value> - Change cvar value
|
||||
- css_rcon <command> - Run command as server
|
||||
|
||||
{Green}This is a sample admin_help.txt file
|
||||
{LightRed}Write all useful information for admins here
|
||||
139
CS2-SimpleAdmin/lang/ar.json
Normal file
139
CS2-SimpleAdmin/lang/ar.json
Normal file
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"sa_title": "SimpleAdmin",
|
||||
"sa_prefix": "{lightred}[SA] {default}",
|
||||
|
||||
"sa_unknown": "مجهول",
|
||||
"sa_no_permission": "ليس لديك الصلاحيات لاستخدام هذا الأمر.",
|
||||
"sa_ban_max_duration_exceeded": "مدة الحظر لا يمكن أن تتجاوز {lightred}{0}{default} دقيقة.",
|
||||
"sa_ban_perm_restricted": "ليس لديك الحق في الحظر الدائم.",
|
||||
|
||||
"sa_admin_add": "إضافة مسؤول",
|
||||
"sa_admin_remove": "إزالة المسؤول",
|
||||
"sa_admin_reload": "إعادة تحميل المسؤولين",
|
||||
|
||||
"sa_godmode": "وضع الإله",
|
||||
"sa_noclip": "بدون قصاصات",
|
||||
"sa_respawn": "إعادة الظهور",
|
||||
"sa_give_weapon": "إعطاء سلاح",
|
||||
"sa_strip_weapons": "تجريد الأسلحة",
|
||||
"sa_freeze": "تجميد",
|
||||
"sa_set_hp": "تعيين الصحة",
|
||||
"sa_set_speed": "تعيين السرعة",
|
||||
"sa_set_gravity": "تعيين الجاذبية",
|
||||
"sa_set_money": "تعيين المال",
|
||||
|
||||
"sa_changemap": "تغيير الخريطة",
|
||||
"sa_restart_game": "إعادة تشغيل اللعبة",
|
||||
|
||||
"sa_team_ct": "CT",
|
||||
"sa_team_t": "T",
|
||||
"sa_team_swap": "تبديل",
|
||||
"sa_team_spec": "المشاهدة",
|
||||
|
||||
"sa_slap": "صفعة",
|
||||
"sa_slay": "قتل",
|
||||
"sa_kick": "طرد",
|
||||
"sa_ban": "حظر",
|
||||
"sa_gag": "كتم",
|
||||
"sa_mute": "كتم",
|
||||
"sa_silence": "صمت",
|
||||
"sa_warn": "تحذير",
|
||||
"sa_team_force": "فرض الفريق",
|
||||
|
||||
"sa_menu_custom_commands": "الأوامر المخصصة",
|
||||
"sa_menu_server_manage": "إدارة الخادم",
|
||||
"sa_menu_fun_commands": "أوامر ممتعة",
|
||||
"sa_menu_admins_manage": "إدارة المسؤولين",
|
||||
"sa_menu_players_manage": "إدارة اللاعبين",
|
||||
"sa_menu_disconnected_title": "اللاعبون الأخيرون",
|
||||
"sa_menu_disconnected_action_title": "اختر الإجراء",
|
||||
"sa_menu_pluginsmanager_title": "إدارة الإضافات",
|
||||
|
||||
"sa_player": "اللاعب",
|
||||
"sa_console": "وحدة التحكم",
|
||||
"sa_steamid": "معرف البخار",
|
||||
"sa_duration": "المدة",
|
||||
"sa_reason": "السبب",
|
||||
"sa_admin": "المشرف",
|
||||
"sa_permanent": "دائم",
|
||||
|
||||
"sa_discord_penalty_ban": "الحظر مسجل",
|
||||
"sa_discord_penalty_mute": "الكتم مسجل",
|
||||
"sa_discord_penalty_gag": "الصمت مسجل",
|
||||
"sa_discord_penalty_silence": "الصمت مسجل",
|
||||
"sa_discord_penalty_warn": "التحذير مسجل",
|
||||
"sa_discord_penalty_unknown": "غير معروف مسجل",
|
||||
|
||||
"sa_player_penalty_chat_active": "{lightred}تم حظر الدردشة الخاصة بك إلى: {grey}{0}",
|
||||
|
||||
"sa_player_penalty_info_active_mute": "➔ كتم [{lightred}❌{default}] - ينتهي [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_gag": "➔ صمت [{lightred}❌{default}] - ينتهي [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_silence": "➔ سكوت [{lightred}❌{default}] - ينتهي [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_warn": "➔ تحذير [{lightred}❌{default}] - ينتهي [{lightred}{0}{default}] - السبب [{lightred}{1}{default}]",
|
||||
|
||||
"sa_player_penalty_info_no_active_mute": "➔ كتم [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_gag": "➔ صمت [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_silence": "➔ سكوت [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_warn": "➔ تحذير [{lime}✔{default}]",
|
||||
|
||||
"sa_player_penalty_info": "===========================\nعقوبات اللاعبين لـ {lightred}{0}{default},\nعدد الحظر: {lightred}{1}{default}, عدد الصمت: {lightred}{2}{default}, عدد الكتم: {lightred}{3}{default}, عدد السكوت: {lightred}{4}{default}, عدد التحذيرات: {lightred}{5}{default}\nالعقوبات النشطة:\n{6}\nالتحذيرات النشطة:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}عقوبات اللاعبين لـ {lightred}{0}{grey}, حظر: {lightred}{1}{grey}, صمت: {lightred}{2}{grey}, كتم: {lightred}{3}{grey}, سكوت: {lightred}{4}{grey}, تحذيرات: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}الحسابات المرتبطة باللاعب {lightred}{0}{grey}: {1}",
|
||||
"sa_player_ban_message_time": "تم حظرك لمدة {lightred}{0}{default} لمدة {lightred}{1}{default} دقيقة من قبل {lightred}{2}{default}!",
|
||||
"sa_player_ban_message_perm": "تم حظرك بشكل دائم لمدة {lightred}{0}{default} من قبل {lightred}{1}{default}!",
|
||||
"sa_player_kick_message": "تم طردك لمدة {lightred}{0}{default} من قبل {lightred}{1}{default}!",
|
||||
"sa_player_gag_message_time": "تم تكميم فمك لمدة {lightred}{0}{default} لمدة {lightred}{1}{default} دقيقة من قبل {lightred}{2}{default}!",
|
||||
"sa_player_gag_message_perm": "تم تكميم فمك بشكل دائم لمدة {lightred}{0}{default} من قبل {lightred}{1}{default}!",
|
||||
"sa_player_mute_message_time": "تم كتم صوتك لمدة {lightred}{0}{default} لمدة {lightred}{1}{default} دقيقة من قبل {lightred}{2}{default}!",
|
||||
"sa_player_mute_message_perm": "تم كتم صوتك بشكل دائم لمدة {lightred}{0}{default} من قبل {lightred}{1}{default}!",
|
||||
"sa_player_silence_message_time": "تم إسكاتك لمدة {lightred}{0}{default} لمدة {lightred}{1}{default} دقيقة من قبل {lightred}{2}{default}!",
|
||||
"sa_player_silence_message_perm": "تم إسكاتك بشكل دائم لمدة {lightred}{0}{default} من قبل {lightred}{1}{default}!",
|
||||
"sa_player_warn_message_time": "لقد تم تحذيرك بسبب {lightred}{0}{default} لمدة {lightred}{1}{default} دقيقة بواسطة {lightred}{2}{default}!",
|
||||
"sa_player_warn_message_perm": "لقد تم تحذيرك بشكل دائم بسبب {lightred}{0}{default} بواسطة {lightred}{1}{default}!",
|
||||
"sa_admin_ban_message_time": "{lightred}{0}{default} حظر {lightred}{1}{default} لمدة {lightred}{3}{default} دقائق بسبب {lightred}{2}{default}!",
|
||||
"sa_admin_ban_message_perm": "{lightred}{0}{default} حظر {lightred}{1}{default} بشكل دائم بسبب {lightred}{2}{default}!",
|
||||
"sa_admin_kick_message": "{lightred}{0}{default} طرد {lightred}{1}{default} بسبب {lightred}{2}{default}!",
|
||||
"sa_admin_gag_message_time": "{lightred}{0}{default} كتم {lightred}{1}{default} لمدة {lightred}{3}{default} دقائق بسبب {lightred}{2}{default}!",
|
||||
"sa_admin_gag_message_perm": "{lightred}{0}{default} كتم {lightred}{1}{default} بشكل دائم بسبب {lightred}{2}{default}!",
|
||||
"sa_admin_mute_message_time": "{lightred}{0}{default} أسكت {lightred}{1}{default} لمدة {lightred}{3}{default} دقائق بسبب {lightred}{2}{default}!",
|
||||
"sa_admin_mute_message_perm": "{lightred}{0}{default} أسكت {lightred}{1}{default} بشكل دائم بسبب {lightred}{2}{default}!",
|
||||
"sa_admin_silence_message_time": "{lightred}{0}{default} أسكت {lightred}{1}{default} لمدة {lightred}{3}{default} دقائق بسبب {lightred}{2}{default}!",
|
||||
"sa_admin_silence_message_perm": "{lightred}{0}{default} أسكت {lightred}{1}{default} بشكل دائم بسبب {lightred}{2}{default}!",
|
||||
"sa_admin_warn_message_time": "{lightred}{0}{default} حذر {lightred}{1}{default} لمدة {lightred}{3}{default} دقائق بسبب {lightred}{2}{default}!",
|
||||
"sa_admin_warn_message_perm": "{lightred}{0}{default} حذر {lightred}{1}{default} بشكل دائم بسبب {lightred}{2}{default}!",
|
||||
"sa_admin_give_message": "{lightred}{0}{default} أعطى {lightred}{1}{default} {lightred}{2}{default}!",
|
||||
"sa_admin_strip_message": "{lightred}{0}{default} أخذ جميع أسلحة اللاعب {lightred}{1}{default}!",
|
||||
"sa_admin_hp_message": "{lightred}{0}{default} غيّر عدد نقاط الحياة لـ {lightred}{1}{default}!",
|
||||
"sa_admin_speed_message": "{lightred}{0}{default} غيّر السرعة لـ {lightred}{1}{default}!",
|
||||
"sa_admin_gravity_message": "{lightred}{0}{default} غيّر الجاذبية لـ {lightred}{1}{default}!",
|
||||
"sa_admin_money_message": "{lightred}{0}{default} غيّر المال لـ {lightred}{1}{default}!",
|
||||
"sa_admin_god_message": "{lightred}{0}{default} غيّر وضع الله لـ {lightred}{1}{default}!",
|
||||
"sa_admin_resize_message": "{lightred}{0}{default} قام بتغيير الحجم لـ {lightred}{1}{default}!",
|
||||
"sa_admin_slay_message": "{lightred}{0}{default} قتل {lightred}{1}{default}!",
|
||||
"sa_admin_slap_message": "{lightred}{0}{default} صفع {lightred}{1}{default}!",
|
||||
"sa_admin_changemap_message": "{lightred}{0}{default} غيّر الخريطة إلى {lightred}{1}{default}!",
|
||||
"sa_admin_noclip_message": "{lightred}{0}{default} فعّل/ألغى نمط اللا تصادم لـ {lightred}{1}{default}!",
|
||||
"sa_admin_freeze_message": "{lightred}{0}{default} جمد {lightred}{1}{default}!",
|
||||
"sa_admin_unfreeze_message": "{lightred}{0}{default} أذاب {lightred}{1}{default}!",
|
||||
"sa_admin_rename_message": "{lightred}{0}{default} غيّر اسم {lightred}{1}{default} إلى {lightred}{2}{default}!",
|
||||
"sa_admin_respawn_message": "{lightred}{0}{default} أحيى {lightred}{1}{default}!",
|
||||
"sa_admin_tp_message": "{lightred}{0}{default} نقل إلى {lightred}{1}{default}!",
|
||||
"sa_admin_bring_message": "{lightred}{0}{default} نقل إلى نفسه {lightred}{1}{default}!",
|
||||
"sa_admin_team_message": "{lightred}{0}{default} نقل {lightred}{1}{default} إلى {lightred}{2}{default}!",
|
||||
"sa_admin_warns_menu_title": "{gold}{0} {lime}تحذيرات",
|
||||
"sa_admin_warns_unwarn": "{lime}تم إلغاء التحذير بنجاح{default} لـ {gold}{0} {default}بسبب {gold}{1}{default}!",
|
||||
"sa_admin_vote_menu_title": "{lime}تصويت لـ {gold}{0}",
|
||||
"sa_admin_vote_message": "{lightred}{0}{default} بدأ التصويت لـ {lightred}{1}{default}",
|
||||
"sa_admin_vote_message_results": "{lime}نتائج التصويت لـ {gold}{0}",
|
||||
"sa_admin_vote_message_results_answer": "{lime}{0} {default}- {gold}{1}",
|
||||
"sa_admin_voice_mute_all": "{Lightred}لقد قمت بكتم {default}جميع اللاعبين أثناء حديثك",
|
||||
"sa_admin_voice_unmute_all": "{Lime}لقد ألغيت كتم {default}جميع اللاعبين",
|
||||
"sa_admin_voice_listen_all": "{Default}أنت الآن تسمع {lime}جميع {default}اللاعبين",
|
||||
"sa_admin_voice_unlisten_all": "{Default}أنت الآن لا تسمع {lime}جميع {default}اللاعبين",
|
||||
"sa_adminsay_prefix": "{RED}الإداري: {lightred}{0}{default}",
|
||||
"sa_adminchat_template_admin": "{LIME}(إداري) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_adminchat_template_player": "{SILVER}(لاعب) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_discord_log_command": "**{0}** أصدر الأمر `{1}` على الخادم `HOSTNAME`",
|
||||
"sa_menu_pluginsmanager_loaded": "{lime}مفعل {default}الإضافة {lime}{0}",
|
||||
"sa_menu_pluginsmanager_unloaded": "{lightred}معطل {default}الإضافة {lightred}{0}"
|
||||
}
|
||||
139
CS2-SimpleAdmin/lang/de.json
Normal file
139
CS2-SimpleAdmin/lang/de.json
Normal file
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"sa_title": "SimpleAdmin",
|
||||
"sa_prefix": "{lightred}[SA] {default}",
|
||||
|
||||
"sa_unknown": "Unbekannt",
|
||||
"sa_no_permission": "Du hast keine Berechtigung zur Verwendung dieses Befehls.",
|
||||
"sa_ban_max_duration_exceeded": "Die Dauer des Banns darf {lightred}{0}{default} Minuten nicht überschreiten.",
|
||||
"sa_ban_perm_restricted": "Du hast nicht die Berechtigung, einen permanenten Bann auszusprechen.",
|
||||
|
||||
"sa_admin_add": "Admin hinzufügen",
|
||||
"sa_admin_remove": "Admin entfernen",
|
||||
"sa_admin_reload": "Admins neuladen",
|
||||
|
||||
"sa_godmode": "Gottmodus",
|
||||
"sa_noclip": "No Clip",
|
||||
"sa_respawn": "Wiederbeleben",
|
||||
"sa_give_weapon": "Waffe gegeben",
|
||||
"sa_strip_weapons": "Waffen abnehmen",
|
||||
"sa_freeze": "Einfrieren",
|
||||
"sa_set_hp": "Lp setzen",
|
||||
"sa_set_speed": "Geschwindigkeit setzen",
|
||||
"sa_set_gravity": "Gravitation setzen",
|
||||
"sa_set_money": "Geld setzen",
|
||||
|
||||
"sa_changemap": "Map wechseln",
|
||||
"sa_restart_game": "Spiel neustarten",
|
||||
|
||||
"sa_team_ct": "AT",
|
||||
"sa_team_t": "T",
|
||||
"sa_team_swap": "Wechseln",
|
||||
"sa_team_spec": "Zuschauer",
|
||||
|
||||
"sa_slap": "Klaps",
|
||||
"sa_slay": "töten",
|
||||
"sa_kick": "Kicken",
|
||||
"sa_ban": "Bann",
|
||||
"sa_gag": "Chat stummschalten",
|
||||
"sa_mute": "Sprachchat stummschalten",
|
||||
"sa_silence": "Komplett stummschalten",
|
||||
"sa_warn": "Warnen",
|
||||
"sa_team_force": "Team zuweisen",
|
||||
|
||||
"sa_menu_custom_commands": "Eigene Befehle",
|
||||
"sa_menu_server_manage": "Server Verwalten",
|
||||
"sa_menu_fun_commands": "Spaß Befehle",
|
||||
"sa_menu_admins_manage": "Admins verwalten",
|
||||
"sa_menu_players_manage": "Spieler verwalten",
|
||||
"sa_menu_disconnected_title": "Letzte Spieler",
|
||||
"sa_menu_disconnected_action_title": "Aktion auswählen",
|
||||
"sa_menu_pluginsmanager_title": "Plugins verwalten",
|
||||
|
||||
"sa_player": "Spieler",
|
||||
"sa_console": "Konsole",
|
||||
"sa_steamid": "SteamID",
|
||||
"sa_duration": "Dauer",
|
||||
"sa_reason": "Grund",
|
||||
"sa_admin": "Admin",
|
||||
"sa_permanent": "Permanent",
|
||||
|
||||
"sa_discord_penalty_ban": "Bann registriert",
|
||||
"sa_discord_penalty_mute": "Chat-Stummschaltung registriert",
|
||||
"sa_discord_penalty_gag": "Sprachchat-Stummschaltung registriert",
|
||||
"sa_discord_penalty_silence": "Komplett-Stummschaltung registriert",
|
||||
"sa_discord_penalty_warn": "Warnung registriert",
|
||||
"sa_discord_penalty_unknown": "Unbekanntes registriert",
|
||||
|
||||
"sa_player_penalty_chat_active": "{lightred}Dein Chat ist blockiert für: {grey}{0}",
|
||||
|
||||
"sa_player_penalty_info_active_mute": "➔ Stummschaltung [{lightred}❌{default}] - Ablauf [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_gag": "➔ Mundtot [{lightred}❌{default}] - Ablauf [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_silence": "➔ Stille [{lightred}❌{default}] - Ablauf [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_warn": "➔ Warnung [{lightred}❌{default}] - Ablauf [{lightred}{0}{default}] - Grund [{lightred}{1}{default}]",
|
||||
|
||||
"sa_player_penalty_info_no_active_mute": "➔ Stummschaltung [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_gag": "➔ Mundtot [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_silence": "➔ Stille [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_warn": "➔ Warnung [{lime}✔{default}]",
|
||||
|
||||
"sa_player_penalty_info": "===========================\nSpielerstrafe für {lightred}{0}{default},\nAnzahl der Sperren: {lightred}{1}{default}, Anzahl der Mundtot: {lightred}{2}{default}, Anzahl der Stummschaltungen: {lightred}{3}{default}, Anzahl der Stille: {lightred}{4}{default}, Anzahl der Warnungen: {lightred}{5}{default}\nAktive Strafen:\n{6}\nAktive Warnungen:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Spielerstrafe für {lightred}{0}{grey}, Sperren: {lightred}{1}{grey}, Mundtot: {lightred}{2}{grey}, Stummschaltungen: {lightred}{3}{grey}, Stille: {lightred}{4}{grey}, Warnungen: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}Verknüpfte Konten des Spielers {lightred}{0}{grey}: {1}",
|
||||
"sa_player_ban_message_time": "Du wurdest wegen {lightred}{0}{default} für {lightred}{1}{default} Minuten von {lightred}{2}{default} gebannt!",
|
||||
"sa_player_ban_message_perm": "Du wurdest wegen {lightred}{0}{default} von {lightred}{1}{default} permanent gebannt!",
|
||||
"sa_player_kick_message": "Du wurdest wegen {lightred}{0}{default} von {lightred}{1}{default} gekickt!",
|
||||
"sa_player_gag_message_time": "Du wurdest wegen {lightred}{0}{default} für {lightred}{1}{default} Minuten von {lightred}{2}{default} im Chat stummgeschaltet!",
|
||||
"sa_player_gag_message_perm": "Du wurdest wegen {lightred}{0}{default} von {lightred}{1}{default} permanent im Chat stummgeschaltet!",
|
||||
"sa_player_mute_message_time": "Du wurdest wegen {lightred}{0}{default} für {lightred}{1}{default} Minuten von {lightred}{2}{default} im Sprachchat stummgeschaltet!",
|
||||
"sa_player_mute_message_perm": "Du wurdest wegen {lightred}{0}{default} von {lightred}{1}{default} permanent im Sprachchat stummgeschaltet!",
|
||||
"sa_player_silence_message_time": "Du wurdest wegen {lightred}{0}{default} für {lightred}{1}{default} Minuten von {lightred}{2}{default} vollständig stummgeschaltet!",
|
||||
"sa_player_silence_message_perm": "Du wurdest wegen {lightred}{0}{default} von {lightred}{1}{default} permanent vollständig stummgeschaltet!",
|
||||
"sa_player_warn_message_time": "Du wurdest wegen {lightred}{0}{default} für {lightred}{1}{default} Minuten von {lightred}{2}{default} gewarnt!",
|
||||
"sa_player_warn_message_perm": "Du wurdest dauerhaft wegen {lightred}{0}{default} von {lightred}{1}{default} gewarnt!",
|
||||
"sa_admin_ban_message_time": "{lightred}{0}{default} hat {lightred}{1}{default} für {lightred}{2}{default} für {lightred}{3}{default} Minuten gebannt!",
|
||||
"sa_admin_ban_message_perm": "{lightred}{0}{default} hat {lightred}{1}{default} permanent für {lightred}{2}{default} gebannt!",
|
||||
"sa_admin_kick_message": "{lightred}{0}{default} hat {lightred}{1}{default} für {lightred}{2}{default} gekickt!",
|
||||
"sa_admin_gag_message_time": "{lightred}{0}{default} hat {lightred}{1}{default} für {lightred}{2}{default} für {lightred}{3}{default} Minuten stummgeschaltet!",
|
||||
"sa_admin_gag_message_perm": "{lightred}{0}{default} hat {lightred}{1}{default} permanent für {lightred}{2}{default} stummgeschaltet!",
|
||||
"sa_admin_mute_message_time": "{lightred}{0}{default} hat {lightred}{1}{default} für {lightred}{2}{default} für {lightred}{3}{default} Minuten gemutet!",
|
||||
"sa_admin_mute_message_perm": "{lightred}{0}{default} hat {lightred}{1}{default} permanent für {lightred}{2}{default} gemutet!",
|
||||
"sa_admin_silence_message_time": "{lightred}{0}{default} hat {lightred}{1}{default} für {lightred}{2}{default} für {lightred}{3}{default} Minuten stummgeschaltet!",
|
||||
"sa_admin_silence_message_perm": "{lightred}{0}{default} hat {lightred}{1}{default} permanent für {lightred}{2}{default} stummgeschaltet!",
|
||||
"sa_admin_warn_message_time": "{lightred}{0}{default} hat {lightred}{1}{default} für {lightred}{2}{default} für {lightred}{3}{default} Minuten verwarnt!",
|
||||
"sa_admin_warn_message_perm": "{lightred}{0}{default} hat {lightred}{1}{default} permanent für {lightred}{2}{default} verwarnt!",
|
||||
"sa_admin_give_message": "{lightred}{0}{default} hat {lightred}{1}{default} ein {lightred}{2}{default} gegeben!",
|
||||
"sa_admin_strip_message": "{lightred}{0}{default} hat alle Waffen von Spieler {lightred}{1}{default} entfernt!",
|
||||
"sa_admin_hp_message": "{lightred}{0}{default} hat die Lebenspunkte von {lightred}{1}{default} geändert!",
|
||||
"sa_admin_speed_message": "{lightred}{0}{default} hat die Geschwindigkeit von {lightred}{1}{default} geändert!",
|
||||
"sa_admin_gravity_message": "{lightred}{0}{default} hat die Schwerkraft von {lightred}{1}{default} geändert!",
|
||||
"sa_admin_money_message": "{lightred}{0}{default} hat das Geld von {lightred}{1}{default} geändert!",
|
||||
"sa_admin_god_message": "{lightred}{0}{default} hat den Gottmodus von {lightred}{1}{default} geändert!",
|
||||
"sa_admin_resize_message": "{lightred}{0}{default} hat die Größe für {lightred}{1}{default} geändert!",
|
||||
"sa_admin_slay_message": "{lightred}{0}{default} hat {lightred}{1}{default} getötet!",
|
||||
"sa_admin_slap_message": "{lightred}{0}{default} hat {lightred}{1}{default} geschlagen!",
|
||||
"sa_admin_changemap_message": "{lightred}{0}{default} hat die Karte zu {lightred}{1}{default} geändert!",
|
||||
"sa_admin_noclip_message": "{lightred}{0}{default} hat den Noclip-Modus für {lightred}{1}{default} aktiviert/deaktiviert!",
|
||||
"sa_admin_freeze_message": "{lightred}{0}{default} hat {lightred}{1}{default} eingefroren!",
|
||||
"sa_admin_unfreeze_message": "{lightred}{0}{default} hat {lightred}{1}{default} aufgetaut!",
|
||||
"sa_admin_rename_message": "{lightred}{0}{default} hat den Namen von {lightred}{1}{default} in {lightred}{2}{default} geändert!",
|
||||
"sa_admin_respawn_message": "{lightred}{0}{default} hat {lightred}{1}{default} wiederbelebt!",
|
||||
"sa_admin_tp_message": "{lightred}{0}{default} ist zu {lightred}{1}{default} teleportiert!",
|
||||
"sa_admin_bring_message": "{lightred}{0}{default} hat {lightred}{1}{default} zu sich teleportiert!",
|
||||
"sa_admin_team_message": "{lightred}{0}{default} hat {lightred}{1}{default} zu {lightred}{2}{default} transferiert!",
|
||||
"sa_admin_warns_menu_title": "{gold}{0} {lime}VERWARNUNGEN",
|
||||
"sa_admin_warns_unwarn": "{lime}Erfolgreich{default} Verwarnung von {gold}{0} {default}aufgehoben wegen {gold}{1}{default}!",
|
||||
"sa_admin_vote_menu_title": "{lime}ABSTIMMUNG FÜR {gold}{0}",
|
||||
"sa_admin_vote_message": "{lightred}{0}{default} hat eine Abstimmung für {lightred}{1}{default} gestartet",
|
||||
"sa_admin_vote_message_results": "{lime}ABSTIMMUNGSERGEBNISSE FÜR {gold}{0}",
|
||||
"sa_admin_vote_message_results_answer": "{lime}{0} {default}- {gold}{1}",
|
||||
"sa_admin_voice_mute_all": "{Lightred}Du hast {default}alle Spieler während deiner Ansprache stummgeschaltet",
|
||||
"sa_admin_voice_unmute_all": "{Lime}Du hast {default}alle Spieler wieder aktiviert",
|
||||
"sa_admin_voice_listen_all": "{Default}Du hörst jetzt {lime}alle {default}Spieler",
|
||||
"sa_admin_voice_unlisten_all": "{Default}Du hörst jetzt {lime}nicht mehr {default}alle Spieler",
|
||||
"sa_adminsay_prefix": "{RED}ADMIN: {lightred}{0}{default}",
|
||||
"sa_adminchat_template_admin": "{LIME}(ADMIN) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_adminchat_template_player": "{SILVER}(SPIELER) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_discord_log_command": "**{0}** hat den Befehl `{1}` auf dem Server `HOSTNAME` ausgeführt",
|
||||
"sa_menu_pluginsmanager_loaded": "{lime}Aktiviert {default}Plugin {lime}{0}",
|
||||
"sa_menu_pluginsmanager_unloaded": "{lightred}Deaktiviert {default}Plugin {lightred}{0}"
|
||||
}
|
||||
139
CS2-SimpleAdmin/lang/en.json
Normal file
139
CS2-SimpleAdmin/lang/en.json
Normal file
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"sa_title": "SimpleAdmin",
|
||||
"sa_prefix": "{lightred}[SA] {default}",
|
||||
|
||||
"sa_unknown": "Unknown",
|
||||
"sa_no_permission": "You do not have permissions to use this command.",
|
||||
"sa_ban_max_duration_exceeded": "Ban duration cannot exceed {lightred}{0}{default} minutes.",
|
||||
"sa_ban_perm_restricted": "You do not have the right to permanently ban.",
|
||||
|
||||
"sa_admin_add": "Add Admin",
|
||||
"sa_admin_remove": "Remove Admin",
|
||||
"sa_admin_reload": "Reload Admins",
|
||||
|
||||
"sa_godmode": "God Mode",
|
||||
"sa_noclip": "No Clip",
|
||||
"sa_respawn": "Respawn",
|
||||
"sa_give_weapon": "Give Weapon",
|
||||
"sa_strip_weapons": "Strip Weapons",
|
||||
"sa_freeze": "Freeze",
|
||||
"sa_set_hp": "Set Hp",
|
||||
"sa_set_speed": "Set Speed",
|
||||
"sa_set_gravity": "Set Gravity",
|
||||
"sa_set_money": "Set Money",
|
||||
|
||||
"sa_changemap": "Change Map",
|
||||
"sa_restart_game": "Restart Game",
|
||||
|
||||
"sa_team_ct": "CT",
|
||||
"sa_team_t": "T",
|
||||
"sa_team_swap": "Swap",
|
||||
"sa_team_spec": "Spec",
|
||||
|
||||
"sa_slap": "Slap",
|
||||
"sa_slay": "slay",
|
||||
"sa_kick": "Kick",
|
||||
"sa_ban": "Ban",
|
||||
"sa_gag": "Gag",
|
||||
"sa_mute": "Mute",
|
||||
"sa_silence": "Silence",
|
||||
"sa_warn": "Warn",
|
||||
"sa_team_force": "Force Team",
|
||||
|
||||
"sa_menu_custom_commands": "Custom Commands",
|
||||
"sa_menu_server_manage": "Server Manage",
|
||||
"sa_menu_fun_commands": "Fun Commands",
|
||||
"sa_menu_admins_manage": "Admins Manage",
|
||||
"sa_menu_players_manage": "Players Manage",
|
||||
"sa_menu_disconnected_title": "Recent players",
|
||||
"sa_menu_disconnected_action_title": "Select action",
|
||||
"sa_menu_pluginsmanager_title": "Plugins Manage",
|
||||
|
||||
"sa_player": "Player",
|
||||
"sa_console": "Console",
|
||||
"sa_steamid": "SteamID",
|
||||
"sa_duration": "Duration",
|
||||
"sa_reason": "Reason",
|
||||
"sa_admin": "Admin",
|
||||
"sa_permanent": "Permanent",
|
||||
|
||||
"sa_discord_penalty_ban": "Ban registered",
|
||||
"sa_discord_penalty_mute": "Mute registered",
|
||||
"sa_discord_penalty_gag": "Gag registered",
|
||||
"sa_discord_penalty_silence": "Silence registered",
|
||||
"sa_discord_penalty_warn": "Warn registered",
|
||||
"sa_discord_penalty_unknown": "Unknown registered",
|
||||
|
||||
"sa_player_penalty_chat_active": "{lightred}Your chat is blocked to: {grey}{0}",
|
||||
|
||||
"sa_player_penalty_info_active_mute": "➔ Mute [{lightred}❌{default}] - Expire [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_gag": "➔ Gag [{lightred}❌{default}] - Expire [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_silence": "➔ Silence [{lightred}❌{default}] - Expire [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_warn": "➔ Warn [{lightred}❌{default}] - Expire [{lightred}{0}{default}] - Reason [{lightred}{1}{default}]",
|
||||
|
||||
"sa_player_penalty_info_no_active_mute": "➔ Mute [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_gag": "➔ Gag [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_silence": "➔ Silence [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_warn": "➔ Warn [{lime}✔{default}]",
|
||||
|
||||
"sa_player_penalty_info": "===========================\nPlayer penalties for {lightred}{0}{default},\nNumber of bans: {lightred}{1}{default}, Number of gags: {lightred}{2}{default}, Number of mutes: {lightred}{3}{default}, Number of silences: {lightred}{4}{default}, Number of warnings: {lightred}{5}{default}\nActive penalties:\n{6}\nActive warnings:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Player penalties for {lightred}{0}{grey}, Bans: {lightred}{1}{grey}, Gags: {lightred}{2}{grey}, Mutes: {lightred}{3}{grey}, Silences: {lightred}{4}{grey}, Warns: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}Associated accounts of player {lightred}{0}{grey}: {1}",
|
||||
"sa_player_ban_message_time": "You have been banned for {lightred}{0}{default} for {lightred}{1}{default} minutes by {lightred}{2}{default}!",
|
||||
"sa_player_ban_message_perm": "You have been banned permanently for {lightred}{0}{default} by {lightred}{1}{default}!",
|
||||
"sa_player_kick_message": "You have been kicked for {lightred}{0}{default} by {lightred}{1}{default}!",
|
||||
"sa_player_gag_message_time": "You have been gagged for {lightred}{0}{default} for {lightred}{1}{default} minutes by {lightred}{2}{default}!",
|
||||
"sa_player_gag_message_perm": "You have been gagged permanently for {lightred}{0}{default} by {lightred}{1}{default}!",
|
||||
"sa_player_mute_message_time": "You have been muted for {lightred}{0}{default} for {lightred}{1}{default} minutes by {lightred}{2}{default}!",
|
||||
"sa_player_mute_message_perm": "You have been muted permanently for {lightred}{0}{default} by {lightred}{1}{default}!",
|
||||
"sa_player_silence_message_time": "You have been silenced for {lightred}{0}{default} for {lightred}{1}{default} minutes by {lightred}{2}{default}!",
|
||||
"sa_player_silence_message_perm": "You have been silenced permanently for {lightred}{0}{default} by {lightred}{1}{default}!",
|
||||
"sa_player_warn_message_time": "You have been warned for {lightred}{0}{default} for {lightred}{1}{default} minutes by {lightred}{2}{default}!",
|
||||
"sa_player_warn_message_perm": "You have been warned permanently for {lightred}{0}{default} by {lightred}{1}{default}!",
|
||||
"sa_admin_ban_message_time": "{lightred}{0}{default} banned {lightred}{1}{default} for {lightred}{2}{default} for {lightred}{3}{default} minutes!",
|
||||
"sa_admin_ban_message_perm": "{lightred}{0}{default} banned {lightred}{1}{default} permanently for {lightred}{2}{default}!",
|
||||
"sa_admin_kick_message": "{lightred}{0}{default} kicked {lightred}{1}{default} for {lightred}{2}{default}!",
|
||||
"sa_admin_gag_message_time": "{lightred}{0}{default} gagged {lightred}{1}{default} for {lightred}{2}{default} for {lightred}{3}{default} minutes!",
|
||||
"sa_admin_gag_message_perm": "{lightred}{0}{default} gagged {lightred}{1}{default} permanently for {lightred}{2}{default}!",
|
||||
"sa_admin_mute_message_time": "{lightred}{0}{default} muted {lightred}{1}{default} for {lightred}{2}{default} for {lightred}{3}{default} minutes!",
|
||||
"sa_admin_mute_message_perm": "{lightred}{0}{default} muted {lightred}{1}{default} permanently for {lightred}{2}{default}!",
|
||||
"sa_admin_silence_message_time": "{lightred}{0}{default} silenced {lightred}{1}{default} for {lightred}{2}{default} for {lightred}{3}{default} minutes!",
|
||||
"sa_admin_silence_message_perm": "{lightred}{0}{default} silenced {lightred}{1}{default} permanently for {lightred}{2}{default}!",
|
||||
"sa_admin_warn_message_time": "{lightred}{0}{default} warned {lightred}{1}{default} for {lightred}{2}{default} for {lightred}{3}{default} minutes!",
|
||||
"sa_admin_warn_message_perm": "{lightred}{0}{default} warned {lightred}{1}{default} permanently for {lightred}{2}{default}!",
|
||||
"sa_admin_give_message": "{lightred}{0}{default} gave {lightred}{1}{default} a {lightred}{2}{default}!",
|
||||
"sa_admin_strip_message": "{lightred}{0}{default} took all of player {lightred}{1}{default} weapons!",
|
||||
"sa_admin_hp_message": "{lightred}{0}{default} changed {lightred}{1}{default} hp amount{default}!",
|
||||
"sa_admin_speed_message": "{lightred}{0}{default} changed speed for {lightred}{1}{default}!",
|
||||
"sa_admin_gravity_message": "{lightred}{0}{default} changed gravity for {lightred}{1}{default}!",
|
||||
"sa_admin_money_message": "{lightred}{0}{default} changed money for {lightred}{1}{default}!",
|
||||
"sa_admin_god_message": "{lightred}{0}{default} changed god mode for {lightred}{1}{default}!",
|
||||
"sa_admin_resize_message": "{lightred}{0}{default} changed size for {lightred}{1}{default}!",
|
||||
"sa_admin_slay_message": "{lightred}{0}{default} slayed {lightred}{1}{default}!",
|
||||
"sa_admin_slap_message": "{lightred}{0}{default} slapped {lightred}{1}{default}!",
|
||||
"sa_admin_changemap_message": "{lightred}{0}{default} changed map to {lightred}{1}{default}!",
|
||||
"sa_admin_noclip_message": "{lightred}{0}{default} toggled noclip for {lightred}{1}{default}!",
|
||||
"sa_admin_freeze_message": "{lightred}{0}{default} froze {lightred}{1}{default}!",
|
||||
"sa_admin_unfreeze_message": "{lightred}{0}{default} unfroze {lightred}{1}{default}!",
|
||||
"sa_admin_rename_message": "{lightred}{0}{default} changed {lightred}{1}{default} nickname to {lightred}{2}{default}!",
|
||||
"sa_admin_respawn_message": "{lightred}{0}{default} respawned {lightred}{1}{default}!",
|
||||
"sa_admin_tp_message": "{lightred}{0}{default} teleported to {lightred}{1}{default}!",
|
||||
"sa_admin_bring_message": "{lightred}{0}{default} teleported to himself {lightred}{1}{default}!",
|
||||
"sa_admin_team_message": "{lightred}{0}{default} transfered {lightred}{1}{default} to {lightred}{2}{default}!",
|
||||
"sa_admin_warns_menu_title": "{gold}{0} {lime}WARNS",
|
||||
"sa_admin_warns_unwarn": "{lime}Successfully{default} unwarned {gold}{0} {default}for {gold}{1}{default}!",
|
||||
"sa_admin_vote_menu_title": "{lime}VOTING FOR {gold}{0}",
|
||||
"sa_admin_vote_message": "{lightred}{0}{default} started voting for {lightred}{1}{default}",
|
||||
"sa_admin_vote_message_results": "{lime}VOTING RESULTS FOR {gold}{0}",
|
||||
"sa_admin_vote_message_results_answer": "{lime}{0} {default}- {gold}{1}",
|
||||
"sa_admin_voice_mute_all": "{Lightred}You muted {default}all players during your speech",
|
||||
"sa_admin_voice_unmute_all": "{Lime}You unmuted {default}all players",
|
||||
"sa_admin_voice_listen_all": "{Default}You can now hear {lime}all {default}players",
|
||||
"sa_admin_voice_unlisten_all": "{Default}You no longer hear {lime}all {default}players",
|
||||
"sa_adminsay_prefix": "{RED}ADMIN: {lightred}{0}{default}",
|
||||
"sa_adminchat_template_admin": "{LIME}(ADMIN) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_adminchat_template_player": "{SILVER}(PLAYER) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_discord_log_command": "**{0}** issued command `{1}` on server `HOSTNAME`",
|
||||
"sa_menu_pluginsmanager_loaded": "{lime}Enabled {default}plugin {lime}{0}",
|
||||
"sa_menu_pluginsmanager_unloaded": "{lightred}Disabled {default}plugin {lightred}{0}"
|
||||
}
|
||||
139
CS2-SimpleAdmin/lang/es.json
Normal file
139
CS2-SimpleAdmin/lang/es.json
Normal file
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"sa_title": "SimpleAdmin",
|
||||
"sa_prefix": "{lightred}[SA] {default}",
|
||||
|
||||
"sa_unknown": "Desconocido",
|
||||
"sa_no_permission": "No tienes permisos para usar este comando.",
|
||||
"sa_ban_max_duration_exceeded": "La duración de la prohibición no puede exceder {lightred}{0}{default} minutos.",
|
||||
"sa_ban_perm_restricted": "No tienes derecho a prohibir permanentemente.",
|
||||
|
||||
"sa_admin_add": "Agregar Administrador",
|
||||
"sa_admin_remove": "Eliminar Administrador",
|
||||
"sa_admin_reload": "Recargar Administradores",
|
||||
|
||||
"sa_godmode": "Modo Dios",
|
||||
"sa_noclip": "Sin Colisión",
|
||||
"sa_respawn": "Reaparecer",
|
||||
"sa_give_weapon": "Dar Arma",
|
||||
"sa_strip_weapons": "Eliminar Armas",
|
||||
"sa_freeze": "Congelar",
|
||||
"sa_set_hp": "Establecer Vida",
|
||||
"sa_set_speed": "Establecer Velocidad",
|
||||
"sa_set_gravity": "Establecer Gravedad",
|
||||
"sa_set_money": "Establecer Dinero",
|
||||
|
||||
"sa_changemap": "Cambiar Mapa",
|
||||
"sa_restart_game": "Reiniciar Juego",
|
||||
|
||||
"sa_team_ct": "CT",
|
||||
"sa_team_t": "T",
|
||||
"sa_team_swap": "Intercambiar",
|
||||
"sa_team_spec": "Espectador",
|
||||
|
||||
"sa_slap": "Golpear",
|
||||
"sa_slay": "Matar",
|
||||
"sa_kick": "Expulsar",
|
||||
"sa_ban": "Banear",
|
||||
"sa_gag": "Callar",
|
||||
"sa_mute": "Silenciar",
|
||||
"sa_silence": "Silencio",
|
||||
"sa_warn": "Advertencia",
|
||||
"sa_team_force": "Forzar Equipo",
|
||||
|
||||
"sa_menu_custom_commands": "Comandos Personalizados",
|
||||
"sa_menu_server_manage": "Administrar Servidor",
|
||||
"sa_menu_fun_commands": "Comandos Divertidos",
|
||||
"sa_menu_admins_manage": "Administrar Administradores",
|
||||
"sa_menu_players_manage": "Administrar Jugadores",
|
||||
"sa_menu_disconnected_title": "Jugadores recientes",
|
||||
"sa_menu_disconnected_action_title": "Seleccionar acción",
|
||||
"sa_menu_pluginsmanager_title": "Gestionar plugins",
|
||||
|
||||
"sa_player": "Jugador",
|
||||
"sa_console": "Consola",
|
||||
"sa_steamid": "ID de Steam",
|
||||
"sa_duration": "Duración",
|
||||
"sa_reason": "Motivo",
|
||||
"sa_admin": "Admin",
|
||||
"sa_permanent": "Permanente",
|
||||
|
||||
"sa_discord_penalty_ban": "Ban registrado",
|
||||
"sa_discord_penalty_mute": "Silencio registrado",
|
||||
"sa_discord_penalty_gag": "Mordaza registrada",
|
||||
"sa_discord_penalty_silence": "Silencio registrado",
|
||||
"sa_discord_penalty_warn": "Advertencia registrada",
|
||||
"sa_discord_penalty_unknown": "Registro desconocido",
|
||||
|
||||
"sa_player_penalty_chat_active": "{lightred}Tu chat está bloqueado para: {grey}{0}",
|
||||
|
||||
"sa_player_penalty_info_active_mute": "➔ Silenciado [{lightred}❌{default}] - Expira [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_gag": "➔ Boqueado [{lightred}❌{default}] - Expira [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_silence": "➔ Silencio [{lightred}❌{default}] - Expira [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_warn": "➔ Advertencia [{lightred}❌{default}] - Expira [{lightred}{0}{default}] - Razón [{lightred}{1}{default}]",
|
||||
|
||||
"sa_player_penalty_info_no_active_mute": "➔ Silenciado [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_gag": "➔ Boqueado [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_silence": "➔ Silencio [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_warn": "➔ Advertencia [{lime}✔{default}]",
|
||||
|
||||
"sa_player_penalty_info": "===========================\nPenalizaciones del jugador para {lightred}{0}{default},\nNúmero de prohibiciones: {lightred}{1}{default}, Número de boqueos: {lightred}{2}{default}, Número de silenciamientos: {lightred}{3}{default}, Número de silencios: {lightred}{4}{default}, Número de advertencias: {lightred}{5}{default}\nPenalizaciones activas:\n{6}\nAdvertencias activas:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Penalizaciones del jugador para {lightred}{0}{grey}, Prohibiciones: {lightred}{1}{grey}, Boqueos: {lightred}{2}{grey}, Silenciamientos: {lightred}{3}{grey}, Silencios: {lightred}{4}{grey}, Advertencias: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}Cuentas asociadas del jugador {lightred}{0}{grey}: {1}",
|
||||
"sa_player_ban_message_time": "Has sido baneado por {lightred}{0}{default} durante {lightred}{1}{default} minutos por {lightred}{2}{default}!",
|
||||
"sa_player_ban_message_perm": "Has sido baneado permanentemente por {lightred}{0}{default} por {lightred}{1}{default}!",
|
||||
"sa_player_kick_message": "Has sido expulsado por {lightred}{0}{default} durante {lightred}{1}{default}!",
|
||||
"sa_player_gag_message_time": "Has sido silenciado por {lightred}{0}{default} durante {lightred}{1}{default} minutos por {lightred}{2}{default}!",
|
||||
"sa_player_gag_message_perm": "Has sido silenciado permanentemente por {lightred}{0}{default} por {lightred}{1}{default}!",
|
||||
"sa_player_mute_message_time": "Has sido muteado por {lightred}{0}{default} durante {lightred}{1}{default} minutos por {lightred}{2}{default}!",
|
||||
"sa_player_mute_message_perm": "Has sido muteado permanentemente por {lightred}{0}{default} por {lightred}{1}{default}!",
|
||||
"sa_player_silence_message_time": "Has sido silenciado por {lightred}{0}{default} durante {lightred}{1}{default} minutos por {lightred}{2}{default}!",
|
||||
"sa_player_silence_message_perm": "Has sido silenciado permanentemente por {lightred}{0}{default} por {lightred}{1}{default}!",
|
||||
"sa_player_warn_message_time": "¡Has sido advertido por {lightred}{0}{default} durante {lightred}{1}{default} minutos por {lightred}{2}{default}!",
|
||||
"sa_player_warn_message_perm": "¡Has sido advertido permanentemente por {lightred}{0}{default} por {lightred}{1}{default}!",
|
||||
"sa_admin_ban_message_time": "{lightred}{0}{default} baneó a {lightred}{1}{default} por {lightred}{2}{default} durante {lightred}{3}{default} minutos!",
|
||||
"sa_admin_ban_message_perm": "{lightred}{0}{default} baneó a {lightred}{1}{default} permanentemente por {lightred}{2}{default}!",
|
||||
"sa_admin_kick_message": "{lightred}{0}{default} expulsó a {lightred}{1}{default} por {lightred}{2}{default}!",
|
||||
"sa_admin_gag_message_time": "{lightred}{0}{default} amordazó a {lightred}{1}{default} por {lightred}{2}{default} durante {lightred}{3}{default} minutos!",
|
||||
"sa_admin_gag_message_perm": "{lightred}{0}{default} amordazó a {lightred}{1}{default} permanentemente por {lightred}{2}{default}!",
|
||||
"sa_admin_mute_message_time": "{lightred}{0}{default} silenció a {lightred}{1}{default} por {lightred}{2}{default} durante {lightred}{3}{default} minutos!",
|
||||
"sa_admin_mute_message_perm": "{lightred}{0}{default} silenció a {lightred}{1}{default} permanentemente por {lightred}{2}{default}!",
|
||||
"sa_admin_silence_message_time": "{lightred}{0}{default} silenció a {lightred}{1}{default} por {lightred}{2}{default} durante {lightred}{3}{default} minutos!",
|
||||
"sa_admin_silence_message_perm": "{lightred}{0}{default} silenció a {lightred}{1}{default} permanentemente por {lightred}{2}{default}!",
|
||||
"sa_admin_warn_message_time": "{lightred}{0}{default} advirtió a {lightred}{1}{default} por {lightred}{2}{default} durante {lightred}{3}{default} minutos!",
|
||||
"sa_admin_warn_message_perm": "{lightred}{0}{default} advirtió a {lightred}{1}{default} permanentemente por {lightred}{2}{default}!",
|
||||
"sa_admin_give_message": "{lightred}{0}{default} dio {lightred}{1}{default} un {lightred}{2}{default}!",
|
||||
"sa_admin_strip_message": "{lightred}{0}{default} quitó todas las armas del jugador {lightred}{1}{default}!",
|
||||
"sa_admin_hp_message": "{lightred}{0}{default} cambió la cantidad de HP de {lightred}{1}{default}!",
|
||||
"sa_admin_speed_message": "{lightred}{0}{default} cambió la velocidad de {lightred}{1}{default}!",
|
||||
"sa_admin_gravity_message": "{lightred}{0}{default} cambió la gravedad de {lightred}{1}{default}!",
|
||||
"sa_admin_money_message": "{lightred}{0}{default} cambió el dinero de {lightred}{1}{default}!",
|
||||
"sa_admin_god_message": "{lightred}{0}{default} cambió el modo dios de {lightred}{1}{default}!",
|
||||
"sa_admin_resize_message": "{lightred}{0}{default} cambió el tamaño de {lightred}{1}{default}!",
|
||||
"sa_admin_slay_message": "{lightred}{0}{default} mató a {lightred}{1}{default}!",
|
||||
"sa_admin_slap_message": "{lightred}{0}{default} abofeteó a {lightred}{1}{default}!",
|
||||
"sa_admin_changemap_message": "{lightred}{0}{default} cambió el mapa a {lightred}{1}{default}!",
|
||||
"sa_admin_noclip_message": "{lightred}{0}{default} alternó noclip para {lightred}{1}{default}!",
|
||||
"sa_admin_freeze_message": "{lightred}{0}{default} congeló a {lightred}{1}{default}!",
|
||||
"sa_admin_unfreeze_message": "{lightred}{0}{default} descongeló a {lightred}{1}{default}!",
|
||||
"sa_admin_rename_message": "{lightred}{0}{default} cambió el apodo de {lightred}{1}{default} a {lightred}{2}{default}!",
|
||||
"sa_admin_respawn_message": "{lightred}{0}{default} reapareció a {lightred}{1}{default}!",
|
||||
"sa_admin_tp_message": "{lightred}{0}{default} se teletransportó a {lightred}{1}{default}!",
|
||||
"sa_admin_bring_message": "{lightred}{0}{default} teletransportó a {lightred}{1}{default} hacia sí mismo!",
|
||||
"sa_admin_team_message": "{lightred}{0}{default} transfirió a {lightred}{1}{default} al {lightred}{2}{default}!",
|
||||
"sa_admin_warns_menu_title": "{gold}{0} {lime}ADVERTENCIAS",
|
||||
"sa_admin_warns_unwarn": "{lime}Desadvertencia{default} exitosa de {gold}{0} {default}por {gold}{1}{default}!",
|
||||
"sa_admin_vote_menu_title": "{lime}VOTACIÓN PARA {gold}{0}",
|
||||
"sa_admin_vote_message": "{lightred}{0}{default} inició una votación para {lightred}{1}{default}",
|
||||
"sa_admin_vote_message_results": "{lime}RESULTADOS DE LA VOTACIÓN PARA {gold}{0}",
|
||||
"sa_admin_vote_message_results_answer": "{lime}{0} {default}- {gold}{1}",
|
||||
"sa_admin_voice_mute_all": "{Lightred}Has silenciado a {default}todos los jugadores durante tu discurso",
|
||||
"sa_admin_voice_unmute_all": "{Lime}Has reactivado el sonido de {default}todos los jugadores",
|
||||
"sa_admin_voice_listen_all": "{Default}Ahora puedes escuchar a {lime}todos {default}los jugadores",
|
||||
"sa_admin_voice_unlisten_all": "{Default}Ya no escuchas a {lime}todos {default}los jugadores",
|
||||
"sa_adminsay_prefix": "{RED}ADMIN: {lightred}{0}{default}",
|
||||
"sa_adminchat_template_admin": "{LIME}(ADMIN) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_adminchat_template_player": "{SILVER}(JUGADOR) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_discord_log_command": "**{0}** ejecutó el comando `{1}` en el servidor `HOSTNAME`",
|
||||
"sa_menu_pluginsmanager_loaded": "{lime}Habilitado {default}plugin {lime}{0}",
|
||||
"sa_menu_pluginsmanager_unloaded": "{lightred}Deshabilitado {default}plugin {lightred}{0}"
|
||||
}
|
||||
139
CS2-SimpleAdmin/lang/fa.json
Normal file
139
CS2-SimpleAdmin/lang/fa.json
Normal file
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"sa_title": "SimpleAdmin",
|
||||
"sa_prefix": "{lightred}[SA] {default}",
|
||||
|
||||
"sa_unknown": "ناشناخته",
|
||||
"sa_no_permission": "شما دسترسی برای استفاده از این دستور را ندارید.",
|
||||
"sa_ban_max_duration_exceeded": "مدت ممنوعیت نمیتواند بیشتر از {lightred}{0}{default} دقیقه باشد.",
|
||||
"sa_ban_perm_restricted": "شما اجازه ممنوعیت دائم را ندارید.",
|
||||
|
||||
"sa_admin_add": "افزودن مدیر",
|
||||
"sa_admin_remove": "حذف مدیر",
|
||||
"sa_admin_reload": "بارگذاری مجدد مدیران",
|
||||
|
||||
"sa_godmode": "حالت خدا",
|
||||
"sa_noclip": "بدون بریدن",
|
||||
"sa_respawn": "باززایی",
|
||||
"sa_give_weapon": "دادن اسلحه",
|
||||
"sa_strip_weapons": "برداشتن اسلحه",
|
||||
"sa_freeze": "یخزدن",
|
||||
"sa_set_hp": "تنظیم پلیر",
|
||||
"sa_set_speed": "تنظیم سرعت",
|
||||
"sa_set_gravity": "تنظیم گرانش",
|
||||
"sa_set_money": "تنظیم پول",
|
||||
|
||||
"sa_changemap": "تغییر نقشه",
|
||||
"sa_restart_game": "شروع مجدد بازی",
|
||||
|
||||
"sa_team_ct": "CT",
|
||||
"sa_team_t": "T",
|
||||
"sa_team_swap": "جابهجایی",
|
||||
"sa_team_spec": "ناظر",
|
||||
|
||||
"sa_slap": "چپاد زدن",
|
||||
"sa_slay": "کشتن",
|
||||
"sa_kick": "اخراج",
|
||||
"sa_ban": "مسدود کردن",
|
||||
"sa_gag": "بیصدا کردن",
|
||||
"sa_mute": "بیصدا کردن",
|
||||
"sa_silence": "سکوت",
|
||||
"sa_warn": "هشدار",
|
||||
"sa_team_force": "اجبار تیم",
|
||||
|
||||
"sa_menu_custom_commands": "دستورات سفارشی",
|
||||
"sa_menu_server_manage": "مدیریت سرور",
|
||||
"sa_menu_fun_commands": "دستورات جالب",
|
||||
"sa_menu_admins_manage": "مدیریت مدیران",
|
||||
"sa_menu_players_manage": "مدیریت بازیکنان",
|
||||
"sa_menu_disconnected_title": "آخرین بازیکنان",
|
||||
"sa_menu_disconnected_action_title": "انتخاب عملیات",
|
||||
"sa_menu_pluginsmanager_title": "مدیریت پلاگینها",
|
||||
|
||||
"sa_player": "بازیکن",
|
||||
"sa_console": "کنسول",
|
||||
"sa_steamid": "شناسه استیم",
|
||||
"sa_duration": "مدت زمان",
|
||||
"sa_reason": "دلیل",
|
||||
"sa_admin": "مدیر",
|
||||
"sa_permanent": "دائمی",
|
||||
|
||||
"sa_discord_penalty_ban": "بن انجام شده",
|
||||
"sa_discord_penalty_mute": "سکوت انجام شده",
|
||||
"sa_discord_penalty_gag": "بند زدن انجام شده",
|
||||
"sa_discord_penalty_silence": "سکوت انجام شده",
|
||||
"sa_discord_penalty_warn": "هشدار ثبت شد",
|
||||
"sa_discord_penalty_unknown": "ناشناخته انجام شده",
|
||||
|
||||
"sa_player_penalty_chat_active": "{lightred}چت شما برای: {grey}{0} مسدود شده است",
|
||||
|
||||
"sa_player_penalty_info_active_mute": "➔ بیصدا [{lightred}❌{default}] - منقضی شدن [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_gag": "➔ مسدود کردن صدا [{lightred}❌{default}] - منقضی شدن [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_silence": "➔ سکوت [{lightred}❌{default}] - منقضی شدن [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_warn": "➔ هشدار [{lightred}❌{default}] - منقضی شدن [{lightred}{0}{default}] - دلیل [{lightred}{1}{default}]",
|
||||
|
||||
"sa_player_penalty_info_no_active_mute": "➔ بیصدا [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_gag": "➔ مسدود کردن صدا [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_silence": "➔ سکوت [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_warn": "➔ هشدار [{lime}✔{default}]",
|
||||
|
||||
"sa_player_penalty_info": "===========================\nتنبیهات بازیکن برای {lightred}{0}{default},\nتعداد مسدودیتها: {lightred}{1}{default}, تعداد سکوتها: {lightred}{2}{default}, تعداد بیصدا کردنها: {lightred}{3}{default}, تعداد سکوتها: {lightred}{4}{default}, تعداد هشدارها: {lightred}{5}{default}\nتنبیهات فعال:\n{6}\nهشدارهای فعال:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}تنبیهات بازیکن برای {lightred}{0}{grey}, مسدودیتها: {lightred}{1}{grey}, سکوتها: {lightred}{2}{grey}, بیصدا کردنها: {lightred}{3}{grey}, سکوتها: {lightred}{4}{grey}, هشدارها: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}حسابهای مرتبط با بازیکن {lightred}{0}{grey}: {1}",
|
||||
"sa_player_ban_message_time": "شما توسط {lightred}{2}{default} برای {lightred}{1}{default} دقیقه به دلیل {lightred}{0}{default} مسدود شدهاید!",
|
||||
"sa_player_ban_message_perm": "شما توسط {lightred}{1}{default} به دلیل {lightred}{0}{default} برای همیشه مسدود شدهاید!",
|
||||
"sa_player_kick_message": "شما توسط {lightred}{1}{default} به دلیل {lightred}{0}{default} اخراج شدهاید!",
|
||||
"sa_player_gag_message_time": "شما توسط {lightred}{2}{default} برای {lightred}{1}{default} دقیقه به دلیل {lightred}{0}{default} خفه شدهاید!",
|
||||
"sa_player_gag_message_perm": "شما توسط {lightred}{1}{default} به دلیل {lightred}{0}{default} برای همیشه خفه شدهاید!",
|
||||
"sa_player_mute_message_time": "شما توسط {lightred}{2}{default} برای {lightred}{1}{default} دقیقه به دلیل {lightred}{0}{default} بیصدا شدهاید!",
|
||||
"sa_player_mute_message_perm": "شما توسط {lightred}{1}{default} به دلیل {lightred}{0}{default} برای همیشه بیصدا شدهاید!",
|
||||
"sa_player_silence_message_time": "شما توسط {lightred}{2}{default} برای {lightred}{1}{default} دقیقه به دلیل {lightred}{0}{default} ساکت شدهاید!",
|
||||
"sa_player_silence_message_perm": "شما توسط {lightred}{1}{default} به دلیل {lightred}{0}{default} برای همیشه ساکت شدهاید!",
|
||||
"sa_player_warn_message_time": "شما به خاطر {lightred}{0}{default} به مدت {lightred}{1}{default} دقیقه توسط {lightred}{2}{default} هشدار داده شده\u200Cاید!",
|
||||
"sa_player_warn_message_perm": "شما به طور دائم به خاطر {lightred}{0}{default} توسط {lightred}{1}{default} هشدار داده شده\u200Cاید!",
|
||||
"sa_admin_ban_message_time": "{lightred}{0}{default} {lightred}{1}{default} را برای {lightred}{3}{default} دقیقه به دلیل {lightred}{2}{default} بن کرد!",
|
||||
"sa_admin_ban_message_perm": "{lightred}{0}{default} {lightred}{1}{default} را بهطور دائم به دلیل {lightred}{2}{default} بن کرد!",
|
||||
"sa_admin_kick_message": "{lightred}{0}{default} {lightred}{1}{default} را به دلیل {lightred}{2}{default} اخراج کرد!",
|
||||
"sa_admin_gag_message_time": "{lightred}{0}{default} {lightred}{1}{default} را برای {lightred}{3}{default} دقیقه به دلیل {lightred}{2}{default} بیصدا کرد!",
|
||||
"sa_admin_gag_message_perm": "{lightred}{0}{default} {lightred}{1}{default} را بهطور دائم به دلیل {lightred}{2}{default} بیصدا کرد!",
|
||||
"sa_admin_mute_message_time": "{lightred}{0}{default} {lightred}{1}{default} را برای {lightred}{3}{default} دقیقه به دلیل {lightred}{2}{default} قطع صدا کرد!",
|
||||
"sa_admin_mute_message_perm": "{lightred}{0}{default} {lightred}{1}{default} را بهطور دائم به دلیل {lightred}{2}{default} قطع صدا کرد!",
|
||||
"sa_admin_silence_message_time": "{lightred}{0}{default} {lightred}{1}{default} را برای {lightred}{3}{default} دقیقه به دلیل {lightred}{2}{default} بیصدا کرد!",
|
||||
"sa_admin_silence_message_perm": "{lightred}{0}{default} {lightred}{1}{default} را بهطور دائم به دلیل {lightred}{2}{default} بیصدا کرد!",
|
||||
"sa_admin_warn_message_time": "{lightred}{0}{default} به {lightred}{1}{default} هشدار داد برای {lightred}{3}{default} دقیقه به دلیل {lightred}{2}{default}!",
|
||||
"sa_admin_warn_message_perm": "{lightred}{0}{default} به {lightred}{1}{default} بهطور دائم به دلیل {lightred}{2}{default} هشدار داد!",
|
||||
"sa_admin_give_message": "{lightred}{0}{default} {lightred}{2}{default} را به {lightred}{1}{default} داد!",
|
||||
"sa_admin_strip_message": "{lightred}{0}{default} تمام سلاحهای بازیکن {lightred}{1}{default} را گرفت!",
|
||||
"sa_admin_hp_message": "{lightred}{0}{default} مقدار سلامت {lightred}{1}{default} را تغییر داد!",
|
||||
"sa_admin_speed_message": "{lightred}{0}{default} سرعت {lightred}{1}{default} را تغییر داد!",
|
||||
"sa_admin_gravity_message": "{lightred}{0}{default} جاذبه {lightred}{1}{default} را تغییر داد!",
|
||||
"sa_admin_money_message": "{lightred}{0}{default} پول {lightred}{1}{default} را تغییر داد!",
|
||||
"sa_admin_god_message": "{lightred}{0}{default} حالت خدا را برای {lightred}{1}{default} تغییر داد!",
|
||||
"sa_admin_resize_message": "{lightred}{0}{default} اندازه {lightred}{1}{default} را تغییر داد!",
|
||||
"sa_admin_slay_message": "{lightred}{0}{default} {lightred}{1}{default} را کشت!",
|
||||
"sa_admin_slap_message": "{lightred}{0}{default} به {lightred}{1}{default} سیلی زد!",
|
||||
"sa_admin_changemap_message": "{lightred}{0}{default} نقشه را به {lightred}{1}{default} تغییر داد!",
|
||||
"sa_admin_noclip_message": "{lightred}{0}{default} ناپدیدی را برای {lightred}{1}{default} فعال/غیرفعال کرد!",
|
||||
"sa_admin_freeze_message": "{lightred}{0}{default} {lightred}{1}{default} را یخزده کرد!",
|
||||
"sa_admin_unfreeze_message": "{lightred}{0}{default} {lightred}{1}{default} را از حالت یخ خارج کرد!",
|
||||
"sa_admin_rename_message": "{lightred}{0}{default} نام {lightred}{1}{default} را به {lightred}{2}{default} تغییر داد!",
|
||||
"sa_admin_respawn_message": "{lightred}{0}{default} {lightred}{1}{default} را دوباره زنده کرد!",
|
||||
"sa_admin_tp_message": "{lightred}{0}{default} به {lightred}{1}{default} تلهپورت شد!",
|
||||
"sa_admin_bring_message": "{lightred}{0}{default} {lightred}{1}{default} را به خود تلهپورت کرد!",
|
||||
"sa_admin_team_message": "{lightred}{0}{default} {lightred}{1}{default} را به {lightred}{2}{default} انتقال داد!",
|
||||
"sa_admin_warns_menu_title": "{gold}{0} {lime}هشدارها",
|
||||
"sa_admin_warns_unwarn": "{lime}هشدار با موفقیت{default} برای {gold}{0} {default}لغو شد به دلیل {gold}{1}{default}!",
|
||||
"sa_admin_vote_menu_title": "{lime}رأیگیری برای {gold}{0}",
|
||||
"sa_admin_vote_message": "{lightred}{0}{default} رأیگیری برای {lightred}{1}{default} را شروع کرد",
|
||||
"sa_admin_vote_message_results": "{lime}نتایج رأیگیری برای {gold}{0}",
|
||||
"sa_admin_vote_message_results_answer": "{lime}{0} {default}- {gold}{1}",
|
||||
"sa_admin_voice_mute_all": "{Lightred}شما {default}همه بازیکنان را در طول سخنرانی بیصدا کردید",
|
||||
"sa_admin_voice_unmute_all": "{Lime}شما {default}همه بازیکنان را از حالت بیصدا خارج کردید",
|
||||
"sa_admin_voice_listen_all": "{Default}اکنون میتوانید {lime}همه {default}بازیکنان را بشنوید",
|
||||
"sa_admin_voice_unlisten_all": "{Default}شما دیگر {lime}همه {default}بازیکنان را نمیشنوید",
|
||||
"sa_adminsay_prefix": "{RED}ادمین: {lightred}{0}{default}",
|
||||
"sa_adminchat_template_admin": "{LIME}(ادمین) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_adminchat_template_player": "{SILVER}(بازیکن) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_discord_log_command": "**{0}** فرمان `{1}` را در سرور `HOSTNAME` اجرا کرد",
|
||||
"sa_menu_pluginsmanager_loaded": "{lime}فعال {default}پلاگین {lime}{0}",
|
||||
"sa_menu_pluginsmanager_unloaded": "{lightred}غیرفعال {default}پلاگین {lightred}{0}"
|
||||
}
|
||||
139
CS2-SimpleAdmin/lang/fr.json
Normal file
139
CS2-SimpleAdmin/lang/fr.json
Normal file
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"sa_title": "SimpleAdmin",
|
||||
"sa_prefix": "{lightred}[SA] {default}",
|
||||
|
||||
"sa_unknown": "Inconnu",
|
||||
"sa_no_permission": "Vous n'avez pas les permissions pour utiliser cette commande.",
|
||||
"sa_ban_max_duration_exceeded": "La durée d'interdiction ne peut pas dépasser {lightred}{0}{default} minutes.",
|
||||
"sa_ban_perm_restricted": "Vous n'avez pas le droit de bannir définitivement.",
|
||||
|
||||
"sa_admin_add": "Ajouter un administrateur",
|
||||
"sa_admin_remove": "Supprimer un administrateur",
|
||||
"sa_admin_reload": "Recharger les administrateurs",
|
||||
|
||||
"sa_godmode": "Mode Dieu",
|
||||
"sa_noclip": "Mode Spectateur",
|
||||
"sa_respawn": "Réapparaître",
|
||||
"sa_give_weapon": "Donner une arme",
|
||||
"sa_strip_weapons": "Retirer les armes",
|
||||
"sa_freeze": "Geler",
|
||||
"sa_set_hp": "Définir les PV",
|
||||
"sa_set_speed": "Définir la vitesse",
|
||||
"sa_set_gravity": "Définir la gravité",
|
||||
"sa_set_money": "Définir l'argent",
|
||||
|
||||
"sa_changemap": "Changer de carte",
|
||||
"sa_restart_game": "Redémarrer le jeu",
|
||||
|
||||
"sa_team_ct": "CT",
|
||||
"sa_team_t": "T",
|
||||
"sa_team_swap": "Échanger",
|
||||
"sa_team_spec": "Spectateur",
|
||||
|
||||
"sa_slap": "Gifler",
|
||||
"sa_slay": "Tuer",
|
||||
"sa_kick": "Expulser",
|
||||
"sa_ban": "Bannir",
|
||||
"sa_gag": "Réduire au silence",
|
||||
"sa_mute": "Muter",
|
||||
"sa_silence": "Silence",
|
||||
"sa_warn": "Avertir",
|
||||
"sa_team_force": "Forcer l'équipe",
|
||||
|
||||
"sa_menu_custom_commands": "Commandes personnalisées",
|
||||
"sa_menu_server_manage": "Gérer le serveur",
|
||||
"sa_menu_fun_commands": "Commandes amusantes",
|
||||
"sa_menu_admins_manage": "Gérer les administrateurs",
|
||||
"sa_menu_players_manage": "Gérer les joueurs",
|
||||
"sa_menu_disconnected_title": "Derniers joueurs",
|
||||
"sa_menu_disconnected_action_title": "Choisir une action",
|
||||
"sa_menu_pluginsmanager_title": "Gérer les plugins",
|
||||
|
||||
"sa_player": "Joueur",
|
||||
"sa_console": "Console",
|
||||
"sa_steamid": "ID Steam",
|
||||
"sa_duration": "Durée",
|
||||
"sa_reason": "Raison",
|
||||
"sa_admin": "Admin",
|
||||
"sa_permanent": "Permanent",
|
||||
|
||||
"sa_discord_penalty_ban": "Bannissement enregistré",
|
||||
"sa_discord_penalty_mute": "Mute enregistré",
|
||||
"sa_discord_penalty_gag": "Gag enregistré",
|
||||
"sa_discord_penalty_silence": "Silence enregistré",
|
||||
"sa_discord_penalty_warn": "Avertissement enregistré",
|
||||
"sa_discord_penalty_unknown": "Inconnu enregistré",
|
||||
|
||||
"sa_player_penalty_chat_active": "{lightred}Votre chat est bloqué pour : {grey}{0}",
|
||||
|
||||
"sa_player_penalty_info_active_mute": "➔ Muet [{lightred}❌{default}] - Expire [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_gag": "➔ Gag [{lightred}❌{default}] - Expire [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_silence": "➔ Silence [{lightred}❌{default}] - Expire [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_warn": "➔ Avertissement [{lightred}❌{default}] - Expire [{lightred}{0}{default}] - Raison [{lightred}{1}{default}]",
|
||||
|
||||
"sa_player_penalty_info_no_active_mute": "➔ Muet [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_gag": "➔ Gag [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_silence": "➔ Silence [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_warn": "➔ Avertissement [{lime}✔{default}]",
|
||||
|
||||
"sa_player_penalty_info": "===========================\nPénalités du joueur pour {lightred}{0}{default},\nNombre de bannissements: {lightred}{1}{default}, Nombre de gag: {lightred}{2}{default}, Nombre de mutes: {lightred}{3}{default}, Nombre de silences: {lightred}{4}{default}, Nombre d’avertissements: {lightred}{5}{default}\nPénalités actives:\n{6}\nAvertissements actifs:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Pénalités du joueur pour {lightred}{0}{grey}, Bannissements: {lightred}{1}{grey}, Gags: {lightred}{2}{grey}, Mutes: {lightred}{3}{grey}, Silences: {lightred}{4}{grey}, Avertissements: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}Comptes associés du joueur {lightred}{0}{grey} : {1}",
|
||||
"sa_player_ban_message_time": "Vous avez été banni pour {lightred}{0}{default} pendant {lightred}{1}{default} minutes par {lightred}{2}{default}!",
|
||||
"sa_player_ban_message_perm": "Vous avez été banni définitivement pour {lightred}{0}{default} par {lightred}{1}{default}!",
|
||||
"sa_player_kick_message": "Vous avez été expulsé pour {lightred}{0}{default} par {lightred}{1}{default}!",
|
||||
"sa_player_gag_message_time": "Vous avez été réduit au silence pour {lightred}{0}{default} pendant {lightred}{1}{default} minutes par {lightred}{2}{default}!",
|
||||
"sa_player_gag_message_perm": "Vous avez été réduit au silence définitivement pour {lightred}{0}{default} par {lightred}{1}{default}!",
|
||||
"sa_player_mute_message_time": "Vous avez été réduit au silence pour {lightred}{0}{default} pendant {lightred}{1}{default} minutes par {lightred}{2}{default}!",
|
||||
"sa_player_mute_message_perm": "Vous avez été réduit au silence définitivement pour {lightred}{0}{default} par {lightred}{1}{default}!",
|
||||
"sa_player_silence_message_time": "Vous avez été mis en sourdine pour {lightred}{0}{default} pendant {lightred}{1}{default} minutes par {lightred}{2}{default}!",
|
||||
"sa_player_silence_message_perm": "Vous avez été mis en sourdine définitivement pour {lightred}{0}{default} par {lightred}{1}{default}!",
|
||||
"sa_player_warn_message_time": "Vous avez été averti pour {lightred}{0}{default} pendant {lightred}{1}{default} minutes par {lightred}{2}{default}!",
|
||||
"sa_player_warn_message_perm": "Vous avez été averti définitivement pour {lightred}{0}{default} par {lightred}{1}{default}!",
|
||||
"sa_admin_ban_message_time": "{lightred}{0}{default} a banni {lightred}{1}{default} pendant {lightred}{3}{default} minutes pour {lightred}{2}{default}!",
|
||||
"sa_admin_ban_message_perm": "{lightred}{0}{default} a banni {lightred}{1}{default} définitivement pour {lightred}{2}{default}!",
|
||||
"sa_admin_kick_message": "{lightred}{0}{default} a expulsé {lightred}{1}{default} pour {lightred}{2}{default}!",
|
||||
"sa_admin_gag_message_time": "{lightred}{0}{default} a bâillonné {lightred}{1}{default} pendant {lightred}{3}{default} minutes pour {lightred}{2}{default}!",
|
||||
"sa_admin_gag_message_perm": "{lightred}{0}{default} a bâillonné {lightred}{1}{default} définitivement pour {lightred}{2}{default}!",
|
||||
"sa_admin_mute_message_time": "{lightred}{0}{default} a rendu {lightred}{1}{default} muet pendant {lightred}{3}{default} minutes pour {lightred}{2}{default}!",
|
||||
"sa_admin_mute_message_perm": "{lightred}{0}{default} a rendu {lightred}{1}{default} muet définitivement pour {lightred}{2}{default}!",
|
||||
"sa_admin_silence_message_time": "{lightred}{0}{default} a rendu {lightred}{1}{default} silencieux pendant {lightred}{3}{default} minutes pour {lightred}{2}{default}!",
|
||||
"sa_admin_silence_message_perm": "{lightred}{0}{default} a rendu {lightred}{1}{default} silencieux définitivement pour {lightred}{2}{default}!",
|
||||
"sa_admin_warn_message_time": "{lightred}{0}{default} a averti {lightred}{1}{default} pendant {lightred}{3}{default} minutes pour {lightred}{2}{default}!",
|
||||
"sa_admin_warn_message_perm": "{lightred}{0}{default} a averti {lightred}{1}{default} définitivement pour {lightred}{2}{default}!",
|
||||
"sa_admin_give_message": "{lightred}{0}{default} a donné {lightred}{2}{default} à {lightred}{1}{default}!",
|
||||
"sa_admin_strip_message": "{lightred}{0}{default} a retiré toutes les armes de {lightred}{1}{default}!",
|
||||
"sa_admin_hp_message": "{lightred}{0}{default} a modifié la quantité de HP de {lightred}{1}{default}!",
|
||||
"sa_admin_speed_message": "{lightred}{0}{default} a modifié la vitesse de {lightred}{1}{default}!",
|
||||
"sa_admin_gravity_message": "{lightred}{0}{default} a modifié la gravité de {lightred}{1}{default}!",
|
||||
"sa_admin_money_message": "{lightred}{0}{default} a modifié l'argent de {lightred}{1}{default}!",
|
||||
"sa_admin_god_message": "{lightred}{0}{default} a modifié le mode dieu de {lightred}{1}{default}!",
|
||||
"sa_admin_resize_message": "{lightred}{0}{default} a changé la taille de {lightred}{1}{default}!",
|
||||
"sa_admin_slay_message": "{lightred}{0}{default} a tué {lightred}{1}{default}!",
|
||||
"sa_admin_slap_message": "{lightred}{0}{default} a giflé {lightred}{1}{default}!",
|
||||
"sa_admin_changemap_message": "{lightred}{0}{default} a changé la carte pour {lightred}{1}{default}!",
|
||||
"sa_admin_noclip_message": "{lightred}{0}{default} a activé/désactivé le noclip pour {lightred}{1}{default}!",
|
||||
"sa_admin_freeze_message": "{lightred}{0}{default} a gelé {lightred}{1}{default}!",
|
||||
"sa_admin_unfreeze_message": "{lightred}{0}{default} a dégivré {lightred}{1}{default}!",
|
||||
"sa_admin_rename_message": "{lightred}{0}{default} a renommé {lightred}{1}{default} en {lightred}{2}{default}!",
|
||||
"sa_admin_respawn_message": "{lightred}{0}{default} a réapparu {lightred}{1}{default}!",
|
||||
"sa_admin_tp_message": "{lightred}{0}{default} s'est téléporté à {lightred}{1}{default}!",
|
||||
"sa_admin_bring_message": "{lightred}{0}{default} a téléporté {lightred}{1}{default} à lui!",
|
||||
"sa_admin_team_message": "{lightred}{0}{default} a transféré {lightred}{1}{default} à l'équipe {lightred}{2}{default}!",
|
||||
"sa_admin_warns_menu_title": "{gold}{0} {lime}AVERTISSEMENTS",
|
||||
"sa_admin_warns_unwarn": "{lime}Avertissement annulé{default} avec succès pour {gold}{0} {default}en raison de {gold}{1}{default}!",
|
||||
"sa_admin_vote_menu_title": "{lime}VOTE POUR {gold}{0}",
|
||||
"sa_admin_vote_message": "{lightred}{0}{default} a lancé un vote pour {lightred}{1}{default}",
|
||||
"sa_admin_vote_message_results": "{lime}RÉSULTATS DU VOTE POUR {gold}{0}",
|
||||
"sa_admin_vote_message_results_answer": "{lime}{0} {default}- {gold}{1}",
|
||||
"sa_admin_voice_mute_all": "{Lightred}Vous avez mis en sourdine {default}tous les joueurs pendant votre discours",
|
||||
"sa_admin_voice_unmute_all": "{Lime}Vous avez réactivé le son de {default}tous les joueurs",
|
||||
"sa_admin_voice_listen_all": "{Default}Vous entendez maintenant {lime}tous {default}les joueurs",
|
||||
"sa_admin_voice_unlisten_all": "{Default}Vous n'entendez plus {lime}tous {default}les joueurs",
|
||||
"sa_adminsay_prefix": "{RED}ADMIN: {lightred}{0}{default}",
|
||||
"sa_adminchat_template_admin": "{LIME}(ADMIN) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_adminchat_template_player": "{SILVER}(JOUEUR) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_discord_log_command": "**{0}** a exécuté la commande `{1}` sur le serveur `HOSTNAME`",
|
||||
"sa_menu_pluginsmanager_loaded": "{lime}Activé {default}plugin {lime}{0}",
|
||||
"sa_menu_pluginsmanager_unloaded": "{lightred}Désactivé {default}plugin {lightred}{0}"
|
||||
}
|
||||
139
CS2-SimpleAdmin/lang/lv.json
Normal file
139
CS2-SimpleAdmin/lang/lv.json
Normal file
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"sa_title": "SimpleAdmin",
|
||||
"sa_prefix": "{lightred}[SA] {default}",
|
||||
|
||||
"sa_unknown": "Nezināms",
|
||||
"sa_no_permission": "Jums nav atļauju izmantot šo komandu.",
|
||||
"sa_ban_max_duration_exceeded": "Aizlieguma ilgums nevar pārsniegt {lightred}{0}{default} minūtes.",
|
||||
"sa_ban_perm_restricted": "Jums nav tiesību uz pastāvīgu aizliegumu.",
|
||||
|
||||
"sa_admin_add": "Pievienot administratoru",
|
||||
"sa_admin_remove": "Noņemt administratoru",
|
||||
"sa_admin_reload": "Pārlādēt administratorus",
|
||||
|
||||
"sa_godmode": "Dieva režīms",
|
||||
"sa_noclip": "Bez šķēršļiem",
|
||||
"sa_respawn": "Atdzimt",
|
||||
"sa_give_weapon": "Dot ieroci",
|
||||
"sa_strip_weapons": "Noņemt ieročus",
|
||||
"sa_freeze": "Salauzt",
|
||||
"sa_set_hp": "Iestatīt veselību",
|
||||
"sa_set_speed": "Iestatīt ātrumu",
|
||||
"sa_set_gravity": "Iestatīt gravitāciju",
|
||||
"sa_set_money": "Iestatīt naudu",
|
||||
|
||||
"sa_changemap": "Mainīt karti",
|
||||
"sa_restart_game": "Restartēt spēli",
|
||||
|
||||
"sa_team_ct": "CT",
|
||||
"sa_team_t": "T",
|
||||
"sa_team_swap": "Mainīt",
|
||||
"sa_team_spec": "Skatītājs",
|
||||
|
||||
"sa_slap": "Plašs",
|
||||
"sa_slay": "Nogalināt",
|
||||
"sa_kick": "Izraidīt",
|
||||
"sa_ban": "Bloķēt",
|
||||
"sa_gag": "Izslēgt runu",
|
||||
"sa_mute": "Noklusināt",
|
||||
"sa_silence": "Klusums",
|
||||
"sa_warn": "Brīdināt",
|
||||
"sa_team_force": "Spēka komanda",
|
||||
|
||||
"sa_menu_custom_commands": "Pielāgotās komandas",
|
||||
"sa_menu_server_manage": "Servera pārvaldība",
|
||||
"sa_menu_fun_commands": "Jautras komandas",
|
||||
"sa_menu_admins_manage": "Administratoru pārvaldība",
|
||||
"sa_menu_players_manage": "Spēlētāju pārvaldība",
|
||||
"sa_menu_disconnected_title": "Pēdējie spēlētāji",
|
||||
"sa_menu_disconnected_action_title": "Izvēlieties darbību",
|
||||
"sa_menu_pluginsmanager_title": "Pārvaldīt spraudņus",
|
||||
|
||||
"sa_player": "Spēlētājs",
|
||||
"sa_console": "Konsole",
|
||||
"sa_steamid": "Steam ID",
|
||||
"sa_duration": "Ilgums",
|
||||
"sa_reason": "Iemesls",
|
||||
"sa_admin": "Admins",
|
||||
"sa_permanent": "Pastāvīgs",
|
||||
|
||||
"sa_discord_penalty_ban": "Bans reģistrēts",
|
||||
"sa_discord_penalty_mute": "Mute reģistrēts",
|
||||
"sa_discord_penalty_gag": "Gag reģistrēts",
|
||||
"sa_discord_penalty_silence": "Klusums reģistrēts",
|
||||
"sa_discord_penalty_warn": "Brīdinājums reģistrēts",
|
||||
"sa_discord_penalty_unknown": "Nezināms reģistrēts",
|
||||
|
||||
"sa_player_penalty_chat_active": "{lightred}Jūsu čats ir bloķēts uz: {grey}{0}",
|
||||
|
||||
"sa_player_penalty_info_active_mute": "➔ Izslēgts [{lightred}❌{default}] - Beidzas [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_gag": "➔ Klusums [{lightred}❌{default}] - Beidzas [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_silence": "➔ Klusēšana [{lightred}❌{default}] - Beidzas [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_warn": "➔ Brīdinājums [{lightred}❌{default}] - Beidzas [{lightred}{0}{default}] - Iemesls [{lightred}{1}{default}]",
|
||||
|
||||
"sa_player_penalty_info_no_active_mute": "➔ Izslēgts [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_gag": "➔ Klusums [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_silence": "➔ Klusēšana [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_warn": "➔ Brīdinājums [{lime}✔{default}]",
|
||||
|
||||
"sa_player_penalty_info": "===========================\nSpēlētāja sods priekš {lightred}{0}{default},\nAizliegumu skaits: {lightred}{1}{default}, Klusumu skaits: {lightred}{2}{default}, Izslēgšanas skaits: {lightred}{3}{default}, Klusēšanas skaits: {lightred}{4}{default}, Brīdinājumu skaits: {lightred}{5}{default}\nAktīvie sodi:\n{6}\nAktīvie brīdinājumi:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Spēlētāja sods priekš {lightred}{0}{grey}, Aizliegumi: {lightred}{1}{grey}, Klusumi: {lightred}{2}{grey}, Izslēgšana: {lightred}{3}{grey}, Klusēšana: {lightred}{4}{grey}, Brīdinājumi: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}Spēlētāja {lightred}{0}{grey} saistītie konti: {1}",
|
||||
"sa_player_ban_message_time": "Tu esi nobanots uz {lightred}{0}{default} uz {lightred}{1}{default} minūtēm, iemesls: {lightred}{2}{default}!",
|
||||
"sa_player_ban_message_perm": "Tevis bans ir uz mūžu, iemesls: {lightred}{0}{default}, Admins: {lightred}{1}{default}!",
|
||||
"sa_player_kick_message": "Tu esi izmests, iemesls: {lightred}{0}{default}, Admins: {lightred}{1}{default}!",
|
||||
"sa_player_gag_message_time": "Tev ir izliegta čata rakstīšana uz {lightred}{0}{default} uz {lightred}{1}{default} minūtēm, iemesls: {lightred}{2}{default}, Admins: {lightred}{3}{default}!",
|
||||
"sa_player_gag_message_perm": "Tev ir izliegta čata rakstīšana uz mūžu, iemesls: {lightred}{0}{default}, Admins: {lightred}{1}{default}!",
|
||||
"sa_player_mute_message_time": "Tev ir izliegta balsu rakstīšana uz {lightred}{0}{default} uz {lightred}{1}{default} minūtēm, iemesls: {lightred}{2}{default}, Admins: {lightred}{3}{default}!",
|
||||
"sa_player_mute_message_perm": "Tev ir izliegta balsu rakstīšana uz mūžu, iemesls: {lightred}{0}{default}, Admins: {lightred}{1}{default}!",
|
||||
"sa_player_silence_message_time": "Tevis balss ir izslēgta uz {lightred}{0}{default} uz {lightred}{1}{default} minūtēm, iemesls: {lightred}{2}{default}, Admins: {lightred}{3}{default}!",
|
||||
"sa_player_silence_message_perm": "Tevis balss ir izslēgta uz mūžu, iemesls: {lightred}{0}{default}, Admins: {lightred}{1}{default}!",
|
||||
"sa_player_warn_message_time": "Jums ir izteikts brīdinājums par {lightred}{0}{default} uz {lightred}{1}{default} minūtēm no {lightred}{2}{default}!",
|
||||
"sa_player_warn_message_perm": "Jums ir izteikts pastāvīgs brīdinājums par {lightred}{0}{default} no {lightred}{1}{default}!",
|
||||
"sa_admin_ban_message_time": "{lightred}{0}{default} nobanoja {lightred}{1}{default} uz {lightred}{3}{default} minūtēm par {lightred}{2}{default}!",
|
||||
"sa_admin_ban_message_perm": "{lightred}{0}{default} nobanoja {lightred}{1}{default} uz visiem laikiem par {lightred}{2}{default}!",
|
||||
"sa_admin_kick_message": "{lightred}{0}{default} izmeta {lightred}{1}{default} par {lightred}{2}{default}!",
|
||||
"sa_admin_gag_message_time": "{lightred}{0}{default} apklusināja {lightred}{1}{default} uz {lightred}{3}{default} minūtēm par {lightred}{2}{default}!",
|
||||
"sa_admin_gag_message_perm": "{lightred}{0}{default} apklusināja {lightred}{1}{default} uz visiem laikiem par {lightred}{2}{default}!",
|
||||
"sa_admin_mute_message_time": "{lightred}{0}{default} izslēdza {lightred}{1}{default} uz {lightred}{3}{default} minūtēm par {lightred}{2}{default}!",
|
||||
"sa_admin_mute_message_perm": "{lightred}{0}{default} izslēdza {lightred}{1}{default} uz visiem laikiem par {lightred}{2}{default}!",
|
||||
"sa_admin_silence_message_time": "{lightred}{0}{default} apklusināja {lightred}{1}{default} uz {lightred}{3}{default} minūtēm par {lightred}{2}{default}!",
|
||||
"sa_admin_silence_message_perm": "{lightred}{0}{default} apklusināja {lightred}{1}{default} uz visiem laikiem par {lightred}{2}{default}!",
|
||||
"sa_admin_warn_message_time": "{lightred}{0}{default} brīdināja {lightred}{1}{default} uz {lightred}{3}{default} minūtēm par {lightred}{2}{default}!",
|
||||
"sa_admin_warn_message_perm": "{lightred}{0}{default} brīdināja {lightred}{1}{default} uz visiem laikiem par {lightred}{2}{default}!",
|
||||
"sa_admin_give_message": "{lightred}{0}{default} iedeva {lightred}{1}{default} {lightred}{2}{default}!",
|
||||
"sa_admin_strip_message": "{lightred}{0}{default} noņēma visus {lightred}{1}{default} ieročus!",
|
||||
"sa_admin_hp_message": "{lightred}{0}{default} mainīja {lightred}{1}{default} HP daudzumu!",
|
||||
"sa_admin_speed_message": "{lightred}{0}{default} mainīja {lightred}{1}{default} ātrumu!",
|
||||
"sa_admin_gravity_message": "{lightred}{0}{default} mainīja {lightred}{1}{default} gravitāciju!",
|
||||
"sa_admin_money_message": "{lightred}{0}{default} mainīja {lightred}{1}{default} naudu!",
|
||||
"sa_admin_god_message": "{lightred}{0}{default} mainīja dieva režīmu priekš {lightred}{1}{default}!",
|
||||
"sa_admin_resize_message": "{lightred}{0}{default} mainīja izmēru {lightred}{1}{default}!",
|
||||
"sa_admin_slay_message": "{lightred}{0}{default} nogalināja {lightred}{1}{default}!",
|
||||
"sa_admin_slap_message": "{lightred}{0}{default} iedeva {lightred}{1}{default} pa seju!",
|
||||
"sa_admin_changemap_message": "{lightred}{0}{default} mainīja karti uz {lightred}{1}{default}!",
|
||||
"sa_admin_noclip_message": "{lightred}{0}{default} aktivizēja/deaktivizēja noclip priekš {lightred}{1}{default}!",
|
||||
"sa_admin_freeze_message": "{lightred}{0}{default} sasaldēja {lightred}{1}{default}!",
|
||||
"sa_admin_unfreeze_message": "{lightred}{0}{default} atkausēja {lightred}{1}{default}!",
|
||||
"sa_admin_rename_message": "{lightred}{0}{default} pārdēvēja {lightred}{1}{default} uz {lightred}{2}{default}!",
|
||||
"sa_admin_respawn_message": "{lightred}{0}{default} atdzīvināja {lightred}{1}{default}!",
|
||||
"sa_admin_tp_message": "{lightred}{0}{default} teleporta uz {lightred}{1}{default}!",
|
||||
"sa_admin_bring_message": "{lightred}{0}{default} teleporta {lightred}{1}{default} pie sevis!",
|
||||
"sa_admin_team_message": "{lightred}{0}{default} pārvietoja {lightred}{1}{default} uz {lightred}{2}{default} komandu!",
|
||||
"sa_admin_warns_menu_title": "{gold}{0} {lime}BRĪDINĀJUMI",
|
||||
"sa_admin_warns_unwarn": "{lime}Brīdinājums veiksmīgi{default} atsaukts priekš {gold}{0} {default}dēļ {gold}{1}{default}!",
|
||||
"sa_admin_vote_menu_title": "{lime}BALSOŠANA PAR {gold}{0}",
|
||||
"sa_admin_vote_message": "{lightred}{0}{default} uzsāka balsošanu par {lightred}{1}{default}",
|
||||
"sa_admin_vote_message_results": "{lime}BALSOŠANAS REZULTĀTI PAR {gold}{0}",
|
||||
"sa_admin_vote_message_results_answer": "{lime}{0} {default}- {gold}{1}",
|
||||
"sa_admin_voice_mute_all": "{Lightred}Tu esi apklusinājis {default}visus spēlētājus runas laikā",
|
||||
"sa_admin_voice_unmute_all": "{Lime}Tu atvienoji {default}visus spēlētājus",
|
||||
"sa_admin_voice_listen_all": "{Default}Tu tagad dzirdi {lime}visus {default}spēlētājus",
|
||||
"sa_admin_voice_unlisten_all": "{Default}Tu vairs nedzirdi {lime}visus {default}spēlētājus",
|
||||
"sa_adminsay_prefix": "{RED}ADMIN: {lightred}{0}{default}",
|
||||
"sa_adminchat_template_admin": "{LIME}(ADMIN) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_adminchat_template_player": "{SILVER}(SPĒLĒTĀJS) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_discord_log_command": "**{0}** izpildīja komandu `{1}` serverī `HOSTNAME`",
|
||||
"sa_menu_pluginsmanager_loaded": "{lime}Ieslēgts {default}spraudnis {lime}{0}",
|
||||
"sa_menu_pluginsmanager_unloaded": "{lightred}Izslēgts {default}spraudnis {lightred}{0}"
|
||||
}
|
||||
140
CS2-SimpleAdmin/lang/pl.json
Normal file
140
CS2-SimpleAdmin/lang/pl.json
Normal file
@@ -0,0 +1,140 @@
|
||||
{
|
||||
"sa_title": "SimpleAdmin",
|
||||
"sa_prefix": "{lightred}[SA] {default}",
|
||||
|
||||
"sa_unknown": "Brak",
|
||||
"sa_no_permission": "Nie masz uprawnień do korzystania z tej komendy.",
|
||||
"sa_ban_max_duration_exceeded": "Czas bana nie może przekraczać {lightred}{0}{default} minut.",
|
||||
"sa_ban_perm_restricted": "Nie masz prawa do trwałego zbanowania.",
|
||||
|
||||
"sa_admin_add": "Dodaj administratora",
|
||||
"sa_admin_remove": "Usuń administratora",
|
||||
"sa_admin_reload": "Przeładuj administratorów",
|
||||
|
||||
"sa_godmode": "Tryb Boga",
|
||||
"sa_noclip": "Tryb Latania",
|
||||
"sa_respawn": "Odrodzenie",
|
||||
"sa_give_weapon": "Daj broń",
|
||||
"sa_strip_weapons": "Usuń bronie",
|
||||
"sa_freeze": "Zamroź",
|
||||
"sa_set_hp": "Ustaw HP",
|
||||
"sa_set_speed": "Ustaw prędkość",
|
||||
"sa_set_gravity": "Ustaw grawitację",
|
||||
"sa_set_money": "Ustaw pieniądze",
|
||||
|
||||
"sa_changemap": "Zmień mapę",
|
||||
"sa_restart_game": "Zrestartuj mapę",
|
||||
|
||||
"sa_team_ct": "CT",
|
||||
"sa_team_t": "T",
|
||||
"sa_team_swap": "Zamień",
|
||||
"sa_team_spec": "Spec",
|
||||
|
||||
"sa_slap": "Uderz",
|
||||
"sa_slay": "Zabij",
|
||||
"sa_kick": "Wyrzuć",
|
||||
"sa_ban": "Zbanuj",
|
||||
"sa_gag": "Zaknebluj",
|
||||
"sa_mute": "Wycisz",
|
||||
"sa_silence": "Ucisz",
|
||||
"sa_warn": "Ostrzeżenie",
|
||||
"sa_team_force": "Wymuś drużynę",
|
||||
|
||||
"sa_menu_custom_commands": "Komendy niestandardowe",
|
||||
"sa_menu_server_manage": "Zarządzaj serwerem",
|
||||
"sa_menu_fun_commands": "Komendy rozrywkowe",
|
||||
"sa_menu_admins_manage": "Zarządzaj administratorami",
|
||||
"sa_menu_players_manage": "Zarządzaj graczami",
|
||||
"sa_menu_disconnected_title": "Ostatni gracze",
|
||||
"sa_menu_disconnected_action_title": "Wybierz akcje",
|
||||
"sa_menu_pluginsmanager_title": "Zarządzaj pluginami",
|
||||
|
||||
"sa_player": "Gracz",
|
||||
"sa_console": "Konsola",
|
||||
"sa_steamid": "SteamID",
|
||||
"sa_duration": "Wygasa",
|
||||
"sa_reason": "Powód",
|
||||
"sa_admin": "Admin",
|
||||
"sa_permanent": "Na zawsze",
|
||||
|
||||
"sa_discord_penalty_ban": "Nowy ban",
|
||||
"sa_discord_penalty_mute": "Nowe wyciszenie",
|
||||
"sa_discord_penalty_gag": "Nowe zakneblowanie",
|
||||
"sa_discord_penalty_silence": "Nowe uciszenie",
|
||||
"sa_discord_penalty_warn": "Nowe ostrzeżenie",
|
||||
"sa_discord_penalty_unknown": "Nowa nieznana blokada",
|
||||
|
||||
"sa_player_penalty_chat_active": "{lightred}Twój czat jest zablokowany do: {grey}{0}",
|
||||
|
||||
"sa_player_penalty_info_active_mute": "➔ Zakneblowanie [{lightred}❌{default}] - Wygasa [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_gag": "➔ Wyciszenie [{lightred}❌{default}] - Wygasa [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_silence": "➔ Uciszenie [{lightred}❌{default}] - Wygasa [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_warn": "➔ Ostrzeżenie [{lightred}❌{default}] - Wygasa [{lightred}{0}{default}] - Powód [{lightred}{1}{default}]",
|
||||
|
||||
"sa_player_penalty_info_no_active_mute": "➔ Zakneblowanie [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_gag": "➔ Wyciszenie [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_silence": "➔ Uciszenie [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_warn": "➔ Ostrzeżenie [{lime}✔{default}]",
|
||||
|
||||
"sa_player_penalty_info": "===========================\nBlokady gracza {lightred}{0}{default},\nIlość banów: {lightred}{1}{default}, Ilość zakneblowań: {lightred}{2}{default}, Ilość wyciszeń: {lightred}{3}{default}, Ilość uciszeń: {lightred}{4}{default}Ilość ostrzeżeń: {lightred}{5}{default}\nAktywne blokady:\n{6}\nAktywne ostrzeżenia:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Blokady gracza {lightred}{0}{grey} - bany: {lightred}{1}{grey}, zakneblowania: {lightred}{2}{grey}, wyciszenia: {lightred}{3}{grey}, uciszenia: {lightred}{4}{grey}, ostrzeżenia: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}Powiązane konta gracza {lightred}{0}{grey}: {1}",
|
||||
"sa_player_ban_message_time": "Zostałeś zbanowany za {lightred}{0}{default} na {lightred}{1}{default} minut przez {lightred}{2}{default}!",
|
||||
"sa_player_ban_message_perm": "Zostałeś zbanowany na zawsze za {lightred}{0}{default} przez {lightred}{1}{default}!",
|
||||
"sa_player_kick_message": "Zostałeś wyrzucony za {lightred}{0}{default} przez {lightred}{1}{default}!",
|
||||
"sa_player_gag_message_time": "Zostałeś zakneblowany za {lightred}{0}{default} na {lightred}{1}{default} minut przez {lightred}{2}{default}!",
|
||||
"sa_player_gag_message_perm": "Zostałeś zakneblowany na zawsze za {lightred}{0}{default} przez {lightred}{1}{default}!",
|
||||
"sa_player_mute_message_time": "Zostałeś uciszony za {lightred}{0}{default} na {lightred}{1}{default} minute przez {lightred}{2}{default}!",
|
||||
"sa_player_mute_message_perm": "Zostałeś uciszony na zawsze za {lightred}{0}{default} przez {lightred}{1}{default}!",
|
||||
"sa_player_silence_message_time": "Zostałeś wyciszony za {lightred}{0}{default} na {lightred}{1}{default} minut przez {lightred}{2}{default}!",
|
||||
"sa_player_silence_message_perm": "Zostałeś wyciszony na zawsze za {lightred}{0}{default} przez {lightred}{1}{default}!",
|
||||
"sa_player_warn_message_time": "Otrzymałeś ostrzeżenie za {lightred}{0}{default} na {lightred}{1}{default} minut od {lightred}{2}{default}!",
|
||||
"sa_player_warn_message_perm": "Otrzymałeś ostrzeżenie za {lightred}{0}{default} od {lightred}{1}{default}!",
|
||||
"sa_admin_ban_message_time": "{lightred}{0}{default} zbanował {lightred}{1}{default} za {lightred}{2}{default} na {lightred}{3}{default} minut!",
|
||||
"sa_admin_ban_message_perm": "{lightred}{0}{default} zbanował {lightred}{1}{default} na zawsze za {lightred}{2}{default}!",
|
||||
"sa_admin_kick_message": "{lightred}{0}{default} wyrzucił {lightred}{1}{default} za {lightred}{2}{default}!",
|
||||
"sa_admin_gag_message_time": "{lightred}{0}{default} zakneblował {lightred}{1}{default} za {lightred}{2}{default} na {lightred}{3}{default} minut!",
|
||||
"sa_admin_gag_message_perm": "{lightred}{0}{default} zakneblował {lightred}{1}{default} na zawsze za {lightred}{2}{default}!",
|
||||
"sa_admin_mute_message_time": "{lightred}{0}{default} uciszył {lightred}{1}{default} za {lightred}{2}{default} na {lightred}{3}{default} minut!",
|
||||
"sa_admin_mute_message_perm": "{lightred}{0}{default} uciszył {lightred}{1}{default} na zawsze za {lightred}{2}{default}!",
|
||||
"sa_admin_silence_message_time": "{lightred}{0}{default} wyciszył {lightred}{1}{default} za {lightred}{2}{default} na {lightred}{3}{default} minut!",
|
||||
"sa_admin_silence_message_perm": "{lightred}{0}{default} wyciszył {lightred}{1}{default} na zawsze za {lightred}{2}{default}!",
|
||||
"sa_admin_warn_message_time": "{lightred}{0}{default} ostrzegł {lightred}{1}{default} za {lightred}{2}{default} na {lightred}{3}{default} minut!",
|
||||
"sa_admin_warn_message_perm": "{lightred}{0}{default} ostrzegł {lightred}{1}{default} na zawsze za {lightred}{2}{default}!",
|
||||
"sa_admin_give_message": "{lightred}{0}{default} dał {lightred}{1}{default} przedmiot {lightred}{2}{default}!",
|
||||
"sa_admin_strip_message": "{lightred}{0}{default} zabrał wszystkie bronie {lightred}{1}{default}!",
|
||||
"sa_admin_hp_message": "{lightred}{0}{default} zmienił ilość hp dla {lightred}{1}{default}!",
|
||||
"sa_admin_speed_message": "{lightred}{0}{default} zmienił prędkość dla {lightred}{1}{default}!",
|
||||
"sa_admin_gravity_message": "{lightred}{0}{default} zmienił grawitacje dla {lightred}{1}{default}!",
|
||||
"sa_admin_money_message": "{lightred}{0}{default} zmienił pieniądze dla {lightred}{1}{default}!",
|
||||
"sa_admin_god_message": "{lightred}{0}{default} zmienił tryb Boga dla {lightred}{1}{default}!",
|
||||
"sa_admin_resize_message": "{lightred}{0}{default} zmienił rozmiar dla {lightred}{1}{default}!",
|
||||
"sa_admin_slay_message": "{lightred}{0}{default} zgładził {lightred}{1}{default}!",
|
||||
"sa_admin_slap_message": "{lightred}{0}{default} uderzył {lightred}{1}{default}!",
|
||||
"sa_admin_changemap_message": "{lightred}{0}{default} zmienił mapę na {lightred}{1}{default}!",
|
||||
"sa_admin_noclip_message": "{lightred}{0}{default} ustawił latanie dla {lightred}{1}{default}!",
|
||||
"sa_admin_freeze_message": "{lightred}{0}{default} zamroził {lightred}{1}{default}!",
|
||||
"sa_admin_unfreeze_message": "{lightred}{0}{default} odmroził {lightred}{1}{default}!",
|
||||
"sa_admin_rename_message": "{lightred}{0}{default} zmienił nick gracza {lightred}{1}{default} na {lightred}{2}{default}!",
|
||||
"sa_admin_respawn_message": "{lightred}{0}{default} odrodził {lightred}{1}{default}!",
|
||||
"sa_admin_tp_message": "{lightred}{0}{default} teleportował się do {lightred}{1}{default}!",
|
||||
"sa_admin_bring_message": "{lightred}{0}{default} teleportował do siebie {lightred}{1}{default}!",
|
||||
"sa_admin_team_message": "{lightred}{0}{default} przerzucił {lightred}{1}{default} do {lightred}{2}{default}!",
|
||||
"sa_admin_warns_menu_title": "{lime}OSTRZEŻENIA {gold}{0}",
|
||||
"sa_admin_warns_unwarn": "{lime}Pomyślnie{default} usunięto ostrzeżenie dla {gold}{0} {default}za {gold}{1}{default}!",
|
||||
"sa_admin_vote_menu_title": "{lime}GŁOSOWANIE NA {gold}{0}",
|
||||
"sa_admin_vote_message": "{lightred}{0}{default} rozpoczął głosowanie na {lightred}{1}{default}",
|
||||
"sa_admin_vote_message_results": "{lime}WYNIKI GŁOSOWANIA {gold}{0}",
|
||||
"sa_admin_vote_message_results_answer": "{lime}{0} {default}- {gold}[{1}]",
|
||||
"sa_admin_voice_mute_all": "{Lightred}Wyciszyłeś {default}wszystkich graczy na czas przemówienia",
|
||||
"sa_admin_voice_unmute_all": "{Lime}Odciszyłeś {default}wszystkich graczy",
|
||||
"sa_admin_voice_listen_all": "{Default}Słyszysz teraz {lime}wszystkich {default}graczy",
|
||||
"sa_admin_voice_unlisten_all": "{Default}Nie słyszysz teraz {lime}wszystkich {default}graczy",
|
||||
"sa_adminsay_prefix": "{RED}ADMIN: {lightred}{0}{default}",
|
||||
"sa_adminchat_template_admin": "{LIME}(ADMIN) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_adminchat_template_player": "{SILVER}(GRACZ) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_discord_log_command": "**{0}** użył komendy `{1}` na serwerze `HOSTNAME`",
|
||||
|
||||
"sa_menu_pluginsmanager_loaded": "{lime}Włączono {default}plugin {lime}{0}",
|
||||
"sa_menu_pluginsmanager_unloaded": "{lightred}Wyłączono {default}plugin {lightred}{0}"
|
||||
}
|
||||
139
CS2-SimpleAdmin/lang/pt-BR.json
Normal file
139
CS2-SimpleAdmin/lang/pt-BR.json
Normal file
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"sa_title": "SimpleAdmin",
|
||||
"sa_prefix": "{lightred}[SA] {default}",
|
||||
|
||||
"sa_unknown": "Desconhecido",
|
||||
"sa_no_permission": "Você não tem permissão para usar este comando.",
|
||||
"sa_ban_max_duration_exceeded": "A duração da proibição não pode exceder {lightred}{0}{default} minutos.",
|
||||
"sa_ban_perm_restricted": "Você não tem permissão para banir permanentemente.",
|
||||
|
||||
"sa_admin_add": "Adicionar Admin",
|
||||
"sa_admin_remove": "Remover Admin",
|
||||
"sa_admin_reload": "Recarregar Admins",
|
||||
|
||||
"sa_godmode": "Modo Deus",
|
||||
"sa_noclip": "Modo Espectador",
|
||||
"sa_respawn": "Ressurgir",
|
||||
"sa_give_weapon": "Dar Arma",
|
||||
"sa_strip_weapons": "Remover Armas",
|
||||
"sa_freeze": "Congelar",
|
||||
"sa_set_hp": "Definir HP",
|
||||
"sa_set_speed": "Definir Velocidade",
|
||||
"sa_set_gravity": "Definir Gravidade",
|
||||
"sa_set_money": "Definir Dinheiro",
|
||||
|
||||
"sa_changemap": "Mudar Mapa",
|
||||
"sa_restart_game": "Reiniciar Jogo",
|
||||
|
||||
"sa_team_ct": "CT",
|
||||
"sa_team_t": "T",
|
||||
"sa_team_swap": "Trocar",
|
||||
"sa_team_spec": "Espec",
|
||||
|
||||
"sa_slap": "Tapa",
|
||||
"sa_slay": "Matar",
|
||||
"sa_kick": "Expulsar",
|
||||
"sa_ban": "Banir",
|
||||
"sa_gag": "Silenciar",
|
||||
"sa_mute": "Mutar",
|
||||
"sa_silence": "Silêncio",
|
||||
"sa_warn": "Aviso",
|
||||
"sa_team_force": "Forçar Time",
|
||||
|
||||
"sa_menu_custom_commands": "Comandos Personalizados",
|
||||
"sa_menu_server_manage": "Gerenciar Servidor",
|
||||
"sa_menu_fun_commands": "Comandos Divertidos",
|
||||
"sa_menu_admins_manage": "Gerenciar Admins",
|
||||
"sa_menu_players_manage": "Gerenciar Jogadores",
|
||||
"sa_menu_disconnected_title": "Jogadores recentes",
|
||||
"sa_menu_disconnected_action_title": "Selecionar ação",
|
||||
"sa_menu_pluginsmanager_title": "Gerenciar Plugins",
|
||||
|
||||
"sa_player": "Jogador",
|
||||
"sa_console": "Console",
|
||||
"sa_steamid": "SteamID",
|
||||
"sa_duration": "Duração",
|
||||
"sa_reason": "Motivo",
|
||||
"sa_admin": "Admin",
|
||||
"sa_permanent": "Permanente",
|
||||
|
||||
"sa_discord_penalty_ban": "Banimento registrado",
|
||||
"sa_discord_penalty_mute": "Mute registrado",
|
||||
"sa_discord_penalty_gag": "Gag registrado",
|
||||
"sa_discord_penalty_silence": "Silêncio registrado",
|
||||
"sa_discord_penalty_warn": "Aviso registrado",
|
||||
"sa_discord_penalty_unknown": "Desconhecido registrado",
|
||||
|
||||
"sa_player_penalty_chat_active": "{lightred}Seu chat está bloqueado para: {grey}{0}",
|
||||
|
||||
"sa_player_penalty_info_active_mute": "➔ Mudo [{lightred}❌{default}] - Expira [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_gag": "➔ Gag [{lightred}❌{default}] - Expira [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_silence": "➔ Silêncio [{lightred}❌{default}] - Expira [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_warn": "➔ Aviso [{lightred}❌{default}] - Expira [{lightred}{0}{default}] - Razão [{lightred}{1}{default}]",
|
||||
|
||||
"sa_player_penalty_info_no_active_mute": "➔ Mudo [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_gag": "➔ Gag [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_silence": "➔ Silêncio [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_warn": "➔ Aviso [{lime}✔{default}]",
|
||||
|
||||
"sa_player_penalty_info": "===========================\nPenalidades do jogador para {lightred}{0}{default},\nNúmero de banimentos: {lightred}{1}{default}, Número de gags: {lightred}{2}{default}, Número de mutes: {lightred}{3}{default}, Número de silêncios: {lightred}{4}{default}, Número de avisos: {lightred}{5}{default}\nPenalidades ativas:\n{6}\nAvisos ativos:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Penalidades do jogador para {lightred}{0}{grey}, Banimentos: {lightred}{1}{grey}, Gags: {lightred}{2}{grey}, Mutes: {lightred}{3}{grey}, Silêncios: {lightred}{4}{grey}, Avisos: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}Contas associadas do jogador {lightred}{0}{grey}: {1}",
|
||||
"sa_player_ban_message_time": "Você foi banido por {lightred}{0}{default} por {lightred}{1}{default} minutos por {lightred}{2}{default}!",
|
||||
"sa_player_ban_message_perm": "Você foi banido permanentemente por {lightred}{0}{default} por {lightred}{1}{default}!",
|
||||
"sa_player_kick_message": "Você foi expulso por {lightred}{0}{default} por {lightred}{1}{default}!",
|
||||
"sa_player_gag_message_time": "Você foi silenciado por {lightred}{0}{default} por {lightred}{1}{default} minutos por {lightred}{2}{default}!",
|
||||
"sa_player_gag_message_perm": "Você foi silenciado permanentemente por {lightred}{0}{default} por {lightred}{1}{default}!",
|
||||
"sa_player_mute_message_time": "Você foi mutado por {lightred}{0}{default} por {lightred}{1}{default} minutos por {lightred}{2}{default}!",
|
||||
"sa_player_mute_message_perm": "Você foi mutado permanentemente por {lightred}{0}{default} por {lightred}{1}{default}!",
|
||||
"sa_player_silence_message_time": "Você foi silenciado por {lightred}{0}{default} por {lightred}{1}{default} minutos por {lightred}{2}{default}!",
|
||||
"sa_player_silence_message_perm": "Você foi silenciado permanentemente por {lightred}{0}{default} por {lightred}{1}{default}!",
|
||||
"sa_player_warn_message_time": "Você foi advertido por {lightred}{0}{default} por {lightred}{1}{default} minutos por {lightred}{2}{default}!",
|
||||
"sa_player_warn_message_perm": "Você foi advertido permanentemente por {lightred}{0}{default} por {lightred}{1}{default}!",
|
||||
"sa_admin_ban_message_time": "{lightred}{0}{default} baniu {lightred}{1}{default} por {lightred}{3}{default} minutos por {lightred}{2}{default}!",
|
||||
"sa_admin_ban_message_perm": "{lightred}{0}{default} baniu {lightred}{1}{default} permanentemente por {lightred}{2}{default}!",
|
||||
"sa_admin_kick_message": "{lightred}{0}{default} expulsou {lightred}{1}{default} por {lightred}{2}{default}!",
|
||||
"sa_admin_gag_message_time": "{lightred}{0}{default} silenciou {lightred}{1}{default} por {lightred}{3}{default} minutos por {lightred}{2}{default}!",
|
||||
"sa_admin_gag_message_perm": "{lightred}{0}{default} silenciou {lightred}{1}{default} permanentemente por {lightred}{2}{default}!",
|
||||
"sa_admin_mute_message_time": "{lightred}{0}{default} mutou {lightred}{1}{default} por {lightred}{3}{default} minutos por {lightred}{2}{default}!",
|
||||
"sa_admin_mute_message_perm": "{lightred}{0}{default} mutou {lightred}{1}{default} permanentemente por {lightred}{2}{default}!",
|
||||
"sa_admin_silence_message_time": "{lightred}{0}{default} silenciou {lightred}{1}{default} por {lightred}{3}{default} minutos por {lightred}{2}{default}!",
|
||||
"sa_admin_silence_message_perm": "{lightred}{0}{default} silenciou {lightred}{1}{default} permanentemente por {lightred}{2}{default}!",
|
||||
"sa_admin_warn_message_time": "{lightred}{0}{default} advertiu {lightred}{1}{default} por {lightred}{3}{default} minutos por {lightred}{2}{default}!",
|
||||
"sa_admin_warn_message_perm": "{lightred}{0}{default} advertiu {lightred}{1}{default} permanentemente por {lightred}{2}{default}!",
|
||||
"sa_admin_give_message": "{lightred}{0}{default} deu {lightred}{1}{default} um(a) {lightred}{2}{default}!",
|
||||
"sa_admin_strip_message": "{lightred}{0}{default} tirou todas as armas de {lightred}{1}{default}!",
|
||||
"sa_admin_hp_message": "{lightred}{0}{default} mudou a quantidade de HP de {lightred}{1}{default}!",
|
||||
"sa_admin_speed_message": "{lightred}{0}{default} mudou a velocidade de {lightred}{1}{default}!",
|
||||
"sa_admin_gravity_message": "{lightred}{0}{default} mudou a gravidade de {lightred}{1}{default}!",
|
||||
"sa_admin_money_message": "{lightred}{0}{default} mudou a quantidade de dinheiro de {lightred}{1}{default}!",
|
||||
"sa_admin_god_message": "{lightred}{0}{default} mudou o modo Deus de {lightred}{1}{default}!",
|
||||
"sa_admin_resize_message": "{lightred}{0}{default} alterou o tamanho de {lightred}{1}{default}!",
|
||||
"sa_admin_slay_message": "{lightred}{0}{default} matou {lightred}{1}{default}!",
|
||||
"sa_admin_slap_message": "{lightred}{0}{default} deu um tapa em {lightred}{1}{default}!",
|
||||
"sa_admin_changemap_message": "{lightred}{0}{default} mudou o mapa para {lightred}{1}{default}!",
|
||||
"sa_admin_noclip_message": "{lightred}{0}{default} ativou/desativou o noclip para {lightred}{1}{default}!",
|
||||
"sa_admin_freeze_message": "{lightred}{0}{default} congelou {lightred}{1}{default}!",
|
||||
"sa_admin_unfreeze_message": "{lightred}{0}{default} descongelou {lightred}{1}{default}!",
|
||||
"sa_admin_rename_message": "{lightred}{0}{default} renomeou {lightred}{1}{default} para {lightred}{2}{default}!",
|
||||
"sa_admin_respawn_message": "{lightred}{0}{default} reanimou {lightred}{1}{default}!",
|
||||
"sa_admin_tp_message": "{lightred}{0}{default} teleportou para {lightred}{1}{default}!",
|
||||
"sa_admin_bring_message": "{lightred}{0}{default} teleportou {lightred}{1}{default} para si mesmo!",
|
||||
"sa_admin_team_message": "{lightred}{0}{default} transferiu {lightred}{1}{default} para a equipe {lightred}{2}{default}!",
|
||||
"sa_admin_warns_menu_title": "{gold}{0} {lime}ADVERTÊNCIAS",
|
||||
"sa_admin_warns_unwarn": "{lime}Advertência removida com sucesso{default} para {gold}{0} {default}por {gold}{1}{default}!",
|
||||
"sa_admin_vote_menu_title": "{lime}VOTAÇÃO PARA {gold}{0}",
|
||||
"sa_admin_vote_message": "{lightred}{0}{default} iniciou uma votação para {lightred}{1}{default}",
|
||||
"sa_admin_vote_message_results": "{lime}RESULTADOS DA VOTAÇÃO PARA {gold}{0}",
|
||||
"sa_admin_vote_message_results_answer": "{lime}{0} {default}- {gold}{1}",
|
||||
"sa_admin_voice_mute_all": "{Lightred}Você silenciou {default}todos os jogadores durante o seu discurso",
|
||||
"sa_admin_voice_unmute_all": "{Lime}Você reativou o som de {default}todos os jogadores",
|
||||
"sa_admin_voice_listen_all": "{Default}Agora você pode ouvir {lime}todos {default}os jogadores",
|
||||
"sa_admin_voice_unlisten_all": "{Default}Você não ouve mais {lime}todos {default}os jogadores",
|
||||
"sa_adminsay_prefix": "{RED}ADMIN: {lightred}{0}{default}",
|
||||
"sa_adminchat_template_admin": "{LIME}(ADMIN) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_adminchat_template_player": "{SILVER}(JOGADOR) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_discord_log_command": "**{0}** executou o comando `{1}` no servidor `HOSTNAME`",
|
||||
"sa_menu_pluginsmanager_loaded": "{lime}Ativado {default}plugin {lime}{0}",
|
||||
"sa_menu_pluginsmanager_unloaded": "{lightred}Desativado {default}plugin {lightred}{0}"
|
||||
}
|
||||
139
CS2-SimpleAdmin/lang/pt-PT.json
Normal file
139
CS2-SimpleAdmin/lang/pt-PT.json
Normal file
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"sa_title": "SimpleAdmin",
|
||||
"sa_prefix": "{lightred}[SA] {default}",
|
||||
|
||||
"sa_unknown": "Desconhecido",
|
||||
"sa_no_permission": "Não tens permissão para usar este comando.",
|
||||
"sa_ban_max_duration_exceeded": "A duração da proibição não pode exceder {lightred}{0}{default} minutos.",
|
||||
"sa_ban_perm_restricted": "Não tens permissão para banir permanentemente.",
|
||||
|
||||
"sa_admin_add": "Adicionar Admin",
|
||||
"sa_admin_remove": "Remover Admin",
|
||||
"sa_admin_reload": "Recarregar Admins",
|
||||
|
||||
"sa_godmode": "Modo Deus",
|
||||
"sa_noclip": "Modo Espectador",
|
||||
"sa_respawn": "Ressurgir",
|
||||
"sa_give_weapon": "Dar Arma",
|
||||
"sa_strip_weapons": "Remover Armas",
|
||||
"sa_freeze": "Congelar",
|
||||
"sa_set_hp": "Definir HP",
|
||||
"sa_set_speed": "Definir Velocidade",
|
||||
"sa_set_gravity": "Definir Gravidade",
|
||||
"sa_set_money": "Definir Dinheiro",
|
||||
|
||||
"sa_changemap": "Mudar Mapa",
|
||||
"sa_restart_game": "Reiniciar Jogo",
|
||||
|
||||
"sa_team_ct": "CT",
|
||||
"sa_team_t": "T",
|
||||
"sa_team_swap": "Trocar",
|
||||
"sa_team_spec": "Espec",
|
||||
|
||||
"sa_slap": "Tapa",
|
||||
"sa_slay": "Matar",
|
||||
"sa_kick": "Expulsar",
|
||||
"sa_ban": "Banir",
|
||||
"sa_gag": "Silenciar",
|
||||
"sa_mute": "Mutar",
|
||||
"sa_silence": "Silêncio",
|
||||
"sa_warn": "Aviso",
|
||||
"sa_team_force": "Forçar Time",
|
||||
|
||||
"sa_menu_custom_commands": "Comandos Personalizados",
|
||||
"sa_menu_server_manage": "Gerenciar Servidor",
|
||||
"sa_menu_fun_commands": "Comandos Divertidos",
|
||||
"sa_menu_admins_manage": "Gerenciar Admins",
|
||||
"sa_menu_players_manage": "Gerenciar Jogadores",
|
||||
"sa_menu_disconnected_title": "Jogadores recentes",
|
||||
"sa_menu_disconnected_action_title": "Selecionar ação",
|
||||
"sa_menu_pluginsmanager_title": "Gerir Plugins",
|
||||
|
||||
"sa_player": "Jogador",
|
||||
"sa_console": "Console",
|
||||
"sa_steamid": "SteamID",
|
||||
"sa_duration": "Duração",
|
||||
"sa_reason": "Motivo",
|
||||
"sa_admin": "Admin",
|
||||
"sa_permanent": "Permanente",
|
||||
|
||||
"sa_discord_penalty_ban": "Banimento registrado",
|
||||
"sa_discord_penalty_mute": "Mute registrado",
|
||||
"sa_discord_penalty_gag": "Gag registrado",
|
||||
"sa_discord_penalty_silence": "Silêncio registrado",
|
||||
"sa_discord_penalty_warn": "Aviso registrado",
|
||||
"sa_discord_penalty_unknown": "Desconhecido registrado",
|
||||
|
||||
"sa_player_penalty_chat_active": "{lightred}O seu chat está bloqueado para: {grey}{0}",
|
||||
|
||||
"sa_player_penalty_info_active_mute": "➔ Mudo [{lightred}❌{default}] - Expira [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_gag": "➔ Gag [{lightred}❌{default}] - Expira [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_silence": "➔ Silêncio [{lightred}❌{default}] - Expira [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_warn": "➔ Aviso [{lightred}❌{default}] - Expira [{lightred}{0}{default}] - Razão [{lightred}{1}{default}]",
|
||||
|
||||
"sa_player_penalty_info_no_active_mute": "➔ Mudo [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_gag": "➔ Gag [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_silence": "➔ Silêncio [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_warn": "➔ Aviso [{lime}✔{default}]",
|
||||
|
||||
"sa_player_penalty_info": "===========================\nPenalidades do jogador para {lightred}{0}{default},\nNúmero de banimentos: {lightred}{1}{default}, Número de gags: {lightred}{2}{default}, Número de mutes: {lightred}{3}{default}, Número de silêncios: {lightred}{4}{default}, Número de avisos: {lightred}{5}{default}\nPenalidades ativas:\n{6}\nAvisos ativos:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Penalidades do jogador para {lightred}{0}{grey}, Banimentos: {lightred}{1}{grey}, Gags: {lightred}{2}{grey}, Mutes: {lightred}{3}{grey}, Silêncios: {lightred}{4}{grey}, Avisos: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}Contas associadas do jogador {lightred}{0}{grey}: {1}",
|
||||
"sa_player_ban_message_time": "Foste banido pelo administrador {lightred}{0}{default} durante {lightred}{1}{default} minutos. Motivo: {lightred}{2}{default}!",
|
||||
"sa_player_ban_message_perm": "Foste banido permanentemente pelo administrador {lightred}{0}{default}. Motivo: {lightred}{1}{default}!",
|
||||
"sa_player_kick_message": "Foste expulso pelo administrador {lightred}{0}{default}. Motivo: {lightred}{1}{default}!",
|
||||
"sa_player_gag_message_time": "Foste gagged pelo administrador {lightred}{0}{default} durante {lightred}{1}{default} minutos. Motivo: {lightred}{2}{default}!",
|
||||
"sa_player_gag_message_perm": "Foste permanentemente gagged pelo administrador {lightred}{0}{default}. Motivo: {lightred}{1}{default}!",
|
||||
"sa_player_mute_message_time": "Foste mutado pelo administrador {lightred}{0}{default} durante {lightred}{1}{default} minutos. Motivo: {lightred}{2}{default}!",
|
||||
"sa_player_mute_message_perm": "Foste permanentemente mutado pelo administrador {lightred}{0}{default}. Motivo: {lightred}{1}{default}!",
|
||||
"sa_player_silence_message_time": "Foste silenciado (chat de voz e texto) pelo administrador {lightred}{0}{default} durante {lightred}{1}{default} minutos. Motivo: {lightred}{2}{default}!",
|
||||
"sa_player_silence_message_perm": "Foste permanentemente silenciado (chat de voz e texto) pelo administrador {lightred}{0}{default}. Motivo: {lightred}{1}{default}!",
|
||||
"sa_player_warn_message_time": "Recebeste um aviso do administrador {lightred}{0}{default} válido por {lightred}{1}{default} minutos. Motivo: {lightred}{2}{default}!",
|
||||
"sa_player_warn_message_perm": "Recebeste um aviso permanente do administrador {lightred}{0}{default}. Motivo: {lightred}{1}{default}!",
|
||||
"sa_admin_ban_message_time": "{lightred}{0}{default} baniu {lightred}{1}{default} durante {lightred}{3}{default} minutos. Motivo: {lightred}{2}{default}!",
|
||||
"sa_admin_ban_message_perm": "{lightred}{0}{default} baniu {lightred}{1}{default} permanentemente. Motivo: {lightred}{2}{default}!",
|
||||
"sa_admin_kick_message": "{lightred}{0}{default} expulsou {lightred}{1}{default}. Motivo: {lightred}{2}{default}!",
|
||||
"sa_admin_gag_message_time": "{lightred}{0}{default} silenciou {lightred}{1}{default} durante {lightred}{3}{default} minutos. Motivo: {lightred}{2}{default}!",
|
||||
"sa_admin_gag_message_perm": "{lightred}{0}{default} silenciou {lightred}{1}{default} permanentemente. Motivo: {lightred}{2}{default}!",
|
||||
"sa_admin_mute_message_time": "{lightred}{0}{default} mutou {lightred}{1}{default} durante {lightred}{3}{default} minutos. Motivo: {lightred}{2}{default}!",
|
||||
"sa_admin_mute_message_perm": "{lightred}{0}{default} mutou {lightred}{1}{default} permanentemente. Motivo: {lightred}{2}{default}!",
|
||||
"sa_admin_silence_message_time": "{lightred}{0}{default} silenciou {lightred}{1}{default} durante {lightred}{3}{default} minutos. Motivo: {lightred}{2}{default}!",
|
||||
"sa_admin_silence_message_perm": "{lightred}{0}{default} silenciou {lightred}{1}{default} permanentemente. Motivo: {lightred}{2}{default}!",
|
||||
"sa_admin_warn_message_time": "{lightred}{0}{default} avisou {lightred}{1}{default} durante {lightred}{3}{default} minutos. Motivo: {lightred}{2}{default}!",
|
||||
"sa_admin_warn_message_perm": "{lightred}{0}{default} avisou {lightred}{1}{default} permanentemente. Motivo: {lightred}{2}{default}!",
|
||||
"sa_admin_give_message": "{lightred}{0}{default} deu {lightred}{1}{default} um(a) {lightred}{2}{default}!",
|
||||
"sa_admin_strip_message": "{lightred}{0}{default} tirou todas as armas de {lightred}{1}{default}!",
|
||||
"sa_admin_hp_message": "{lightred}{0}{default} mudou a quantidade de HP de {lightred}{1}{default}!",
|
||||
"sa_admin_speed_message": "{lightred}{0}{default} mudou a velocidade de {lightred}{1}{default}!",
|
||||
"sa_admin_gravity_message": "{lightred}{0}{default} mudou a gravidade de {lightred}{1}{default}!",
|
||||
"sa_admin_money_message": "{lightred}{0}{default} mudou a quantidade de dinheiro de {lightred}{1}{default}!",
|
||||
"sa_admin_god_message": "{lightred}{0}{default} mudou o modo Deus de {lightred}{1}{default}!",
|
||||
"sa_admin_resize_message": "{lightred}{0}{default} alterou o tamanho de {lightred}{1}{default}!",
|
||||
"sa_admin_slay_message": "{lightred}{0}{default} matou {lightred}{1}{default}!",
|
||||
"sa_admin_slap_message": "{lightred}{0}{default} deu um tapa em {lightred}{1}{default}!",
|
||||
"sa_admin_changemap_message": "{lightred}{0}{default} mudou o mapa para {lightred}{1}{default}!",
|
||||
"sa_admin_noclip_message": "{lightred}{0}{default} ativou/desativou o noclip para {lightred}{1}{default}!",
|
||||
"sa_admin_freeze_message": "{lightred}{0}{default} congelou {lightred}{1}{default}!",
|
||||
"sa_admin_unfreeze_message": "{lightred}{0}{default} descongelou {lightred}{1}{default}!",
|
||||
"sa_admin_rename_message": "{lightred}{0}{default} renomeou {lightred}{1}{default} para {lightred}{2}{default}!",
|
||||
"sa_admin_respawn_message": "{lightred}{0}{default} reanimou {lightred}{1}{default}!",
|
||||
"sa_admin_tp_message": "{lightred}{0}{default} teleportou para {lightred}{1}{default}!",
|
||||
"sa_admin_bring_message": "{lightred}{0}{default} teleportou {lightred}{1}{default} para si próprio!",
|
||||
"sa_admin_team_message": "{lightred}{0}{default} transferiu {lightred}{1}{default} para a equipe {lightred}{2}{default}!",
|
||||
"sa_admin_warns_menu_title": "{gold}{0} {lime}ADVERTÊNCIAS",
|
||||
"sa_admin_warns_unwarn": "{lime}Advertência removida com sucesso{default} para {gold}{0} {default}por {gold}{1}{default}!",
|
||||
"sa_admin_vote_menu_title": "{lime}VOTAÇÃO PARA {gold}{0}",
|
||||
"sa_admin_vote_message": "{lightred}{0}{default} iniciou uma votação para {lightred}{1}{default}",
|
||||
"sa_admin_vote_message_results": "{lime}RESULTADOS DA VOTAÇÃO PARA {gold}{0}",
|
||||
"sa_admin_vote_message_results_answer": "{lime}{0} {default}- {gold}{1}",
|
||||
"sa_admin_voice_mute_all": "{Lightred}Silenciaste {default}todos os jogadores durante o teu discurso",
|
||||
"sa_admin_voice_unmute_all": "{Lime}Ativaste novamente o som de {default}todos os jogadores",
|
||||
"sa_admin_voice_listen_all": "{Default}Agora consegues ouvir {lime}todos {default}os jogadores",
|
||||
"sa_admin_voice_unlisten_all": "{Default}Já não ouves {lime}todos {default}os jogadores",
|
||||
"sa_adminsay_prefix": "{RED}ADMIN: {lightred}{0}{default}",
|
||||
"sa_adminchat_template_admin": "{LIME}(ADMIN) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_adminchat_template_player": "{SILVER}(JOGADOR) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_discord_log_command": "**{0}** executou o comando `{1}` no servidor `HOSTNAME`",
|
||||
"sa_menu_pluginsmanager_loaded": "{lime}Ativado {default}plugin {lime}{0}",
|
||||
"sa_menu_pluginsmanager_unloaded": "{lightred}Desativado {default}plugin {lightred}{0}"
|
||||
}
|
||||
139
CS2-SimpleAdmin/lang/ru.json
Normal file
139
CS2-SimpleAdmin/lang/ru.json
Normal file
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"sa_title": "SimpleAdmin",
|
||||
"sa_prefix": "{lightred}[SA] {default}",
|
||||
|
||||
"sa_unknown": "Неизвестный",
|
||||
"sa_no_permission": "У вас нет прав для использования этой команды.",
|
||||
"sa_ban_max_duration_exceeded": "Продолжительность бана не может превышать {lightred}{0}{default} минут.",
|
||||
"sa_ban_perm_restricted": "У вас нет прав на постоянный бан.",
|
||||
|
||||
"sa_admin_add": "Добавить администратора",
|
||||
"sa_admin_remove": "Удалить администратора",
|
||||
"sa_admin_reload": "Перезагрузить администраторов",
|
||||
|
||||
"sa_godmode": "Режим бога",
|
||||
"sa_noclip": "Режим бесконечного прохождения",
|
||||
"sa_respawn": "Возрождение",
|
||||
"sa_give_weapon": "Выдать оружие",
|
||||
"sa_strip_weapons": "Удалить оружие",
|
||||
"sa_freeze": "Заморозить",
|
||||
"sa_set_hp": "Установить здоровье",
|
||||
"sa_set_speed": "Установить скорость",
|
||||
"sa_set_gravity": "Установить гравитацию",
|
||||
"sa_set_money": "Установить деньги",
|
||||
|
||||
"sa_changemap": "Сменить карту",
|
||||
"sa_restart_game": "Перезапустить игру",
|
||||
|
||||
"sa_team_ct": "CT",
|
||||
"sa_team_t": "T",
|
||||
"sa_team_swap": "Поменять",
|
||||
"sa_team_spec": "Спец",
|
||||
|
||||
"sa_slap": "Шлепнуть",
|
||||
"sa_slay": "Убить",
|
||||
"sa_kick": "Выгнать",
|
||||
"sa_ban": "Забанить",
|
||||
"sa_gag": "Заглушить",
|
||||
"sa_mute": "Отключить звук",
|
||||
"sa_silence": "Тишина",
|
||||
"sa_warn": "Предупреждение",
|
||||
"sa_team_force": "Принудить к команде",
|
||||
|
||||
"sa_menu_custom_commands": "Пользовательские команды",
|
||||
"sa_menu_server_manage": "Управление сервером",
|
||||
"sa_menu_fun_commands": "Развлекательные команды",
|
||||
"sa_menu_admins_manage": "Управление администраторами",
|
||||
"sa_menu_players_manage": "Управление игроками",
|
||||
"sa_menu_disconnected_title": "Последние игроки",
|
||||
"sa_menu_disconnected_action_title": "Выберите действие",
|
||||
"sa_menu_pluginsmanager_title": "Управление плагинами",
|
||||
|
||||
"sa_player": "Игрок",
|
||||
"sa_console": "Консоль",
|
||||
"sa_steamid": "SteamID",
|
||||
"sa_duration": "Продолжительность",
|
||||
"sa_reason": "Причина",
|
||||
"sa_admin": "Администратор",
|
||||
"sa_permanent": "Постоянный",
|
||||
|
||||
"sa_discord_penalty_ban": "Бан зарегистрирован",
|
||||
"sa_discord_penalty_mute": "Мут зарегистрирован",
|
||||
"sa_discord_penalty_gag": "Запрет зарегистрирован",
|
||||
"sa_discord_penalty_silence": "Молчание зарегистрировано",
|
||||
"sa_discord_penalty_warn": "Предупреждение зарегистрировано",
|
||||
"sa_discord_penalty_unknown": "Неизвестно зарегистрировано",
|
||||
|
||||
"sa_player_penalty_chat_active": "{lightred}Ваш чат заблокирован для: {grey}{0}",
|
||||
|
||||
"sa_player_penalty_info_active_mute": "➔ Мут [{lightred}❌{default}] - Истекает [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_gag": "➔ Гэг [{lightred}❌{default}] - Истекает [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_silence": "➔ Тишина [{lightred}❌{default}] - Истекает [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_warn": "➔ Предупреждение [{lightred}❌{default}] - Истекает [{lightred}{0}{default}] - Причина [{lightred}{1}{default}]",
|
||||
|
||||
"sa_player_penalty_info_no_active_mute": "➔ Мут [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_gag": "➔ Гэг [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_silence": "➔ Тишина [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_warn": "➔ Предупреждение [{lime}✔{default}]",
|
||||
|
||||
"sa_player_penalty_info": "===========================\nШтрафы игрока для {lightred}{0}{default},\nКоличество банов: {lightred}{1}{default}, Количество гэгов: {lightred}{2}{default}, Количество мутов: {lightred}{3}{default}, Количество тишин: {lightred}{4}{default}, Количество предупреждений: {lightred}{5}{default}\nАктивные штрафы:\n{6}\nАктивные предупреждения:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Штрафы игрока для {lightred}{0}{grey}, Баны: {lightred}{1}{grey}, Гэги: {lightred}{2}{grey}, Муты: {lightred}{3}{grey}, Тишины: {lightred}{4}{grey}, Предупреждения: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}Связанные аккаунты игрока {lightred}{0}{grey}: {1}",
|
||||
"sa_player_ban_message_time": "Вы были забанены по причине {lightred}{0}{default} на {lightred}{1}{default} минут(ы) администратором {lightred}{2}{default}!",
|
||||
"sa_player_ban_message_perm": "Вас забанили навсегда по причине {lightred}{0}{default} администратором {lightred}{1}{default}!",
|
||||
"sa_player_kick_message": "Вы были выгнаны {lightred}{0}{default} администратором {lightred}{1}{default}!",
|
||||
"sa_player_gag_message_time": "Вам запрещено общаться в чате по причине {lightred}{0}{default} на {lightred}{1}{default} минут(ы) администратором {lightred}{2}{default}!",
|
||||
"sa_player_gag_message_perm": "Вам навсегда запрещено общаться в чате по причине {lightred}{0}{default} администратором {lightred}{1}{default}!",
|
||||
"sa_player_mute_message_time": "Вам запрещено использовать голосовой чат по причине {lightred}{0}{default} на {lightred}{1}{default} минут(ы) администратором {lightred}{2}{default}!",
|
||||
"sa_player_mute_message_perm": "Вам навсегда запрещено использовать голосовой чат по причине {lightred}{0}{default} администратором {lightred}{1}{default}!",
|
||||
"sa_player_silence_message_time": "Вам запрещено общаться по причине {lightred}{0}{default} на {lightred}{1}{default} минут(ы) администратором {lightred}{2}{default}!",
|
||||
"sa_player_silence_message_perm": "Вам навсегда запрещено общаться по причине {lightred}{0}{default} администратором {lightred}{1}{default}!",
|
||||
"sa_player_warn_message_time": "Вы получили предупреждение за {lightred}{0}{default} на {lightred}{1}{default} минут от {lightred}{2}{default}!",
|
||||
"sa_player_warn_message_perm": "Вы получили постоянное предупреждение за {lightred}{0}{default} от {lightred}{1}{default}!",
|
||||
"sa_admin_ban_message_time": "{lightred}{0}{default} забанил {lightred}{1}{default} на {lightred}{3}{default} минут за {lightred}{2}{default}!",
|
||||
"sa_admin_ban_message_perm": "{lightred}{0}{default} забанил {lightred}{1}{default} навсегда за {lightred}{2}{default}!",
|
||||
"sa_admin_kick_message": "{lightred}{0}{default} выгнал {lightred}{1}{default} за {lightred}{2}{default}!",
|
||||
"sa_admin_gag_message_time": "{lightred}{0}{default} замолчал {lightred}{1}{default} на {lightred}{3}{default} минут за {lightred}{2}{default}!",
|
||||
"sa_admin_gag_message_perm": "{lightred}{0}{default} замолчал {lightred}{1}{default} навсегда за {lightred}{2}{default}!",
|
||||
"sa_admin_mute_message_time": "{lightred}{0}{default} отключил звук {lightred}{1}{default} на {lightred}{3}{default} минут за {lightred}{2}{default}!",
|
||||
"sa_admin_mute_message_perm": "{lightred}{0}{default} отключил звук {lightred}{1}{default} навсегда за {lightred}{2}{default}!",
|
||||
"sa_admin_silence_message_time": "{lightred}{0}{default} замолчал {lightred}{1}{default} на {lightred}{3}{default} минут за {lightred}{2}{default}!",
|
||||
"sa_admin_silence_message_perm": "{lightred}{0}{default} замолчал {lightred}{1}{default} навсегда за {lightred}{2}{default}!",
|
||||
"sa_admin_warn_message_time": "{lightred}{0}{default} предупредил {lightred}{1}{default} на {lightred}{3}{default} минут за {lightred}{2}{default}!",
|
||||
"sa_admin_warn_message_perm": "{lightred}{0}{default} предупредил {lightred}{1}{default} навсегда за {lightred}{2}{default}!",
|
||||
"sa_admin_give_message": "{lightred}{0}{default} дал {lightred}{1}{default} {lightred}{2}{default}!",
|
||||
"sa_admin_strip_message": "{lightred}{0}{default} забрал все оружие у {lightred}{1}{default}!",
|
||||
"sa_admin_hp_message": "{lightred}{0}{default} изменил количество HP у {lightred}{1}{default}!",
|
||||
"sa_admin_speed_message": "{lightred}{0}{default} изменил скорость {lightred}{1}{default}!",
|
||||
"sa_admin_gravity_message": "{lightred}{0}{default} изменил гравитацию для {lightred}{1}{default}!",
|
||||
"sa_admin_money_message": "{lightred}{0}{default} изменил количество денег у {lightred}{1}{default}!",
|
||||
"sa_admin_god_message": "{lightred}{0}{default} изменил режим бога для {lightred}{1}{default}!",
|
||||
"sa_admin_resize_message": "{lightred}{0}{default} изменил размер {lightred}{1}{default}!",
|
||||
"sa_admin_slay_message": "{lightred}{0}{default} убил {lightred}{1}{default}!",
|
||||
"sa_admin_slap_message": "{lightred}{0}{default} дал пощечину {lightred}{1}{default}!",
|
||||
"sa_admin_changemap_message": "{lightred}{0}{default} изменил карту на {lightred}{1}{default}!",
|
||||
"sa_admin_noclip_message": "{lightred}{0}{default} включил/выключил noclip для {lightred}{1}{default}!",
|
||||
"sa_admin_freeze_message": "{lightred}{0}{default} заморозил {lightred}{1}{default}!",
|
||||
"sa_admin_unfreeze_message": "{lightred}{0}{default} разморозил {lightred}{1}{default}!",
|
||||
"sa_admin_rename_message": "{lightred}{0}{default} изменил никнейм {lightred}{1}{default} на {lightred}{2}{default}!",
|
||||
"sa_admin_respawn_message": "{lightred}{0}{default} возродил {lightred}{1}{default}!",
|
||||
"sa_admin_tp_message": "{lightred}{0}{default} телепортировался к {lightred}{1}{default}!",
|
||||
"sa_admin_bring_message": "{lightred}{0}{default} телепортировал {lightred}{1}{default} к себе!",
|
||||
"sa_admin_team_message": "{lightred}{0}{default} перевел {lightred}{1}{default} в команду {lightred}{2}{default}!",
|
||||
"sa_admin_warns_menu_title": "{gold}{0} {lime}ПРЕДУПРЕЖДЕНИЯ",
|
||||
"sa_admin_warns_unwarn": "{lime}Предупреждение успешно удалено{default} для {gold}{0} {default}за {gold}{1}{default}!",
|
||||
"sa_admin_vote_menu_title": "{lime}ГОЛОСОВАНИЕ ЗА {gold}{0}",
|
||||
"sa_admin_vote_message": "{lightred}{0}{default} начал голосование за {lightred}{1}{default}",
|
||||
"sa_admin_vote_message_results": "{lime}РЕЗУЛЬТАТЫ ГОЛОСОВАНИЯ ЗА {gold}{0}",
|
||||
"sa_admin_vote_message_results_answer": "{lime}{0} {default}- {gold}{1}",
|
||||
"sa_admin_voice_mute_all": "{Lightred}Вы заглушили {default}всех игроков во время своей речи",
|
||||
"sa_admin_voice_unmute_all": "{Lime}Вы включили звук {default}всем игрокам",
|
||||
"sa_admin_voice_listen_all": "{Default}Теперь вы слышите {lime}всех {default}игроков",
|
||||
"sa_admin_voice_unlisten_all": "{Default}Вы больше не слышите {lime}всех {default}игроков",
|
||||
"sa_adminsay_prefix": "{RED}АДМИН: {lightred}{0}{default}",
|
||||
"sa_adminchat_template_admin": "{LIME}(АДМИН) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_adminchat_template_player": "{SILVER}(ИГРОК) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_discord_log_command": "**{0}** выполнил команду `{1}` на сервере `HOSTNAME`",
|
||||
"sa_menu_pluginsmanager_loaded": "{lime}Включен {default}плагин {lime}{0}",
|
||||
"sa_menu_pluginsmanager_unloaded": "{lightred}Выключен {default}плагин {lightred}{0}"
|
||||
}
|
||||
139
CS2-SimpleAdmin/lang/tr.json
Normal file
139
CS2-SimpleAdmin/lang/tr.json
Normal file
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"sa_title": "SimpleAdmin",
|
||||
"sa_prefix": "{lightred}[SA] {default}",
|
||||
|
||||
"sa_unknown": "Bilinmeyen",
|
||||
"sa_no_permission": "Bu komutu kullanma izniniz yok.",
|
||||
"sa_ban_max_duration_exceeded": "Yasaklama süresi {lightred}{0}{default} dakikadan fazla olamaz.",
|
||||
"sa_ban_perm_restricted": "Kalıcı yasaklama hakkınız yok.",
|
||||
|
||||
"sa_admin_add": "Yönetici Ekle",
|
||||
"sa_admin_remove": "Yönetici Kaldır",
|
||||
"sa_admin_reload": "Yöneticileri Yeniden Yükle",
|
||||
|
||||
"sa_godmode": "Tanrı Modu",
|
||||
"sa_noclip": "Duvar Arkası Modu",
|
||||
"sa_respawn": "Tekrar Doğma",
|
||||
"sa_give_weapon": "Silah Ver",
|
||||
"sa_strip_weapons": "Silahları Çıkart",
|
||||
"sa_freeze": "Dondur",
|
||||
"sa_set_hp": "HP Ayarla",
|
||||
"sa_set_speed": "Hız Ayarla",
|
||||
"sa_set_gravity": "Yerçekimi Ayarla",
|
||||
"sa_set_money": "Para Ayarla",
|
||||
|
||||
"sa_changemap": "Haritayı Değiştir",
|
||||
"sa_restart_game": "Oyunu Yeniden Başlat",
|
||||
|
||||
"sa_team_ct": "CT",
|
||||
"sa_team_t": "T",
|
||||
"sa_team_swap": "Değiştir",
|
||||
"sa_team_spec": "İzleyici",
|
||||
|
||||
"sa_slap": "Tokatla",
|
||||
"sa_slay": "Öldür",
|
||||
"sa_kick": "At",
|
||||
"sa_ban": "Yasakla",
|
||||
"sa_gag": "Ağzını Kapat",
|
||||
"sa_mute": "Sustur",
|
||||
"sa_silence": "Sessizlik",
|
||||
"sa_warn": "Uyarı",
|
||||
"sa_team_force": "Takımı Zorla",
|
||||
|
||||
"sa_menu_custom_commands": "Özel Komutlar",
|
||||
"sa_menu_server_manage": "Sunucu Yönetimi",
|
||||
"sa_menu_fun_commands": "Eğlenceli Komutlar",
|
||||
"sa_menu_admins_manage": "Yönetici Yönetimi",
|
||||
"sa_menu_players_manage": "Oyuncu Yönetimi",
|
||||
"sa_menu_disconnected_title": "Son oyuncular",
|
||||
"sa_menu_disconnected_action_title": "Eylem seçin",
|
||||
"sa_menu_pluginsmanager_title": "Eklentileri Yönet",
|
||||
|
||||
"sa_player": "Oyuncu",
|
||||
"sa_console": "Konsol",
|
||||
"sa_steamid": "SteamID",
|
||||
"sa_duration": "Süre",
|
||||
"sa_reason": "Neden",
|
||||
"sa_admin": "Yönetici",
|
||||
"sa_permanent": "Kalıcı",
|
||||
|
||||
"sa_discord_penalty_ban": "Yasak kaydedildi",
|
||||
"sa_discord_penalty_mute": "Susturma kaydedildi",
|
||||
"sa_discord_penalty_gag": "Susturma kaydedildi",
|
||||
"sa_discord_penalty_silence": "Sessizlik kaydedildi",
|
||||
"sa_discord_penalty_warn": "Uyarı kaydedildi",
|
||||
"sa_discord_penalty_unknown": "Bilinmeyen kaydedildi",
|
||||
|
||||
"sa_player_penalty_chat_active": "{lightred}Sohbetiniz şu kişiye engellendi: {grey}{0}",
|
||||
|
||||
"sa_player_penalty_info_active_mute": "➔ Mute [{lightred}❌{default}] - Süre Dolacak [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_gag": "➔ Gag [{lightred}❌{default}] - Süre Dolacak [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_silence": "➔ Sessizlik [{lightred}❌{default}] - Süre Dolacak [{lightred}{0}{default}]",
|
||||
"sa_player_penalty_info_active_warn": "➔ Uyarı [{lightred}❌{default}] - Süre Dolacak [{lightred}{0}{default}] - Sebep [{lightred}{1}{default}]",
|
||||
|
||||
"sa_player_penalty_info_no_active_mute": "➔ Mute [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_gag": "➔ Gag [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_silence": "➔ Sessizlik [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_warn": "➔ Uyarı [{lime}✔{default}]",
|
||||
|
||||
"sa_player_penalty_info": "===========================\nOyuncunun cezaları {lightred}{0}{default} için,\nBan sayısı: {lightred}{1}{default}, Gag sayısı: {lightred}{2}{default}, Mute sayısı: {lightred}{3}{default}, Sessizlik sayısı: {lightred}{4}{default}, Uyarı sayısı: {lightred}{5}{default}\nAktif cezalar:\n{6}\nAktif uyarılar:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Oyuncunun cezaları {lightred}{0}{grey}, Banlar: {lightred}{1}{grey}, Gaglar: {lightred}{2}{grey}, Mute'lar: {lightred}{3}{grey}, Sessizlikler: {lightred}{4}{grey}, Uyarılar: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}{lightred}{0}{grey} oyuncusunun bağlı hesapları: {1}",
|
||||
"sa_player_ban_message_time": "Senaryo nedeniyle {lightred}{0}{default} dakika boyunca {lightred}{1}{default} tarafından yasaklandınız!",
|
||||
"sa_player_ban_message_perm": "Senaryo nedeniyle kalıcı olarak {lightred}{0}{default} tarafından yasaklandınız!",
|
||||
"sa_player_kick_message": "Senaryo nedeniyle {lightred}{0}{default} tarafından atıldınız!",
|
||||
"sa_player_gag_message_time": "Senaryo nedeniyle {lightred}{0}{default} dakika boyunca {lightred}{1}{default} tarafından susturuldunuz!",
|
||||
"sa_player_gag_message_perm": "Senaryo nedeniyle kalıcı olarak {lightred}{0}{default} tarafından susturuldunuz!",
|
||||
"sa_player_mute_message_time": "Senaryo nedeniyle {lightred}{0}{default} dakika boyunca {lightred}{1}{default} tarafından sessize alındınız!",
|
||||
"sa_player_mute_message_perm": "Senaryo nedeniyle kalıcı olarak {lightred}{0}{default} tarafından sessize alındınız!",
|
||||
"sa_player_silence_message_time": "Senaryo nedeniyle {lightred}{0}{default} dakika boyunca {lightred}{1}{default} tarafından susturuldunuz!",
|
||||
"sa_player_silence_message_perm": "Senaryo nedeniyle kalıcı olarak {lightred}{0}{default} tarafından susturuldunuz!",
|
||||
"sa_player_warn_message_time": "{lightred}{0}{default} nedeniyle {lightred}{1}{default} dakika boyunca {lightred}{2}{default} tarafından uyarıldınız!",
|
||||
"sa_player_warn_message_perm": "{lightred}{0}{default} nedeniyle {lightred}{1}{default} tarafından kalıcı olarak uyarıldınız!",
|
||||
"sa_admin_ban_message_time": "{lightred}{0}{default} {lightred}{1}{default}'i {lightred}{2}{default} nedeniyle {lightred}{3}{default} dakika süreyle yasakladı!",
|
||||
"sa_admin_ban_message_perm": "{lightred}{0}{default} {lightred}{1}{default}'i kalıcı olarak {lightred}{2}{default} nedeniyle yasakladı!",
|
||||
"sa_admin_kick_message": "{lightred}{0}{default} {lightred}{1}{default}'i {lightred}{2}{default} nedeniyle atıldı!",
|
||||
"sa_admin_gag_message_time": "{lightred}{0}{default} {lightred}{1}{default}'i {lightred}{2}{default} nedeniyle {lightred}{3}{default} dakika süreyle sessizleştirdi!",
|
||||
"sa_admin_gag_message_perm": "{lightred}{0}{default} {lightred}{1}{default}'i kalıcı olarak {lightred}{2}{default} nedeniyle sessizleştirdi!",
|
||||
"sa_admin_mute_message_time": "{lightred}{0}{default} {lightred}{1}{default}'in sesini {lightred}{2}{default} nedeniyle {lightred}{3}{default} dakika süreyle kesti!",
|
||||
"sa_admin_mute_message_perm": "{lightred}{0}{default} {lightred}{1}{default}'in sesini kalıcı olarak {lightred}{2}{default} nedeniyle kesti!",
|
||||
"sa_admin_silence_message_time": "{lightred}{0}{default} {lightred}{1}{default}'i {lightred}{2}{default} nedeniyle {lightred}{3}{default} dakika süreyle sessizleştirdi!",
|
||||
"sa_admin_silence_message_perm": "{lightred}{0}{default} {lightred}{1}{default}'i kalıcı olarak {lightred}{2}{default} nedeniyle sessizleştirdi!",
|
||||
"sa_admin_warn_message_time": "{lightred}{0}{default} {lightred}{1}{default}'i {lightred}{2}{default} nedeniyle {lightred}{3}{default} dakika süreyle uyardı!",
|
||||
"sa_admin_warn_message_perm": "{lightred}{0}{default} {lightred}{1}{default}'i kalıcı olarak {lightred}{2}{default} nedeniyle uyardı!",
|
||||
"sa_admin_give_message": "{lightred}{0}{default} {lightred}{1}{default}'e {lightred}{2}{default} verdi!",
|
||||
"sa_admin_strip_message": "{lightred}{0}{default} {lightred}{1}{default}'in tüm silahlarını aldı!",
|
||||
"sa_admin_hp_message": "{lightred}{0}{default} {lightred}{1}{default}'in HP miktarını değiştirdi!",
|
||||
"sa_admin_speed_message": "{lightred}{0}{default} {lightred}{1}{default}'in hızını değiştirdi!",
|
||||
"sa_admin_gravity_message": "{lightred}{0}{default} {lightred}{1}{default}'in yer çekimini değiştirdi!",
|
||||
"sa_admin_money_message": "{lightred}{0}{default} {lightred}{1}{default}'in parasını değiştirdi!",
|
||||
"sa_admin_god_message": "{lightred}{0}{default} {lightred}{1}{default}'in tanrı modunu değiştirdi!",
|
||||
"sa_admin_resize_message": "{lightred}{0}{default} boyutu {lightred}{1}{default} için değiştirdi!",
|
||||
"sa_admin_slay_message": "{lightred}{0}{default} {lightred}{1}{default}'i öldürdü!",
|
||||
"sa_admin_slap_message": "{lightred}{0}{default} {lightred}{1}{default}'e tokat attı!",
|
||||
"sa_admin_changemap_message": "{lightred}{0}{default} haritayı {lightred}{1}{default} olarak değiştirdi!",
|
||||
"sa_admin_noclip_message": "{lightred}{0}{default} {lightred}{1}{default} için noclip'i açtı/kapatı!",
|
||||
"sa_admin_freeze_message": "{lightred}{0}{default} {lightred}{1}{default}'i dondurdu!",
|
||||
"sa_admin_unfreeze_message": "{lightred}{0}{default} {lightred}{1}{default}'in dondurmasını çözdü!",
|
||||
"sa_admin_rename_message": "{lightred}{0}{default} {lightred}{1}{default}'in takma adını {lightred}{2}{default} olarak değiştirdi!",
|
||||
"sa_admin_respawn_message": "{lightred}{0}{default} {lightred}{1}{default}'i yeniden doğurdu!",
|
||||
"sa_admin_tp_message": "{lightred}{0}{default} {lightred}{1}{default}'e teleporto etti!",
|
||||
"sa_admin_bring_message": "{lightred}{0}{default} {lightred}{1}{default}'i kendisine teleporto etti!",
|
||||
"sa_admin_team_message": "{lightred}{0}{default} {lightred}{1}{default}'i {lightred}{2}{default} takımına transfer etti!",
|
||||
"sa_admin_warns_menu_title": "{gold}{0} {lime}UYARILAR",
|
||||
"sa_admin_warns_unwarn": "{lime}Uyarı başarıyla kaldırıldı{default} {gold}{0} {default} için {gold}{1}{default}!",
|
||||
"sa_admin_vote_menu_title": "{lime}OY VERME {gold}{0}",
|
||||
"sa_admin_vote_message": "{lightred}{0}{default} {lightred}{1}{default} için oy kullanmaya başladı",
|
||||
"sa_admin_vote_message_results": "{lime}OY SONUÇLARI {gold}{0}",
|
||||
"sa_admin_vote_message_results_answer": "{lime}{0} {default}- {gold}{1}",
|
||||
"sa_admin_voice_mute_all": "{Lightred}Konuşman sırasında {default}tüm oyuncuları susturdun",
|
||||
"sa_admin_voice_unmute_all": "{Lime}Tüm oyuncuların sesini {default}açtın",
|
||||
"sa_admin_voice_listen_all": "{Default}Artık {lime}tüm {default}oyuncuları duyabiliyorsun",
|
||||
"sa_admin_voice_unlisten_all": "{Default}Artık {lime}tüm {default}oyuncuları duymuyorsun",
|
||||
"sa_adminsay_prefix": "{RED}Yönetici: {lightred}{0}{default}",
|
||||
"sa_adminchat_template_admin": "{LIME}(Yönetici) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_adminchat_template_player": "{SILVER}(Oyuncu) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_discord_log_command": "**{0}** `{1}` komutunu `HOSTNAME` sunucusunda gerçekleştirdi",
|
||||
"sa_menu_pluginsmanager_loaded": "{lime}Etkinleştirildi {default}eklenti {lime}{0}",
|
||||
"sa_menu_pluginsmanager_unloaded": "{lightred}Devre Dışı Bırakıldı {default}eklenti {lightred}{0}"
|
||||
}
|
||||
137
CS2-SimpleAdmin/lang/zh-Hans.json
Normal file
137
CS2-SimpleAdmin/lang/zh-Hans.json
Normal file
@@ -0,0 +1,137 @@
|
||||
{
|
||||
"sa_title": "SimpleAdmin",
|
||||
"sa_prefix": "{lightred}[SA] {default}",
|
||||
|
||||
"sa_unknown": "未知",
|
||||
"sa_no_permission": "您没有权限使用此命令。",
|
||||
"sa_ban_max_duration_exceeded": "禁止时长不能超过 {lightred}{0}{default} 分钟。",
|
||||
"sa_ban_perm_restricted": "您没有永久禁止权限。",
|
||||
|
||||
"sa_admin_add": "添加管理员",
|
||||
"sa_admin_remove": "移除管理员",
|
||||
"sa_admin_reload": "重新加载管理员",
|
||||
|
||||
"sa_godmode": "上帝模式",
|
||||
"sa_noclip": "穿墙模式",
|
||||
"sa_respawn": "重生",
|
||||
"sa_give_weapon": "给予武器",
|
||||
"sa_strip_weapons": "移除武器",
|
||||
"sa_freeze": "冻结",
|
||||
"sa_set_hp": "设置生命值",
|
||||
"sa_set_speed": "设置速度",
|
||||
"sa_set_gravity": "设置重力",
|
||||
"sa_set_money": "设置金钱",
|
||||
|
||||
"sa_changemap": "更换地图",
|
||||
"sa_restart_game": "重新开始游戏",
|
||||
|
||||
"sa_team_ct": "CT",
|
||||
"sa_team_t": "T",
|
||||
"sa_team_swap": "交换",
|
||||
"sa_team_spec": "观察者",
|
||||
|
||||
"sa_slap": "掌掴",
|
||||
"sa_slay": "击杀",
|
||||
"sa_kick": "踢出",
|
||||
"sa_ban": "禁止",
|
||||
"sa_gag": "禁言",
|
||||
"sa_mute": "静音",
|
||||
"sa_silence": "沉默",
|
||||
"sa_warn": "警告",
|
||||
"sa_team_force": "强制分队",
|
||||
|
||||
"sa_menu_custom_commands": "自定义命令",
|
||||
"sa_menu_server_manage": "服务器管理",
|
||||
"sa_menu_fun_commands": "娱乐命令",
|
||||
"sa_menu_admins_manage": "管理员管理",
|
||||
"sa_menu_players_manage": "玩家管理",
|
||||
"sa_menu_disconnected_title": "最近的玩家",
|
||||
"sa_menu_disconnected_action_title": "选择操作",
|
||||
"sa_menu_pluginsmanager_title": "插件管理",
|
||||
|
||||
"sa_player": "玩家",
|
||||
"sa_console": "控制台",
|
||||
"sa_steamid": "SteamID",
|
||||
"sa_duration": "时长",
|
||||
"sa_reason": "原因",
|
||||
"sa_admin": "管理员",
|
||||
"sa_permanent": "永久",
|
||||
|
||||
"sa_discord_penalty_ban": "禁止记录",
|
||||
"sa_discord_penalty_mute": "静音记录",
|
||||
"sa_discord_penalty_gag": "禁言记录",
|
||||
"sa_discord_penalty_silence": "沉默记录",
|
||||
"sa_discord_penalty_warn": "警告记录",
|
||||
"sa_discord_penalty_unknown": "未知记录",
|
||||
|
||||
"sa_player_penalty_info_active_mute": "➔ 静音 [{lightred}❌{default}] - 将于 [{lightred}{0}{default}] 过期",
|
||||
"sa_player_penalty_info_active_gag": "➔ 禁言 [{lightred}❌{default}] - 将于 [{lightred}{0}{default}] 过期",
|
||||
"sa_player_penalty_info_active_silence": "➔ 沉默 [{lightred}❌{default}] - 将于 [{lightred}{0}{default}] 过期",
|
||||
"sa_player_penalty_info_active_warn": "➔ 警告 [{lightred}❌{default}] - 将于 [{lightred}{0}{default}] 过期 - 原因 [{lightred}{1}{default}]",
|
||||
|
||||
"sa_player_penalty_info_no_active_mute": "➔ 静音 [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_gag": "➔ 禁言 [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_silence": "➔ 沉默 [{lime}✔{default}]",
|
||||
"sa_player_penalty_info_no_active_warn": "➔ 警告 [{lime}✔{default}]",
|
||||
|
||||
"sa_player_penalty_info": "===========================\n玩家 {lightred}{0}{default} 的处罚信息,\n禁止次数: {lightred}{1}{default}, 禁言次数: {lightred}{2}{default}, 静音次数: {lightred}{3}{default}, 沉默次数: {lightred}{4}{default}, 警告次数: {lightred}{5}{default}\n活跃的处罚:\n{6}\n活跃的警告:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}玩家 {lightred}{0}{grey} 的处罚信息, 禁止: {lightred}{1}{grey}, 禁言: {lightred}{2}{grey}, 静音: {lightred}{3}{grey}, 沉默: {lightred}{4}{grey}, 警告: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}玩家 {lightred}{0}{grey} 的关联账户:{1}",
|
||||
"sa_player_ban_message_time": "您已被 {lightred}{0}{default} 因 {lightred}{2}{default} 禁止 {lightred}{1}{default} 分钟!",
|
||||
"sa_player_ban_message_perm": "您已被 {lightred}{0}{default} 因 {lightred}{1}{default} 永久禁止!",
|
||||
"sa_player_kick_message": "您已被 {lightred}{0}{default} 因 {lightred}{1}{default} 踢出!",
|
||||
"sa_player_gag_message_time": "您已被 {lightred}{0}{default} 因 {lightred}{2}{default} 禁言 {lightred}{1}{default} 分钟!",
|
||||
"sa_player_gag_message_perm": "您已被 {lightred}{0}{default} 因 {lightred}{1}{default} 永久禁言!",
|
||||
"sa_player_mute_message_time": "您已被 {lightred}{0}{default} 因 {lightred}{2}{default} 静音 {lightred}{1}{default} 分钟!",
|
||||
"sa_player_mute_message_perm": "您已被 {lightred}{0}{default} 因 {lightred}{1}{default} 永久静音!",
|
||||
"sa_player_silence_message_time": "您已被 {lightred}{0}{default} 因 {lightred}{2}{default} 沉默 {lightred}{1}{default} 分钟!",
|
||||
"sa_player_silence_message_perm": "您已被 {lightred}{0}{default} 因 {lightred}{1}{default} 永久沉默!",
|
||||
"sa_player_warn_message_time": "您已被 {lightred}{0}{default} 因 {lightred}{2}{default} 警告 {lightred}{1}{default} 分钟!",
|
||||
"sa_player_warn_message_perm": "您已被 {lightred}{0}{default} 因 {lightred}{1}{default} 永久警告!",
|
||||
"sa_admin_ban_message_time": "{lightred}{0}{default} 禁止了 {lightred}{1}{default} 因 {lightred}{2}{default} 共 {lightred}{3}{default} 分钟!",
|
||||
"sa_admin_ban_message_perm": "{lightred}{0}{default} 永久禁止了 {lightred}{1}{default} 因 {lightred}{2}{default}!",
|
||||
"sa_admin_kick_message": "{lightred}{0}{default} 踢出了 {lightred}{1}{default} 因 {lightred}{2}{default}!",
|
||||
"sa_admin_gag_message_time": "{lightred}{0}{default} 禁言了 {lightred}{1}{default} 因 {lightred}{2}{default} 共 {lightred}{3}{default} 分钟!",
|
||||
"sa_admin_gag_message_perm": "{lightred}{0}{default} 永久禁言了 {lightred}{1}{default} 因 {lightred}{2}{default}!",
|
||||
"sa_admin_mute_message_time": "{lightred}{0}{default} 静音了 {lightred}{1}{default} 因 {lightred}{2}{default} 共 {lightred}{3}{default} 分钟!",
|
||||
"sa_admin_mute_message_perm": "{lightred}{0}{default} 永久静音了 {lightred}{1}{default} 因 {lightred}{2}{default}!",
|
||||
"sa_admin_silence_message_time": "{lightred}{0}{default} 沉默了 {lightred}{1}{default} 因 {lightred}{2}{default} 共 {lightred}{3}{default} 分钟!",
|
||||
"sa_admin_silence_message_perm": "{lightred}{0}{default} 永久沉默了 {lightred}{1}{default} 因 {lightred}{2}{default}!",
|
||||
"sa_admin_warn_message_time": "{lightred}{0}{default} 警告了 {lightred}{1}{default} 因 {lightred}{2}{default} 共 {lightred}{3}{default} 分钟!",
|
||||
"sa_admin_warn_message_perm": "{lightred}{0}{default} 永久警告了 {lightred}{1}{default} 因 {lightred}{2}{default}!",
|
||||
"sa_admin_give_message": "{lightred}{0}{default} 给了 {lightred}{1}{default} {lightred}{2}{default}!",
|
||||
"sa_admin_strip_message": "{lightred}{0}{default} 移除了 {lightred}{1}{default} 的所有武器!",
|
||||
"sa_admin_hp_message": "{lightred}{0}{default} 修改了 {lightred}{1}{default} 的生命值!",
|
||||
"sa_admin_speed_message": "{lightred}{0}{default} 修改了 {lightred}{1}{default} 的速度!",
|
||||
"sa_admin_gravity_message": "{lightred}{0}{default} 修改了 {lightred}{1}{default} 的重力!",
|
||||
"sa_admin_money_message": "{lightred}{0}{default} 修改了 {lightred}{1}{default} 的金钱!",
|
||||
"sa_admin_god_message": "{lightred}{0}{default} 切换了 {lightred}{1}{default} 的上帝模式!",
|
||||
"sa_admin_resize_message": "{lightred}{0}{default} 更改了 {lightred}{1}{default} 的大小!",
|
||||
"sa_admin_slay_message": "{lightred}{0}{default} 击杀了 {lightred}{1}{default}!",
|
||||
"sa_admin_slap_message": "{lightred}{0}{default} 掌掴了 {lightred}{1}{default}!",
|
||||
"sa_admin_changemap_message": "{lightred}{0}{default} 将地图更换为 {lightred}{1}{default}!",
|
||||
"sa_admin_noclip_message": "{lightred}{0}{default} 切换了 {lightred}{1}{default} 的穿墙模式!",
|
||||
"sa_admin_freeze_message": "{lightred}{0}{default} 冻结了 {lightred}{1}{default}!",
|
||||
"sa_admin_unfreeze_message": "{lightred}{0}{default} 解冻了 {lightred}{1}{default}!",
|
||||
"sa_admin_rename_message": "{lightred}{0}{default} 将 {lightred}{1}{default} 的昵称改为 {lightred}{2}{default}!",
|
||||
"sa_admin_respawn_message": "{lightred}{0}{default} 复活了 {lightred}{1}{default}!",
|
||||
"sa_admin_tp_message": "{lightred}{0}{default} 传送到 {lightred}{1}{default}!",
|
||||
"sa_admin_bring_message": "{lightred}{0}{default} 将 {lightred}{1}{default} 传送到自己!",
|
||||
"sa_admin_team_message": "{lightred}{0}{default} 将 {lightred}{1}{default} 转移到 {lightred}{2}{default} 队伍!",
|
||||
"sa_admin_warns_menu_title": "{gold}{0} {lime}警告",
|
||||
"sa_admin_warns_unwarn": "{lime}成功移除警告{default} {gold}{0} 对于 {gold}{1}{default}!",
|
||||
"sa_admin_vote_menu_title": "{lime}投票 {gold}{0}",
|
||||
"sa_admin_vote_message": "{lightred}{0}{default} 发起了 [{lightred}{1}{default}] 的投票",
|
||||
"sa_admin_vote_message_results": "{lime}投票结果 {gold}{0}",
|
||||
"sa_admin_vote_message_results_answer": "{lime}{0} {default}- {gold}{1}",
|
||||
"sa_admin_voice_mute_all": "{Lightred}你已在讲话期间静音了{default}所有玩家",
|
||||
"sa_admin_voice_unmute_all": "{Lime}你已取消了{default}所有玩家的静音",
|
||||
"sa_admin_voice_listen_all": "{Default}你现在可以听到{lime}所有{default}玩家了",
|
||||
"sa_admin_voice_unlisten_all": "{Default}你现在不再听到{lime}所有{default}玩家了",
|
||||
"sa_adminsay_prefix": "{RED}管理员: {lightred}{0}{default}",
|
||||
"sa_adminchat_template_admin": "{LIME}(管理员) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_adminchat_template_player": "{SILVER}(玩家) {lightred}{0}{default}: {lightred}{1}{default}",
|
||||
"sa_discord_log_command": "**{0}** 在服务器 `HOSTNAME` 上执行了命令 `{1}`",
|
||||
"sa_menu_pluginsmanager_loaded": "{lime}激活了 {default}插件 {lime}{0}",
|
||||
"sa_menu_pluginsmanager_unloaded": "{lightred}停用了 {default}插件 {lightred}{0}"
|
||||
}
|
||||
15
CS2-SimpleAdminApi/CS2-SimpleAdminApi.csproj
Normal file
15
CS2-SimpleAdminApi/CS2-SimpleAdminApi.csproj
Normal file
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RootNamespace>CS2_SimpleAdminApi</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.340" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
105
CS2-SimpleAdminApi/ICS2-SimpleAdminApi.cs
Normal file
105
CS2-SimpleAdminApi/ICS2-SimpleAdminApi.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Capabilities;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CS2_SimpleAdminApi;
|
||||
|
||||
public interface ICS2_SimpleAdminApi
|
||||
{
|
||||
public static readonly PluginCapability<ICS2_SimpleAdminApi?> PluginCapability = new("simpleadmin:api");
|
||||
|
||||
/// <summary>
|
||||
/// Gets player information associated with the specified player controller.
|
||||
/// </summary>
|
||||
/// <param name="player">The player controller.</param>
|
||||
/// <returns>PlayerInfo object representing player data.</returns>
|
||||
public PlayerInfo GetPlayerInfo(CCSPlayerController player);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the database connection string used by the plugin.
|
||||
/// </summary>
|
||||
public string GetConnectionString();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the configured server IP address with port.
|
||||
/// </summary>
|
||||
public string GetServerAddress();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the internal server ID assigned in the plugin's database.
|
||||
/// </summary>
|
||||
public int? GetServerId();
|
||||
|
||||
/// <summary>
|
||||
/// Returns mute-related penalties for the specified player.
|
||||
/// </summary>
|
||||
/// <param name="player">The player controller.</param>
|
||||
/// <returns>A dictionary mapping penalty types to lists of penalties with end date, duration, and pass state.</returns>
|
||||
public Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>> GetPlayerMuteStatus(CCSPlayerController player);
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when a player receives a penalty.
|
||||
/// </summary>
|
||||
public event Action<PlayerInfo, PlayerInfo?, PenaltyType, string, int, int?, int?>? OnPlayerPenaltied;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when a penalty is added to a player by SteamID.
|
||||
/// </summary>
|
||||
public event Action<SteamID, PlayerInfo?, PenaltyType, string, int, int?, int?>? OnPlayerPenaltiedAdded;
|
||||
|
||||
/// <summary>
|
||||
/// Event to show admin activity messages.
|
||||
/// </summary>
|
||||
public event Action<string, string?, bool, object>? OnAdminShowActivity;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when an admin toggles silent mode.
|
||||
/// </summary>
|
||||
public event Action<int, bool>? OnAdminToggleSilent;
|
||||
|
||||
/// <summary>
|
||||
/// Issues a penalty to a player controller with specified type, reason, and optional duration.
|
||||
/// </summary>
|
||||
public void IssuePenalty(CCSPlayerController player, CCSPlayerController? admin, PenaltyType penaltyType, string reason, int duration = -1);
|
||||
|
||||
/// <summary>
|
||||
/// Issues a penalty to a player identified by SteamID with specified type, reason, and optional duration.
|
||||
/// </summary>
|
||||
public void IssuePenalty(SteamID steamid, CCSPlayerController? admin, PenaltyType penaltyType, string reason, int duration = -1);
|
||||
|
||||
/// <summary>
|
||||
/// Logs a command invoked by a caller with the command string.
|
||||
/// </summary>
|
||||
public void LogCommand(CCSPlayerController? caller, string command);
|
||||
|
||||
/// <summary>
|
||||
/// Logs a command invoked by a caller with the command info object.
|
||||
/// </summary>
|
||||
public void LogCommand(CCSPlayerController? caller, CommandInfo command);
|
||||
|
||||
/// <summary>
|
||||
/// Shows an admin activity message, optionally suppressing broadcasting.
|
||||
/// </summary>
|
||||
public void ShowAdminActivity(string messageKey, string? callerName = null, bool dontPublish = false, params object[] messageArgs);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the specified admin player is in silent mode (not broadcasting activity).
|
||||
/// </summary>
|
||||
public bool IsAdminSilent(CCSPlayerController player);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a set of player slots representing admins currently in silent mode.
|
||||
/// </summary>
|
||||
public HashSet<int> ListSilentAdminsSlots();
|
||||
|
||||
/// <summary>
|
||||
/// Registers a new command with the specified name, description, and callback.
|
||||
/// </summary>
|
||||
public void RegisterCommand(string name, string? description, CommandInfo.CommandCallback callback);
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters an existing command by its name.
|
||||
/// </summary>
|
||||
public void UnRegisterCommand(string name);
|
||||
}
|
||||
11
CS2-SimpleAdminApi/PenaltyType.cs
Normal file
11
CS2-SimpleAdminApi/PenaltyType.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace CS2_SimpleAdminApi;
|
||||
|
||||
public enum PenaltyType
|
||||
{
|
||||
Ban = 0,
|
||||
Kick,
|
||||
Mute,
|
||||
Gag,
|
||||
Silence,
|
||||
Warn
|
||||
}
|
||||
40
CS2-SimpleAdminApi/PlayerInfo.cs
Normal file
40
CS2-SimpleAdminApi/PlayerInfo.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System.Numerics;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
|
||||
namespace CS2_SimpleAdminApi;
|
||||
|
||||
public class PlayerInfo(
|
||||
int? userId,
|
||||
int slot,
|
||||
SteamID steamId,
|
||||
string name,
|
||||
string? ipAddress,
|
||||
int totalBans = 0,
|
||||
int totalMutes = 0,
|
||||
int totalGags = 0,
|
||||
int totalSilences = 0,
|
||||
int totalWarns = 0)
|
||||
{
|
||||
public int? UserId { get; } = userId;
|
||||
public int Slot { get; } = slot;
|
||||
public SteamID SteamId { get; } = steamId;
|
||||
public string Name { get; } = name;
|
||||
public string? IpAddress { get; } = ipAddress;
|
||||
public int TotalBans { get; set; } = totalBans;
|
||||
public int TotalMutes { get; set; } = totalMutes;
|
||||
public int TotalGags { get; set; } = totalGags;
|
||||
public int TotalSilences { get; set; } = totalSilences;
|
||||
public int TotalWarns { get; set; } = totalWarns;
|
||||
public bool WaitingForKick { get; set; } = false;
|
||||
public List<(ulong SteamId, string PlayerName)> AccountsAssociated { get; set; } = [];
|
||||
public DiePosition? DiePosition { get; set; }
|
||||
public bool IsLoaded { get; set; }
|
||||
}
|
||||
|
||||
public class DiePosition(Vector3 position, Vector3 angle)
|
||||
{
|
||||
public Vector3 Position { get; } = position;
|
||||
public Vector3 Angle { get; } = angle;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RootNamespace>AntiDLL_CS2_SimpleAdmin</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.305" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="AntiDLL.API">
|
||||
<HintPath>AntiDLL.API.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="CS2-SimpleAdminApi">
|
||||
<HintPath>CS2-SimpleAdminApi.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user