A CounterStrikeSharp plugin for CS2 that filters out accounts that don't meet minimum requirements when they connect and kicks them.
Today there is no reliable way to read a player's Prime status from the server:
- There is no networked field (netvar) the server receives carrying the Prime flag.
- The classic Steam Web API method (looking for AppID
624820) only detects players who got Prime back in the CS:GO era; those who bought it after the CS2 launch show up as false negatives. That's why it isn't even attempted here.
Instead, PrimeGuard uses Steam profile signals (level, account age, playtime, bans, privacy) that in practice filter out free-to-play accounts, smurfs and freshly created accounts — which is usually the real goal behind "Prime only".
- A CS2 dedicated server with Metamod + CounterStrikeSharp.
- A Steam Web API key (free): https://steamcommunity.com/dev/apikey
- Download
PrimeGuard.dllfrom the latest release and copy it to:game/csgo/addons/counterstrikesharp/plugins/PrimeGuard/ - Start the server once: the config is auto-generated at
game/csgo/addons/counterstrikesharp/configs/plugins/PrimeGuard/PrimeGuard.json - Edit that JSON, set your
SteamWebApiKey, and restart (orcss_plugins reload PrimeGuard).
| Option | Default | Description |
|---|---|---|
SteamWebApiKey |
"" |
Required. Your key from https://steamcommunity.com/dev/apikey |
MinimumSteamLevel |
2 |
Minimum Steam level. -1 disables. |
MinimumAccountAgeDays |
30 |
Minimum account age in days. -1 disables. |
MinimumCS2HoursPlaytime |
-1 |
Minimum hours played in CS2. -1 disables. |
BlockPrivateProfile |
true |
Kick private profiles (they can't be verified). |
BlockVACBanned |
true |
Kick accounts with a VAC ban. |
BlockGameBanned |
true |
Kick accounts with a game ban. |
IgnoreBansOlderThanDays |
0 |
Ignore bans older than this (e.g. 1825 = 5 years). 0 counts every ban. |
ImmunityAdminFlag |
@css/bypasspremiumcheck |
Admin flag that bypasses every check. Empty to disable. |
BypassAccountChecksFlag |
@css/bypassaccountcheck |
Flag that skips the level / account age / playtime checks. |
BypassAccountChecksSteamIds |
[] |
SteamID64s that skip those same checks. |
BypassBanChecksFlag |
@css/bypassbancheck |
Flag that skips the VAC / game ban checks. |
BypassBanChecksSteamIds |
[] |
SteamID64s that skip the ban checks. |
BypassPrivateProfileFlag |
@css/bypassprivatecheck |
Flag that skips the private profile check. |
BypassPrivateProfileSteamIds |
[] |
SteamID64s that skip the private profile check. |
ChatPrefix |
{LightBlue}[PrimeGuard]{Default} |
Prefix for chat messages. Supports color tags (see below). |
AdminNotifyMessage |
(see config) | Admin alert text. Placeholders: {prefix}, {player}, {reasons}. |
KickMessage |
(see config) | Message shown when kicking. |
LogChecks |
true |
Log each connecting player's info to the console (handy for tuning thresholds). |
CacheMinutes |
360 |
Minutes to cache a player's lookup so reconnects don't re-query the API. 0 disables. |
NotifyAdminsOnBan |
false |
Message online admins when a VAC/game-banned player connects. |
NotifyAdminsOnPrivateProfile |
false |
Message online admins when a private-profile player connects. |
AdminNotifyFlag |
@css/generic |
Which admins receive the notifications. |
TagPrivateInTab |
false |
Show a scoreboard tag on private-profile players who aren't kicked. |
PrivateProfileTag |
[PRIVATE] |
Tag text used for private profiles. |
The default preset is Medium: it blocks bans, private profiles, accounts younger than 30 days, and Steam level below 2. CS2 hours are disabled (
-1).
ChatPrefix and AdminNotifyMessage accept {ColorName} tags (case-insensitive), using CS2's chat palette:
{Default} {White} {DarkRed} {Green} {LightYellow} {LightBlue} {Olive} {Lime} {Red}
{LightPurple} {Purple} {Grey} {Yellow} {Gold} {Silver} {Blue} {DarkBlue} {Magenta}
{LightRed} {Orange} {BlueGrey}
Example:
ImmunityAdminFlag skips everything. When you want finer control, each category has its own admin flag and its own SteamID64 list — a player is exempt if either matches:
| Category | Checks it skips | Flag | SteamID list |
|---|---|---|---|
| Account | level, account age, CS2 playtime | BypassAccountChecksFlag |
BypassAccountChecksSteamIds |
| Bans | VAC ban, game ban | BypassBanChecksFlag |
BypassBanChecksSteamIds |
| Privacy | private profile | BypassPrivateProfileFlag |
BypassPrivateProfileSteamIds |
So a trusted regular can skip the "new account" filter while still being checked for bans:
"BypassAccountChecksSteamIds": ["76561198000000000"]Leave a flag empty ("") to disable that flag, and the list empty ([]) to disable the whitelist.
SteamIDs must be SteamID64s written as quoted strings (
"76561198000000000", not76561198000000000). Anything else is logged as a warning on load and never matches.
ImmunityAdminFlagskips the checks and suppresses the admin alert and tab tag for that player — it exempts them from the plugin entirely. Use the per-category bypasses if you still want flagged players reported.
IgnoreBansOlderThanDays lets an old ban stop counting — e.g. 1825 allows players whose ban is more than 5 years old.
Caveat: Steam only reports the age of the player's most recent ban of any type (
DaysSinceLastBan); there is no per-ban date. So an account with an old VAC and a recent game ban is treated as recently banned.
Each signal can act on its own, so you don't have to auto-kick. The admin notification and the tab tag are independent of the kick checks:
- Notify admins instead of kicking a banned player: set
BlockVACBanned: false,BlockGameBanned: false,NotifyAdminsOnBan: true. Admins get a heads-up on connect; the player stays. (This is often smarter for VAC — Steam's VAC flag is true for a ban on any game, not necessarily CS2.) - Tag instead of kicking a private profile: set
BlockPrivateProfile: false,TagPrivateInTab: true. Private profiles get a[PRIVATE]scoreboard tag instead of a kick. AddNotifyAdminsOnPrivateProfile: trueto also ping admins.
The admin notification fires even when the player is also kicked, so admins always see who was flagged.
- If the Steam Web API fails or doesn't respond, the player is not kicked (the profile is treated as "unverifiable").
- If the profile is private, only the
BlockPrivateProfilecheck applies; the others (level/age/hours) are skipped because they can't be read. - The CS2 hours check only applies when game details are public.
- Admins with the immunity flag are never filtered.
| Command | Permission | Description |
|---|---|---|
css_pgcheck |
@css/generic |
Shows your own Steam info as seen by PrimeGuard, to help calibrate thresholds. |
css_pgclearcache |
@css/generic |
Clears cached lookups so players are re-checked immediately. |
- Lookups run on a background thread, never on the game tick — they don't affect tickrate.
- ~4 API calls per uncached connect. A Steam Web API key allows 100,000 calls/day (~25,000 connects), so even high-churn servers stay well under the limit.
- Results are cached per SteamID for
CacheMinutes(default 6h), so the same players reconnecting cost zero extra calls. A lookup is cached only when every endpoint answered, so a partial failure is retried on the next connect instead of disabling that check for the whole TTL. - Because results are cached, a player who fixes their profile (e.g. makes it public) keeps their old result until the TTL expires. Run
css_pgclearcacheto re-check them immediately.
- CS2 is free-to-play: the playtime lookup uses
include_played_free_games=1, which is required for AppID 730 to appear for accounts that didn't purchase the game. - For a stricter "Prime only" style filter, raise the thresholds: e.g.
MinimumCS2HoursPlaytime: 10,MinimumSteamLevel: 5,MinimumAccountAgeDays: 90.