mirror of
https://github.com/daffyyyy/CS2-SimpleAdmin.git
synced 2026-03-08 23:59:06 +00:00
Compare commits
37 Commits
cc1fbb88d6
...
build-1.7.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
099e91b43b | ||
|
|
206c18db66 | ||
|
|
78318102fe | ||
|
|
2edacc2b3f | ||
|
|
e1e66441f2 | ||
|
|
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 |
171
.github/workflows/build.yml
vendored
171
.github/workflows/build.yml
vendored
@@ -1,6 +1,7 @@
|
|||||||
name: Build
|
name: Build and Publish
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches: [ "main" ]
|
branches: [ "main" ]
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
@@ -11,85 +12,117 @@ on:
|
|||||||
- '**/README.md'
|
- '**/README.md'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BUILD_NUMBER: ${{ github.run_number }}
|
|
||||||
PROJECT_PATH_CS2_SIMPLEADMIN: "CS2-SimpleAdmin/CS2-SimpleAdmin.csproj"
|
PROJECT_PATH_CS2_SIMPLEADMIN: "CS2-SimpleAdmin/CS2-SimpleAdmin.csproj"
|
||||||
PROJECT_PATH_CS2_SIMPLEADMINAPI: "CS2-SimpleAdminApi/CS2-SimpleAdminApi.csproj"
|
|
||||||
PROJECT_NAME_CS2_SIMPLEADMIN: "CS2-SimpleAdmin"
|
PROJECT_NAME_CS2_SIMPLEADMIN: "CS2-SimpleAdmin"
|
||||||
|
PROJECT_PATH_CS2_SIMPLEADMINAPI: "CS2-SimpleAdminApi/CS2-SimpleAdminApi.csproj"
|
||||||
PROJECT_NAME_CS2_SIMPLEADMINAPI: "CS2-SimpleAdminApi"
|
PROJECT_NAME_CS2_SIMPLEADMINAPI: "CS2-SimpleAdminApi"
|
||||||
|
PROJECT_PATH_FUNCOMMANDSMODULE: "Modules/CS2-SimpleAdmin_FunCommands/CS2-SimpleAdmin_FunCommands/CS2-SimpleAdmin_FunCommands.csproj"
|
||||||
|
PROJECT_NAME_FUNCOMMANDSMODULE: "CS2-SimpleAdmin_FunCommands"
|
||||||
|
PROJECT_PATH_STEALTHMODULE: "Modules/CS2-SimpleAdmin_StealthModule/CS2-SimpleAdmin_StealthModule.csproj"
|
||||||
|
PROJECT_NAME_STEALTHMODULE: "CS2-SimpleAdmin_StealthModule"
|
||||||
OUTPUT_PATH: "./counterstrikesharp"
|
OUTPUT_PATH: "./counterstrikesharp"
|
||||||
TMP_PATH: "./tmp"
|
TMP_PATH: "./tmp"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
permissions: write-all
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions: write-all
|
||||||
|
outputs:
|
||||||
|
build_version: ${{ steps.get_version.outputs.VERSION }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup .NET
|
|
||||||
uses: actions/setup-dotnet@v4
|
- name: Setup .NET
|
||||||
with:
|
uses: actions/setup-dotnet@v4
|
||||||
dotnet-version: 8.0.x
|
with:
|
||||||
- name: Restore CS2-SimpleAdmin
|
dotnet-version: 8.0.x
|
||||||
run: dotnet restore ${{ env.PROJECT_PATH_CS2_SIMPLEADMIN }}
|
|
||||||
- name: Build CS2-SimpleAdmin
|
- name: Get Version
|
||||||
run: dotnet build ${{ env.PROJECT_PATH_CS2_SIMPLEADMIN }} -c Release -o ${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMIN }}
|
id: get_version
|
||||||
- name: Restore CS2-SimpleAdminApi
|
run: echo "VERSION=$(cat CS2-SimpleAdmin/VERSION)" >> $GITHUB_OUTPUT
|
||||||
run: dotnet restore ${{ env.PROJECT_PATH_CS2_SIMPLEADMINAPI }}
|
|
||||||
- name: Build CS2-SimpleAdminApi
|
- name: Restore & Build All Projects
|
||||||
run: dotnet build ${{ env.PROJECT_PATH_CS2_SIMPLEADMINAPI }} -c Release -o ${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMINAPI }}
|
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_FUNCOMMANDSMODULE }}
|
||||||
|
dotnet build ${{ env.PROJECT_PATH_FUNCOMMANDSMODULE }} -c Release -o ${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_FUNCOMMANDSMODULE }}
|
||||||
|
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_FunCommands
|
||||||
|
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_FUNCOMMANDSMODULE }}/* ${{ env.OUTPUT_PATH }}/plugins/CS2-SimpleAdmin_FunCommands
|
||||||
|
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:
|
publish:
|
||||||
if: github.event_name == 'push'
|
|
||||||
permissions: write-all
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build
|
needs: build
|
||||||
|
if: github.event_name == 'push'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions: write-all
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup .NET
|
- name: Download build artifacts
|
||||||
uses: actions/setup-dotnet@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
dotnet-version: 8.0.x
|
name: CS2-SimpleAdmin-Build-Artifacts
|
||||||
- name: Restore CS2-SimpleAdmin
|
path: .
|
||||||
run: dotnet restore ${{ env.PROJECT_PATH_CS2_SIMPLEADMIN }}
|
- name: Unzip main build artifact
|
||||||
- name: Build CS2-SimpleAdmin
|
run: unzip CS2-SimpleAdmin-${{ needs.build.outputs.build_version }}.zip -d ./counterstrikesharp
|
||||||
run: dotnet build ${{ env.PROJECT_PATH_CS2_SIMPLEADMIN }} -c Release -o ${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMIN }}
|
- name: Publish combined release
|
||||||
- name: Restore CS2-SimpleAdminApi
|
uses: ncipollo/release-action@v1.14.0
|
||||||
run: dotnet restore ${{ env.PROJECT_PATH_CS2_SIMPLEADMINAPI }}
|
with:
|
||||||
- name: Build CS2-SimpleAdminApi
|
artifacts: |
|
||||||
run: dotnet build ${{ env.PROJECT_PATH_CS2_SIMPLEADMINAPI }} -c Release -o ${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMINAPI }}
|
CS2-SimpleAdmin-${{ needs.build.outputs.build_version }}.zip
|
||||||
- name: Clean files
|
StatusBlocker-linux-${{ needs.build.outputs.build_version }}.zip
|
||||||
run: |
|
StatusBlocker-windows-${{ needs.build.outputs.build_version }}.zip
|
||||||
rm -f \
|
name: "CS2-SimpleAdmin-${{ needs.build.outputs.build_version }}"
|
||||||
${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMIN }}/CounterStrikeSharp.API.dll \
|
tag: "build-${{ needs.build.outputs.build_version }}"
|
||||||
${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMIN }}/McMaster.NETCore.Plugins.dll \
|
body: |
|
||||||
${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMIN }}/Microsoft.DotNet.PlatformAbstractions.dll \
|
Place the files in your server as follows:
|
||||||
${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMIN }}/Microsoft.Extensions.DependencyModel.dll \
|
|
||||||
${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMIN }}/CS2-SimpleAdminApi.* \
|
- CS2-SimpleAdmin:
|
||||||
${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMIN }}/Microsoft.* \
|
Place the files inside the addons/counterstrikesharp directory.
|
||||||
${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMINAPI }}/CounterStrikeSharp.API.dll \
|
After the first launch, configure the plugin using the JSON config file at:
|
||||||
${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMINAPI }}/McMaster.NETCore.Plugins.dll \
|
addons/counterstrikesharp/configs/plugins/CS2-SimpleAdmin/CS2-SimpleAdmin.json
|
||||||
${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMINAPI }}/Microsoft.DotNet.PlatformAbstractions.dll \
|
|
||||||
${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMINAPI }}/Microsoft.Extensions.DependencyModel.dll
|
- StatusBlocker:
|
||||||
- name: Combine projects
|
Place the plugin files directly into the addons directory.
|
||||||
run: |
|
This plugin is a Metamod module for the StealthModule and does not require a subfolder.
|
||||||
mkdir -p ${{ env.OUTPUT_PATH }}/plugins
|
|
||||||
mkdir -p ${{ env.OUTPUT_PATH }}/shared
|
Remember to restart or reload your game server after installing and configuring the plugins.
|
||||||
cp -r ${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMIN }} ${{ env.OUTPUT_PATH }}/plugins/
|
|
||||||
cp -r ${{ env.TMP_PATH }}/${{ env.PROJECT_NAME_CS2_SIMPLEADMINAPI }} ${{ env.OUTPUT_PATH }}/shared/
|
|
||||||
- name: Zip combined
|
|
||||||
uses: thedoctor0/zip-release@0.7.6
|
|
||||||
with:
|
|
||||||
type: 'zip'
|
|
||||||
filename: '${{ env.PROJECT_NAME_CS2_SIMPLEADMIN }}-${{ env.BUILD_NUMBER }}.zip'
|
|
||||||
path: ${{ env.OUTPUT_PATH }}
|
|
||||||
- name: Publish combined release
|
|
||||||
uses: ncipollo/release-action@v1.14.0
|
|
||||||
with:
|
|
||||||
artifacts: "${{ env.PROJECT_NAME_CS2_SIMPLEADMIN }}-${{ env.BUILD_NUMBER }}.zip"
|
|
||||||
name: "CS2-SimpleAdmin - Build ${{ env.BUILD_NUMBER }}"
|
|
||||||
tag: "build-${{ env.BUILD_NUMBER }}"
|
|
||||||
body: |
|
|
||||||
Place files in addons/counterstrikesharp
|
|
||||||
After first launch, configure the plugins in the respective configs:
|
|
||||||
- CS2-SimpleAdmin: addons/counterstrikesharp/configs/plugins/CS2-SimpleAdmin/CS2-SimpleAdmin.json
|
|
||||||
|
|||||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -3,4 +3,10 @@ obj/
|
|||||||
.vs/
|
.vs/
|
||||||
.git
|
.git
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.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
|
||||||
|
CLAUDE.md
|
||||||
|
|||||||
BIN
CS2-SimpleAdmin/3rd_party/MenuManagerApi.dll
vendored
BIN
CS2-SimpleAdmin/3rd_party/MenuManagerApi.dll
vendored
Binary file not shown.
@@ -1,40 +1,57 @@
|
|||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
|
using CounterStrikeSharp.API.Core.Commands;
|
||||||
using CounterStrikeSharp.API.Modules.Commands;
|
using CounterStrikeSharp.API.Modules.Commands;
|
||||||
|
using CounterStrikeSharp.API.Modules.Commands.Targeting;
|
||||||
using CounterStrikeSharp.API.Modules.Entities;
|
using CounterStrikeSharp.API.Modules.Entities;
|
||||||
using CS2_SimpleAdmin.Managers;
|
using CS2_SimpleAdmin.Managers;
|
||||||
|
using CS2_SimpleAdmin.Menus;
|
||||||
using CS2_SimpleAdminApi;
|
using CS2_SimpleAdminApi;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
namespace CS2_SimpleAdmin.Api;
|
namespace CS2_SimpleAdmin.Api;
|
||||||
|
|
||||||
public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
||||||
{
|
{
|
||||||
|
public event Action? OnSimpleAdminReady;
|
||||||
|
public void OnSimpleAdminReadyEvent() => OnSimpleAdminReady?.Invoke();
|
||||||
|
|
||||||
public PlayerInfo GetPlayerInfo(CCSPlayerController player)
|
public PlayerInfo GetPlayerInfo(CCSPlayerController player)
|
||||||
{
|
{
|
||||||
if (!player.UserId.HasValue)
|
return !player.UserId.HasValue
|
||||||
throw new KeyNotFoundException("Player with specific UserId not found");
|
? throw new KeyNotFoundException("Player with specific UserId not found")
|
||||||
|
: CS2_SimpleAdmin.PlayersInfo[player.SteamID];
|
||||||
return CS2_SimpleAdmin.PlayersInfo[player.UserId.Value];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
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);
|
return PlayerPenaltyManager.GetAllPlayerPenalties(player.Slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
public event Action<PlayerInfo, PlayerInfo?, PenaltyType, string, int, int?>? OnPlayerPenaltied;
|
public event Action<PlayerInfo, PlayerInfo?, PenaltyType, string, int, int?, int?>? OnPlayerPenaltied;
|
||||||
public event Action<SteamID, PlayerInfo?, PenaltyType, string, int, int?>? OnPlayerPenaltiedAdded;
|
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,
|
public void OnPlayerPenaltiedEvent(PlayerInfo player, PlayerInfo? admin, PenaltyType penaltyType, string reason,
|
||||||
int duration = -1) => OnPlayerPenaltied?.Invoke(player, admin, penaltyType, reason, duration, CS2_SimpleAdmin.ServerId);
|
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) => OnPlayerPenaltiedAdded?.Invoke(player, admin, penaltyType, reason, duration, CS2_SimpleAdmin.ServerId);
|
|
||||||
|
|
||||||
public void IssuePenalty(CCSPlayerController player, CCSPlayerController? admin, PenaltyType penaltyType, string reason, int duration = -1)
|
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)
|
switch (penaltyType)
|
||||||
{
|
{
|
||||||
@@ -73,6 +90,41 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
public void LogCommand(CCSPlayerController? caller, string command)
|
||||||
{
|
{
|
||||||
Helper.LogCommand(caller, command);
|
Helper.LogCommand(caller, command);
|
||||||
@@ -82,9 +134,184 @@ public class CS2_SimpleAdminApi : ICS2_SimpleAdminApi
|
|||||||
{
|
{
|
||||||
Helper.LogCommand(caller, command);
|
Helper.LogCommand(caller, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsAdminSilent(CCSPlayerController player)
|
public bool IsAdminSilent(CCSPlayerController player)
|
||||||
{
|
{
|
||||||
return CS2_SimpleAdmin.SilentPlayers.Contains(player.Slot);
|
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 TargetResult? GetTarget(CommandInfo command)
|
||||||
|
{
|
||||||
|
return CS2_SimpleAdmin.GetTarget(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowAdminActivity(string messageKey, string? callerName = null, bool dontPublish = false,
|
||||||
|
params object[] messageArgs)
|
||||||
|
{
|
||||||
|
Helper.ShowAdminActivity(messageKey, callerName, dontPublish, messageArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowAdminActivityTranslated(string translatedMessage, string? callerName = null,
|
||||||
|
bool dontPublish = false)
|
||||||
|
{
|
||||||
|
Helper.ShowAdminActivityTranslated(translatedMessage, callerName, dontPublish);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowAdminActivityLocalized(object moduleLocalizer, string messageKey, string? callerName = null,
|
||||||
|
bool dontPublish = false, params object[] messageArgs)
|
||||||
|
{
|
||||||
|
if (moduleLocalizer is not IStringLocalizer localizer)
|
||||||
|
throw new InvalidOperationException("moduleLocalizer must be an IStringLocalizer instance");
|
||||||
|
|
||||||
|
Helper.ShowAdminActivityLocalized(localizer, messageKey, callerName, dontPublish, messageArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterMenuCategory(string categoryId, string categoryName, string permission = "@css/generic")
|
||||||
|
{
|
||||||
|
Menus.MenuManager.Instance.RegisterCategory(categoryId, categoryName, permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterMenu(string categoryId, string menuId, string menuName,
|
||||||
|
Func<CCSPlayerController, object> menuFactory, string? permission = null, string? commandName = null)
|
||||||
|
{
|
||||||
|
Menus.MenuManager.Instance.RegisterMenu(categoryId, menuId, menuName, BuilderFactory, permission, commandName);
|
||||||
|
return;
|
||||||
|
|
||||||
|
MenuBuilder BuilderFactory(CCSPlayerController player)
|
||||||
|
{
|
||||||
|
if (menuFactory(player) is not MenuBuilder menuBuilder)
|
||||||
|
throw new InvalidOperationException("Menu factory must return MenuBuilder");
|
||||||
|
|
||||||
|
// Dodaj automatyczną obsługę przycisku 'Wróć'
|
||||||
|
menuBuilder.WithBackAction(p =>
|
||||||
|
{
|
||||||
|
if (Menus.MenuManager.Instance.GetMenuCategories().TryGetValue(categoryId, out var category))
|
||||||
|
{
|
||||||
|
Menus.MenuManager.Instance.CreateCategoryMenuPublic(category, p).OpenMenu(p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Menus.MenuManager.Instance.OpenMainMenu(p);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return menuBuilder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void UnregisterMenu(string categoryId, string menuId)
|
||||||
|
{
|
||||||
|
Menus.MenuManager.Instance.UnregisterMenu(categoryId, menuId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object CreateMenuWithBack(string title, string categoryId, CCSPlayerController player)
|
||||||
|
{
|
||||||
|
var builder = new MenuBuilder(title);
|
||||||
|
builder.WithBackAction(p =>
|
||||||
|
{
|
||||||
|
if (Menus.MenuManager.Instance.GetMenuCategories().TryGetValue(categoryId, out var category))
|
||||||
|
{
|
||||||
|
Menus.MenuManager.Instance.CreateCategoryMenuPublic(category, p).OpenMenu(p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Menus.MenuManager.Instance.OpenMainMenu(p);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CCSPlayerController> GetValidPlayers()
|
||||||
|
{
|
||||||
|
return Helper.GetValidPlayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public object CreateMenuWithPlayers(string title, string categoryId, CCSPlayerController admin,
|
||||||
|
Func<CCSPlayerController, bool> filter, Action<CCSPlayerController, CCSPlayerController> onSelect)
|
||||||
|
{
|
||||||
|
var menu = (MenuBuilder)CreateMenuWithBack(title, categoryId, admin);
|
||||||
|
var players = Helper.GetValidPlayers().Where(filter);
|
||||||
|
|
||||||
|
foreach (var player in players)
|
||||||
|
{
|
||||||
|
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
||||||
|
menu.AddOption(playerName, _ =>
|
||||||
|
{
|
||||||
|
if (player.IsValid)
|
||||||
|
{
|
||||||
|
onSelect(admin, player);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddMenuOption(object menu, string name, Action<CCSPlayerController> action, bool disabled = false,
|
||||||
|
string? permission = null)
|
||||||
|
{
|
||||||
|
if (menu is not MenuBuilder menuBuilder)
|
||||||
|
throw new InvalidOperationException("Menu must be a MenuBuilder instance");
|
||||||
|
|
||||||
|
menuBuilder.AddOption(name, action, disabled, permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddSubMenu(object menu, string name, Func<CCSPlayerController, object> subMenuFactory,
|
||||||
|
bool disabled = false, string? permission = null)
|
||||||
|
{
|
||||||
|
if (menu is not MenuBuilder menuBuilder)
|
||||||
|
throw new InvalidOperationException("Menu must be a MenuBuilder instance");
|
||||||
|
|
||||||
|
menuBuilder.AddSubMenu(name, player =>
|
||||||
|
{
|
||||||
|
var subMenu = subMenuFactory(player);
|
||||||
|
if (subMenu is not MenuBuilder builder)
|
||||||
|
throw new InvalidOperationException("SubMenu factory must return MenuBuilder");
|
||||||
|
return builder;
|
||||||
|
}, disabled, permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OpenMenu(object menu, CCSPlayerController player)
|
||||||
|
{
|
||||||
|
if (menu is not MenuBuilder menuBuilder)
|
||||||
|
throw new InvalidOperationException("Menu must be a MenuBuilder instance");
|
||||||
|
|
||||||
|
menuBuilder.OpenMenu(player);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,17 +1,20 @@
|
|||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API;
|
||||||
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Core.Attributes;
|
using CounterStrikeSharp.API.Core.Attributes;
|
||||||
using CounterStrikeSharp.API.Core.Capabilities;
|
using CounterStrikeSharp.API.Core.Capabilities;
|
||||||
using CounterStrikeSharp.API.Modules.Commands;
|
using CounterStrikeSharp.API.Modules.Commands;
|
||||||
using CounterStrikeSharp.API.Modules.Commands.Targeting;
|
using CounterStrikeSharp.API.Modules.Commands.Targeting;
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||||
|
using CS2_SimpleAdmin.Database;
|
||||||
using CS2_SimpleAdmin.Managers;
|
using CS2_SimpleAdmin.Managers;
|
||||||
|
using CS2_SimpleAdmin.Menus;
|
||||||
using CS2_SimpleAdminApi;
|
using CS2_SimpleAdminApi;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using MySqlConnector;
|
using MySqlConnector;
|
||||||
|
|
||||||
namespace CS2_SimpleAdmin;
|
namespace CS2_SimpleAdmin;
|
||||||
|
|
||||||
[MinimumApiVersion(286)]
|
[MinimumApiVersion(300)]
|
||||||
public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdminConfig>
|
public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdminConfig>
|
||||||
{
|
{
|
||||||
internal static CS2_SimpleAdmin Instance { get; private set; } = new();
|
internal static CS2_SimpleAdmin Instance { get; private set; } = new();
|
||||||
@@ -19,45 +22,58 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||||||
public override string ModuleName => "CS2-SimpleAdmin" + (Helper.IsDebugBuild ? " (DEBUG)" : " (RELEASE)");
|
public override string ModuleName => "CS2-SimpleAdmin" + (Helper.IsDebugBuild ? " (DEBUG)" : " (RELEASE)");
|
||||||
public override string ModuleDescription => "Simple admin plugin for Counter-Strike 2 :)";
|
public override string ModuleDescription => "Simple admin plugin for Counter-Strike 2 :)";
|
||||||
public override string ModuleAuthor => "daffyy & Dliix66";
|
public override string ModuleAuthor => "daffyy & Dliix66";
|
||||||
public override string ModuleVersion => "1.6.9c";
|
public override string ModuleVersion => "1.7.8-beta-2";
|
||||||
|
|
||||||
public override void Load(bool hotReload)
|
public override void Load(bool hotReload)
|
||||||
{
|
{
|
||||||
Instance = this;
|
Instance = this;
|
||||||
|
|
||||||
RegisterEvents();
|
|
||||||
|
|
||||||
if (hotReload)
|
if (hotReload)
|
||||||
{
|
{
|
||||||
ServerLoaded = false;
|
ServerLoaded = false;
|
||||||
_serverLoading = false;
|
_serverLoading = false;
|
||||||
OnGameServerSteamAPIActivated();
|
|
||||||
|
CacheManager?.Dispose();
|
||||||
|
CacheManager = new CacheManager();
|
||||||
|
|
||||||
|
// OnGameServerSteamAPIActivated();
|
||||||
OnMapStart(string.Empty);
|
OnMapStart(string.Empty);
|
||||||
|
|
||||||
AddTimer(2.0f, () =>
|
AddTimer(6.0f, () =>
|
||||||
{
|
{
|
||||||
if (Database == null) return;
|
if (DatabaseProvider == null) return;
|
||||||
|
|
||||||
var playerManager = new PlayerManager();
|
PlayersInfo.Clear();
|
||||||
|
CachedPlayers.Clear();
|
||||||
Helper.GetValidPlayers().ForEach(player =>
|
BotPlayers.Clear();
|
||||||
|
|
||||||
|
foreach (var player in Utilities.GetPlayers().Where(p => p.IsValid && !p.IsHLTV).ToArray())
|
||||||
{
|
{
|
||||||
playerManager.LoadPlayerData(player);
|
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"));
|
_cBasePlayerControllerSetPawnFunc = new MemoryFunctionVoid<CBasePlayerController, CCSPlayerPawn, bool, bool>(GameData.GetSignature("CBasePlayerController_SetPawn"));
|
||||||
|
|
||||||
SimpleAdminApi = new Api.CS2_SimpleAdminApi();
|
SimpleAdminApi = new Api.CS2_SimpleAdminApi();
|
||||||
Capabilities.RegisterPluginCapability(ICS2_SimpleAdminApi.PluginCapability, () => SimpleAdminApi);
|
Capabilities.RegisterPluginCapability(ICS2_SimpleAdminApi.PluginCapability, () => SimpleAdminApi);
|
||||||
|
|
||||||
new PlayerManager().CheckPlayersTimer();
|
PlayersTimer?.Kill();
|
||||||
|
PlayersTimer = null;
|
||||||
|
PlayerManager.CheckPlayersTimer();
|
||||||
|
|
||||||
|
Menus.MenuManager.Instance.InitializeDefaultCategories();
|
||||||
|
BasicMenu.Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnAllPluginsLoaded(bool hotReload)
|
public override void OnAllPluginsLoaded(bool hotReload)
|
||||||
{
|
{
|
||||||
AddTimer(3.0f, () => ReloadAdmins(null));
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
MenuApi = MenuCapability.Get();
|
MenuApi = MenuCapability.Get();
|
||||||
@@ -65,50 +81,141 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogError("Unable to load required plugins ... \n{exception}", ex.Message);
|
Logger.LogError("Unable to load required plugins ... \n{exception}", ex.Message);
|
||||||
|
Unload(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
AddTimer(6.0f, () => ReloadAdmins(null));
|
||||||
|
RegisterEvents();
|
||||||
|
AddTimer(0.5f, 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"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterCommands.InitializeCommands();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnConfigParsed(CS2_SimpleAdminConfig config)
|
public void OnConfigParsed(CS2_SimpleAdminConfig config)
|
||||||
{
|
{
|
||||||
Instance = this;
|
if (System.Diagnostics.Debugger.IsAttached)
|
||||||
_logger = Logger;
|
Environment.FailFast(":(!");
|
||||||
|
|
||||||
if (config.DatabaseHost.Length < 1 || config.DatabaseName.Length < 1 || config.DatabaseUser.Length < 1)
|
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)
|
||||||
{
|
{
|
||||||
throw new Exception("[CS2-SimpleAdmin] You need to setup Database credentials in config!");
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MySqlConnectionStringBuilder builder = new()
|
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))
|
||||||
{
|
{
|
||||||
Server = config.DatabaseHost,
|
throw new Exception("[CS2-SimpleAdmin] You need to setup MySQL credentials in config!");
|
||||||
Database = config.DatabaseName,
|
}
|
||||||
UserID = config.DatabaseUser,
|
|
||||||
Password = config.DatabasePassword,
|
var builder = new MySqlConnectionStringBuilder()
|
||||||
Port = (uint)config.DatabasePort,
|
{
|
||||||
|
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,
|
Pooling = true,
|
||||||
MinimumPoolSize = 0,
|
|
||||||
MaximumPoolSize = 640,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DbConnectionString = builder.ConnectionString;
|
DbConnectionString = builder.ConnectionString;
|
||||||
Database = new Database.Database(DbConnectionString);
|
DatabaseProvider = new MySqlDatabaseProvider(DbConnectionString);
|
||||||
|
}
|
||||||
if (!Database.CheckDatabaseConnection(out var exception))
|
else
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(config.DatabaseConfig.SqliteFilePath))
|
||||||
{
|
{
|
||||||
if (exception != null)
|
throw new Exception("[CS2-SimpleAdmin] You need to specify SQLite file path in config!");
|
||||||
Logger.LogError("Problem with database connection! \n{exception}", exception);
|
|
||||||
|
|
||||||
Unload(false);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Task.Run(() => Database.DatabaseMigration());
|
|
||||||
|
|
||||||
Config = config;
|
DatabaseProvider = new SqliteDatabaseProvider(ModuleDirectory + "/" + config.DatabaseConfig.SqliteFilePath);
|
||||||
Helper.UpdateConfig(config);
|
}
|
||||||
|
|
||||||
|
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"))
|
if (!Directory.Exists(ModuleDirectory + "/data"))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(ModuleDirectory + "/data");
|
Directory.CreateDirectory(ModuleDirectory + "/data");
|
||||||
@@ -119,33 +226,46 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
|||||||
if (!string.IsNullOrEmpty(Config.Discord.DiscordLogWebhook))
|
if (!string.IsNullOrEmpty(Config.Discord.DiscordLogWebhook))
|
||||||
DiscordWebhookClientLog = new DiscordManager(Config.Discord.DiscordLogWebhook);
|
DiscordWebhookClientLog = new DiscordManager(Config.Discord.DiscordLogWebhook);
|
||||||
|
|
||||||
PluginInfo.ShowAd(ModuleVersion);
|
|
||||||
if (Config.EnableUpdateCheck)
|
if (Config.EnableUpdateCheck)
|
||||||
Task.Run(async () => await PluginInfo.CheckVersion(ModuleVersion, _logger));
|
Task.Run(async () => await PluginInfo.CheckVersion(ModuleVersion, Logger));
|
||||||
|
|
||||||
PermissionManager = new PermissionManager(Database);
|
PermissionManager = new PermissionManager(DatabaseProvider);
|
||||||
BanManager = new BanManager(Database);
|
BanManager = new BanManager(DatabaseProvider);
|
||||||
MuteManager = new MuteManager(Database);
|
MuteManager = new MuteManager(DatabaseProvider);
|
||||||
WarnManager = new WarnManager(Database);
|
WarnManager = new WarnManager(DatabaseProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TargetResult? GetTarget(CommandInfo command)
|
internal static TargetResult? GetTarget(CommandInfo command, int argument = 1)
|
||||||
{
|
{
|
||||||
var matches = command.GetArgTargetResult(1);
|
var matches = command.GetArgTargetResult(argument);
|
||||||
|
|
||||||
if (!matches.Any())
|
if (!matches.Any())
|
||||||
{
|
{
|
||||||
command.ReplyToCommand($"Target {command.GetArg(1)} not found.");
|
command.ReplyToCommand($"Target {command.GetArg(argument)} not found.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.GetArg(1).StartsWith('@'))
|
if (command.GetArg(argument).StartsWith('@'))
|
||||||
return matches;
|
return matches;
|
||||||
|
|
||||||
if (matches.Count() == 1)
|
if (matches.Count() == 1)
|
||||||
return matches;
|
return matches;
|
||||||
|
|
||||||
command.ReplyToCommand($"Multiple targets found for \"{command.GetArg(1)}\".");
|
command.ReplyToCommand($"Multiple targets found for \"{command.GetArg(argument)}\".");
|
||||||
return null;
|
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}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -7,25 +7,132 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.287" />
|
<DebugType>none</DebugType>
|
||||||
<PackageReference Include="Dapper" Version="2.1.35" />
|
<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="MySqlConnector" Version="2.4.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="*" />
|
<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>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CS2-SimpleAdminApi\CS2-SimpleAdminApi.csproj" />
|
<ProjectReference Include="..\CS2-SimpleAdminApi\CS2-SimpleAdminApi.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
<ItemGroup>
|
<None Update="lang\**\*.*" CopyToOutputDirectory="PreserveNewest" />
|
||||||
<None Update="lang\**\*.*" CopyToOutputDirectory="PreserveNewest" />
|
<None Update="Database\Migrations\Mysql\001_CreateTables.sql">
|
||||||
<None Update="Database\Migrations\010_CreateWarnsTable.sql">
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
</None>
|
||||||
</None>
|
<None Update="Database\Migrations\Mysql\002_CreateFlagsTable.sql">
|
||||||
</ItemGroup>
|
<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\Mysql\016_OptimizeTablesAndIndexes.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>
|
||||||
|
<None Update="Database\Migrations\Sqlite\016_OptimizeTablesAndIndexes.sql">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="Database\Migrations\*.sql" CopyToOutputDirectory="PreserveNewest" />
|
<None Update="Database\Migrations\*.sql" CopyToOutputDirectory="PreserveNewest" />
|
||||||
@@ -38,7 +145,8 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="MenuManagerApi">
|
<Reference Include="MenuManagerApi">
|
||||||
<HintPath>3rd_party\MenuManagerApi.dll</HintPath>
|
<HintPath>3rd_party\MenuManagerApi.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,86 +1,86 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Text.Json;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
|
using CounterStrikeSharp.API.Core.Commands;
|
||||||
using CounterStrikeSharp.API.Modules.Commands;
|
using CounterStrikeSharp.API.Modules.Commands;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace CS2_SimpleAdmin;
|
namespace CS2_SimpleAdmin;
|
||||||
|
|
||||||
public static class RegisterCommands
|
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 delegate void CommandCallback(CCSPlayerController? caller, CommandInfo.CommandCallback callback);
|
||||||
|
|
||||||
private static readonly string CommandsPath = Path.Combine(CS2_SimpleAdmin.ConfigDirectory, "Commands.json");
|
private static readonly string CommandsPath = Path.Combine(CS2_SimpleAdmin.ConfigDirectory, "Commands.json");
|
||||||
private static readonly List<CommandMapping> CommandMappings =
|
private static readonly List<CommandMapping> CommandMappings =
|
||||||
[
|
[
|
||||||
new CommandMapping("css_ban", CS2_SimpleAdmin.Instance.OnBanCommand),
|
new("css_ban", CS2_SimpleAdmin.Instance.OnBanCommand),
|
||||||
new CommandMapping("css_addban", CS2_SimpleAdmin.Instance.OnAddBanCommand),
|
new("css_addban", CS2_SimpleAdmin.Instance.OnAddBanCommand),
|
||||||
new CommandMapping("css_banip", CS2_SimpleAdmin.Instance.OnBanIpCommand),
|
new("css_banip", CS2_SimpleAdmin.Instance.OnBanIpCommand),
|
||||||
new CommandMapping("css_unban", CS2_SimpleAdmin.Instance.OnUnbanCommand),
|
new("css_unban", CS2_SimpleAdmin.Instance.OnUnbanCommand),
|
||||||
new CommandMapping("css_warn", CS2_SimpleAdmin.Instance.OnWarnCommand),
|
new("css_warn", CS2_SimpleAdmin.Instance.OnWarnCommand),
|
||||||
new CommandMapping("css_unwarn", CS2_SimpleAdmin.Instance.OnUnwarnCommand),
|
new("css_unwarn", CS2_SimpleAdmin.Instance.OnUnwarnCommand),
|
||||||
|
|
||||||
new CommandMapping("css_asay", CS2_SimpleAdmin.Instance.OnAdminToAdminSayCommand),
|
new("css_asay", CS2_SimpleAdmin.Instance.OnAdminToAdminSayCommand),
|
||||||
new CommandMapping("css_cssay", CS2_SimpleAdmin.Instance.OnAdminCustomSayCommand),
|
new("css_cssay", CS2_SimpleAdmin.Instance.OnAdminCustomSayCommand),
|
||||||
new CommandMapping("css_say", CS2_SimpleAdmin.Instance.OnAdminSayCommand),
|
new("css_say", CS2_SimpleAdmin.Instance.OnAdminSayCommand),
|
||||||
new CommandMapping("css_psay", CS2_SimpleAdmin.Instance.OnAdminPrivateSayCommand),
|
new("css_psay", CS2_SimpleAdmin.Instance.OnAdminPrivateSayCommand),
|
||||||
new CommandMapping("css_csay", CS2_SimpleAdmin.Instance.OnAdminCenterSayCommand),
|
new("css_csay", CS2_SimpleAdmin.Instance.OnAdminCenterSayCommand),
|
||||||
new CommandMapping("css_hsay", CS2_SimpleAdmin.Instance.OnAdminHudSayCommand),
|
new("css_hsay", CS2_SimpleAdmin.Instance.OnAdminHudSayCommand),
|
||||||
|
|
||||||
new CommandMapping("css_penalties", CS2_SimpleAdmin.Instance.OnPenaltiesCommand),
|
new("css_penalties", CS2_SimpleAdmin.Instance.OnPenaltiesCommand),
|
||||||
new CommandMapping("css_admin", CS2_SimpleAdmin.Instance.OnAdminCommand),
|
new("css_admin", CS2_SimpleAdmin.Instance.OnAdminCommand),
|
||||||
new CommandMapping("css_adminhelp", CS2_SimpleAdmin.Instance.OnAdminHelpCommand),
|
new("css_adminhelp", CS2_SimpleAdmin.Instance.OnAdminHelpCommand),
|
||||||
new CommandMapping("css_addadmin", CS2_SimpleAdmin.Instance.OnAddAdminCommand),
|
new("css_addadmin", CS2_SimpleAdmin.Instance.OnAddAdminCommand),
|
||||||
new CommandMapping("css_deladmin", CS2_SimpleAdmin.Instance.OnDelAdminCommand),
|
new("css_deladmin", CS2_SimpleAdmin.Instance.OnDelAdminCommand),
|
||||||
new CommandMapping("css_addgroup", CS2_SimpleAdmin.Instance.OnAddGroup),
|
new("css_addgroup", CS2_SimpleAdmin.Instance.OnAddGroup),
|
||||||
new CommandMapping("css_delgroup", CS2_SimpleAdmin.Instance.OnDelGroupCommand),
|
new("css_delgroup", CS2_SimpleAdmin.Instance.OnDelGroupCommand),
|
||||||
new CommandMapping("css_reloadadmins", CS2_SimpleAdmin.Instance.OnRelAdminCommand),
|
new("css_reloadadmins", CS2_SimpleAdmin.Instance.OnRelAdminCommand),
|
||||||
new CommandMapping("css_hide", CS2_SimpleAdmin.Instance.OnHideCommand),
|
new("css_reloadbans", CS2_SimpleAdmin.Instance.OnRelBans),
|
||||||
new CommandMapping("css_hidecomms", CS2_SimpleAdmin.Instance.OnHideCommsCommand),
|
new("css_hide", CS2_SimpleAdmin.Instance.OnHideCommand),
|
||||||
new CommandMapping("css_who", CS2_SimpleAdmin.Instance.OnWhoCommand),
|
new("css_hidecomms", CS2_SimpleAdmin.Instance.OnHideCommsCommand),
|
||||||
new CommandMapping("css_disconnected", CS2_SimpleAdmin.Instance.OnDisconnectedCommand),
|
new("css_who", CS2_SimpleAdmin.Instance.OnWhoCommand),
|
||||||
new CommandMapping("css_warns", CS2_SimpleAdmin.Instance.OnWarnsCommand),
|
new("css_disconnected", CS2_SimpleAdmin.Instance.OnDisconnectedCommand),
|
||||||
new CommandMapping("css_players", CS2_SimpleAdmin.Instance.OnPlayersCommand),
|
new("css_warns", CS2_SimpleAdmin.Instance.OnWarnsCommand),
|
||||||
new CommandMapping("css_kick", CS2_SimpleAdmin.Instance.OnKickCommand),
|
new("css_players", CS2_SimpleAdmin.Instance.OnPlayersCommand),
|
||||||
new CommandMapping("css_map", CS2_SimpleAdmin.Instance.OnMapCommand),
|
new("css_kick", CS2_SimpleAdmin.Instance.OnKickCommand),
|
||||||
new CommandMapping("css_wsmap", CS2_SimpleAdmin.Instance.OnWorkshopMapCommand),
|
new("css_map", CS2_SimpleAdmin.Instance.OnMapCommand),
|
||||||
new CommandMapping("css_cvar", CS2_SimpleAdmin.Instance.OnCvarCommand),
|
new("css_wsmap", CS2_SimpleAdmin.Instance.OnWorkshopMapCommand),
|
||||||
new CommandMapping("css_rcon", CS2_SimpleAdmin.Instance.OnRconCommand),
|
new("css_cvar", CS2_SimpleAdmin.Instance.OnCvarCommand),
|
||||||
new CommandMapping("css_rr", CS2_SimpleAdmin.Instance.OnRestartCommand),
|
new("css_rcon", CS2_SimpleAdmin.Instance.OnRconCommand),
|
||||||
|
new("css_rr", CS2_SimpleAdmin.Instance.OnRestartCommand),
|
||||||
|
|
||||||
new CommandMapping("css_gag", CS2_SimpleAdmin.Instance.OnGagCommand),
|
new("css_gag", CS2_SimpleAdmin.Instance.OnGagCommand),
|
||||||
new CommandMapping("css_addgag", CS2_SimpleAdmin.Instance.OnAddGagCommand),
|
new("css_addgag", CS2_SimpleAdmin.Instance.OnAddGagCommand),
|
||||||
new CommandMapping("css_ungag", CS2_SimpleAdmin.Instance.OnUngagCommand),
|
new("css_ungag", CS2_SimpleAdmin.Instance.OnUngagCommand),
|
||||||
new CommandMapping("css_mute", CS2_SimpleAdmin.Instance.OnMuteCommand),
|
new("css_mute", CS2_SimpleAdmin.Instance.OnMuteCommand),
|
||||||
new CommandMapping("css_addmute", CS2_SimpleAdmin.Instance.OnAddMuteCommand),
|
new("css_addmute", CS2_SimpleAdmin.Instance.OnAddMuteCommand),
|
||||||
new CommandMapping("css_unmute", CS2_SimpleAdmin.Instance.OnUnmuteCommand),
|
new("css_unmute", CS2_SimpleAdmin.Instance.OnUnmuteCommand),
|
||||||
new CommandMapping("css_silence", CS2_SimpleAdmin.Instance.OnSilenceCommand),
|
new("css_silence", CS2_SimpleAdmin.Instance.OnSilenceCommand),
|
||||||
new CommandMapping("css_addsilence", CS2_SimpleAdmin.Instance.OnAddSilenceCommand),
|
new("css_addsilence", CS2_SimpleAdmin.Instance.OnAddSilenceCommand),
|
||||||
new CommandMapping("css_unsilence", CS2_SimpleAdmin.Instance.OnUnsilenceCommand),
|
new("css_unsilence", CS2_SimpleAdmin.Instance.OnUnsilenceCommand),
|
||||||
|
|
||||||
new CommandMapping("css_vote", CS2_SimpleAdmin.Instance.OnVoteCommand),
|
new("css_vote", CS2_SimpleAdmin.Instance.OnVoteCommand),
|
||||||
|
|
||||||
new CommandMapping("css_noclip", CS2_SimpleAdmin.Instance.OnNoclipCommand),
|
new("css_slay", CS2_SimpleAdmin.Instance.OnSlayCommand),
|
||||||
new CommandMapping("css_freeze", CS2_SimpleAdmin.Instance.OnFreezeCommand),
|
new("css_slap", CS2_SimpleAdmin.Instance.OnSlapCommand),
|
||||||
new CommandMapping("css_unfreeze", CS2_SimpleAdmin.Instance.OnUnfreezeCommand),
|
new("css_team", CS2_SimpleAdmin.Instance.OnTeamCommand),
|
||||||
new CommandMapping("css_godmode", CS2_SimpleAdmin.Instance.OnGodCommand),
|
new("css_rename", CS2_SimpleAdmin.Instance.OnRenameCommand),
|
||||||
|
new("css_prename", CS2_SimpleAdmin.Instance.OnPrenameCommand),
|
||||||
new CommandMapping("css_slay", CS2_SimpleAdmin.Instance.OnSlayCommand),
|
new("css_tp", CS2_SimpleAdmin.Instance.OnGotoCommand),
|
||||||
new CommandMapping("css_slap", CS2_SimpleAdmin.Instance.OnSlapCommand),
|
new("css_bring", CS2_SimpleAdmin.Instance.OnBringCommand),
|
||||||
new CommandMapping("css_give", CS2_SimpleAdmin.Instance.OnGiveCommand),
|
new("css_pluginsmanager", CS2_SimpleAdmin.Instance.OnPluginManagerCommand),
|
||||||
new CommandMapping("css_strip", CS2_SimpleAdmin.Instance.OnStripCommand),
|
new("css_adminvoice", CS2_SimpleAdmin.Instance.OnAdminVoiceCommand)
|
||||||
new CommandMapping("css_hp", CS2_SimpleAdmin.Instance.OnHpCommand),
|
|
||||||
new CommandMapping("css_speed", CS2_SimpleAdmin.Instance.OnSpeedCommand),
|
|
||||||
new CommandMapping("css_gravity", CS2_SimpleAdmin.Instance.OnGravityCommand),
|
|
||||||
new CommandMapping("css_money", CS2_SimpleAdmin.Instance.OnMoneyCommand),
|
|
||||||
new CommandMapping("css_team", CS2_SimpleAdmin.Instance.OnTeamCommand),
|
|
||||||
new CommandMapping("css_rename", CS2_SimpleAdmin.Instance.OnRenameCommand),
|
|
||||||
new CommandMapping("css_prename", CS2_SimpleAdmin.Instance.OnPrenameCommand),
|
|
||||||
new CommandMapping("css_respawn", CS2_SimpleAdmin.Instance.OnRespawnCommand),
|
|
||||||
new CommandMapping("css_tp", CS2_SimpleAdmin.Instance.OnGotoCommand),
|
|
||||||
new CommandMapping("css_bring", CS2_SimpleAdmin.Instance.OnBringCommand),
|
|
||||||
new CommandMapping("css_pluginsmanager", CS2_SimpleAdmin.Instance.OnPluginManagerCommand)
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/// <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()
|
public static void InitializeCommands()
|
||||||
{
|
{
|
||||||
if (!File.Exists(CommandsPath))
|
if (!File.Exists(CommandsPath))
|
||||||
@@ -94,6 +94,10 @@ public static class RegisterCommands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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()
|
private static void CreateConfig()
|
||||||
{
|
{
|
||||||
var commands = new CommandsConfig
|
var commands = new CommandsConfig
|
||||||
@@ -120,6 +124,7 @@ public static class RegisterCommands
|
|||||||
{ "css_addgroup", new Command { Aliases = ["css_addgroup"] } },
|
{ "css_addgroup", new Command { Aliases = ["css_addgroup"] } },
|
||||||
{ "css_delgroup", new Command { Aliases = ["css_delgroup"] } },
|
{ "css_delgroup", new Command { Aliases = ["css_delgroup"] } },
|
||||||
{ "css_reloadadmins", new Command { Aliases = ["css_reloadadmins"] } },
|
{ "css_reloadadmins", new Command { Aliases = ["css_reloadadmins"] } },
|
||||||
|
{ "css_reloadbans", new Command { Aliases = ["css_reloadbans"] } },
|
||||||
{ "css_hide", new Command { Aliases = ["css_hide", "css_stealth"] } },
|
{ "css_hide", new Command { Aliases = ["css_hide", "css_stealth"] } },
|
||||||
{ "css_hidecomms", new Command { Aliases = ["css_hidecomms"] } },
|
{ "css_hidecomms", new Command { Aliases = ["css_hidecomms"] } },
|
||||||
{ "css_who", new Command { Aliases = ["css_who"] } },
|
{ "css_who", new Command { Aliases = ["css_who"] } },
|
||||||
@@ -142,66 +147,88 @@ public static class RegisterCommands
|
|||||||
{ "css_addsilence", new Command { Aliases = ["css_addsilence"] } },
|
{ "css_addsilence", new Command { Aliases = ["css_addsilence"] } },
|
||||||
{ "css_unsilence", new Command { Aliases = ["css_unsilence"] } },
|
{ "css_unsilence", new Command { Aliases = ["css_unsilence"] } },
|
||||||
{ "css_vote", new Command { Aliases = ["css_vote"] } },
|
{ "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_slay", new Command { Aliases = ["css_slay"] } },
|
||||||
{ "css_slap", new Command { Aliases = ["css_slap"] } },
|
{ "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_money", new Command { Aliases = ["css_money"] } },
|
|
||||||
{ "css_team", new Command { Aliases = ["css_team"] } },
|
{ "css_team", new Command { Aliases = ["css_team"] } },
|
||||||
{ "css_rename", new Command { Aliases = ["css_rename"] } },
|
{ "css_rename", new Command { Aliases = ["css_rename"] } },
|
||||||
{ "css_prename", new Command { Aliases = ["css_prename"] } },
|
{ "css_prename", new Command { Aliases = ["css_prename"] } },
|
||||||
{ "css_respawn", new Command { Aliases = ["css_respawn"] } },
|
{ "css_resize", new Command { Aliases = ["css_resize", "css_size"] } },
|
||||||
{ "css_tp", new Command { Aliases = ["css_tp", "css_tpto", "css_goto"] } },
|
{ "css_tp", new Command { Aliases = ["css_tp", "css_tpto", "css_goto"] } },
|
||||||
{ "css_bring", new Command { Aliases = ["css_bring", "css_tphere"] } },
|
{ "css_bring", new Command { Aliases = ["css_bring", "css_tphere"] } },
|
||||||
{ "css_pluginsmanager", new Command { Aliases = ["css_pluginsmanager", "css_pluginmanager"] } }
|
{ "css_pluginsmanager", new Command { Aliases = ["css_pluginsmanager", "css_pluginmanager"] } },
|
||||||
|
{ "css_adminvoice", new Command { Aliases = ["css_adminvoice", "css_listenall"] } }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var json = JsonConvert.SerializeObject(commands, Formatting.Indented);
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
WriteIndented = true,
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||||
|
};
|
||||||
|
|
||||||
|
var json = JsonSerializer.Serialize(commands, options);
|
||||||
File.WriteAllText(CommandsPath, json);
|
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()
|
private static void Register()
|
||||||
{
|
{
|
||||||
var json = File.ReadAllText(CommandsPath);
|
var json = File.ReadAllText(CommandsPath);
|
||||||
var commandsConfig = JsonConvert.DeserializeObject<CommandsConfig>(json);
|
var commandsConfig = JsonSerializer.Deserialize<CommandsConfig>(json,
|
||||||
|
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||||
|
|
||||||
if (commandsConfig?.Commands == null) return;
|
if (commandsConfig?.Commands != null)
|
||||||
|
|
||||||
foreach (var command in commandsConfig.Commands)
|
|
||||||
{
|
{
|
||||||
if (command.Value.Aliases == null) continue;
|
foreach (var command in commandsConfig.Commands)
|
||||||
|
|
||||||
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);
|
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 _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
|
private class CommandsConfig
|
||||||
{
|
{
|
||||||
public Dictionary<string, Command>? Commands { get; init; }
|
public Dictionary<string, Command>? Commands { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a command definition containing a list of aliases.
|
||||||
|
/// </summary>
|
||||||
private class Command
|
private class Command
|
||||||
{
|
{
|
||||||
public string[]? Aliases { get; init; }
|
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)
|
private class CommandMapping(string commandKey, CommandInfo.CommandCallback callback)
|
||||||
{
|
{
|
||||||
public string CommandKey { get; } = commandKey;
|
public string CommandKey { get; } = commandKey;
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ namespace CS2_SimpleAdmin;
|
|||||||
|
|
||||||
public partial class 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")]
|
[RequiresPermissions("@css/ban")]
|
||||||
[CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[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)
|
public void OnBanCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
@@ -19,9 +24,7 @@ public partial class CS2_SimpleAdmin
|
|||||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
if (command.ArgCount < 2)
|
if (command.ArgCount < 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var reason = _localizer?["sa_unknown"] ?? "Unknown";
|
|
||||||
|
|
||||||
var targets = GetTarget(command);
|
var targets = GetTarget(command);
|
||||||
if (targets == null) return;
|
if (targets == null) return;
|
||||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, Connected: PlayerConnectedState.PlayerConnected, IsHLTV: false }).ToList();
|
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, Connected: PlayerConnectedState.PlayerConnected, IsHLTV: false }).ToList();
|
||||||
@@ -31,14 +34,18 @@ public partial class CS2_SimpleAdmin
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.ArgCount >= 3 && command.GetArg(3).Length > 0)
|
var reason = command.ArgCount >= 3
|
||||||
reason = command.GetArg(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 =>
|
playersToTarget.ForEach(player =>
|
||||||
{
|
{
|
||||||
if (!caller.CanTarget(player)) return;
|
if (!caller.CanTarget(player)) return;
|
||||||
|
|
||||||
if (!int.TryParse(command.GetArg(2), out var time) && caller != null && caller.IsValid && Config.OtherSettings.ShowBanMenuIfNoTime)
|
if (time < 0 && caller != null && caller.IsValid && Config.OtherSettings.ShowBanMenuIfNoTime)
|
||||||
{
|
{
|
||||||
DurationMenu.OpenMenu(caller, $"{_localizer?["sa_ban"] ?? "Ban"}: {player.PlayerName}", player,
|
DurationMenu.OpenMenu(caller, $"{_localizer?["sa_ban"] ?? "Ban"}: {player.PlayerName}", player,
|
||||||
ManagePlayersMenu.BanMenu);
|
ManagePlayersMenu.BanMenu);
|
||||||
@@ -49,37 +56,42 @@ public partial class CS2_SimpleAdmin
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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)
|
internal void Ban(CCSPlayerController? caller, CCSPlayerController player, int time, string reason, string? callerName = null, BanManager? banManager = null, CommandInfo? command = null, bool silent = false)
|
||||||
{
|
{
|
||||||
if (Database == null || !player.IsValid || !player.UserId.HasValue) return;
|
if (DatabaseProvider == null || !player.IsValid || !player.UserId.HasValue) return;
|
||||||
if (!caller.CanTarget(player)) return;
|
if (!caller.CanTarget(player)) return;
|
||||||
if (!CheckValidBan(caller, time)) return;
|
if (!CheckValidBan(caller, time)) return;
|
||||||
|
|
||||||
// Set default caller name if not provided
|
// Set default caller name if not provided
|
||||||
callerName ??= _localizer?["sa_console"] ?? "Console";
|
callerName = !string.IsNullOrEmpty(caller?.PlayerName)
|
||||||
|
? caller.PlayerName
|
||||||
// Freeze player pawn if alive
|
: (_localizer?["sa_console"] ?? "Console");
|
||||||
if (player.PawnIsAlive)
|
|
||||||
{
|
|
||||||
player.Pawn.Value?.Freeze();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get player and admin information
|
// Get player and admin information
|
||||||
var playerInfo = PlayersInfo[player.UserId.Value];
|
var playerInfo = PlayersInfo[player.SteamID];
|
||||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null;
|
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||||
|
|
||||||
// Asynchronously handle banning logic
|
// Asynchronously handle banning logic
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await BanManager.BanPlayer(playerInfo, adminInfo, reason, time);
|
int? penaltyId = await BanManager.BanPlayer(playerInfo, adminInfo, reason, time);
|
||||||
|
await Server.NextWorldUpdateAsync(() =>
|
||||||
|
{
|
||||||
|
SimpleAdminApi?.OnPlayerPenaltiedEvent(playerInfo, adminInfo, PenaltyType.Ban, reason, time, penaltyId);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update banned players list
|
|
||||||
if (playerInfo.IpAddress != null && !BannedPlayers.Contains(playerInfo.IpAddress))
|
|
||||||
BannedPlayers.Add(playerInfo.IpAddress);
|
|
||||||
if (!BannedPlayers.Contains(player.SteamID.ToString()))
|
|
||||||
BannedPlayers.Add(player.SteamID.ToString());
|
|
||||||
|
|
||||||
// Determine message keys and arguments based on ban time
|
// Determine message keys and arguments based on ban time
|
||||||
var (messageKey, activityMessageKey, centerArgs, adminActivityArgs) = time == 0
|
var (messageKey, activityMessageKey, centerArgs, adminActivityArgs) = time == 0
|
||||||
? ("sa_player_ban_message_perm", "sa_admin_ban_message_perm",
|
? ("sa_player_ban_message_perm", "sa_admin_ban_message_perm",
|
||||||
@@ -95,19 +107,13 @@ public partial class CS2_SimpleAdmin
|
|||||||
// Display admin activity message if necessary
|
// Display admin activity message if necessary
|
||||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
{
|
{
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schedule a kick timer
|
// Schedule a kick timer
|
||||||
if (player.UserId.HasValue)
|
if (player.UserId.HasValue)
|
||||||
{
|
{
|
||||||
AddTimer(Config.OtherSettings.KickTime, () =>
|
Helper.KickPlayer(player.UserId.Value, NetworkDisconnectionReason.NETWORK_DISCONNECT_KICKBANADDED, Config.OtherSettings.KickTime);
|
||||||
{
|
|
||||||
if (player is { IsValid: true, UserId: not null })
|
|
||||||
{
|
|
||||||
Helper.KickPlayer(player.UserId.Value, NetworkDisconnectionReason.NETWORK_DISCONNECT_KICKBANADDED);
|
|
||||||
}
|
|
||||||
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute ban command if necessary
|
// Execute ban command if necessary
|
||||||
@@ -125,14 +131,61 @@ public partial class CS2_SimpleAdmin
|
|||||||
}
|
}
|
||||||
|
|
||||||
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Ban, _localizer);
|
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Ban, _localizer);
|
||||||
SimpleAdminApi?.OnPlayerPenaltiedEvent(playerInfo, adminInfo, PenaltyType.Ban, reason, time);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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")]
|
[RequiresPermissions("@css/ban")]
|
||||||
[CommandHelper(minArgs: 1, usage: "<steamid> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[CommandHelper(minArgs: 1, usage: "<steamid> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
public void OnAddBanCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnAddBanCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (Database == null) return;
|
if (DatabaseProvider == null) return;
|
||||||
var callerName = caller?.PlayerName ?? _localizer?["sa_console"] ?? "Console";
|
var callerName = caller?.PlayerName ?? _localizer?["sa_console"] ?? "Console";
|
||||||
if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1))) return;
|
if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1))) return;
|
||||||
if (!Helper.ValidateSteamId(command.GetArg(1), out var steamId) || steamId == null)
|
if (!Helper.ValidateSteamId(command.GetArg(1), out var steamId) || steamId == null)
|
||||||
@@ -141,21 +194,21 @@ public partial class CS2_SimpleAdmin
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var steamid = steamId.SteamId64.ToString();
|
var steamid = steamId.SteamId64;
|
||||||
var reason = command.ArgCount >= 3 && !string.IsNullOrEmpty(command.GetArg(3))
|
var reason = command.ArgCount >= 3
|
||||||
? command.GetArg(3)
|
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||||
|
|
||||||
|
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||||
|
|
||||||
int.TryParse(command.GetArg(2), out var time);
|
var time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2)));
|
||||||
if (!CheckValidBan(caller, time)) return;
|
if (!CheckValidBan(caller, time)) return;
|
||||||
|
|
||||||
var adminInfo = caller != null && caller.UserId.HasValue
|
var adminInfo = caller != null && caller.UserId.HasValue
|
||||||
? PlayersInfo[caller.UserId.Value]
|
? PlayersInfo[caller.SteamID]
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
var matches = Helper.GetPlayerFromSteamid64(steamid);
|
var player = Helper.GetPlayerFromSteamid64(steamid);
|
||||||
var player = matches.Count == 1 ? matches.FirstOrDefault() : null;
|
|
||||||
|
|
||||||
if (player != null && player.IsValid)
|
if (player != null && player.IsValid)
|
||||||
{
|
{
|
||||||
if (!caller.CanTarget(player))
|
if (!caller.CanTarget(player))
|
||||||
@@ -166,13 +219,21 @@ public partial class CS2_SimpleAdmin
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (!caller.CanTarget(new SteamID(steamId.SteamId64)))
|
||||||
|
return;
|
||||||
|
|
||||||
// Asynchronous ban operation if player is not online or not found
|
// Asynchronous ban operation if player is not online or not found
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await BanManager.AddBanBySteamid(steamid, adminInfo, reason, time);
|
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, reason, time, PenaltyType.Ban, _localizer);
|
Helper.SendDiscordPenaltyMessage(caller, steamid.ToString(), reason, time, PenaltyType.Ban, _localizer);
|
||||||
|
|
||||||
command.ReplyToCommand($"Player with steamid {steamid} is not online. Ban has been added offline.");
|
command.ReplyToCommand($"Player with steamid {steamid} is not online. Ban has been added offline.");
|
||||||
}
|
}
|
||||||
@@ -181,15 +242,18 @@ public partial class CS2_SimpleAdmin
|
|||||||
|
|
||||||
if (UnlockedCommands)
|
if (UnlockedCommands)
|
||||||
Server.ExecuteCommand($"banid 1 {steamId.SteamId3}");
|
Server.ExecuteCommand($"banid 1 {steamId.SteamId3}");
|
||||||
|
|
||||||
SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamId, adminInfo, PenaltyType.Ban, reason, time);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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")]
|
[RequiresPermissions("@css/ban")]
|
||||||
[CommandHelper(minArgs: 1, usage: "<ip> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[CommandHelper(minArgs: 1, usage: "<ip> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
public void OnBanIpCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnBanIpCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (Database == null) return;
|
if (DatabaseProvider == null) return;
|
||||||
var callerName = caller?.PlayerName ?? _localizer?["sa_console"] ?? "Console";
|
var callerName = caller?.PlayerName ?? _localizer?["sa_console"] ?? "Console";
|
||||||
if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1))) return;
|
if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1))) return;
|
||||||
var ipAddress = command.GetArg(1);
|
var ipAddress = command.GetArg(1);
|
||||||
@@ -200,26 +264,31 @@ public partial class CS2_SimpleAdmin
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var reason = command.ArgCount >= 3 && !string.IsNullOrEmpty(command.GetArg(3))
|
var reason = command.ArgCount >= 3
|
||||||
? command.GetArg(3)
|
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||||
: _localizer?["sa_unknown"] ?? "Unknown";
|
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||||
|
|
||||||
|
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||||
|
|
||||||
|
var time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2)));
|
||||||
|
|
||||||
int.TryParse(command.GetArg(2), out var time);
|
|
||||||
if (!CheckValidBan(caller, time)) return;
|
if (!CheckValidBan(caller, time)) return;
|
||||||
|
|
||||||
var adminInfo = caller != null && caller.UserId.HasValue
|
var adminInfo = caller != null && caller.UserId.HasValue
|
||||||
? PlayersInfo[caller.UserId.Value]
|
? PlayersInfo[caller.SteamID]
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
var matches = Helper.GetPlayerFromIp(ipAddress);
|
var players = Helper.GetPlayerFromIp(ipAddress);
|
||||||
var player = matches.Count == 1 ? matches.FirstOrDefault() : null;
|
if (players.Count >= 1)
|
||||||
|
|
||||||
if (player != null && player.IsValid)
|
|
||||||
{
|
{
|
||||||
if (!caller.CanTarget(player))
|
foreach (var player in players)
|
||||||
return;
|
{
|
||||||
|
if (player == null || !player.IsValid) continue;
|
||||||
|
if (!caller.CanTarget(player))
|
||||||
|
return;
|
||||||
|
|
||||||
Ban(caller, player, time, reason, callerName, silent: true);
|
Ban(caller, player, time, reason, callerName, silent: true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -235,13 +304,19 @@ public partial class CS2_SimpleAdmin
|
|||||||
Helper.LogCommand(caller, command);
|
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)
|
private bool CheckValidBan(CCSPlayerController? caller, int duration)
|
||||||
{
|
{
|
||||||
if (caller == null) return true;
|
if (caller == null) return true;
|
||||||
|
|
||||||
var canPermBan = AdminManager.PlayerHasPermissions(caller, "@css/permban");
|
var canPermBan = AdminManager.PlayerHasPermissions(new SteamID(caller.SteamID), "@css/permban");
|
||||||
|
|
||||||
if (duration <= 0 && canPermBan == false)
|
if (duration <= 0 && !canPermBan)
|
||||||
{
|
{
|
||||||
caller.PrintToChat($"{_localizer!["sa_prefix"]} {_localizer["sa_ban_perm_restricted"]}");
|
caller.PrintToChat($"{_localizer!["sa_prefix"]} {_localizer["sa_ban_perm_restricted"]}");
|
||||||
return false;
|
return false;
|
||||||
@@ -253,14 +328,17 @@ public partial class CS2_SimpleAdmin
|
|||||||
return false;
|
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")]
|
[RequiresPermissions("@css/unban")]
|
||||||
[CommandHelper(minArgs: 1, usage: "<steamid or name or ip> [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[CommandHelper(minArgs: 1, usage: "<steamid or name or ip> [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
public void OnUnbanCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnUnbanCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (Database == null) return;
|
if (DatabaseProvider == null) return;
|
||||||
|
|
||||||
var callerSteamId = caller?.SteamID.ToString() ?? _localizer?["sa_console"] ?? "Console";
|
var callerSteamId = caller?.SteamID.ToString() ?? _localizer?["sa_console"] ?? "Console";
|
||||||
|
|
||||||
if (command.GetArg(1).Length <= 1)
|
if (command.GetArg(1).Length <= 1)
|
||||||
{
|
{
|
||||||
command.ReplyToCommand($"Too short pattern to search.");
|
command.ReplyToCommand($"Too short pattern to search.");
|
||||||
@@ -268,27 +346,31 @@ public partial class CS2_SimpleAdmin
|
|||||||
}
|
}
|
||||||
|
|
||||||
var pattern = command.GetArg(1);
|
var pattern = command.GetArg(1);
|
||||||
var reason = command.GetArg(2);
|
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));
|
Task.Run(async () => await BanManager.UnbanPlayer(pattern, callerSteamId, reason));
|
||||||
|
|
||||||
Helper.LogCommand(caller, command);
|
Helper.LogCommand(caller, command);
|
||||||
|
|
||||||
command.ReplyToCommand($"Unbanned player with pattern {pattern}.");
|
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")]
|
[RequiresPermissions("@css/kick")]
|
||||||
[CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[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)
|
public void OnWarnCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (Database == null)
|
if (DatabaseProvider == null)
|
||||||
return;
|
return;
|
||||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
if (command.ArgCount < 2)
|
if (command.ArgCount < 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var reason = _localizer?["sa_unknown"] ?? "Unknown";
|
|
||||||
|
|
||||||
var targets = GetTarget(command);
|
var targets = GetTarget(command);
|
||||||
if (targets == null) return;
|
if (targets == null) return;
|
||||||
var playersToTarget = targets.Players.Where(player => player.IsValid && player.Connected == PlayerConnectedState.PlayerConnected && !player.IsHLTV).ToList();
|
var playersToTarget = targets.Players.Where(player => player.IsValid && player.Connected == PlayerConnectedState.PlayerConnected && !player.IsHLTV).ToList();
|
||||||
@@ -298,49 +380,65 @@ public partial class CS2_SimpleAdmin
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WarnManager warnManager = new(Database);
|
var time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2)));
|
||||||
|
var reason = command.ArgCount >= 3
|
||||||
int.TryParse(command.GetArg(2), out var time);
|
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||||
|
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||||
if (command.ArgCount >= 3 && command.GetArg(3).Length > 0)
|
|
||||||
reason = command.GetArg(3);
|
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
playersToTarget.ForEach(player =>
|
||||||
{
|
{
|
||||||
if (caller!.CanTarget(player))
|
if (caller!.CanTarget(player))
|
||||||
{
|
{
|
||||||
Warn(caller, player, time, reason, callerName, warnManager, command);
|
Warn(caller, player, time, reason, callerName, command);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Warn(CCSPlayerController? caller, CCSPlayerController player, int time, string reason, string? callerName = null, WarnManager? warnManager = null, CommandInfo? command = null)
|
/// <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 (Database == null || !player.IsValid || !player.UserId.HasValue) return;
|
if (DatabaseProvider == null || !player.IsValid || !player.UserId.HasValue) return;
|
||||||
if (!caller.CanTarget(player)) return;
|
if (!caller.CanTarget(player)) return;
|
||||||
if (!CheckValidBan(caller, time)) return;
|
if (!CheckValidBan(caller, time)) return;
|
||||||
|
|
||||||
// Set default caller name if not provided
|
// Set default caller name if not provided
|
||||||
callerName ??= _localizer?["sa_console"] ?? "Console";
|
callerName = !string.IsNullOrEmpty(caller?.PlayerName)
|
||||||
|
? caller.PlayerName
|
||||||
|
: (_localizer?["sa_console"] ?? "Console");
|
||||||
|
|
||||||
// Freeze player pawn if alive
|
// Freeze player pawn if alive
|
||||||
if (player.PawnIsAlive)
|
if (player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE)
|
||||||
{
|
{
|
||||||
player.Pawn.Value?.Freeze();
|
player.PlayerPawn?.Value?.Freeze();
|
||||||
|
AddTimer(5.0f, () => player.PlayerPawn?.Value?.Unfreeze(), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get player and admin information
|
// Get player and admin information
|
||||||
var playerInfo = PlayersInfo[player.UserId.Value];
|
var playerInfo = PlayersInfo[player.SteamID];
|
||||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null;
|
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||||
|
|
||||||
// Asynchronously handle warning logic
|
// Asynchronously handle warning logic
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
warnManager ??= new WarnManager(Database);
|
int? penaltyId = await WarnManager.WarnPlayer(playerInfo, adminInfo, reason, time);
|
||||||
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
|
// Check for warn thresholds and execute punish command if applicable
|
||||||
var totalWarns = await warnManager.GetPlayerWarnsCount(player.SteamID.ToString());
|
var totalWarns = await WarnManager.GetPlayerWarnsCount(player.SteamID);
|
||||||
if (Config.WarnThreshold.Count > 0)
|
if (Config.WarnThreshold.Count > 0)
|
||||||
{
|
{
|
||||||
string? punishCommand = null;
|
string? punishCommand = null;
|
||||||
@@ -353,7 +451,7 @@ public partial class CS2_SimpleAdmin
|
|||||||
|
|
||||||
if (!string.IsNullOrEmpty(punishCommand))
|
if (!string.IsNullOrEmpty(punishCommand))
|
||||||
{
|
{
|
||||||
await Server.NextFrameAsync(() =>
|
await Server.NextWorldUpdateAsync(() =>
|
||||||
{
|
{
|
||||||
Server.ExecuteCommand(punishCommand.Replace("USERID", playerInfo.UserId.ToString()).Replace("STEAMID64", playerInfo.SteamId?.ToString()));
|
Server.ExecuteCommand(punishCommand.Replace("USERID", playerInfo.UserId.ToString()).Replace("STEAMID64", playerInfo.SteamId?.ToString()));
|
||||||
});
|
});
|
||||||
@@ -376,7 +474,7 @@ public partial class CS2_SimpleAdmin
|
|||||||
// Display admin activity message if necessary
|
// Display admin activity message if necessary
|
||||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
{
|
{
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log the warning command
|
// Log the warning command
|
||||||
@@ -387,14 +485,86 @@ public partial class CS2_SimpleAdmin
|
|||||||
|
|
||||||
// Send Discord notification for the warning
|
// Send Discord notification for the warning
|
||||||
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Warn, _localizer);
|
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Warn, _localizer);
|
||||||
SimpleAdminApi?.OnPlayerPenaltiedEvent(playerInfo, adminInfo, PenaltyType.Warn, reason, time);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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")]
|
[RequiresPermissions("@css/kick")]
|
||||||
[CommandHelper(minArgs: 1, usage: "<steamid or name or ip>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[CommandHelper(minArgs: 1, usage: "<steamid or name or ip>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
public void OnUnwarnCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnUnwarnCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (Database == null) return;
|
if (DatabaseProvider == null) return;
|
||||||
|
|
||||||
if (command.GetArg(1).Length <= 1)
|
if (command.GetArg(1).Length <= 1)
|
||||||
{
|
{
|
||||||
@@ -403,9 +573,7 @@ public partial class CS2_SimpleAdmin
|
|||||||
}
|
}
|
||||||
|
|
||||||
var pattern = command.GetArg(1);
|
var pattern = command.GetArg(1);
|
||||||
|
|
||||||
Task.Run(async () => await WarnManager.UnwarnPlayer(pattern));
|
Task.Run(async () => await WarnManager.UnwarnPlayer(pattern));
|
||||||
|
|
||||||
Helper.LogCommand(caller, command);
|
Helper.LogCommand(caller, command);
|
||||||
command.ReplyToCommand($"Unwarned player with pattern {pattern}.");
|
command.ReplyToCommand($"Unwarned player with pattern {pattern}.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,18 @@ using CounterStrikeSharp.API.Modules.Commands;
|
|||||||
using CounterStrikeSharp.API.Modules.Memory;
|
using CounterStrikeSharp.API.Modules.Memory;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using CounterStrikeSharp.API.Modules.Entities;
|
||||||
|
|
||||||
namespace CS2_SimpleAdmin;
|
namespace CS2_SimpleAdmin;
|
||||||
|
|
||||||
public partial class 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>")]
|
[CommandHelper(1, "<message>")]
|
||||||
[RequiresPermissions("@css/chat")]
|
[RequiresPermissions("@css/chat")]
|
||||||
public void OnAdminToAdminSayCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnAdminToAdminSayCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
@@ -20,7 +27,7 @@ public partial class CS2_SimpleAdmin
|
|||||||
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||||
|
|
||||||
foreach (var player in Helper.GetValidPlayers()
|
foreach (var player in Helper.GetValidPlayers()
|
||||||
.Where(p => AdminManager.PlayerHasPermissions(p, "@css/chat")))
|
.Where(p => AdminManager.PlayerHasPermissions(new SteamID(p.SteamID), "@css/chat")))
|
||||||
{
|
{
|
||||||
if (_localizer != null)
|
if (_localizer != null)
|
||||||
player.PrintToChat(_localizer["sa_adminchat_template_admin",
|
player.PrintToChat(_localizer["sa_adminchat_template_admin",
|
||||||
@@ -29,6 +36,11 @@ public partial class CS2_SimpleAdmin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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>")]
|
[CommandHelper(1, "<message>")]
|
||||||
[RequiresPermissions("@css/chat")]
|
[RequiresPermissions("@css/chat")]
|
||||||
public void OnAdminCustomSayCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnAdminCustomSayCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
@@ -46,6 +58,11 @@ public partial class CS2_SimpleAdmin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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>")]
|
[CommandHelper(1, "<message>")]
|
||||||
[RequiresPermissions("@css/chat")]
|
[RequiresPermissions("@css/chat")]
|
||||||
public void OnAdminSayCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnAdminSayCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
@@ -56,7 +73,6 @@ public partial class CS2_SimpleAdmin
|
|||||||
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||||
|
|
||||||
Helper.LogCommand(caller, command);
|
Helper.LogCommand(caller, command);
|
||||||
|
|
||||||
foreach (var player in Helper.GetValidPlayers())
|
foreach (var player in Helper.GetValidPlayers())
|
||||||
{
|
{
|
||||||
player.SendLocalizedMessage(_localizer,
|
player.SendLocalizedMessage(_localizer,
|
||||||
@@ -65,6 +81,11 @@ public partial class CS2_SimpleAdmin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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>")]
|
[CommandHelper(2, "<#userid or name> <message>")]
|
||||||
[RequiresPermissions("@css/chat")]
|
[RequiresPermissions("@css/chat")]
|
||||||
public void OnAdminPrivateSayCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnAdminPrivateSayCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
@@ -91,6 +112,11 @@ public partial class CS2_SimpleAdmin
|
|||||||
command.ReplyToCommand($" Private message sent!");
|
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>")]
|
[CommandHelper(1, "<message>")]
|
||||||
[RequiresPermissions("@css/chat")]
|
[RequiresPermissions("@css/chat")]
|
||||||
public void OnAdminCenterSayCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnAdminCenterSayCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
@@ -99,10 +125,14 @@ public partial class CS2_SimpleAdmin
|
|||||||
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
|
||||||
|
|
||||||
Helper.LogCommand(caller, command);
|
Helper.LogCommand(caller, command);
|
||||||
|
|
||||||
Helper.PrintToCenterAll(utf8String.ReplaceColorTags());
|
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>")]
|
[CommandHelper(1, "<message>")]
|
||||||
[RequiresPermissions("@css/chat")]
|
[RequiresPermissions("@css/chat")]
|
||||||
public void OnAdminHudSayCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnAdminHudSayCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
@@ -115,6 +145,6 @@ public partial class CS2_SimpleAdmin
|
|||||||
VirtualFunctions.ClientPrintAll(
|
VirtualFunctions.ClientPrintAll(
|
||||||
HudDestination.Alert,
|
HudDestination.Alert,
|
||||||
utf8String.ReplaceColorTags(),
|
utf8String.ReplaceColorTags(),
|
||||||
0, 0, 0, 0);
|
0, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@ using CounterStrikeSharp.API;
|
|||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Admin;
|
using CounterStrikeSharp.API.Modules.Admin;
|
||||||
using CounterStrikeSharp.API.Modules.Commands;
|
using CounterStrikeSharp.API.Modules.Commands;
|
||||||
|
using CounterStrikeSharp.API.Modules.Entities;
|
||||||
using CS2_SimpleAdmin.Managers;
|
using CS2_SimpleAdmin.Managers;
|
||||||
using CS2_SimpleAdmin.Menus;
|
using CS2_SimpleAdmin.Menus;
|
||||||
using CS2_SimpleAdminApi;
|
using CS2_SimpleAdminApi;
|
||||||
@@ -10,15 +11,18 @@ namespace CS2_SimpleAdmin;
|
|||||||
|
|
||||||
public partial class 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")]
|
[RequiresPermissions("@css/chat")]
|
||||||
[CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[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)
|
public void OnGagCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (Database == null) return;
|
if (DatabaseProvider == null) return;
|
||||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
|
|
||||||
var reason = _localizer?["sa_unknown"] ?? "Unknown";
|
|
||||||
|
|
||||||
var targets = GetTarget(command);
|
var targets = GetTarget(command);
|
||||||
if (targets == null) return;
|
if (targets == null) return;
|
||||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
||||||
@@ -28,13 +32,18 @@ public partial class CS2_SimpleAdmin
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.ArgCount >= 3 && command.GetArg(3).Length > 0)
|
var reason = command.ArgCount >= 3
|
||||||
reason = command.GetArg(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 =>
|
playersToTarget.ForEach(player =>
|
||||||
{
|
{
|
||||||
if (!caller!.CanTarget(player)) return;
|
if (!caller!.CanTarget(player)) return;
|
||||||
if (!int.TryParse(command.GetArg(2), out var time) && caller != null && caller.IsValid && Config.OtherSettings.ShowBanMenuIfNoTime)
|
if (time < 0 && caller != null && caller.IsValid && Config.OtherSettings.ShowBanMenuIfNoTime)
|
||||||
{
|
{
|
||||||
DurationMenu.OpenMenu(caller, $"{_localizer?["sa_gag"] ?? "Gag"}: {player.PlayerName}", player,
|
DurationMenu.OpenMenu(caller, $"{_localizer?["sa_gag"] ?? "Gag"}: {player.PlayerName}", player,
|
||||||
ManagePlayersMenu.GagMenu);
|
ManagePlayersMenu.GagMenu);
|
||||||
@@ -45,9 +54,19 @@ public partial class CS2_SimpleAdmin
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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)
|
internal void Gag(CCSPlayerController? caller, CCSPlayerController player, int time, string reason, string? callerName = null, CommandInfo? command = null, bool silent = false)
|
||||||
{
|
{
|
||||||
if (Database == null || !player.IsValid || !player.UserId.HasValue) return;
|
if (DatabaseProvider == null || !player.IsValid || !player.UserId.HasValue) return;
|
||||||
if (!caller.CanTarget(player)) return;
|
if (!caller.CanTarget(player)) return;
|
||||||
if (!CheckValidMute(caller, time)) return;
|
if (!CheckValidMute(caller, time)) return;
|
||||||
|
|
||||||
@@ -55,13 +74,18 @@ public partial class CS2_SimpleAdmin
|
|||||||
callerName ??= caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
callerName ??= caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
|
|
||||||
// Get player and admin information
|
// Get player and admin information
|
||||||
var playerInfo = PlayersInfo[player.UserId.Value];
|
var playerInfo = PlayersInfo[player.SteamID];
|
||||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null;
|
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||||
|
|
||||||
// Asynchronously handle gag logic
|
// Asynchronously handle gag logic
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await MuteManager.MutePlayer(playerInfo, adminInfo, reason, time);
|
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
|
// Add penalty to the player's penalty manager
|
||||||
@@ -82,11 +106,11 @@ public partial class CS2_SimpleAdmin
|
|||||||
// Display admin activity message to other players
|
// Display admin activity message to other players
|
||||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
{
|
{
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment the player's total gags count
|
// Increment the player's total gags count
|
||||||
PlayersInfo[player.UserId.Value].TotalGags++;
|
PlayersInfo[player.SteamID].TotalGags++;
|
||||||
|
|
||||||
// Log the gag command and send Discord notification
|
// Log the gag command and send Discord notification
|
||||||
if (!silent)
|
if (!silent)
|
||||||
@@ -98,14 +122,63 @@ public partial class CS2_SimpleAdmin
|
|||||||
}
|
}
|
||||||
|
|
||||||
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Gag, _localizer);
|
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Gag, _localizer);
|
||||||
SimpleAdminApi?.OnPlayerPenaltiedEvent(playerInfo, adminInfo, PenaltyType.Gag, reason, time);
|
}
|
||||||
|
|
||||||
|
/// <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")]
|
[RequiresPermissions("@css/chat")]
|
||||||
[CommandHelper(minArgs: 1, usage: "<steamid> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[CommandHelper(minArgs: 1, usage: "<steamid> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
public void OnAddGagCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnAddGagCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (Database == null) return;
|
if (DatabaseProvider == null) return;
|
||||||
|
|
||||||
// Set caller name
|
// Set caller name
|
||||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
@@ -120,20 +193,21 @@ public partial class CS2_SimpleAdmin
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var steamid = steamId.SteamId64.ToString();
|
var steamid = steamId.SteamId64;
|
||||||
var reason = command.ArgCount >= 3 && command.GetArg(3).Length > 0
|
var reason = command.ArgCount >= 3
|
||||||
? command.GetArg(3)
|
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||||
: (_localizer?["sa_unknown"] ?? "Unknown");
|
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||||
|
|
||||||
|
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||||
|
|
||||||
int.TryParse(command.GetArg(2), out var time);
|
var time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2)));
|
||||||
if (!CheckValidMute(caller, time)) return;
|
if (!CheckValidMute(caller, time)) return;
|
||||||
|
|
||||||
// Get player and admin info
|
// Get player and admin info
|
||||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null;
|
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||||
|
|
||||||
// Attempt to match player based on SteamID
|
// Attempt to match player based on SteamID
|
||||||
var matches = Helper.GetPlayerFromSteamid64(steamid);
|
var player = Helper.GetPlayerFromSteamid64(steamid);
|
||||||
var player = matches.Count == 1 ? matches.FirstOrDefault() : null;
|
|
||||||
|
|
||||||
if (player != null && player.IsValid)
|
if (player != null && player.IsValid)
|
||||||
{
|
{
|
||||||
@@ -145,32 +219,48 @@ public partial class CS2_SimpleAdmin
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (!caller.CanTarget(new SteamID(steamId.SteamId64)))
|
||||||
|
return;
|
||||||
|
|
||||||
// Asynchronous gag operation for offline players
|
// Asynchronous gag operation for offline players
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await MuteManager.AddMuteBySteamid(steamid, adminInfo, reason, time);
|
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, reason, time, PenaltyType.Gag, _localizer);
|
Helper.SendDiscordPenaltyMessage(caller, steamid.ToString(), reason, time, PenaltyType.Gag, _localizer);
|
||||||
|
|
||||||
command.ReplyToCommand($"Player with steamid {steamid} is not online. Gag has been added offline.");
|
command.ReplyToCommand($"Player with steamid {steamid} is not online. Gag has been added offline.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log the gag command and respond to the command
|
// Log the gag command and respond to the command
|
||||||
Helper.LogCommand(caller, command);
|
Helper.LogCommand(caller, command);
|
||||||
SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamId, adminInfo, PenaltyType.Gag, reason, time);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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")]
|
[RequiresPermissions("@css/chat")]
|
||||||
[CommandHelper(minArgs: 1, usage: "<steamid or name> [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[CommandHelper(minArgs: 1, usage: "<steamid or name> [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
public void OnUngagCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnUngagCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (Database == null) return;
|
if (DatabaseProvider == null) return;
|
||||||
|
|
||||||
var callerSteamId = caller?.SteamID.ToString() ?? _localizer?["sa_console"] ?? "Console";
|
var callerSteamId = caller?.SteamID.ToString() ?? _localizer?["sa_console"] ?? "Console";
|
||||||
var pattern = command.GetArg(1);
|
var pattern = command.GetArg(1);
|
||||||
var reason = command.GetArg(2);
|
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)
|
if (pattern.Length <= 1)
|
||||||
{
|
{
|
||||||
command.ReplyToCommand($"Too short pattern to search.");
|
command.ReplyToCommand($"Too short pattern to search.");
|
||||||
@@ -182,8 +272,7 @@ public partial class CS2_SimpleAdmin
|
|||||||
// Check if pattern is a valid SteamID64
|
// Check if pattern is a valid SteamID64
|
||||||
if (Helper.ValidateSteamId(pattern, out var steamId) && steamId != null)
|
if (Helper.ValidateSteamId(pattern, out var steamId) && steamId != null)
|
||||||
{
|
{
|
||||||
var matches = Helper.GetPlayerFromSteamid64(steamId.SteamId64.ToString());
|
var player = Helper.GetPlayerFromSteamid64(steamId.SteamId64);
|
||||||
var player = matches.Count == 1 ? matches.FirstOrDefault() : null;
|
|
||||||
|
|
||||||
if (player != null && player.IsValid)
|
if (player != null && player.IsValid)
|
||||||
{
|
{
|
||||||
@@ -207,8 +296,8 @@ public partial class CS2_SimpleAdmin
|
|||||||
{
|
{
|
||||||
PlayerPenaltyManager.RemovePenaltiesByType(namePlayer.Slot, PenaltyType.Gag);
|
PlayerPenaltyManager.RemovePenaltiesByType(namePlayer.Slot, PenaltyType.Gag);
|
||||||
|
|
||||||
if (namePlayer.UserId.HasValue && PlayersInfo[namePlayer.UserId.Value].TotalGags > 0)
|
if (namePlayer.UserId.HasValue && PlayersInfo[namePlayer.SteamID].TotalGags > 0)
|
||||||
PlayersInfo[namePlayer.UserId.Value].TotalGags--;
|
PlayersInfo[namePlayer.SteamID].TotalGags--;
|
||||||
|
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
@@ -228,15 +317,18 @@ public partial class CS2_SimpleAdmin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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")]
|
[RequiresPermissions("@css/chat")]
|
||||||
[CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[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)
|
public void OnMuteCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (Database == null) return;
|
if (DatabaseProvider == null) return;
|
||||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
|
|
||||||
var reason = _localizer?["sa_unknown"] ?? "Unknown";
|
|
||||||
|
|
||||||
var targets = GetTarget(command);
|
var targets = GetTarget(command);
|
||||||
if (targets == null) return;
|
if (targets == null) return;
|
||||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
||||||
@@ -246,13 +338,18 @@ public partial class CS2_SimpleAdmin
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.ArgCount >= 3 && command.GetArg(3).Length > 0)
|
var reason = command.ArgCount >= 3
|
||||||
reason = command.GetArg(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 =>
|
playersToTarget.ForEach(player =>
|
||||||
{
|
{
|
||||||
if (!caller!.CanTarget(player)) return;
|
if (!caller!.CanTarget(player)) return;
|
||||||
if (!int.TryParse(command.GetArg(2), out var time) && caller != null && caller.IsValid && Config.OtherSettings.ShowBanMenuIfNoTime)
|
if (time < 0 && caller != null && caller.IsValid && Config.OtherSettings.ShowBanMenuIfNoTime)
|
||||||
{
|
{
|
||||||
DurationMenu.OpenMenu(caller, $"{_localizer?["sa_mute"] ?? "Mute"}: {player.PlayerName}", player,
|
DurationMenu.OpenMenu(caller, $"{_localizer?["sa_mute"] ?? "Mute"}: {player.PlayerName}", player,
|
||||||
ManagePlayersMenu.MuteMenu);
|
ManagePlayersMenu.MuteMenu);
|
||||||
@@ -263,9 +360,19 @@ public partial class CS2_SimpleAdmin
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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)
|
internal void Mute(CCSPlayerController? caller, CCSPlayerController player, int time, string reason, string? callerName = null, CommandInfo? command = null, bool silent = false)
|
||||||
{
|
{
|
||||||
if (Database == null || !player.IsValid || !player.UserId.HasValue) return;
|
if (DatabaseProvider == null || !player.IsValid || !player.UserId.HasValue) return;
|
||||||
if (!caller.CanTarget(player)) return;
|
if (!caller.CanTarget(player)) return;
|
||||||
if (!CheckValidMute(caller, time)) return;
|
if (!CheckValidMute(caller, time)) return;
|
||||||
|
|
||||||
@@ -273,8 +380,8 @@ public partial class CS2_SimpleAdmin
|
|||||||
callerName ??= caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
callerName ??= caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
|
|
||||||
// Get player and admin information
|
// Get player and admin information
|
||||||
var playerInfo = PlayersInfo[player.UserId.Value];
|
var playerInfo = PlayersInfo[player.SteamID];
|
||||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null;
|
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||||
|
|
||||||
// Set player's voice flags to muted
|
// Set player's voice flags to muted
|
||||||
player.VoiceFlags = VoiceFlags.Muted;
|
player.VoiceFlags = VoiceFlags.Muted;
|
||||||
@@ -282,7 +389,12 @@ public partial class CS2_SimpleAdmin
|
|||||||
// Asynchronously handle mute logic
|
// Asynchronously handle mute logic
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await MuteManager.MutePlayer(playerInfo, adminInfo, reason, time, 1);
|
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
|
// Add penalty to the player's penalty manager
|
||||||
@@ -303,11 +415,11 @@ public partial class CS2_SimpleAdmin
|
|||||||
// Display admin activity message to other players
|
// Display admin activity message to other players
|
||||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
{
|
{
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment the player's total mutes count
|
// Increment the player's total mutes count
|
||||||
PlayersInfo[player.UserId.Value].TotalMutes++;
|
PlayersInfo[player.SteamID].TotalMutes++;
|
||||||
|
|
||||||
// Log the mute command and send Discord notification
|
// Log the mute command and send Discord notification
|
||||||
if (!silent)
|
if (!silent)
|
||||||
@@ -319,14 +431,18 @@ public partial class CS2_SimpleAdmin
|
|||||||
}
|
}
|
||||||
|
|
||||||
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Mute, _localizer);
|
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Mute, _localizer);
|
||||||
SimpleAdminApi?.OnPlayerPenaltiedEvent(playerInfo, adminInfo, PenaltyType.Mute, reason, time);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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")]
|
[RequiresPermissions("@css/chat")]
|
||||||
[CommandHelper(minArgs: 1, usage: "<steamid> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[CommandHelper(minArgs: 1, usage: "<steamid> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
public void OnAddMuteCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnAddMuteCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (Database == null) return;
|
if (DatabaseProvider == null) return;
|
||||||
|
|
||||||
// Set caller name
|
// Set caller name
|
||||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
@@ -341,20 +457,21 @@ public partial class CS2_SimpleAdmin
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var steamid = steamId.SteamId64.ToString();
|
var steamid = steamId.SteamId64;
|
||||||
var reason = command.ArgCount >= 3 && command.GetArg(3).Length > 0
|
var reason = command.ArgCount >= 3
|
||||||
? command.GetArg(3)
|
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||||
: (_localizer?["sa_unknown"] ?? "Unknown");
|
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||||
|
|
||||||
|
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||||
|
|
||||||
int.TryParse(command.GetArg(2), out var time);
|
var time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2)));
|
||||||
if (!CheckValidMute(caller, time)) return;
|
if (!CheckValidMute(caller, time)) return;
|
||||||
|
|
||||||
// Get player and admin info
|
// Get player and admin info
|
||||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null;
|
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||||
|
|
||||||
// Attempt to match player based on SteamID
|
// Attempt to match player based on SteamID
|
||||||
var matches = Helper.GetPlayerFromSteamid64(steamid);
|
var player = Helper.GetPlayerFromSteamid64(steamid);
|
||||||
var player = matches.Count == 1 ? matches.FirstOrDefault() : null;
|
|
||||||
|
|
||||||
if (player != null && player.IsValid)
|
if (player != null && player.IsValid)
|
||||||
{
|
{
|
||||||
@@ -366,32 +483,95 @@ public partial class CS2_SimpleAdmin
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (!caller.CanTarget(new SteamID(steamId.SteamId64)))
|
||||||
|
return;
|
||||||
|
|
||||||
// Asynchronous mute operation for offline players
|
// Asynchronous mute operation for offline players
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await MuteManager.AddMuteBySteamid(steamid, adminInfo, reason, time, 1);
|
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, reason, time, PenaltyType.Mute, _localizer);
|
Helper.SendDiscordPenaltyMessage(caller, steamid.ToString(), reason, time, PenaltyType.Mute, _localizer);
|
||||||
|
|
||||||
command.ReplyToCommand($"Player with steamid {steamid} is not online. Mute has been added offline.");
|
command.ReplyToCommand($"Player with steamid {steamid} is not online. Mute has been added offline.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log the mute command and respond to the command
|
// Log the mute command and respond to the command
|
||||||
Helper.LogCommand(caller, command);
|
Helper.LogCommand(caller, command);
|
||||||
SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamId, adminInfo, PenaltyType.Mute, reason, time);
|
}
|
||||||
|
|
||||||
|
/// <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")]
|
[RequiresPermissions("@css/chat")]
|
||||||
[CommandHelper(minArgs: 1, usage: "<steamid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[CommandHelper(minArgs: 1, usage: "<steamid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
public void OnUnmuteCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnUnmuteCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (Database == null) return;
|
if (DatabaseProvider == null) return;
|
||||||
|
|
||||||
var callerSteamId = caller?.SteamID.ToString() ?? _localizer?["sa_console"] ?? "Console";
|
var callerSteamId = caller?.SteamID.ToString() ?? _localizer?["sa_console"] ?? "Console";
|
||||||
var pattern = command.GetArg(1);
|
var pattern = command.GetArg(1);
|
||||||
var reason = command.GetArg(2);
|
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)
|
if (pattern.Length <= 1)
|
||||||
{
|
{
|
||||||
command.ReplyToCommand("Too short pattern to search.");
|
command.ReplyToCommand("Too short pattern to search.");
|
||||||
@@ -403,8 +583,7 @@ public partial class CS2_SimpleAdmin
|
|||||||
// Check if pattern is a valid SteamID64
|
// Check if pattern is a valid SteamID64
|
||||||
if (Helper.ValidateSteamId(pattern, out var steamId) && steamId != null)
|
if (Helper.ValidateSteamId(pattern, out var steamId) && steamId != null)
|
||||||
{
|
{
|
||||||
var matches = Helper.GetPlayerFromSteamid64(steamId.SteamId64.ToString());
|
var player = Helper.GetPlayerFromSteamid64(steamId.SteamId64);
|
||||||
var player = matches.Count == 1 ? matches.FirstOrDefault() : null;
|
|
||||||
|
|
||||||
if (player != null && player.IsValid)
|
if (player != null && player.IsValid)
|
||||||
{
|
{
|
||||||
@@ -430,8 +609,8 @@ public partial class CS2_SimpleAdmin
|
|||||||
PlayerPenaltyManager.RemovePenaltiesByType(namePlayer.Slot, PenaltyType.Mute);
|
PlayerPenaltyManager.RemovePenaltiesByType(namePlayer.Slot, PenaltyType.Mute);
|
||||||
namePlayer.VoiceFlags = VoiceFlags.Normal;
|
namePlayer.VoiceFlags = VoiceFlags.Normal;
|
||||||
|
|
||||||
if (namePlayer.UserId.HasValue && PlayersInfo[namePlayer.UserId.Value].TotalMutes > 0)
|
if (namePlayer.UserId.HasValue && PlayersInfo[namePlayer.SteamID].TotalMutes > 0)
|
||||||
PlayersInfo[namePlayer.UserId.Value].TotalMutes--;
|
PlayersInfo[namePlayer.SteamID].TotalMutes--;
|
||||||
|
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
@@ -451,15 +630,19 @@ public partial class CS2_SimpleAdmin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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")]
|
[RequiresPermissions("@css/chat")]
|
||||||
[CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[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)
|
public void OnSilenceCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (Database == null) return;
|
if (DatabaseProvider == null) return;
|
||||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
|
|
||||||
var reason = _localizer?["sa_unknown"] ?? "Unknown";
|
|
||||||
|
|
||||||
var targets = GetTarget(command);
|
var targets = GetTarget(command);
|
||||||
if (targets == null) return;
|
if (targets == null) return;
|
||||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
||||||
@@ -469,13 +652,18 @@ public partial class CS2_SimpleAdmin
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.ArgCount >= 3 && command.GetArg(3).Length > 0)
|
var reason = command.ArgCount >= 3
|
||||||
reason = command.GetArg(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 =>
|
playersToTarget.ForEach(player =>
|
||||||
{
|
{
|
||||||
if (!caller!.CanTarget(player)) return;
|
if (!caller!.CanTarget(player)) return;
|
||||||
if (!int.TryParse(command.GetArg(2), out var time) && caller != null && caller.IsValid && Config.OtherSettings.ShowBanMenuIfNoTime)
|
if (time < 0 && caller != null && caller.IsValid && Config.OtherSettings.ShowBanMenuIfNoTime)
|
||||||
{
|
{
|
||||||
DurationMenu.OpenMenu(caller, $"{_localizer?["sa_silence"] ?? "Silence"}: {player.PlayerName}", player,
|
DurationMenu.OpenMenu(caller, $"{_localizer?["sa_silence"] ?? "Silence"}: {player.PlayerName}", player,
|
||||||
ManagePlayersMenu.SilenceMenu);
|
ManagePlayersMenu.SilenceMenu);
|
||||||
@@ -486,9 +674,19 @@ public partial class CS2_SimpleAdmin
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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)
|
internal void Silence(CCSPlayerController? caller, CCSPlayerController player, int time, string reason, string? callerName = null, CommandInfo? command = null, bool silent = false)
|
||||||
{
|
{
|
||||||
if (Database == null || !player.IsValid || !player.UserId.HasValue) return;
|
if (DatabaseProvider == null || !player.IsValid || !player.UserId.HasValue) return;
|
||||||
if (!caller.CanTarget(player)) return;
|
if (!caller.CanTarget(player)) return;
|
||||||
if (!CheckValidMute(caller, time)) return;
|
if (!CheckValidMute(caller, time)) return;
|
||||||
|
|
||||||
@@ -496,13 +694,18 @@ public partial class CS2_SimpleAdmin
|
|||||||
callerName ??= caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
callerName ??= caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
|
|
||||||
// Get player and admin information
|
// Get player and admin information
|
||||||
var playerInfo = PlayersInfo[player.UserId.Value];
|
var playerInfo = PlayersInfo[player.SteamID];
|
||||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null;
|
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||||
|
|
||||||
// Asynchronously handle silence logic
|
// Asynchronously handle silence logic
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await MuteManager.MutePlayer(playerInfo, adminInfo, reason, time, 2); // Assuming 2 is the type for silence
|
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
|
// Add penalty to the player's penalty manager
|
||||||
@@ -524,11 +727,11 @@ public partial class CS2_SimpleAdmin
|
|||||||
// Display admin activity message to other players
|
// Display admin activity message to other players
|
||||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
{
|
{
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment the player's total silences count
|
// Increment the player's total silences count
|
||||||
PlayersInfo[player.UserId.Value].TotalSilences++;
|
PlayersInfo[player.SteamID].TotalSilences++;
|
||||||
|
|
||||||
// Log the silence command and send Discord notification
|
// Log the silence command and send Discord notification
|
||||||
if (!silent)
|
if (!silent)
|
||||||
@@ -540,14 +743,19 @@ public partial class CS2_SimpleAdmin
|
|||||||
}
|
}
|
||||||
|
|
||||||
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Silence, _localizer);
|
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Silence, _localizer);
|
||||||
SimpleAdminApi?.OnPlayerPenaltiedEvent(playerInfo, adminInfo, PenaltyType.Silence, reason, time);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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")]
|
[RequiresPermissions("@css/chat")]
|
||||||
[CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[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)
|
public void OnAddSilenceCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (Database == null) return;
|
if (DatabaseProvider == null) return;
|
||||||
|
|
||||||
// Set caller name
|
// Set caller name
|
||||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
@@ -562,20 +770,21 @@ public partial class CS2_SimpleAdmin
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var steamid = steamId.SteamId64.ToString();
|
var steamid = steamId.SteamId64;
|
||||||
var reason = command.ArgCount >= 3 && command.GetArg(3).Length > 0
|
var reason = command.ArgCount >= 3
|
||||||
? command.GetArg(3)
|
? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim()
|
||||||
: (_localizer?["sa_unknown"] ?? "Unknown");
|
: _localizer?["sa_unknown"] ?? "Unknown";
|
||||||
|
|
||||||
|
reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason;
|
||||||
|
|
||||||
int.TryParse(command.GetArg(2), out var time);
|
var time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2)));
|
||||||
if (!CheckValidMute(caller, time)) return;
|
if (!CheckValidMute(caller, time)) return;
|
||||||
|
|
||||||
// Get player and admin info
|
// Get player and admin info
|
||||||
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null;
|
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.SteamID] : null;
|
||||||
|
|
||||||
// Attempt to match player based on SteamID
|
// Attempt to match player based on SteamID
|
||||||
var matches = Helper.GetPlayerFromSteamid64(steamid);
|
var player = Helper.GetPlayerFromSteamid64(steamid);
|
||||||
var player = matches.Count == 1 ? matches.FirstOrDefault() : null;
|
|
||||||
|
|
||||||
if (player != null && player.IsValid)
|
if (player != null && player.IsValid)
|
||||||
{
|
{
|
||||||
@@ -587,45 +796,107 @@ public partial class CS2_SimpleAdmin
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (!caller.CanTarget(new SteamID(steamId.SteamId64)))
|
||||||
|
return;
|
||||||
|
|
||||||
// Asynchronous silence operation for offline players
|
// Asynchronous silence operation for offline players
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await MuteManager.AddMuteBySteamid(steamid, adminInfo, reason, time, 2);
|
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, reason, time, PenaltyType.Silence, _localizer);
|
Helper.SendDiscordPenaltyMessage(caller, steamid.ToString(), reason, time, PenaltyType.Silence, _localizer);
|
||||||
|
|
||||||
command.ReplyToCommand($"Player with steamid {steamid} is not online. Silence has been added offline.");
|
command.ReplyToCommand($"Player with steamid {steamid} is not online. Silence has been added offline.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log the silence command and respond to the command
|
// Log the silence command and respond to the command
|
||||||
Helper.LogCommand(caller, command);
|
Helper.LogCommand(caller, command);
|
||||||
SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamId, adminInfo, PenaltyType.Silence, reason, time);
|
}
|
||||||
|
|
||||||
|
/// <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")]
|
[RequiresPermissions("@css/chat")]
|
||||||
[CommandHelper(minArgs: 1, usage: "<steamid or name> [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[CommandHelper(minArgs: 1, usage: "<steamid or name> [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
public void OnUnsilenceCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnUnsilenceCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (Database == null) return;
|
if (DatabaseProvider == null) return;
|
||||||
|
|
||||||
var callerSteamId = caller?.SteamID.ToString() ?? _localizer?["sa_console"] ?? "Console";
|
var callerSteamId = caller?.SteamID.ToString() ?? _localizer?["sa_console"] ?? "Console";
|
||||||
var pattern = command.GetArg(1);
|
var pattern = command.GetArg(1);
|
||||||
var reason = command.GetArg(2);
|
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)
|
if (pattern.Length <= 1)
|
||||||
{
|
{
|
||||||
command.ReplyToCommand("Too short pattern to search.");
|
command.ReplyToCommand("Too short pattern to search.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Helper.LogCommand(caller, command);
|
Helper.LogCommand(caller, command);
|
||||||
|
|
||||||
// Check if pattern is a valid SteamID64
|
// Check if pattern is a valid SteamID64
|
||||||
if (Helper.ValidateSteamId(pattern, out var steamId) && steamId != null)
|
if (Helper.ValidateSteamId(pattern, out var steamId) && steamId != null)
|
||||||
{
|
{
|
||||||
var matches = Helper.GetPlayerFromSteamid64(steamId.SteamId64.ToString());
|
var player = Helper.GetPlayerFromSteamid64(steamId.SteamId64);
|
||||||
var player = matches.Count == 1 ? matches.FirstOrDefault() : null;
|
|
||||||
|
|
||||||
if (player != null && player.IsValid)
|
if (player != null && player.IsValid)
|
||||||
{
|
{
|
||||||
@@ -655,8 +926,8 @@ public partial class CS2_SimpleAdmin
|
|||||||
// Reset voice flags to normal
|
// Reset voice flags to normal
|
||||||
namePlayer.VoiceFlags = VoiceFlags.Normal;
|
namePlayer.VoiceFlags = VoiceFlags.Normal;
|
||||||
|
|
||||||
if (namePlayer.UserId.HasValue && PlayersInfo[namePlayer.UserId.Value].TotalSilences > 0)
|
if (namePlayer.UserId.HasValue && PlayersInfo[namePlayer.SteamID].TotalSilences > 0)
|
||||||
PlayersInfo[namePlayer.UserId.Value].TotalSilences--;
|
PlayersInfo[namePlayer.SteamID].TotalSilences--;
|
||||||
|
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
@@ -676,13 +947,19 @@ public partial class CS2_SimpleAdmin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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)
|
private bool CheckValidMute(CCSPlayerController? caller, int duration)
|
||||||
{
|
{
|
||||||
if (caller == null) return true;
|
if (caller == null) return true;
|
||||||
|
|
||||||
var canPermMute = AdminManager.PlayerHasPermissions(caller, "@css/permmute");
|
var canPermMute = AdminManager.PlayerHasPermissions(new SteamID(caller.SteamID), "@css/permmute");
|
||||||
|
|
||||||
if (duration <= 0 && canPermMute == false)
|
if (duration <= 0 && !canPermMute)
|
||||||
{
|
{
|
||||||
caller.PrintToChat($"{_localizer!["sa_prefix"]} {_localizer["sa_ban_perm_restricted"]}");
|
caller.PrintToChat($"{_localizer!["sa_prefix"]} {_localizer["sa_ban_perm_restricted"]}");
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ namespace CS2_SimpleAdmin;
|
|||||||
|
|
||||||
public partial class 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")]
|
[RequiresPermissions("@css/generic")]
|
||||||
[CommandHelper(minArgs: 2, usage: "<question> [... options ...]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[CommandHelper(minArgs: 2, usage: "<question> [... options ...]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
public void OnVoteCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnVoteCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
|
|||||||
@@ -1,215 +1,307 @@
|
|||||||
using CounterStrikeSharp.API.Core;
|
// using System.Globalization;
|
||||||
using CounterStrikeSharp.API.Modules.Admin;
|
// using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Modules.Commands;
|
// using CounterStrikeSharp.API.Core;
|
||||||
|
// using CounterStrikeSharp.API.Modules.Admin;
|
||||||
namespace CS2_SimpleAdmin;
|
// using CounterStrikeSharp.API.Modules.Commands;
|
||||||
|
//
|
||||||
public partial class CS2_SimpleAdmin
|
// namespace CS2_SimpleAdmin;
|
||||||
{
|
//
|
||||||
[CommandHelper(1, "<#userid or name>")]
|
// public partial class CS2_SimpleAdmin
|
||||||
[RequiresPermissions("@css/cheats")]
|
// {
|
||||||
public void OnNoclipCommand(CCSPlayerController? caller, CommandInfo command)
|
// /// <summary>
|
||||||
{
|
// /// Enables or disables no-clip mode for specified player(s).
|
||||||
var callerName = caller == null ? _localizer?["sa_console"] ?? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
// /// </summary>
|
||||||
|
// /// <param name="caller">The player issuing the command.</param>
|
||||||
var targets = GetTarget(command);
|
// /// <param name="command">The command input containing targets.</param>
|
||||||
if (targets == null) return;
|
// [CommandHelper(1, "<#userid or name>")]
|
||||||
var playersToTarget = targets.Players.Where(player =>
|
// [RequiresPermissions("@css/cheats")]
|
||||||
player.IsValid &&
|
// public void OnNoclipCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
player is { PawnIsAlive: true, IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected }).ToList();
|
// {
|
||||||
|
// var callerName = caller == null ? _localizer?["sa_console"] ?? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
playersToTarget.ForEach(player =>
|
//
|
||||||
{
|
// var targets = GetTarget(command);
|
||||||
if (caller!.CanTarget(player))
|
// if (targets == null) return;
|
||||||
{
|
// var playersToTarget = targets.Players.Where(player =>
|
||||||
NoClip(caller, player, callerName);
|
// player.IsValid &&
|
||||||
}
|
// player is { IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||||
});
|
//
|
||||||
}
|
// playersToTarget.ForEach(player =>
|
||||||
|
// {
|
||||||
internal static void NoClip(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
|
// if (caller!.CanTarget(player))
|
||||||
{
|
// {
|
||||||
if (!player.IsValid) return;
|
// NoClip(caller, player, callerName);
|
||||||
if (!caller.CanTarget(player)) return;
|
// }
|
||||||
|
// });
|
||||||
// Set default caller name if not provided
|
//
|
||||||
callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
// Helper.LogCommand(caller, command);
|
||||||
|
// }
|
||||||
// Toggle no-clip mode for the player
|
//
|
||||||
player.Pawn.Value?.ToggleNoclip();
|
// /// <summary>
|
||||||
|
// /// Toggles no-clip mode for a player and shows admin activity messages.
|
||||||
// Determine message keys and arguments for the no-clip notification
|
// /// </summary>
|
||||||
var (activityMessageKey, adminActivityArgs) =
|
// /// <param name="caller">The player/admin toggling no-clip.</param>
|
||||||
("sa_admin_noclip_message",
|
// /// <param name="player">The target player whose no-clip state changes.</param>
|
||||||
new object[] { "CALLER", player.PlayerName });
|
// /// <param name="callerName">Optional caller name for messages.</param>
|
||||||
|
// /// <param name="command">Optional command info for logging.</param>
|
||||||
// Display admin activity message to other players
|
// internal static void NoClip(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
|
||||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
// {
|
||||||
{
|
// if (!player.IsValid) return;
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
// if (!caller.CanTarget(player)) return;
|
||||||
}
|
//
|
||||||
|
// // Set default caller name if not provided
|
||||||
// Log the command
|
// callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||||
if (command == null)
|
//
|
||||||
{
|
// // Toggle no-clip mode for the player
|
||||||
Helper.LogCommand(caller, $"css_noclip {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
// player.Pawn.Value?.ToggleNoclip();
|
||||||
}
|
//
|
||||||
else
|
// // Determine message keys and arguments for the no-clip notification
|
||||||
{
|
// var (activityMessageKey, adminActivityArgs) =
|
||||||
Helper.LogCommand(caller, command);
|
// ("sa_admin_noclip_message",
|
||||||
}
|
// new object[] { "CALLER", player.PlayerName });
|
||||||
}
|
//
|
||||||
|
// // Display admin activity message to other players
|
||||||
[RequiresPermissions("@css/cheats")]
|
// if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
[CommandHelper(minArgs: 1, usage: "<#userid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
// {
|
||||||
public void OnGodCommand(CCSPlayerController? caller, CommandInfo command)
|
// Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
{
|
// }
|
||||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
//
|
||||||
var targets = GetTarget(command);
|
// // Log the command
|
||||||
if (targets == null) return;
|
// if (command == null)
|
||||||
|
// Helper.LogCommand(caller, $"css_noclip {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
||||||
var playersToTarget = targets.Players.Where(player => player.IsValid && player is { PawnIsAlive: true, IsHLTV: false }).ToList();
|
// }
|
||||||
|
//
|
||||||
playersToTarget.ForEach(player =>
|
// /// <summary>
|
||||||
{
|
// /// Enables or disables god mode for specified player(s).
|
||||||
if (player.Connected != PlayerConnectedState.PlayerConnected)
|
// /// </summary>
|
||||||
return;
|
// /// <param name="caller">The player issuing the command.</param>
|
||||||
|
// /// <param name="command">The command input containing targets.</param>
|
||||||
if (caller!.CanTarget(player))
|
//
|
||||||
{
|
// [RequiresPermissions("@css/cheats")]
|
||||||
God(caller, player, command);
|
// [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);
|
||||||
internal static void God(CCSPlayerController? caller, CCSPlayerController player, CommandInfo? command = null)
|
// if (targets == null) return;
|
||||||
{
|
//
|
||||||
if (!caller.CanTarget(player)) return;
|
// var playersToTarget = targets.Players.Where(player => player.IsValid && player is {IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||||
|
//
|
||||||
// Set default caller name if not provided
|
// playersToTarget.ForEach(player =>
|
||||||
var callerName = caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
// {
|
||||||
|
// if (player.Connected != PlayerConnectedState.PlayerConnected)
|
||||||
// Toggle god mode for the player
|
// return;
|
||||||
if (!GodPlayers.Add(player.Slot))
|
//
|
||||||
{
|
// if (caller!.CanTarget(player))
|
||||||
GodPlayers.Remove(player.Slot);
|
// {
|
||||||
}
|
// God(caller, player, command);
|
||||||
|
// }
|
||||||
// Log the command
|
// });
|
||||||
if (command == null)
|
//
|
||||||
Helper.LogCommand(caller, $"css_god {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
// Helper.LogCommand(caller, command);
|
||||||
else
|
// }
|
||||||
Helper.LogCommand(caller, command);
|
//
|
||||||
|
// /// <summary>
|
||||||
// Determine message key and arguments for the god mode notification
|
// /// Toggles god mode for a player and notifies admins.
|
||||||
var (activityMessageKey, adminActivityArgs) =
|
// /// </summary>
|
||||||
("sa_admin_god_message",
|
// /// <param name="caller">The player/admin toggling god mode.</param>
|
||||||
new object[] { "CALLER", player.PlayerName });
|
// /// <param name="player">The target player whose god mode changes.</param>
|
||||||
|
// /// <param name="command">Optional command info for logging.</param>
|
||||||
// Display admin activity message to other players
|
// internal static void God(CCSPlayerController? caller, CCSPlayerController player, CommandInfo? command = null)
|
||||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
// {
|
||||||
{
|
// if (!caller.CanTarget(player)) return;
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
//
|
||||||
}
|
// // Set default caller name if not provided
|
||||||
}
|
// var callerName = caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
||||||
|
//
|
||||||
[CommandHelper(1, "<#userid or name> [duration]")]
|
// // Toggle god mode for the player
|
||||||
[RequiresPermissions("@css/slay")]
|
// if (!GodPlayers.Add(player.Slot))
|
||||||
public void OnFreezeCommand(CCSPlayerController? caller, CommandInfo command)
|
// {
|
||||||
{
|
// GodPlayers.Remove(player.Slot);
|
||||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
// }
|
||||||
int.TryParse(command.GetArg(2), out var time);
|
//
|
||||||
|
// // Log the command
|
||||||
var targets = GetTarget(command);
|
// if (command == null)
|
||||||
if (targets == null) return;
|
// Helper.LogCommand(caller, $"css_god {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
||||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, PawnIsAlive: true, IsHLTV: false }).ToList();
|
//
|
||||||
|
// // Determine message key and arguments for the god mode notification
|
||||||
playersToTarget.ForEach(player =>
|
// var (activityMessageKey, adminActivityArgs) =
|
||||||
{
|
// ("sa_admin_god_message",
|
||||||
if (caller!.CanTarget(player))
|
// new object[] { "CALLER", player.PlayerName });
|
||||||
{
|
//
|
||||||
Freeze(caller, player, time, callerName, command);
|
// // Display admin activity message to other players
|
||||||
}
|
// if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
});
|
// {
|
||||||
}
|
// Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
|
// }
|
||||||
internal static void Freeze(CCSPlayerController? caller, CCSPlayerController player, int time, string? callerName = null, CommandInfo? command = null)
|
// }
|
||||||
{
|
//
|
||||||
if (!player.IsValid) return;
|
// /// <summary>
|
||||||
if (!caller.CanTarget(player)) return;
|
// /// Freezes target player(s) for an optional specified duration.
|
||||||
|
// /// </summary>
|
||||||
// Set default caller name if not provided
|
// /// <param name="caller">The player issuing the freeze command.</param>
|
||||||
callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
// /// <param name="command">The command input containing targets and duration.</param>
|
||||||
|
// [CommandHelper(1, "<#userid or name> [duration]")]
|
||||||
// Freeze player pawn
|
// [RequiresPermissions("@css/slay")]
|
||||||
player.Pawn.Value?.Freeze();
|
// public void OnFreezeCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
|
// {
|
||||||
// Determine message keys and arguments for the freeze notification
|
// var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
var (activityMessageKey, adminActivityArgs) =
|
// int.TryParse(command.GetArg(2), out var time);
|
||||||
("sa_admin_freeze_message",
|
//
|
||||||
new object[] { "CALLER", player.PlayerName });
|
// var targets = GetTarget(command);
|
||||||
|
// if (targets == null) return;
|
||||||
// Display admin activity message to other players
|
// var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
//
|
||||||
{
|
// playersToTarget.ForEach(player =>
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
// {
|
||||||
}
|
// if (caller!.CanTarget(player))
|
||||||
|
// {
|
||||||
// Schedule unfreeze for the player if time is specified
|
// Freeze(caller, player, time, callerName, command);
|
||||||
if (time > 0)
|
// }
|
||||||
{
|
// });
|
||||||
Instance.AddTimer(time, () => player.Pawn.Value?.Unfreeze(), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
//
|
||||||
}
|
// Helper.LogCommand(caller, command);
|
||||||
|
// }
|
||||||
// Log the command and send Discord notification
|
//
|
||||||
if (command == null)
|
// /// <summary>
|
||||||
Helper.LogCommand(caller, $"css_freeze {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time}");
|
// /// Resizes the target player(s) models to a specified scale.
|
||||||
else
|
// /// </summary>
|
||||||
Helper.LogCommand(caller, command);
|
// /// <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]")]
|
||||||
[CommandHelper(1, "<#userid or name>")]
|
// [RequiresPermissions("@css/slay")]
|
||||||
[RequiresPermissions("@css/slay")]
|
// public void OnResizeCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
public void OnUnfreezeCommand(CCSPlayerController? caller, CommandInfo command)
|
// {
|
||||||
{
|
// var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
||||||
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);
|
// var targets = GetTarget(command);
|
||||||
if (targets == null) return;
|
// if (targets == null) return;
|
||||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, PawnIsAlive: true, IsHLTV: false }).ToList();
|
// var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||||
|
//
|
||||||
playersToTarget.ForEach(player =>
|
// playersToTarget.ForEach(player =>
|
||||||
{
|
// {
|
||||||
Unfreeze(caller, player, callerName, command);
|
// if (!caller!.CanTarget(player)) return;
|
||||||
});
|
//
|
||||||
}
|
// var sceneNode = player.PlayerPawn.Value!.CBodyComponent?.SceneNode;
|
||||||
|
// if (sceneNode == null) return;
|
||||||
internal static void Unfreeze(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
|
//
|
||||||
{
|
// sceneNode.GetSkeletonInstance().Scale = size;
|
||||||
if (!player.IsValid) return;
|
// player.PlayerPawn.Value.AcceptInput("SetScale", null, null, size.ToString(CultureInfo.InvariantCulture));
|
||||||
if (!caller.CanTarget(player)) return;
|
//
|
||||||
|
// Server.NextWorldUpdate(() =>
|
||||||
// Set default caller name if not provided
|
// {
|
||||||
callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
// Utilities.SetStateChanged(player.PlayerPawn.Value, "CBaseEntity", "m_CBodyComponent");
|
||||||
|
// });
|
||||||
// Unfreeze player pawn
|
//
|
||||||
player.Pawn.Value?.Unfreeze();
|
// var (activityMessageKey, adminActivityArgs) =
|
||||||
|
// ("sa_admin_resize_message",
|
||||||
// Determine message keys and arguments for the unfreeze notification
|
// new object[] { "CALLER", player.PlayerName });
|
||||||
var (activityMessageKey, adminActivityArgs) =
|
//
|
||||||
("sa_admin_unfreeze_message",
|
// // Display admin activity message to other players
|
||||||
new object[] { "CALLER", player.PlayerName });
|
// if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
|
// {
|
||||||
// Display admin activity message to other players
|
// Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
// }
|
||||||
{
|
// });
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
//
|
||||||
}
|
// Helper.LogCommand(caller, command);
|
||||||
|
// }
|
||||||
// Log the command and send Discord notification
|
//
|
||||||
if (command == null)
|
// /// <summary>
|
||||||
Helper.LogCommand(caller, $"css_unfreeze {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
// /// Freezes a single player and optionally schedules automatic unfreeze after a duration.
|
||||||
else
|
// /// </summary>
|
||||||
Helper.LogCommand(caller, command);
|
// /// <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)}");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Admin;
|
using CounterStrikeSharp.API.Modules.Admin;
|
||||||
using CounterStrikeSharp.API.Modules.Commands;
|
using CounterStrikeSharp.API.Modules.Commands;
|
||||||
@@ -10,9 +11,12 @@ namespace CS2_SimpleAdmin;
|
|||||||
|
|
||||||
public partial class CS2_SimpleAdmin
|
public partial class CS2_SimpleAdmin
|
||||||
{
|
{
|
||||||
internal static readonly Dictionary<int, float> SpeedPlayers = [];
|
/// <summary>
|
||||||
internal static readonly Dictionary<CCSPlayerController, float> GravityPlayers = [];
|
/// Executes the 'slay' command, forcing the targeted players to commit suicide.
|
||||||
|
/// Checks player validity and permissions.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">Player or console issuing the command.</param>
|
||||||
|
/// <param name="command">Command details, including targets.</param>
|
||||||
[RequiresPermissions("@css/slay")]
|
[RequiresPermissions("@css/slay")]
|
||||||
[CommandHelper(minArgs: 1, usage: "<#userid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[CommandHelper(minArgs: 1, usage: "<#userid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
public void OnSlayCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnSlayCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
@@ -21,14 +25,23 @@ public partial class CS2_SimpleAdmin
|
|||||||
var targets = GetTarget(command);
|
var targets = GetTarget(command);
|
||||||
if (targets == null) return;
|
if (targets == null) return;
|
||||||
|
|
||||||
var playersToTarget = targets.Players.Where(player => player.IsValid && player is { PawnIsAlive: true, IsHLTV: false }).ToList();
|
var playersToTarget = targets.Players.Where(player => player.IsValid && player is {IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
playersToTarget.ForEach(player =>
|
||||||
{
|
{
|
||||||
Slay(caller, player, callerName, command);
|
Slay(caller, player, callerName, command);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Helper.LogCommand(caller, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs the actual slay action on a player, with notification and logging.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">Admin or console issuing the slay.</param>
|
||||||
|
/// <param name="player">Target player to slay.</param>
|
||||||
|
/// <param name="callerName">Optional name to display as the slayer.</param>
|
||||||
|
/// <param name="command">Optional command info for logging.</param>
|
||||||
internal static void Slay(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
|
internal static void Slay(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
|
||||||
{
|
{
|
||||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected) return;
|
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected) return;
|
||||||
@@ -48,386 +61,19 @@ public partial class CS2_SimpleAdmin
|
|||||||
// Display admin activity message to other players
|
// Display admin activity message to other players
|
||||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
||||||
{
|
{
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log the command and send Discord notification
|
// Log the command and send Discord notification
|
||||||
if (command == null)
|
if (command == null)
|
||||||
Helper.LogCommand(caller, $"css_slay {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
Helper.LogCommand(caller, $"css_slay {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
||||||
else
|
|
||||||
Helper.LogCommand(caller, command);
|
|
||||||
}
|
|
||||||
|
|
||||||
[RequiresPermissions("@css/cheats")]
|
|
||||||
[CommandHelper(minArgs: 2, usage: "<#userid or name> <weapon>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
|
||||||
public void OnGiveCommand(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 { PawnIsAlive: true, IsHLTV: false }).ToList();
|
|
||||||
var weaponName = command.GetArg(2);
|
|
||||||
|
|
||||||
// check if item is typed
|
|
||||||
// if (weaponName.Length < 2)
|
|
||||||
// {
|
|
||||||
// command.ReplyToCommand($"No weapon typed.");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// check if weapon is knife
|
|
||||||
if (weaponName.Contains("_knife") || weaponName.Contains("bayonet"))
|
|
||||||
{
|
|
||||||
if (CoreConfig.FollowCS2ServerGuidelines)
|
|
||||||
{
|
|
||||||
command.ReplyToCommand($"Cannot Give {weaponName} because it's illegal to be given.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (player.Connected != PlayerConnectedState.PlayerConnected)
|
|
||||||
return;
|
|
||||||
|
|
||||||
GiveWeapon(caller, player, weaponName, callerName, command);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GiveWeapon(CCSPlayerController? caller, CCSPlayerController player, string weaponName, string? callerName = null, CommandInfo? command = null)
|
|
||||||
{
|
|
||||||
if (!caller.CanTarget(player)) return;
|
|
||||||
|
|
||||||
// Set default caller name if not provided
|
|
||||||
callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
|
||||||
var weapons = WeaponHelper.GetWeaponsByPartialName(weaponName);
|
|
||||||
|
|
||||||
switch (weapons.Count)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
return;
|
|
||||||
case > 1:
|
|
||||||
{
|
|
||||||
var weaponList = string.Join(", ", weapons.Select(w => w.EnumMemberValue));
|
|
||||||
command?.ReplyToCommand($"Found weapons with a similar name: {weaponList}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Give weapon to the player
|
|
||||||
player.GiveNamedItem(weapons.First().EnumValue);
|
|
||||||
|
|
||||||
// Log the command
|
|
||||||
if (command == null)
|
|
||||||
Helper.LogCommand(caller, $"css_giveweapon {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {weaponName}");
|
|
||||||
else
|
|
||||||
Helper.LogCommand(caller, command);
|
|
||||||
|
|
||||||
// Determine message keys and arguments for the weapon give notification
|
|
||||||
var (activityMessageKey, adminActivityArgs) =
|
|
||||||
("sa_admin_give_message",
|
|
||||||
new object[] { "CALLER", player.PlayerName, weaponName });
|
|
||||||
|
|
||||||
// Display admin activity message to other players
|
|
||||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
|
||||||
{
|
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void GiveWeapon(CCSPlayerController? caller, CCSPlayerController player, CsItem weapon, string? callerName = null, CommandInfo? command = null)
|
|
||||||
{
|
|
||||||
if (!caller.CanTarget(player)) return;
|
|
||||||
|
|
||||||
// Set default caller name if not provided
|
|
||||||
callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
|
||||||
|
|
||||||
// Give weapon to the player
|
|
||||||
player.GiveNamedItem(weapon);
|
|
||||||
|
|
||||||
// Log the command
|
|
||||||
if (command == null)
|
|
||||||
Helper.LogCommand(caller, $"css_giveweapon {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {weapon.ToString()}");
|
|
||||||
else
|
|
||||||
Helper.LogCommand(caller, command);
|
|
||||||
|
|
||||||
// Determine message keys and arguments for the weapon give notification
|
|
||||||
var (activityMessageKey, adminActivityArgs) =
|
|
||||||
("sa_admin_give_message",
|
|
||||||
new object[] { "CALLER", player.PlayerName, weapon.ToString() });
|
|
||||||
|
|
||||||
// Display admin activity message to other players
|
|
||||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
|
||||||
{
|
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[RequiresPermissions("@css/slay")]
|
|
||||||
[CommandHelper(minArgs: 1, usage: "<#userid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
|
||||||
public void OnStripCommand(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 { PawnIsAlive: true, IsHLTV: false }).ToList();
|
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (caller!.CanTarget(player))
|
|
||||||
{
|
|
||||||
StripWeapons(caller, player, callerName, command);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void StripWeapons(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
|
|
||||||
{
|
|
||||||
if (!caller.CanTarget(player)) return;
|
|
||||||
|
|
||||||
// Set default caller name if not provided
|
|
||||||
callerName ??= caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
|
||||||
|
|
||||||
// Check if player is valid, alive, and connected
|
|
||||||
if (!player.IsValid || !player.PawnIsAlive || player.Connected != PlayerConnectedState.PlayerConnected)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Strip weapons from the player
|
|
||||||
player.RemoveWeapons();
|
|
||||||
|
|
||||||
// Log the command
|
|
||||||
if (command == null)
|
|
||||||
Helper.LogCommand(caller, $"css_strip {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
|
||||||
else
|
|
||||||
Helper.LogCommand(caller, command);
|
|
||||||
|
|
||||||
// Determine message keys and arguments for the weapon strip notification
|
|
||||||
var (activityMessageKey, adminActivityArgs) =
|
|
||||||
("sa_admin_strip_message",
|
|
||||||
new object[] { "CALLER", player.PlayerName });
|
|
||||||
|
|
||||||
// Display admin activity message to other players
|
|
||||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
|
||||||
{
|
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[RequiresPermissions("@css/slay")]
|
|
||||||
[CommandHelper(minArgs: 1, usage: "<#userid or name> <health>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
|
||||||
public void OnHpCommand(CCSPlayerController? caller, CommandInfo command)
|
|
||||||
{
|
|
||||||
int.TryParse(command.GetArg(2), out var health);
|
|
||||||
|
|
||||||
var targets = GetTarget(command);
|
|
||||||
if (targets == null) return;
|
|
||||||
|
|
||||||
var playersToTarget = targets.Players.Where(player => player.IsValid && player is { PawnIsAlive: true, IsHLTV: false }).ToList();
|
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (caller!.CanTarget(player))
|
|
||||||
{
|
|
||||||
SetHp(caller, player, health, command);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void SetHp(CCSPlayerController? caller, CCSPlayerController player, int health, CommandInfo? command = null)
|
|
||||||
{
|
|
||||||
if (!player.IsValid || player.IsHLTV) return;
|
|
||||||
if (!caller.CanTarget(player)) return;
|
|
||||||
|
|
||||||
// Set default caller name if not provided
|
|
||||||
var callerName = caller != null ? caller.PlayerName : _localizer?["sa_console"] ?? "Console";
|
|
||||||
|
|
||||||
// Set player's health
|
|
||||||
player.SetHp(health);
|
|
||||||
|
|
||||||
// Log the command
|
|
||||||
if (command == null)
|
|
||||||
Helper.LogCommand(caller, $"css_hp {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {health}");
|
|
||||||
else
|
|
||||||
Helper.LogCommand(caller, command);
|
|
||||||
|
|
||||||
// Determine message keys and arguments for the HP set notification
|
|
||||||
var (activityMessageKey, adminActivityArgs) =
|
|
||||||
("sa_admin_hp_message",
|
|
||||||
new object[] { "CALLER", player.PlayerName });
|
|
||||||
|
|
||||||
// Display admin activity message to other players
|
|
||||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
|
||||||
{
|
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[RequiresPermissions("@css/slay")]
|
|
||||||
[CommandHelper(minArgs: 1, usage: "<#userid or name> <speed>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
|
||||||
public void OnSpeedCommand(CCSPlayerController? caller, CommandInfo command)
|
|
||||||
{
|
|
||||||
float.TryParse(command.GetArg(2), NumberStyles.Float, CultureInfo.InvariantCulture, out var speed);
|
|
||||||
|
|
||||||
var targets = GetTarget(command);
|
|
||||||
if (targets == null) return;
|
|
||||||
|
|
||||||
var playersToTarget = targets.Players.Where(player => player.IsValid && player is { PawnIsAlive: true, IsHLTV: false }).ToList();
|
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (player.Connected != PlayerConnectedState.PlayerConnected)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (caller!.CanTarget(player))
|
|
||||||
{
|
|
||||||
SetSpeed(caller, player, speed, command);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void SetSpeed(CCSPlayerController? caller, CCSPlayerController player, float speed, 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";
|
|
||||||
|
|
||||||
// Set player's speed
|
|
||||||
player.SetSpeed(speed);
|
|
||||||
|
|
||||||
if (speed == 1f)
|
|
||||||
SpeedPlayers.Remove(player.Slot);
|
|
||||||
else
|
|
||||||
SpeedPlayers[player.Slot] = speed;
|
|
||||||
|
|
||||||
// Log the command
|
|
||||||
if (command == null)
|
|
||||||
Helper.LogCommand(caller, $"css_speed {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {speed}");
|
|
||||||
else
|
|
||||||
Helper.LogCommand(caller, command);
|
|
||||||
|
|
||||||
// Determine message keys and arguments for the speed set notification
|
|
||||||
var (activityMessageKey, adminActivityArgs) =
|
|
||||||
("sa_admin_speed_message",
|
|
||||||
new object[] { "CALLER", player.PlayerName });
|
|
||||||
|
|
||||||
// Display admin activity message to other players
|
|
||||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
|
||||||
{
|
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[RequiresPermissions("@css/slay")]
|
|
||||||
[CommandHelper(minArgs: 1, usage: "<#userid or name> <gravity>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
|
||||||
public void OnGravityCommand(CCSPlayerController? caller, CommandInfo command)
|
|
||||||
{
|
|
||||||
float.TryParse(command.GetArg(2), NumberStyles.Float, CultureInfo.InvariantCulture, out var gravity);
|
|
||||||
|
|
||||||
var targets = GetTarget(command);
|
|
||||||
if (targets == null) return;
|
|
||||||
|
|
||||||
var playersToTarget = targets.Players.Where(player => player.IsValid && player is { PawnIsAlive: true, IsHLTV: false }).ToList();
|
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (player.Connected != PlayerConnectedState.PlayerConnected)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (caller!.CanTarget(player))
|
|
||||||
{
|
|
||||||
SetGravity(caller, player, gravity, command);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void SetGravity(CCSPlayerController? caller, CCSPlayerController player, float gravity, 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";
|
|
||||||
|
|
||||||
// Set player's gravity
|
|
||||||
player.SetGravity(gravity);
|
|
||||||
|
|
||||||
if (gravity == 1f)
|
|
||||||
GravityPlayers.Remove(player);
|
|
||||||
else
|
|
||||||
GravityPlayers[player] = gravity;
|
|
||||||
|
|
||||||
// Log the command
|
|
||||||
if (command == null)
|
|
||||||
Helper.LogCommand(caller, $"css_gravity {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {gravity}");
|
|
||||||
else
|
|
||||||
Helper.LogCommand(caller, command);
|
|
||||||
|
|
||||||
// Determine message keys and arguments for the gravity set notification
|
|
||||||
var (activityMessageKey, adminActivityArgs) =
|
|
||||||
("sa_admin_gravity_message",
|
|
||||||
new object[] { "CALLER", player.PlayerName });
|
|
||||||
|
|
||||||
// Display admin activity message to other players
|
|
||||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
|
||||||
{
|
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[RequiresPermissions("@css/slay")]
|
|
||||||
[CommandHelper(minArgs: 1, usage: "<#userid or name> <money>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
|
||||||
public void OnMoneyCommand(CCSPlayerController? caller, CommandInfo command)
|
|
||||||
{
|
|
||||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
|
||||||
int.TryParse(command.GetArg(2), out var money);
|
|
||||||
|
|
||||||
var targets = GetTarget(command);
|
|
||||||
if (targets == null) return;
|
|
||||||
|
|
||||||
var playersToTarget = targets.Players.Where(player => player.IsValid && player is { PawnIsAlive: true, IsHLTV: false }).ToList();
|
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (player.Connected != PlayerConnectedState.PlayerConnected)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (caller!.CanTarget(player))
|
|
||||||
{
|
|
||||||
SetMoney(caller, player, money, command);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void SetMoney(CCSPlayerController? caller, CCSPlayerController player, int money, 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";
|
|
||||||
|
|
||||||
// Set player's money
|
|
||||||
player.SetMoney(money);
|
|
||||||
|
|
||||||
// Log the command
|
|
||||||
if (command == null)
|
|
||||||
Helper.LogCommand(caller, $"css_money {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {money}");
|
|
||||||
else
|
|
||||||
Helper.LogCommand(caller, command);
|
|
||||||
|
|
||||||
// Determine message keys and arguments for the money set notification
|
|
||||||
var (activityMessageKey, adminActivityArgs) =
|
|
||||||
("sa_admin_money_message",
|
|
||||||
new object[] { "CALLER", player.PlayerName });
|
|
||||||
|
|
||||||
// Display admin activity message to other players
|
|
||||||
if (caller == null || !SilentPlayers.Contains(caller.Slot))
|
|
||||||
{
|
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies damage as a slap effect to the targeted players.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">The player/admin executing the slap command.</param>
|
||||||
|
/// <param name="command">The command including targets and optional damage value.</param>
|
||||||
[RequiresPermissions("@css/slay")]
|
[RequiresPermissions("@css/slay")]
|
||||||
[CommandHelper(minArgs: 1, usage: "<#userid or name> [damage]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[CommandHelper(minArgs: 1, usage: "<#userid or name> [damage]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
public void OnSlapCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnSlapCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
@@ -437,7 +83,7 @@ public partial class CS2_SimpleAdmin
|
|||||||
var targets = GetTarget(command);
|
var targets = GetTarget(command);
|
||||||
if (targets == null) return;
|
if (targets == null) return;
|
||||||
|
|
||||||
var playersToTarget = targets.Players.Where(player => player.IsValid && player is { PawnIsAlive: true, IsHLTV: false }).ToList();
|
var playersToTarget = targets.Players.Where(player => player.IsValid && player is { IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
|
||||||
|
|
||||||
if (command.ArgCount >= 2)
|
if (command.ArgCount >= 2)
|
||||||
{
|
{
|
||||||
@@ -454,8 +100,17 @@ public partial class CS2_SimpleAdmin
|
|||||||
Slap(caller, player, damage, command);
|
Slap(caller, player, damage, command);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Helper.LogCommand(caller, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies slap damage to a specific player with notifications and logging.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">The player/admin applying the slap effect.</param>
|
||||||
|
/// <param name="player">The target player to slap.</param>
|
||||||
|
/// <param name="damage">The damage amount to apply.</param>
|
||||||
|
/// <param name="command">Optional command info for logging.</param>
|
||||||
internal static void Slap(CCSPlayerController? caller, CCSPlayerController player, int damage, CommandInfo? command = null)
|
internal static void Slap(CCSPlayerController? caller, CCSPlayerController player, int damage, CommandInfo? command = null)
|
||||||
{
|
{
|
||||||
if (!caller.CanTarget(player)) return;
|
if (!caller.CanTarget(player)) return;
|
||||||
@@ -465,13 +120,12 @@ public partial class CS2_SimpleAdmin
|
|||||||
|
|
||||||
// Apply slap damage to the player
|
// Apply slap damage to the player
|
||||||
player.Pawn.Value?.Slap(damage);
|
player.Pawn.Value?.Slap(damage);
|
||||||
|
player.EmitSound("BaseGrenade.JumpThrowM");
|
||||||
|
|
||||||
// Log the command
|
// Log the command
|
||||||
if (command == null)
|
if (command == null)
|
||||||
Helper.LogCommand(caller, $"css_slap {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {damage}");
|
Helper.LogCommand(caller, $"css_slap {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {damage}");
|
||||||
else
|
|
||||||
Helper.LogCommand(caller, command);
|
|
||||||
|
|
||||||
// Determine message key and arguments for the slap notification
|
// Determine message key and arguments for the slap notification
|
||||||
var (activityMessageKey, adminActivityArgs) =
|
var (activityMessageKey, adminActivityArgs) =
|
||||||
("sa_admin_slap_message",
|
("sa_admin_slap_message",
|
||||||
@@ -482,10 +136,15 @@ public partial class CS2_SimpleAdmin
|
|||||||
|
|
||||||
if (_localizer != null)
|
if (_localizer != null)
|
||||||
{
|
{
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the team of targeted players with optional kill on switch.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">The player/admin issuing the command.</param>
|
||||||
|
/// <param name="command">The command containing targets, team info, and optional kill flag.</param>
|
||||||
[RequiresPermissions("@css/kick")]
|
[RequiresPermissions("@css/kick")]
|
||||||
[CommandHelper(minArgs: 2, usage: "<#userid or name> [<ct/tt/spec>] [-k]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
[CommandHelper(minArgs: 2, usage: "<#userid or name> [<ct/tt/spec>] [-k]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||||
public void OnTeamCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnTeamCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
@@ -531,8 +190,19 @@ public partial class CS2_SimpleAdmin
|
|||||||
{
|
{
|
||||||
ChangeTeam(caller, player, _teamName, teamNum, kill, command);
|
ChangeTeam(caller, player, _teamName, teamNum, kill, command);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Helper.LogCommand(caller, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the team of a player with various conditions and logs the operation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">The player/admin issuing the change.</param>
|
||||||
|
/// <param name="player">The target player.</param>
|
||||||
|
/// <param name="teamName">Team name string.</param>
|
||||||
|
/// <param name="teamNum">Team enumeration value.</param>
|
||||||
|
/// <param name="kill">If true, kills player on team change.</param>
|
||||||
|
/// <param name="command">Optional command info for logging.</param>
|
||||||
internal static void ChangeTeam(CCSPlayerController? caller, CCSPlayerController player, string teamName, CsTeam teamNum, bool kill, CommandInfo? command = null)
|
internal static void ChangeTeam(CCSPlayerController? caller, CCSPlayerController player, string teamName, CsTeam teamNum, bool kill, CommandInfo? command = null)
|
||||||
{
|
{
|
||||||
// Check if the player is valid and connected
|
// Check if the player is valid and connected
|
||||||
@@ -548,7 +218,7 @@ public partial class CS2_SimpleAdmin
|
|||||||
// Change team based on the provided teamName and conditions
|
// Change team based on the provided teamName and conditions
|
||||||
if (!teamName.Equals("swap", StringComparison.OrdinalIgnoreCase))
|
if (!teamName.Equals("swap", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (player.PawnIsAlive && teamNum != CsTeam.Spectator && !kill && Instance.Config.OtherSettings.TeamSwitchType == 1)
|
if (player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && teamNum != CsTeam.Spectator && !kill && Instance.Config.OtherSettings.TeamSwitchType == 1)
|
||||||
player.SwitchTeam(teamNum);
|
player.SwitchTeam(teamNum);
|
||||||
else
|
else
|
||||||
player.ChangeTeam(teamNum);
|
player.ChangeTeam(teamNum);
|
||||||
@@ -559,7 +229,7 @@ public partial class CS2_SimpleAdmin
|
|||||||
{
|
{
|
||||||
var _teamNum = (CsTeam)player.TeamNum == CsTeam.Terrorist ? CsTeam.CounterTerrorist : CsTeam.Terrorist;
|
var _teamNum = (CsTeam)player.TeamNum == CsTeam.Terrorist ? CsTeam.CounterTerrorist : CsTeam.Terrorist;
|
||||||
teamName = _teamNum == CsTeam.Terrorist ? "TT" : "CT";
|
teamName = _teamNum == CsTeam.Terrorist ? "TT" : "CT";
|
||||||
if (player.PawnIsAlive && !kill && Instance.Config.OtherSettings.TeamSwitchType == 1)
|
if (player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && !kill && Instance.Config.OtherSettings.TeamSwitchType == 1)
|
||||||
player.SwitchTeam(_teamNum);
|
player.SwitchTeam(_teamNum);
|
||||||
else
|
else
|
||||||
player.ChangeTeam(_teamNum);
|
player.ChangeTeam(_teamNum);
|
||||||
@@ -569,8 +239,6 @@ public partial class CS2_SimpleAdmin
|
|||||||
// Log the command
|
// Log the command
|
||||||
if (command == null)
|
if (command == null)
|
||||||
Helper.LogCommand(caller, $"css_team {player.PlayerName} {teamName}");
|
Helper.LogCommand(caller, $"css_team {player.PlayerName} {teamName}");
|
||||||
else
|
|
||||||
Helper.LogCommand(caller, command);
|
|
||||||
|
|
||||||
// Determine message key and arguments for the team change notification
|
// Determine message key and arguments for the team change notification
|
||||||
var activityMessageKey = "sa_admin_team_message";
|
var activityMessageKey = "sa_admin_team_message";
|
||||||
@@ -579,9 +247,14 @@ public partial class CS2_SimpleAdmin
|
|||||||
// Display admin activity message to other players
|
// Display admin activity message to other players
|
||||||
if (caller != null && SilentPlayers.Contains(caller.Slot)) return;
|
if (caller != null && SilentPlayers.Contains(caller.Slot)) return;
|
||||||
|
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Renames targeted players to a new name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">The admin issuing the rename command.</param>
|
||||||
|
/// <param name="command">The command including targets and new name.</param>
|
||||||
[CommandHelper(1, "<#userid or name> <new name>")]
|
[CommandHelper(1, "<#userid or name> <new name>")]
|
||||||
[RequiresPermissions("@css/kick")]
|
[RequiresPermissions("@css/kick")]
|
||||||
public void OnRenameCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnRenameCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
@@ -620,13 +293,18 @@ public partial class CS2_SimpleAdmin
|
|||||||
// Display admin activity message to other players
|
// Display admin activity message to other players
|
||||||
if (caller != null && SilentPlayers.Contains(caller.Slot)) return;
|
if (caller != null && SilentPlayers.Contains(caller.Slot)) return;
|
||||||
|
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
|
|
||||||
// Rename the player
|
// Rename the player
|
||||||
player.Rename(newName);
|
player.Rename(newName);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Renames permamently targeted players to a new name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">The admin issuing the pre-rename command.</param>
|
||||||
|
/// <param name="command">The command containing targets and new alias.</param>
|
||||||
[CommandHelper(1, "<#userid or name> <new name>")]
|
[CommandHelper(1, "<#userid or name> <new name>")]
|
||||||
[RequiresPermissions("@css/ban")]
|
[RequiresPermissions("@css/ban")]
|
||||||
public void OnPrenameCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnPrenameCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
@@ -661,7 +339,7 @@ public partial class CS2_SimpleAdmin
|
|||||||
// Display admin activity message to other players
|
// Display admin activity message to other players
|
||||||
if (caller != null && !SilentPlayers.Contains(caller.Slot))
|
if (caller != null && !SilentPlayers.Contains(caller.Slot))
|
||||||
{
|
{
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if the new name is valid and update the renamed players list
|
// Determine if the new name is valid and update the renamed players list
|
||||||
@@ -677,146 +355,270 @@ public partial class CS2_SimpleAdmin
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandHelper(1, "<#userid or name>")]
|
/// <summary>
|
||||||
[RequiresPermissions("@css/cheats")]
|
/// Teleports targeted player(s) to another player's location.
|
||||||
public void OnRespawnCommand(CCSPlayerController? caller, CommandInfo command)
|
/// </summary>
|
||||||
{
|
/// <param name="caller">Admin issuing teleport command.</param>
|
||||||
var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
/// <param name="command">Command containing teleport targets and destination.</param>
|
||||||
|
[CommandHelper(1, "<#userid or name> [#userid or name]")]
|
||||||
var targets = GetTarget(command);
|
|
||||||
if (targets == null) return;
|
|
||||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
|
|
||||||
|
|
||||||
playersToTarget.ForEach(player =>
|
|
||||||
{
|
|
||||||
if (player.Connected != PlayerConnectedState.PlayerConnected)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (caller!.CanTarget(player))
|
|
||||||
{
|
|
||||||
Respawn(caller, player, callerName, command);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void Respawn(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
|
|
||||||
{
|
|
||||||
// Check if the caller can target the player
|
|
||||||
if (!caller.CanTarget(player)) return;
|
|
||||||
|
|
||||||
// Set default caller name if not provided
|
|
||||||
callerName ??= caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName;
|
|
||||||
|
|
||||||
// Ensure the player's pawn is valid before attempting to respawn
|
|
||||||
if (_cBasePlayerControllerSetPawnFunc == null || player.PlayerPawn.Value == null || !player.PlayerPawn.IsValid) return;
|
|
||||||
|
|
||||||
// Perform the respawn operation
|
|
||||||
var playerPawn = player.PlayerPawn.Value;
|
|
||||||
_cBasePlayerControllerSetPawnFunc.Invoke(player, playerPawn, true, false);
|
|
||||||
VirtualFunction.CreateVoid<CCSPlayerController>(player.Handle, GameData.GetOffset("CCSPlayerController_Respawn"))(player);
|
|
||||||
|
|
||||||
if (player.UserId.HasValue && PlayersInfo.TryGetValue(player.UserId.Value, out var value) && value.DiePosition != null)
|
|
||||||
playerPawn.Teleport(value.DiePosition?.Position, value.DiePosition?.Angle);
|
|
||||||
|
|
||||||
// Log the command
|
|
||||||
if (command == null)
|
|
||||||
Helper.LogCommand(caller, $"css_respawn {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
|
|
||||||
else
|
|
||||||
Helper.LogCommand(caller, command);
|
|
||||||
|
|
||||||
// Determine message key and arguments for the respawn notification
|
|
||||||
var activityMessageKey = "sa_admin_respawn_message";
|
|
||||||
var adminActivityArgs = new object[] { "CALLER", player.PlayerName };
|
|
||||||
|
|
||||||
// Display admin activity message to other players
|
|
||||||
if (caller != null && SilentPlayers.Contains(caller.Slot)) return;
|
|
||||||
|
|
||||||
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHelper(1, "<#userid or name>")]
|
|
||||||
[RequiresPermissions("@css/kick")]
|
[RequiresPermissions("@css/kick")]
|
||||||
public void OnGotoCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnGotoCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
{
|
{
|
||||||
// Check if the caller is valid and has a live pawn
|
IEnumerable<CCSPlayerController> playersToTeleport;
|
||||||
if (caller == null || !caller.PawnIsAlive) return;
|
CCSPlayerController? destinationPlayer;
|
||||||
|
|
||||||
// Get the target players
|
|
||||||
var targets = GetTarget(command);
|
var targets = GetTarget(command);
|
||||||
if (targets == null || targets.Count() > 1) return;
|
|
||||||
|
|
||||||
var playersToTarget = targets.Players
|
if (command.ArgCount < 3)
|
||||||
.Where(player => player is { IsValid: true, IsHLTV: false })
|
{
|
||||||
.ToList();
|
if (caller == null || caller.PlayerPawn?.Value?.LifeState != (int)LifeState_t.LIFE_ALIVE)
|
||||||
|
return;
|
||||||
|
|
||||||
// Log the command
|
if (targets == null || targets.Count() != 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
destinationPlayer = targets.Players.FirstOrDefault(p =>
|
||||||
|
p is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE });
|
||||||
|
|
||||||
|
if (destinationPlayer == null || !caller.CanTarget(destinationPlayer) || caller.PlayerPawn.Value == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
playersToTeleport = [caller];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var destination = GetTarget(command, 2);
|
||||||
|
if (targets == null || destination == null || destination.Count() != 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
destinationPlayer = destination.Players.FirstOrDefault(p =>
|
||||||
|
p is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE });
|
||||||
|
|
||||||
|
if (destinationPlayer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
playersToTeleport = targets.Players
|
||||||
|
.Where(p => p is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE } && caller.CanTarget(p))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (!playersToTeleport.Any())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log command
|
||||||
Helper.LogCommand(caller, command);
|
Helper.LogCommand(caller, command);
|
||||||
|
|
||||||
// Process each player to teleport
|
foreach (var player in playersToTeleport)
|
||||||
foreach (var player in playersToTarget.Where(player => player is { Connected: PlayerConnectedState.PlayerConnected, PawnIsAlive: true }).Where(caller.CanTarget))
|
|
||||||
{
|
{
|
||||||
if (caller.PlayerPawn.Value == null)
|
if (player.PlayerPawn?.Value == null || destinationPlayer?.PlayerPawn?.Value == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Teleport the caller to the player and toggle noclip
|
player.TeleportPlayer(destinationPlayer);
|
||||||
caller.TeleportPlayer(player);
|
|
||||||
caller.PlayerPawn.Value.ToggleNoclip();
|
|
||||||
|
|
||||||
// Set a timer to toggle noclip back after 3 seconds
|
player.PlayerPawn.Value.Collision.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_DISSOLVING;
|
||||||
AddTimer(3, () => caller.PlayerPawn.Value.ToggleNoclip());
|
player.PlayerPawn.Value.Collision.CollisionAttribute.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_DISSOLVING;
|
||||||
|
Utilities.SetStateChanged(player, "CCollisionProperty", "m_CollisionGroup");
|
||||||
|
Utilities.SetStateChanged(player, "VPhysicsCollisionAttribute_t", "m_nCollisionGroup");
|
||||||
|
|
||||||
// Prepare message key and arguments for the teleport notification
|
destinationPlayer.PlayerPawn.Value.Collision.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_DISSOLVING;
|
||||||
var activityMessageKey = "sa_admin_tp_message";
|
destinationPlayer.PlayerPawn.Value.Collision.CollisionAttribute.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_DISSOLVING;
|
||||||
var adminActivityArgs = new object[] { "CALLER", player.PlayerName };
|
Utilities.SetStateChanged(destinationPlayer, "CCollisionProperty", "m_CollisionGroup");
|
||||||
|
Utilities.SetStateChanged(destinationPlayer, "VPhysicsCollisionAttribute_t", "m_nCollisionGroup");
|
||||||
|
|
||||||
// Show admin activity
|
AddTimer(4, () =>
|
||||||
if (!SilentPlayers.Contains(caller.Slot) && _localizer != null)
|
|
||||||
{
|
{
|
||||||
Helper.ShowAdminActivity(activityMessageKey, caller.PlayerName, adminActivityArgs);
|
if (player is { IsValid: true, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE })
|
||||||
|
{
|
||||||
|
player.PlayerPawn.Value.Collision.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_PLAYER;
|
||||||
|
player.PlayerPawn.Value.Collision.CollisionAttribute.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_PLAYER;
|
||||||
|
Utilities.SetStateChanged(player, "CCollisionProperty", "m_CollisionGroup");
|
||||||
|
Utilities.SetStateChanged(player, "VPhysicsCollisionAttribute_t", "m_nCollisionGroup");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destinationPlayer.IsValid && destinationPlayer.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE)
|
||||||
|
{
|
||||||
|
destinationPlayer.PlayerPawn.Value.Collision.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_PLAYER;
|
||||||
|
destinationPlayer.PlayerPawn.Value.Collision.CollisionAttribute.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_PLAYER;
|
||||||
|
Utilities.SetStateChanged(destinationPlayer, "CCollisionProperty", "m_CollisionGroup");
|
||||||
|
Utilities.SetStateChanged(destinationPlayer, "VPhysicsCollisionAttribute_t", "m_nCollisionGroup");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (caller != null && !SilentPlayers.Contains(caller.Slot) && _localizer != null)
|
||||||
|
{
|
||||||
|
Helper.ShowAdminActivity("sa_admin_tp_message", player.PlayerName, false, "CALLER", destinationPlayer.PlayerName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandHelper(1, "<#userid or name>")]
|
/// <summary>
|
||||||
|
/// Brings targeted player(s) to the caller or specified destination player's location.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="caller">Player issuing the bring command.</param>
|
||||||
|
/// <param name="command">Command containing the destination and targets.</param>
|
||||||
|
[CommandHelper(1, "<#destination or name> [#userid or name...]")]
|
||||||
[RequiresPermissions("@css/kick")]
|
[RequiresPermissions("@css/kick")]
|
||||||
public void OnBringCommand(CCSPlayerController? caller, CommandInfo command)
|
public void OnBringCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
{
|
{
|
||||||
// Check if the caller is valid and has a live pawn
|
IEnumerable<CCSPlayerController> playersToTeleport;
|
||||||
if (caller == null || !caller.PawnIsAlive) return;
|
CCSPlayerController? destinationPlayer;
|
||||||
|
|
||||||
// Get the target players
|
if (command.ArgCount < 3)
|
||||||
var targets = GetTarget(command);
|
{
|
||||||
if (targets == null || targets.Count() > 1) return;
|
if (caller == null || caller.PlayerPawn?.Value?.LifeState != (int)LifeState_t.LIFE_ALIVE)
|
||||||
|
return;
|
||||||
|
|
||||||
var playersToTarget = targets.Players
|
var targets = GetTarget(command);
|
||||||
.Where(player => player is { IsValid: true, IsHLTV: false })
|
if (targets == null || !targets.Any())
|
||||||
.ToList();
|
return;
|
||||||
|
|
||||||
// Log the command
|
destinationPlayer = caller;
|
||||||
|
|
||||||
|
playersToTeleport = targets.Players
|
||||||
|
.Where(p => p is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE } && caller.CanTarget(p))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var destination = GetTarget(command);
|
||||||
|
if (destination == null || destination.Count() != 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
destinationPlayer = destination.Players.FirstOrDefault(p =>
|
||||||
|
p is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE });
|
||||||
|
|
||||||
|
if (destinationPlayer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Rest args = targets to teleport
|
||||||
|
var targets = GetTarget(command, 2);
|
||||||
|
if (targets == null || !targets.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
playersToTeleport = targets.Players
|
||||||
|
.Where(p => p is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE } && caller!.CanTarget(p))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destinationPlayer == null || !playersToTeleport.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Log command
|
||||||
Helper.LogCommand(caller, command);
|
Helper.LogCommand(caller, command);
|
||||||
|
|
||||||
// Process each player to teleport
|
foreach (var player in playersToTeleport)
|
||||||
foreach (var player in playersToTarget.Where(player => player is { Connected: PlayerConnectedState.PlayerConnected, PawnIsAlive: true }).Where(caller.CanTarget))
|
|
||||||
{
|
{
|
||||||
if (caller.PlayerPawn.Value == null)
|
if (player.PlayerPawn?.Value == null || destinationPlayer.PlayerPawn?.Value == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Teleport the player to the caller and toggle noclip
|
// Teleport
|
||||||
player.TeleportPlayer(caller);
|
player.TeleportPlayer(destinationPlayer);
|
||||||
caller.PlayerPawn.Value.ToggleNoclip();
|
|
||||||
|
|
||||||
// Set a timer to toggle noclip back after 3 seconds
|
player.PlayerPawn.Value.Collision.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_DISSOLVING;
|
||||||
AddTimer(3, () => caller.PlayerPawn.Value.ToggleNoclip());
|
player.PlayerPawn.Value.Collision.CollisionAttribute.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_DISSOLVING;
|
||||||
|
Utilities.SetStateChanged(player, "CCollisionProperty", "m_CollisionGroup");
|
||||||
|
Utilities.SetStateChanged(player, "VPhysicsCollisionAttribute_t", "m_nCollisionGroup");
|
||||||
|
|
||||||
// Prepare message key and arguments for the bring notification
|
destinationPlayer.PlayerPawn.Value.Collision.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_DISSOLVING;
|
||||||
var activityMessageKey = "sa_admin_bring_message";
|
destinationPlayer.PlayerPawn.Value.Collision.CollisionAttribute.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_DISSOLVING;
|
||||||
var adminActivityArgs = new object[] { "CALLER", player.PlayerName };
|
Utilities.SetStateChanged(destinationPlayer, "CCollisionProperty", "m_CollisionGroup");
|
||||||
|
Utilities.SetStateChanged(destinationPlayer, "VPhysicsCollisionAttribute_t", "m_nCollisionGroup");
|
||||||
|
|
||||||
// Show admin activity
|
AddTimer(4, () =>
|
||||||
if (!SilentPlayers.Contains(caller.Slot) && _localizer != null)
|
|
||||||
{
|
{
|
||||||
Helper.ShowAdminActivity(activityMessageKey, caller.PlayerName, adminActivityArgs);
|
if (player is { IsValid: true, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE })
|
||||||
|
{
|
||||||
|
player.PlayerPawn.Value.Collision.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_PLAYER;
|
||||||
|
player.PlayerPawn.Value.Collision.CollisionAttribute.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_PLAYER;
|
||||||
|
Utilities.SetStateChanged(player, "CCollisionProperty", "m_CollisionGroup");
|
||||||
|
Utilities.SetStateChanged(player, "VPhysicsCollisionAttribute_t", "m_nCollisionGroup");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destinationPlayer.IsValid && destinationPlayer.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE)
|
||||||
|
{
|
||||||
|
destinationPlayer.PlayerPawn.Value.Collision.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_PLAYER;
|
||||||
|
destinationPlayer.PlayerPawn.Value.Collision.CollisionAttribute.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_PLAYER;
|
||||||
|
Utilities.SetStateChanged(destinationPlayer, "CCollisionProperty", "m_CollisionGroup");
|
||||||
|
Utilities.SetStateChanged(destinationPlayer, "VPhysicsCollisionAttribute_t", "m_nCollisionGroup");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (caller != null && !SilentPlayers.Contains(caller.Slot) && _localizer != null)
|
||||||
|
{
|
||||||
|
Helper.ShowAdminActivity("sa_admin_bring_message", player.PlayerName, false, "CALLER", destinationPlayer.PlayerName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [CommandHelper(1, "<#userid or name> [#userid or name]")]
|
||||||
|
// [RequiresPermissions("@css/kick")]
|
||||||
|
// public void OnBringCommand(CCSPlayerController? caller, CommandInfo command)
|
||||||
|
// {
|
||||||
|
// // Check if the caller is valid and has a live pawn
|
||||||
|
// if (caller == null || caller.PlayerPawn?.Value?.LifeState != (int)LifeState_t.LIFE_ALIVE)
|
||||||
|
// return;
|
||||||
|
//
|
||||||
|
// // Get the target players
|
||||||
|
// var targets = GetTarget(command);
|
||||||
|
// if (targets == null || targets.Count() > 1) return;
|
||||||
|
//
|
||||||
|
// var playersToTarget = targets.Players
|
||||||
|
// .Where(player => player is { IsValid: true, IsHLTV: false })
|
||||||
|
// .ToList();
|
||||||
|
//
|
||||||
|
// // Log the command
|
||||||
|
// Helper.LogCommand(caller, command);
|
||||||
|
//
|
||||||
|
// // Process each player to teleport
|
||||||
|
// foreach (var player in playersToTarget.Where(player => player is { Connected: PlayerConnectedState.PlayerConnected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).Where(caller.CanTarget))
|
||||||
|
// {
|
||||||
|
// if (caller.PlayerPawn.Value == null || player.PlayerPawn.Value == null)
|
||||||
|
// continue;
|
||||||
|
//
|
||||||
|
// // Teleport the player to the caller and toggle noclip
|
||||||
|
// player.TeleportPlayer(caller);
|
||||||
|
// // caller.PlayerPawn.Value.ToggleNoclip();
|
||||||
|
//
|
||||||
|
// caller.PlayerPawn.Value.Collision.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_DISSOLVING;
|
||||||
|
// caller.PlayerPawn.Value.Collision.CollisionAttribute.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_DISSOLVING;
|
||||||
|
//
|
||||||
|
// Utilities.SetStateChanged(caller, "CCollisionProperty", "m_CollisionGroup");
|
||||||
|
// Utilities.SetStateChanged(caller, "VPhysicsCollisionAttribute_t", "m_nCollisionGroup");
|
||||||
|
//
|
||||||
|
// player.PlayerPawn.Value.Collision.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_DISSOLVING;
|
||||||
|
// player.PlayerPawn.Value.Collision.CollisionAttribute.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_DISSOLVING;
|
||||||
|
//
|
||||||
|
// Utilities.SetStateChanged(player, "CCollisionProperty", "m_CollisionGroup");
|
||||||
|
// Utilities.SetStateChanged(player, "VPhysicsCollisionAttribute_t", "m_nCollisionGroup");
|
||||||
|
//
|
||||||
|
// // Set a timer to toggle collision back after 4 seconds
|
||||||
|
// AddTimer(4, () =>
|
||||||
|
// {
|
||||||
|
// if (!player.IsValid || player.PlayerPawn?.Value?.LifeState != (int)LifeState_t.LIFE_ALIVE)
|
||||||
|
// return;
|
||||||
|
//
|
||||||
|
// caller.PlayerPawn.Value.Collision.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_PLAYER;
|
||||||
|
// caller.PlayerPawn.Value.Collision.CollisionAttribute.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_PLAYER;
|
||||||
|
//
|
||||||
|
// Utilities.SetStateChanged(caller, "CCollisionProperty", "m_CollisionGroup");
|
||||||
|
// Utilities.SetStateChanged(caller, "VPhysicsCollisionAttribute_t", "m_nCollisionGroup");
|
||||||
|
//
|
||||||
|
// player.PlayerPawn.Value.Collision.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_PLAYER;
|
||||||
|
// player.PlayerPawn.Value.Collision.CollisionAttribute.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_PLAYER;
|
||||||
|
//
|
||||||
|
// Utilities.SetStateChanged(player, "CCollisionProperty", "m_CollisionGroup");
|
||||||
|
// Utilities.SetStateChanged(player, "VPhysicsCollisionAttribute_t", "m_nCollisionGroup");
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// // Prepare message key and arguments for the bring notification
|
||||||
|
// var activityMessageKey = "sa_admin_bring_message";
|
||||||
|
// var adminActivityArgs = new object[] { "CALLER", player.PlayerName };
|
||||||
|
//
|
||||||
|
// // Show admin activity
|
||||||
|
// if (!SilentPlayers.Contains(caller.Slot) && _localizer != null)
|
||||||
|
// {
|
||||||
|
// Helper.ShowAdminActivity(activityMessageKey, caller.PlayerName, false, adminActivityArgs);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
@@ -38,56 +38,67 @@ public class Discord
|
|||||||
[JsonPropertyName("DiscordPenaltyBanSettings")]
|
[JsonPropertyName("DiscordPenaltyBanSettings")]
|
||||||
public DiscordPenaltySetting[] DiscordPenaltyBanSettings { get; set; } =
|
public DiscordPenaltySetting[] DiscordPenaltyBanSettings { get; set; } =
|
||||||
[
|
[
|
||||||
new DiscordPenaltySetting { Name = "Color", Value = "" },
|
new() { Name = "Color", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "Webhook", Value = "" },
|
new() { Name = "Webhook", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "ThumbnailUrl", Value = "" },
|
new() { Name = "ThumbnailUrl", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "ImageUrl", Value = "" },
|
new() { Name = "ImageUrl", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "Footer", Value = "" },
|
new() { Name = "Footer", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "Time", Value = "{relative}" },
|
new() { Name = "Time", Value = "{relative}" },
|
||||||
];
|
];
|
||||||
|
|
||||||
[JsonPropertyName("DiscordPenaltyMuteSettings")]
|
[JsonPropertyName("DiscordPenaltyMuteSettings")]
|
||||||
public DiscordPenaltySetting[] DiscordPenaltyMuteSettings { get; set; } =
|
public DiscordPenaltySetting[] DiscordPenaltyMuteSettings { get; set; } =
|
||||||
[
|
[
|
||||||
new DiscordPenaltySetting { Name = "Color", Value = "" },
|
new() { Name = "Color", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "Webhook", Value = "" },
|
new() { Name = "Webhook", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "ThumbnailUrl", Value = "" },
|
new() { Name = "ThumbnailUrl", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "ImageUrl", Value = "" },
|
new() { Name = "ImageUrl", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "Footer", Value = "" },
|
new() { Name = "Footer", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "Time", Value = "{relative}" },
|
new() { Name = "Time", Value = "{relative}" },
|
||||||
];
|
];
|
||||||
|
|
||||||
[JsonPropertyName("DiscordPenaltyGagSettings")]
|
[JsonPropertyName("DiscordPenaltyGagSettings")]
|
||||||
public DiscordPenaltySetting[] DiscordPenaltyGagSettings { get; set; } =
|
public DiscordPenaltySetting[] DiscordPenaltyGagSettings { get; set; } =
|
||||||
[
|
[
|
||||||
new DiscordPenaltySetting { Name = "Color", Value = "" },
|
new() { Name = "Color", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "Webhook", Value = "" },
|
new() { Name = "Webhook", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "ThumbnailUrl", Value = "" },
|
new() { Name = "ThumbnailUrl", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "ImageUrl", Value = "" },
|
new() { Name = "ImageUrl", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "Footer", Value = "" },
|
new() { Name = "Footer", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "Time", Value = "{relative}" },
|
new() { Name = "Time", Value = "{relative}" },
|
||||||
];
|
];
|
||||||
|
|
||||||
[JsonPropertyName("DiscordPenaltySilenceSettings")]
|
[JsonPropertyName("DiscordPenaltySilenceSettings")]
|
||||||
public DiscordPenaltySetting[] DiscordPenaltySilenceSettings { get; set; } =
|
public DiscordPenaltySetting[] DiscordPenaltySilenceSettings { get; set; } =
|
||||||
[
|
[
|
||||||
new DiscordPenaltySetting { Name = "Color", Value = "" },
|
new() { Name = "Color", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "Webhook", Value = "" },
|
new() { Name = "Webhook", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "ThumbnailUrl", Value = "" },
|
new() { Name = "ThumbnailUrl", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "ImageUrl", Value = "" },
|
new() { Name = "ImageUrl", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "Footer", Value = "" },
|
new() { Name = "Footer", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "Time", Value = "{relative}" },
|
new() { Name = "Time", Value = "{relative}" },
|
||||||
];
|
];
|
||||||
|
|
||||||
[JsonPropertyName("DiscordPenaltyWarnSettings")]
|
[JsonPropertyName("DiscordPenaltyWarnSettings")]
|
||||||
public DiscordPenaltySetting[] DiscordPenaltyWarnSettings { get; set; } =
|
public DiscordPenaltySetting[] DiscordPenaltyWarnSettings { get; set; } =
|
||||||
[
|
[
|
||||||
new DiscordPenaltySetting { Name = "Color", Value = "" },
|
new() { Name = "Color", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "Webhook", Value = "" },
|
new() { Name = "Webhook", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "ThumbnailUrl", Value = "" },
|
new() { Name = "ThumbnailUrl", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "ImageUrl", Value = "" },
|
new() { Name = "ImageUrl", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "Footer", Value = "" },
|
new() { Name = "Footer", Value = "" },
|
||||||
new DiscordPenaltySetting { Name = "Time", Value = "{relative}" },
|
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}" },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,15 +124,15 @@ public class MenuConfig
|
|||||||
[JsonPropertyName("Durations")]
|
[JsonPropertyName("Durations")]
|
||||||
public DurationItem[] Durations { get; set; } =
|
public DurationItem[] Durations { get; set; } =
|
||||||
[
|
[
|
||||||
new DurationItem { Name = "1 minute", Duration = 1 },
|
new() { Name = "1 minute", Duration = 1 },
|
||||||
new DurationItem { Name = "5 minutes", Duration = 5 },
|
new() { Name = "5 minutes", Duration = 5 },
|
||||||
new DurationItem { Name = "15 minutes", Duration = 15 },
|
new() { Name = "15 minutes", Duration = 15 },
|
||||||
new DurationItem { Name = "1 hour", Duration = 60 },
|
new() { Name = "1 hour", Duration = 60 },
|
||||||
new DurationItem { Name = "1 day", Duration = 60 * 24 },
|
new() { Name = "1 day", Duration = 60 * 24 },
|
||||||
new DurationItem { Name = "7 days", Duration = 60 * 24 * 7 },
|
new() { Name = "7 days", Duration = 60 * 24 * 7 },
|
||||||
new DurationItem { Name = "14 days", Duration = 60 * 24 * 14 },
|
new() { Name = "14 days", Duration = 60 * 24 * 14 },
|
||||||
new DurationItem { Name = "30 days", Duration = 60 * 24 * 30 },
|
new() { Name = "30 days", Duration = 60 * 24 * 30 },
|
||||||
new DurationItem { Name = "Permanent", Duration = 0 }
|
new() { Name = "Permanent", Duration = 0 }
|
||||||
];
|
];
|
||||||
|
|
||||||
[JsonPropertyName("BanReasons")]
|
[JsonPropertyName("BanReasons")]
|
||||||
@@ -166,18 +177,18 @@ public class MenuConfig
|
|||||||
[JsonPropertyName("AdminFlags")]
|
[JsonPropertyName("AdminFlags")]
|
||||||
public AdminFlag[] AdminFlags { get; set; } =
|
public AdminFlag[] AdminFlags { get; set; } =
|
||||||
[
|
[
|
||||||
new AdminFlag { Name = "Generic", Flag = "@css/generic" },
|
new() { Name = "Generic", Flag = "@css/generic" },
|
||||||
new AdminFlag { Name = "Chat", Flag = "@css/chat" },
|
new() { Name = "Chat", Flag = "@css/chat" },
|
||||||
new AdminFlag { Name = "Change Map", Flag = "@css/changemap" },
|
new() { Name = "Change Map", Flag = "@css/changemap" },
|
||||||
new AdminFlag { Name = "Slay", Flag = "@css/slay" },
|
new() { Name = "Slay", Flag = "@css/slay" },
|
||||||
new AdminFlag { Name = "Kick", Flag = "@css/kick" },
|
new() { Name = "Kick", Flag = "@css/kick" },
|
||||||
new AdminFlag { Name = "Ban", Flag = "@css/ban" },
|
new() { Name = "Ban", Flag = "@css/ban" },
|
||||||
new AdminFlag { Name = "Perm Ban", Flag = "@css/permban" },
|
new() { Name = "Perm Ban", Flag = "@css/permban" },
|
||||||
new AdminFlag { Name = "Unban", Flag = "@css/unban" },
|
new() { Name = "Unban", Flag = "@css/unban" },
|
||||||
new AdminFlag { Name = "Show IP", Flag = "@css/showip" },
|
new() { Name = "Show IP", Flag = "@css/showip" },
|
||||||
new AdminFlag { Name = "Cvar", Flag = "@css/cvar" },
|
new() { Name = "Cvar", Flag = "@css/cvar" },
|
||||||
new AdminFlag { Name = "Rcon", Flag = "@css/rcon" },
|
new() { Name = "Rcon", Flag = "@css/rcon" },
|
||||||
new AdminFlag { Name = "Root (all flags)", Flag = "@css/root" }
|
new() { Name = "Root (all flags)", Flag = "@css/root" }
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,27 +241,17 @@ public class OtherSettings
|
|||||||
|
|
||||||
[JsonPropertyName("AdditionalCommandsToLog")]
|
[JsonPropertyName("AdditionalCommandsToLog")]
|
||||||
public List<string> AdditionalCommandsToLog { get; set; } = new();
|
public List<string> AdditionalCommandsToLog { get; set; } = new();
|
||||||
|
[JsonPropertyName("IgnoredIps")]
|
||||||
|
public List<string> IgnoredIps { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CS2_SimpleAdminConfig : BasePluginConfig
|
public class CS2_SimpleAdminConfig : BasePluginConfig
|
||||||
{
|
{
|
||||||
[JsonPropertyName("ConfigVersion")] public override int Version { get; set; } = 24;
|
[JsonPropertyName("ConfigVersion")] public override int Version { get; set; } = 25;
|
||||||
|
|
||||||
[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("DatabaseConfig")]
|
||||||
|
public DatabaseConfig DatabaseConfig { get; set; } = new();
|
||||||
|
|
||||||
[JsonPropertyName("OtherSettings")]
|
[JsonPropertyName("OtherSettings")]
|
||||||
public OtherSettings OtherSettings { get; set; } = new();
|
public OtherSettings OtherSettings { get; set; } = new();
|
||||||
|
|
||||||
@@ -287,4 +288,38 @@ public class CS2_SimpleAdminConfig : BasePluginConfig
|
|||||||
|
|
||||||
[JsonPropertyName("MenuConfig")]
|
[JsonPropertyName("MenuConfig")]
|
||||||
public MenuConfig MenuConfigs { get; set; } = new();
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ public class Database(string dbConnectionString)
|
|||||||
{
|
{
|
||||||
var connection = new MySqlConnection(dbConnectionString);
|
var connection = new MySqlConnection(dbConnectionString);
|
||||||
connection.Open();
|
connection.Open();
|
||||||
|
|
||||||
|
// using var cmd = connection.CreateCommand();
|
||||||
|
// cmd.CommandText = "SET NAMES 'utf8mb4' COLLATE 'utf8mb4_general_ci';";
|
||||||
|
// cmd.ExecuteNonQueryAsync();
|
||||||
|
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -26,6 +31,11 @@ public class Database(string dbConnectionString)
|
|||||||
{
|
{
|
||||||
var connection = new MySqlConnection(dbConnectionString);
|
var connection = new MySqlConnection(dbConnectionString);
|
||||||
await connection.OpenAsync();
|
await connection.OpenAsync();
|
||||||
|
|
||||||
|
// await using var cmd = connection.CreateCommand();
|
||||||
|
// cmd.CommandText = "SET NAMES 'utf8mb4' COLLATE 'utf8mb4_general_ci';";
|
||||||
|
// await cmd.ExecuteNonQueryAsync();
|
||||||
|
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -35,11 +45,11 @@ public class Database(string dbConnectionString)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DatabaseMigration()
|
// public async Task DatabaseMigration()
|
||||||
{
|
// {
|
||||||
Migration migrator = new(this);
|
// Migration migrator = new(this);
|
||||||
migrator.ExecuteMigrations();
|
// await migrator.ExecuteMigrationsAsync();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public bool CheckDatabaseConnection(out string? exception)
|
public bool CheckDatabaseConnection(out string? exception)
|
||||||
{
|
{
|
||||||
|
|||||||
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);
|
||||||
|
}
|
||||||
@@ -1,61 +1,105 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using System.Data.Common;
|
||||||
using MySqlConnector;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace CS2_SimpleAdmin.Database;
|
namespace CS2_SimpleAdmin.Database;
|
||||||
|
|
||||||
public class Migration(Database database)
|
public class Migration(string migrationsPath)
|
||||||
{
|
{
|
||||||
public void ExecuteMigrations()
|
/// <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()
|
||||||
{
|
{
|
||||||
var migrationsDirectory = CS2_SimpleAdmin.Instance.ModuleDirectory + "/Database/Migrations";
|
if (CS2_SimpleAdmin.DatabaseProvider == null) return;
|
||||||
|
var files = Directory.GetFiles(migrationsPath, "*.sql").OrderBy(f => f).ToList();
|
||||||
|
if (files.Count == 0) return;
|
||||||
|
|
||||||
var files = Directory.GetFiles(migrationsDirectory, "*.sql")
|
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||||
.OrderBy(f => f);
|
await using (var cmd = connection.CreateCommand())
|
||||||
|
{
|
||||||
|
if (migrationsPath.Contains("sqlite", StringComparison.CurrentCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
cmd.CommandText = """
|
||||||
|
CREATE TABLE IF NOT EXISTS sa_migrations (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
version TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmd.CommandText = """
|
||||||
|
CREATE TABLE IF NOT EXISTS sa_migrations (
|
||||||
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
version VARCHAR(128) NOT NULL
|
||||||
|
);
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
using var connection = database.GetConnection();
|
await cmd.ExecuteNonQueryAsync();
|
||||||
|
}
|
||||||
|
|
||||||
// Create sa_migrations table if not exists
|
var lastAppliedVersion = await GetLastAppliedVersionAsync(connection);
|
||||||
using var cmd = new MySqlCommand("""
|
|
||||||
CREATE TABLE IF NOT EXISTS `sa_migrations` (
|
|
||||||
`id` INT PRIMARY KEY AUTO_INCREMENT,
|
|
||||||
`version` VARCHAR(255) NOT NULL
|
|
||||||
);
|
|
||||||
""", connection);
|
|
||||||
|
|
||||||
cmd.ExecuteNonQuery();
|
|
||||||
|
|
||||||
// Get the last applied migration version
|
|
||||||
var lastAppliedVersion = GetLastAppliedVersion(connection);
|
|
||||||
|
|
||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
var version = Path.GetFileNameWithoutExtension(file);
|
var version = Path.GetFileNameWithoutExtension(file);
|
||||||
|
if (string.Compare(version, lastAppliedVersion, StringComparison.OrdinalIgnoreCase) <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
// Check if the migration has already been applied
|
try
|
||||||
if (string.Compare(version, lastAppliedVersion, StringComparison.OrdinalIgnoreCase) <= 0) continue;
|
{
|
||||||
var sqlScript = File.ReadAllText(file);
|
var sqlScript = await File.ReadAllTextAsync(file);
|
||||||
|
|
||||||
using var cmdMigration = new MySqlCommand(sqlScript, connection);
|
await using (var cmdMigration = connection.CreateCommand())
|
||||||
cmdMigration.ExecuteNonQuery();
|
{
|
||||||
|
cmdMigration.CommandText = sqlScript;
|
||||||
|
await cmdMigration.ExecuteNonQueryAsync();
|
||||||
|
}
|
||||||
|
|
||||||
// Update the last applied migration version
|
await UpdateLastAppliedVersionAsync(connection, version);
|
||||||
UpdateLastAppliedVersion(connection, version);
|
|
||||||
|
|
||||||
CS2_SimpleAdmin._logger?.LogInformation($"Migration \"{version}\" successfully applied.");
|
CS2_SimpleAdmin._logger?.LogInformation($"Migration \"{version}\" successfully applied.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
CS2_SimpleAdmin._logger?.LogError(ex, $"Error applying migration \"{version}\".");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetLastAppliedVersion(MySqlConnection connection)
|
/// <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)
|
||||||
{
|
{
|
||||||
using var cmd = new MySqlCommand("SELECT `version` FROM `sa_migrations` ORDER BY `id` DESC LIMIT 1;", connection);
|
await using var cmd = connection.CreateCommand();
|
||||||
var result = cmd.ExecuteScalar();
|
cmd.CommandText = "SELECT version FROM sa_migrations ORDER BY id DESC LIMIT 1;";
|
||||||
|
var result = await cmd.ExecuteScalarAsync();
|
||||||
return result?.ToString() ?? string.Empty;
|
return result?.ToString() ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void UpdateLastAppliedVersion(MySqlConnection connection, string version)
|
/// <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)
|
||||||
{
|
{
|
||||||
using var cmd = new MySqlCommand("INSERT INTO `sa_migrations` (`version`) VALUES (@Version);", connection);
|
await using var cmd = connection.CreateCommand();
|
||||||
cmd.Parameters.AddWithValue("@Version", version);
|
cmd.CommandText = "INSERT INTO sa_migrations (version) VALUES (@Version);";
|
||||||
cmd.ExecuteNonQuery();
|
|
||||||
|
var param = cmd.CreateParameter();
|
||||||
|
param.ParameterName = "@Version";
|
||||||
|
param.Value = version;
|
||||||
|
cmd.Parameters.Add(param);
|
||||||
|
|
||||||
|
await cmd.ExecuteNonQueryAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ CREATE TABLE IF NOT EXISTS `sa_unmutes` (
|
|||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
INSERT INTO `sa_admins` (`id`, `player_name`, `player_steamid`, `flags`, `immunity`, `server_id`, `ends`, `created`)
|
INSERT IGNORE INTO `sa_admins` (`id`, `player_name`, `player_steamid`, `flags`, `immunity`, `server_id`, `ends`, `created`)
|
||||||
VALUES (-1, 'Console', 'Console', '', '0', NULL, NULL, NOW());
|
VALUES (0, 'Console', 'Console', '', '0', NULL, NULL, NOW());
|
||||||
|
|
||||||
UPDATE `sa_admins` SET `id` = 0 WHERE `id` = -1;
|
UPDATE `sa_admins` SET `id` = 0 WHERE `id` = -1;
|
||||||
|
|
||||||
@@ -20,7 +20,6 @@ CREATE TABLE IF NOT EXISTS `sa_groups_servers` (
|
|||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
ALTER TABLE `sa_admins` ADD `group_id` INT NULL AFTER `created`;
|
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_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_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;
|
ALTER TABLE `sa_admins` ADD FOREIGN KEY (`group_id`) REFERENCES `sa_groups`(`id`) ON DELETE SET NULL;
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
CREATE TABLE IF NOT EXISTS `sa_players_ips` (
|
CREATE TABLE IF NOT EXISTS `sa_players_ips` (
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
|
||||||
`steamid` bigint(20) NOT NULL,
|
`steamid` bigint(20) NOT NULL,
|
||||||
`address` varchar(64) NOT NULL,
|
`address` varchar(64) NOT NULL,
|
||||||
`used_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
`used_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`steamid`, `address`)
|
||||||
UNIQUE KEY `steamid` (`steamid`,`address`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
) 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,5 @@
|
|||||||
|
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,33 @@
|
|||||||
|
-- Migration 016: Optimize tables and indexes
|
||||||
|
-- Add proper indexes for all tables to improve query performance
|
||||||
|
|
||||||
|
-- Optimize sa_players_ips table indexes
|
||||||
|
-- Add index on used_at for efficient date-based queries
|
||||||
|
ALTER TABLE `sa_players_ips` ADD INDEX IF NOT EXISTS `idx_used_at` (`used_at` DESC);
|
||||||
|
|
||||||
|
-- Optimize sa_bans table indexes
|
||||||
|
-- Add composite indexes for common query patterns
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_bans_steamid_status` ON `sa_bans` (`player_steamid`, `status`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_bans_ip_status` ON `sa_bans` (`player_ip`, `status`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_bans_status_ends` ON `sa_bans` (`status`, `ends`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_bans_server_status` ON `sa_bans` (`server_id`, `status`, `ends`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_bans_created` ON `sa_bans` (`created` DESC);
|
||||||
|
|
||||||
|
-- Optimize sa_admins table indexes
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_admins_steamid` ON `sa_admins` (`player_steamid`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_admins_server_ends` ON `sa_admins` (`server_id`, `ends`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_admins_ends` ON `sa_admins` (`ends`);
|
||||||
|
|
||||||
|
-- Optimize sa_mutes table indexes (in addition to migration 014)
|
||||||
|
-- Add index for expire queries
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_mutes_status_ends` ON `sa_mutes` (`status`, `ends`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_mutes_server_status` ON `sa_mutes` (`server_id`, `status`, `ends`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_mutes_created` ON `sa_mutes` (`created` DESC);
|
||||||
|
|
||||||
|
-- Optimize sa_warns table indexes (if exists)
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_warns_steamid_status` ON `sa_warns` (`player_steamid`, `status`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_warns_status_ends` ON `sa_warns` (`status`, `ends`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_warns_server_status` ON `sa_warns` (`server_id`, `status`, `ends`);
|
||||||
|
|
||||||
|
-- Add index on sa_servers for faster lookups
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_servers_hostname` ON `sa_servers` (`hostname`);
|
||||||
@@ -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,6 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS `sa_players_ips` (
|
||||||
|
`steamid` INTEGER NOT NULL,
|
||||||
|
`address` INTEGER NOT NULL
|
||||||
|
`used_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`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,4 @@
|
|||||||
|
TRUNCATE TABLE `sa_players_ips`;
|
||||||
|
|
||||||
|
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]*';
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
-- Migration 016: Optimize tables and indexes
|
||||||
|
-- Add proper indexes for all tables to improve query performance
|
||||||
|
|
||||||
|
-- Optimize sa_players_ips table indexes
|
||||||
|
-- Add index on used_at for efficient date-based queries
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_used_at` ON `sa_players_ips` (`used_at` DESC);
|
||||||
|
|
||||||
|
-- Optimize sa_bans table indexes
|
||||||
|
-- Add composite indexes for common query patterns
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_bans_steamid_status` ON `sa_bans` (`player_steamid`, `status`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_bans_ip_status` ON `sa_bans` (`player_ip`, `status`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_bans_status_ends` ON `sa_bans` (`status`, `ends`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_bans_server_status` ON `sa_bans` (`server_id`, `status`, `ends`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_bans_created` ON `sa_bans` (`created` DESC);
|
||||||
|
|
||||||
|
-- Optimize sa_admins table indexes
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_admins_steamid` ON `sa_admins` (`player_steamid`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_admins_server_ends` ON `sa_admins` (`server_id`, `ends`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_admins_ends` ON `sa_admins` (`ends`);
|
||||||
|
|
||||||
|
-- Optimize sa_mutes table indexes (in addition to migration 014)
|
||||||
|
-- Add index for expire queries
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_mutes_status_ends` ON `sa_mutes` (`status`, `ends`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_mutes_server_status` ON `sa_mutes` (`server_id`, `status`, `ends`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_mutes_created` ON `sa_mutes` (`created` DESC);
|
||||||
|
|
||||||
|
-- Optimize sa_warns table indexes (if exists)
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_warns_steamid_status` ON `sa_warns` (`player_steamid`, `status`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_warns_status_ends` ON `sa_warns` (`status`, `ends`);
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_warns_server_status` ON `sa_warns` (`server_id`, `status`, `ends`);
|
||||||
|
|
||||||
|
-- Add index on sa_servers for faster lookups
|
||||||
|
CREATE INDEX IF NOT EXISTS `idx_servers_hostname` ON `sa_servers` (`hostname`);
|
||||||
393
CS2-SimpleAdmin/Database/MysqlDatabaseProvider.cs
Normal file
393
CS2-SimpleAdmin/Database/MysqlDatabaseProvider.cs
Normal file
@@ -0,0 +1,393 @@
|
|||||||
|
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();
|
||||||
|
|
||||||
|
await using var cmd = connection.CreateCommand();
|
||||||
|
|
||||||
|
cmd.CommandText = "SET NAMES 'utf8mb4' COLLATE 'utf8mb4_general_ci';";
|
||||||
|
await cmd.ExecuteNonQueryAsync();
|
||||||
|
|
||||||
|
cmd.CommandText = "SET time_zone = '+00:00';";
|
||||||
|
await cmd.ExecuteNonQueryAsync();
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
|||||||