mirror of
https://github.com/daffyyyy/CS2-SimpleAdmin.git
synced 2026-02-19 10:58:20 +00:00
Compare commits
373 Commits
CS2-Simple
...
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 +1 @@
|
||||
1.7.7-alpha-9
|
||||
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}"
|
||||
}
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.340" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4-beta1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,33 +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");
|
||||
|
||||
public PlayerInfo GetPlayerInfo(CCSPlayerController player);
|
||||
|
||||
public string GetConnectionString();
|
||||
public string GetServerAddress();
|
||||
public int? GetServerId();
|
||||
|
||||
public Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>> GetPlayerMuteStatus(CCSPlayerController player);
|
||||
|
||||
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 IssuePenalty(CCSPlayerController player, CCSPlayerController? admin, PenaltyType penaltyType, string reason, int duration = -1);
|
||||
public void IssuePenalty(SteamID steamid, CCSPlayerController? admin, PenaltyType penaltyType, string reason, int duration = -1);
|
||||
public void LogCommand(CCSPlayerController? caller, string command);
|
||||
public void LogCommand(CCSPlayerController? caller, CommandInfo command);
|
||||
public void ShowAdminActivity(string messageKey, string? callerName = null, bool dontPublish = false, params object[] messageArgs);
|
||||
|
||||
public bool IsAdminSilent(CCSPlayerController player);
|
||||
public HashSet<int> ListSilentAdminsSlots();
|
||||
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);
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user