Skip to content

Repository files navigation

VIPCore

A comprehensive VIP management system for Counter-Strike 2 servers running SwiftlyS2.

Build StatusDownloadsStarsLicense

Overview

VIPCore is a VIP management framework for SwiftlyS2 CS2 servers. It provides database-backed VIP groups, a shared API for feature modules, and a menu-driven experience so VIP players can enable/disable features that your server grants them.

Requirements

  • SwiftlyS2 for Counter-Strike 2
  • A database supported by VIPCore migrations (VIPCore uses FluentMigrator + Dapper)
  • The Cookies plugin (used to persist per-player feature preferences)

Features

  • Database-backed VIP management with FluentMigrator + Dapper
  • Modular feature system - easily extend with custom VIP features
  • Group-based permissions with per-feature configuration
  • Cookie system for saving player preferences
  • Interactive menu system for VIP players
  • Hot-reload support for configuration changes

Installation

  1. Copy the published VIPCore plugin folder to your server: (swRoot)/plugins/VIPCore/
  2. Ensure the plugin has its resources/ folder alongside VIPCore.dll.
  3. Ensure VIPCore.Contract.dll is present at: (swRoot)/plugins/VIPCore/resources/exports/
  4. Configure vip_groups.jsonc and config.jsonc in your SwiftlyS2 configs folder.
  5. Restart the server.

Commands

CommandPermissionDescription
vipPlayerOpens the VIP menu for the current player.
vip_managevipcore.manageOpens the VIP management menu (in-game).
vip_adduser <steamid> <group> <time>vipcore.adduserAdds a SteamID to a VIP group for a duration based on TimeMode. Use 0 for permanent.
vip_deleteuser <steamid>vipcore.deleteuserRemoves VIP status for a SteamID.

Console usage: SwiftlyS2 console commands are typically exposed with the sw_ prefix (for example sw_vip, sw_vip_adduser, etc.).

Configuration

VIPCore uses two configuration files located in your SwiftlyS2 configs folder.

Main Settings — config.jsonc

Controls how VIPCore operates on your server.

{
"vip": {
"Delay": 2.0, // Wait time in seconds before applying VIP features"DatabaseConnection": "default", // Database connection name (from SwiftlyS2 database config)"TimeMode": 3, // Time unit for VIP duration: 0=seconds, 1=minutes, 2=hours, 3=days"VipLogging": true// Enable detailed logs (useful for troubleshooting)
}
}

Settings explained:

SettingWhat it doesRecommended
DelayHow many seconds to wait after spawn before giving VIP features1.0 to 5.0
DatabaseConnectionWhich database to use for storing VIP data (must exist in SwiftlyS2 config)"default"
TimeModeTime format when adding VIPs via commands3 (days)
VipLoggingShow detailed logs for debugging VIP system issuestrue for testing, false for production

VIP Groups — vip_groups.jsonc

Defines VIP groups and what features each group receives.

Simple example (on/off features only):

{
"vip_groups": {
"Groups": {
"VIP": {
"Weight": 10, // Priority (higher number = more important)"Values": {
"vip.zeus": 1, // 1 = enabled, 0 = disabled"vip.bhop": 1,
"vip.antiflash": 1
}
}
}
}
}

Advanced example (features with custom settings):

{
"vip_groups": {
"Groups": {
"VIP": {
"Weight": 10,
"Values": {
"vip.armor": {
"Armor": 100// Give 100 armor points on spawn
},
"vip.health": {
"Health": 120// Give 120 HP on spawn
},
"vip.bhop": {
"Timer": 5.0, // Activate bhop 5 seconds after round start"MaxSpeed": 300.0// Maximum bhop speed
}
}
},
"PREMIUM": {
"Weight": 20, // Higher weight = overrides "VIP" if player has both"Values": {
"vip.armor": {
"Armor": 150
},
"vip.health": {
"Health": 150
},
"vip.bhop": {
"Timer": 3.0,
"MaxSpeed": 350.0
}
}
}
}
}
}

How it works:

  • Group names ("VIP", "PREMIUM", etc.) can be anything — use them when adding VIPs via commands
  • Weight = priority level. If a player has multiple VIP groups, the highest weight wins
  • Values = features granted to this group
  • Feature keys ("vip.armor", "vip.bhop", etc.) must match installed module names

Feature value types:

TypeExampleUse when
Simple toggle"vip.zeus": 1Feature has no extra settings (just on/off)
With settings"vip.armor": { "Armor": 100 }Feature needs customization (amounts, timers, etc.)

Tip

Module-specific configuration: Each module has its own configuration options and setup instructions. See the Modules directory for detailed documentation on each module's settings.

Included Modules

This repository ships several optional VIP modules (each one is a standalone SwiftlyS2 plugin that registers features into VIPCore):

ModuleDescription
VIP_AntiFlashGives flashbang immunity to VIP players
VIP_ArmorGives armor and helmet on spawn
VIP_BhopEnables bunnyhop (auto-jump) for players
VIP_ChatColorColored chat names for VIP players
VIP_DoubleJumpAllows players to jump twice in mid-air
VIP_FastDefuseReduces bomb defuse time
VIP_FastPlantReduces bomb plant time
VIP_FastReloadGives instant weapon reloads
VIP_FovAllows customizable field of view
VIP_GoldMemberAuto-grants VIP to players with specific name tags
VIP_HealthGives increased health on spawn
VIP_ItemsGives configured weapons/items on spawn
VIP_KillScreenApplies screen effect on kills
VIP_MoneyGives money to VIP players
VIP_NightVipGrants VIP access during specific hours
VIP_NoFallDamagePrevents fall damage
VIP_RainbowModelCycles player model render color (rainbow effect)
VIP_SmokeColorCustom smoke grenade colors
VIP_SpeedModifies player movement speed
VIP_TagCustom clan tags for players
VIP_TestProvides players with a timed trial VIP
VIP_VampirismHeals player by percentage of damage dealt
VIP_ZeusGives Zeus x27 taser on spawn

Creating VIP Module Plugins

VIP modules are standalone SwiftlyS2 plugins that extend VIPCore with custom features (e.g., anti-flash, bhop, FOV, armor, etc.).
The API contract is distributed as a NuGet package: SwiftlyS2.VIPCore.Contract (use Version="*" for latest).

Prerequisites

Required dependency: The Cookies plugin must be installed on the server. VIPCore uses it to persist player feature preferences (enable/disable states) across sessions.

Module Structure

VIP_YourFeature/
├── src/
│ └── VIP_YourFeature.cs
├── resources/
│ ├── templates/
│ │ └── template.jsonc (optional — default config for your feature)
│ └── translations/
│ └── en.jsonc (module-specific translations)
├── VIP_YourFeature.csproj

Step 1 — Create the Project File

Create VIP_YourFeature.csproj and add the SwiftlyS2.VIPCore.Contract NuGet package:

<ItemGroup>
<PackageReferenceInclude="SwiftlyS2.VIPCore.Contract"Version="*"ExcludeAssets="runtime"PrivateAssets="all" />
</ItemGroup>

Note: Use ExcludeAssets="runtime" PrivateAssets="all" because VIPCore is already loaded on the server at runtime — you only need the contract at compile time.


Step 2 — Write the Plugin Code

Create src/VIP_YourFeature.cs:

usingSwiftlyS2.Shared;usingSwiftlyS2.Shared.Players;usingSwiftlyS2.Shared.Plugins;usingSwiftlyS2.Shared.Misc;usingVIPCore.Contract;usingMicrosoft.Extensions.Logging;namespaceVIP_YourFeature;[PluginMetadata(Id="VIP_YourFeature",Version="1.0.0",Name="[VIP] YourFeature",Author="YourName")]publicclassVIP_YourFeature:BasePlugin{privateconststringFeatureKey="vip.yourfeature";privateIVipCoreApiV1?_vipApi;privatebool_isFeatureRegistered;publicVIP_YourFeature(ISwiftlyCorecore):base(core){}// ── 1. Retrieve the VIPCore shared interface ──────────────────────publicoverridevoidUseSharedInterface(IInterfaceManagerinterfaceManager){_vipApi=null;_isFeatureRegistered=false;try{if(interfaceManager.HasSharedInterface("VIPCore.Api.v1"))_vipApi=interfaceManager.GetSharedInterface<IVipCoreApiV1>("VIPCore.Api.v1");RegisterWhenReady();}catch(Exceptionex){Core.Logger.LogWarning("[VIP_YourFeature] Failed to get VIPCore API: {Message}",ex.Message);}}// ── 2. Plugin Load ────────────────────────────────────────────────publicoverridevoidLoad(boolhotReload){// Hook game events, register listeners, etc.RegisterWhenReady();}// ── 3. Register feature (respects VIPCore readiness) ──────────────privatevoidRegisterWhenReady(){if(_vipApi==null)return;if(_vipApi.IsCoreReady())RegisterVipFeatures();else_vipApi.OnCoreReady+=RegisterVipFeatures;}privatevoidRegisterVipFeatures(){if(_vipApi==null||_isFeatureRegistered)return;_vipApi.RegisterFeature(FeatureKey,FeatureType.Toggle,(player,state)=>{Core.Scheduler.NextTick(()=>{player.SendMessage(MessageType.Chat,$"YourFeature: {state}");});},displayNameResolver: p =>Core.Translation.GetPlayerLocalizer(p)["vip.yourfeature"]);_isFeatureRegistered=true;// Subscribe to VIPCore events_vipApi.OnPlayerSpawn+=OnVipPlayerSpawn;_vipApi.PlayerLoaded+=OnVipPlayerLoaded;}// ── 4. Feature logic ──────────────────────────────────────────────privatevoidOnVipPlayerSpawn(IPlayerplayer){if(_vipApi==null||!player.IsValid||player.IsFakeClient)return;if(_vipApi.GetPlayerFeatureState(player,FeatureKey)!=FeatureState.Enabled)return;// Apply your feature effect here}privatevoidOnVipPlayerLoaded(IPlayerplayer,stringgroup){// Called when a VIP player's data is loaded from the database}// ── 5. Cleanup ────────────────────────────────────────────────────publicoverridevoidUnload(){if(_vipApi!=null){_vipApi.OnCoreReady-=RegisterVipFeatures;_vipApi.OnPlayerSpawn-=OnVipPlayerSpawn;_vipApi.PlayerLoaded-=OnVipPlayerLoaded;if(_isFeatureRegistered)_vipApi.UnregisterFeature(FeatureKey);}}}

Step 3 — Create Translation Files

Each module manages its own translations independently. VIPCore's translation file only contains core keys (menu title, VIP status messages, etc.) — module-specific display names and messages must live in the module's own translation file.

Create resources/translations/en.jsonc:

{
"vip.yourfeature": "Your Feature",
"yourfeature.activated": "Your feature has been activated!",
"yourfeature.deactivated": "Your feature has been deactivated."
}

The key "vip.yourfeature" is what gets displayed in the VIP menu as the feature name. It is resolved per-player via the displayNameResolver callback you pass during registration:

displayNameResolver: p =>Core.Translation.GetPlayerLocalizer(p)["vip.yourfeature"]

This uses SwiftlyS2's built-in translation system — GetPlayerLocalizer(player) automatically resolves the correct language file based on the player's language preference, and it reads from your module'sresources/translations/ folder (not VIPCore's).

To support additional languages, add more translation files:

resources/translations/
├── en.jsonc (English — required)
├── de.jsonc (German)
├── fr.jsonc (French)
└── ru.jsonc (Russian)

Step 4 — Configure the VIP Group

Add your feature key to vip_groups.jsonc on the server:

{
"vip_groups": {
"Groups": {
"VIP": {
"Values": {
"vip.yourfeature": 1// 1 = enabled by default, 0 = disabled
}
}
}
}
}

For features with extra settings, nest an object instead of a simple value:

{
"vip_groups": {
"Groups": {
"VIP": {
"Values": {
"vip.bhop": {
"Timer": 5.0,
"MaxSpeed": 300.0
}
}
}
}
}
}

Then read it in your module with a config class:

publicclassYourFeatureConfig{publicfloatTimer{get;set;}=5.0f;publicfloatMaxSpeed{get;set;}=300.0f;}// In your feature logic:varconfig=_vipApi.GetFeatureValue<YourFeatureConfig>(player,FeatureKey);

Step 5 — Build & Deploy

dotnet build -c Release

Copy the build output to your server:

(swRoot)/plugins/VIP_YourFeature/
├── VIP_YourFeature.dll
└── resources/
└── ...

Important:VIPCore.Contract.dll must already be present in (swRoot)/plugins/VIPCore/resources/exports/. It ships with VIPCore — you do not need to include it in your module's output.

Restart the server (or hot-reload if supported).


Key Concepts

Shared Interface Retrieval

varapi=interfaceManager.GetSharedInterface<IVipCoreApiV1>("VIPCore.Api.v1");
  • Must be done in the UseSharedInterface() override
  • Always check HasSharedInterface() first to avoid exceptions if VIPCore is not loaded

Feature Registration Timing

if(_vipApi.IsCoreReady())RegisterVipFeatures();else_vipApi.OnCoreReady+=RegisterVipFeatures;

VIPCore loads its database and config asynchronously. Always check IsCoreReady() before registering, and subscribe to OnCoreReady as a fallback.

Feature Key Naming

  • Format: "vip.featurename" (lowercase, dot-separated)
  • Must match the key used in vip_groups.jsonc

Feature Types

TypeDescription
FeatureType.ToggleOn/off toggle (most common)
FeatureType.SelectableCycles through options when selected in menu
FeatureType.HideActive feature with no menu entry

Main Thread Safety

Game API calls must run on the main thread. Always wrap them in NextTick:

Core.Scheduler.NextTick(()=>{player.SendMessage(MessageType.Chat,"Hello!");});

Player Cookies

Persist per-player preferences across sessions:

_vipApi.SetPlayerCookie(player,"vip.yourfeature.value",120);intsaved=_vipApi.GetPlayerCookie<int>(player,"vip.yourfeature.value");

Available API Methods

// Feature ManagementvoidRegisterFeature(stringfeatureKey,FeatureTypetype,Action<IPlayer,FeatureState>?onSelectItem,Func<IPlayer,string>?displayNameResolver=null);voidUnregisterFeature(stringfeatureKey);IEnumerable<string>GetAllRegisteredFeatures();// Player StateboolIsClientVip(IPlayerplayer);boolPlayerHasFeature(IPlayerplayer,stringfeatureKey);FeatureStateGetPlayerFeatureState(IPlayerplayer,stringfeatureKey);voidSetPlayerFeatureState(IPlayerplayer,stringfeatureKey,FeatureStatenewState);stringGetClientVipGroup(IPlayerplayer);string[]GetVipGroups();// Feature Config (reads from group config JSON and binds to your class)T?GetFeatureValue<T>(IPlayerplayer,stringfeatureKey)whereT:class,new();// Player CookiesTGetPlayerCookie<T>(IPlayerplayer,stringkey);voidSetPlayerCookie<T>(IPlayerplayer,stringkey,Tvalue);// Global TogglevoidDisableAllFeatures();voidEnableAllFeatures();// Core StateboolIsCoreReady();// EventseventAction? OnCoreReady;eventAction<IPlayer,string>?PlayerLoaded;eventAction<IPlayer,string>?PlayerRemoved;eventAction<IPlayer>? OnPlayerSpawn;event Func<IPlayer,string,FeatureState,FeatureType,bool?>?OnPlayerUseFeature;

Feature State Values

publicenumFeatureState{Enabled=0,// Feature is activeDisabled=1,// Feature is inactive (player toggled off)NoAccess=2// Player doesn't have access to this feature}

License

MIT License

About

No description, website, or topics provided.

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages