GroupVerify 0.2.0: Steam group / override verification with an API for other plugins
Players are verified when they're in the configured Steam group (public memberslistxml, refetched every 10 minutes, kept on failure) or have an unexpired override row in gv_verified, written by the web panel. Settings and data live in CS2-SimpleAdmin's MySQL database (gv_verified, gv_settings, gv_migrations), reached through its API. - css_gv_group <groupId64|0>, css_gv_enable, css_gv_disable (@css/root): verification runs only when enabled with a group set. - css_verify <target>: why a player is or isn't verified. Admin and above (by immunity from SimpleAdmin's groups) plus Guardian, unless admin_overrides.json says otherwise. - groupverify:api (shared/GroupVerifyApi): IsVerified, GetStatus, IsActive and OnVerifiedChanged. The API object lives in the shared assembly, so it survives hot reloads of GroupVerify.
This commit is contained in:
commit
1f61958099
13 changed files with 1711 additions and 0 deletions
20
GroupVerifyApi/GroupVerifyApi.csproj
Normal file
20
GroupVerifyApi/GroupVerifyApi.csproj
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!-- GroupVerify's API for other plugins. Installed in addons/counterstrikesharp/shared/GroupVerifyApi/,
|
||||
never in a plugin's folder: CSS loads it once from there, so every plugin shares its types. -->
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AssemblyName>GroupVerifyApi</AssemblyName>
|
||||
<RootNamespace>GroupVerifyApi</RootNamespace>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- Compile-time only: the server already has CounterStrikeSharp.API.dll -->
|
||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.376">
|
||||
<PrivateAssets>none</PrivateAssets>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
<IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
86
GroupVerifyApi/GroupVerifyBridge.cs
Normal file
86
GroupVerifyApi/GroupVerifyBridge.cs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
using System.ComponentModel;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
|
||||
namespace GroupVerifyApi;
|
||||
|
||||
/// <summary>What GroupVerify itself implements behind the API. Not for other plugins.</summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public interface IGroupVerifyBackend
|
||||
{
|
||||
/// <summary>See <see cref="IGroupVerifyApi.IsActive"/>.</summary>
|
||||
bool IsActive { get; }
|
||||
|
||||
/// <summary>See <see cref="IGroupVerifyApi.GetStatus"/>.</summary>
|
||||
VerifyStatus GetStatus(ulong steamId64);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The one API object, living in this shared assembly so it outlives GroupVerify reloads: CSS keeps
|
||||
/// the first capability supplier registered forever, and subscribers keep their handlers here.
|
||||
/// GroupVerify attaches its backend on load and detaches it on unload. Not for other plugins.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public sealed class GroupVerifyBridge : IGroupVerifyApi
|
||||
{
|
||||
/// <summary>The instance GroupVerify registers as the capability.</summary>
|
||||
public static GroupVerifyBridge Instance { get; } = new();
|
||||
|
||||
private IGroupVerifyBackend? _backend;
|
||||
|
||||
private GroupVerifyBridge()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event Action<CCSPlayerController, VerifyStatus>? OnVerifiedChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsActive => _backend?.IsActive ?? false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsVerified(CCSPlayerController player)
|
||||
{
|
||||
if (player is not { IsValid: true, IsBot: false, IsHLTV: false })
|
||||
return false;
|
||||
var steamId = player.AuthorizedSteamID?.SteamId64;
|
||||
return steamId != null && IsVerified(steamId.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsVerified(ulong steamId64) => GetStatus(steamId64).Verified;
|
||||
|
||||
/// <inheritdoc />
|
||||
public VerifyStatus GetStatus(ulong steamId64) =>
|
||||
_backend is { IsActive: true } backend ? backend.GetStatus(steamId64) : default;
|
||||
|
||||
/// <summary>Called by GroupVerify on load.</summary>
|
||||
public void Attach(IGroupVerifyBackend backend) => _backend = backend;
|
||||
|
||||
/// <summary>Called by GroupVerify on unload; ignored if a newer backend has attached since.</summary>
|
||||
public void Detach(IGroupVerifyBackend backend)
|
||||
{
|
||||
if (ReferenceEquals(_backend, backend))
|
||||
_backend = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by GroupVerify on the game thread. Each handler runs on its own, so one that throws
|
||||
/// doesn't stop the others; the exceptions are returned for GroupVerify to log.
|
||||
/// </summary>
|
||||
public List<Exception> RaiseVerifiedChanged(CCSPlayerController player, VerifyStatus status)
|
||||
{
|
||||
var errors = new List<Exception>();
|
||||
foreach (var handler in OnVerifiedChanged?.GetInvocationList() ?? [])
|
||||
{
|
||||
try
|
||||
{
|
||||
((Action<CCSPlayerController, VerifyStatus>)handler)(player, status);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errors.Add(ex);
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
56
GroupVerifyApi/IGroupVerifyApi.cs
Normal file
56
GroupVerifyApi/IGroupVerifyApi.cs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Capabilities;
|
||||
|
||||
namespace GroupVerifyApi;
|
||||
|
||||
/// <summary>
|
||||
/// Whether players are verified by GroupVerify: in the configured Steam group, or given a manual
|
||||
/// override on the web panel. Get it in <c>OnAllPluginsLoaded</c> with
|
||||
/// <c>IGroupVerifyApi.Capability.Get()</c>. Use it on the game thread only.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The object stays valid when GroupVerify is hot-reloaded, and event subscriptions stay attached.
|
||||
/// While GroupVerify is unloaded, disabled or has no group set, <see cref="IsActive"/> is false and
|
||||
/// nobody is verified.
|
||||
/// </remarks>
|
||||
public interface IGroupVerifyApi
|
||||
{
|
||||
/// <summary>The capability GroupVerify registers.</summary>
|
||||
public static readonly PluginCapability<IGroupVerifyApi> Capability = new("groupverify:api");
|
||||
|
||||
/// <summary>
|
||||
/// True when GroupVerify is loaded, enabled, has a Steam group set and has read its database.
|
||||
/// When false, every check returns not verified.
|
||||
/// </summary>
|
||||
bool IsActive { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the player is verified. False for bots and players Steam hasn't authorized yet.
|
||||
/// </summary>
|
||||
bool IsVerified(CCSPlayerController player);
|
||||
|
||||
/// <summary>Whether a SteamID64 is verified, online or not.</summary>
|
||||
bool IsVerified(ulong steamId64);
|
||||
|
||||
/// <summary>Why a SteamID64 is or isn't verified, online or not.</summary>
|
||||
VerifyStatus GetStatus(ulong steamId64);
|
||||
|
||||
/// <summary>
|
||||
/// Fired on the game thread when an online player's verified status changes: true when they
|
||||
/// become verified (including right after connecting), false when they stop being verified
|
||||
/// (they left the group, their override ran out, or GroupVerify was disabled). Not fired on
|
||||
/// disconnect. After GroupVerify is hot-reloaded it fires true again for everyone verified,
|
||||
/// so handlers should be idempotent.
|
||||
/// </summary>
|
||||
event Action<CCSPlayerController, VerifyStatus>? OnVerifiedChanged;
|
||||
}
|
||||
|
||||
/// <summary>A player's verification, and why.</summary>
|
||||
/// <param name="InGroup">In the Steam group's member list.</param>
|
||||
/// <param name="HasOverride">Has an unexpired manual override.</param>
|
||||
/// <param name="OverrideExpires">When the override ends, in GroupVerify's database time zone; null if it never does (or there's none).</param>
|
||||
public readonly record struct VerifyStatus(bool InGroup, bool HasOverride, DateTime? OverrideExpires)
|
||||
{
|
||||
/// <summary>In the group, or has an override.</summary>
|
||||
public bool Verified => InGroup || HasOverride;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue