Steam rate-limits its community member lists by IP (429), so the whole-list download is gone. Each player is checked as they join with ISteamUser/GetUserGroupList: a member's 'group' row is added or refreshed, a non-member's deleted, and when Steam won't say (private profile, bad key, outage) the database stands. A player is asked about at most once per min_forced_refresh_seconds; css_verify rechecks its targets, offline SteamIDs too. Changing the group clears the old group's rows. refresh_minutes is gone. The Web API key goes in configs/plugins/GroupVerify/GroupVerify.json (SteamWebApiKey); every other setting stays in the database. New clan_tag setting and css_gv_clantag <tag|-> (@css/root): the scoreboard tag for verified players without a rank, applied by CS2-Tags. The API gains ClanTag and OnClanTagChanged, so shared/GroupVerifyApi changes: needs a server restart.
120 lines
3.9 KiB
C#
120 lines
3.9 KiB
C#
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 configured clan tag; the bridge blanks it while inactive.</summary>
|
|
string ClanTag { get; }
|
|
}
|
|
|
|
/// <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 event Action? OnClanTagChanged;
|
|
|
|
/// <inheritdoc />
|
|
public bool IsActive => _backend?.IsActive ?? false;
|
|
|
|
/// <inheritdoc />
|
|
public string ClanTag => _backend is { IsActive: true } backend ? backend.ClanTag : "";
|
|
|
|
/// <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. Tells
|
|
/// subscribers the clan tag is gone, if there was one.
|
|
/// </summary>
|
|
public void Detach(IGroupVerifyBackend backend)
|
|
{
|
|
if (!ReferenceEquals(_backend, backend))
|
|
return;
|
|
var had = ClanTag;
|
|
_backend = null;
|
|
if (had.Length > 0)
|
|
RaiseClanTagChanged();
|
|
}
|
|
|
|
/// <summary>Called by GroupVerify on the game thread; returns handlers' exceptions, like RaiseVerifiedChanged.</summary>
|
|
public List<Exception> RaiseClanTagChanged()
|
|
{
|
|
var errors = new List<Exception>();
|
|
foreach (var handler in OnClanTagChanged?.GetInvocationList() ?? [])
|
|
{
|
|
try
|
|
{
|
|
((Action)handler)();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
errors.Add(ex);
|
|
}
|
|
}
|
|
return errors;
|
|
}
|
|
|
|
/// <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;
|
|
}
|
|
}
|