Commented out fun command implementations (noclip, godmode, freeze, unfreeze, resize) in funcommands.cs and removed their registration from RegisterCommands.cs. These commands are now intended to be provided by the new CS2-SimpleAdmin_FunCommands external module, improving modularity and maintainability.
CS2-SimpleAdmin Fun Commands Module
This module serves as a reference implementation for creating CS2-SimpleAdmin modules. It demonstrates best practices for menu creation, command registration, translation support, and API usage.
📚 What This Module Teaches
This module is designed to be educational and shows you how to:
- ✅ Register commands dynamically from configuration
- ✅ Create menu categories and menu items
- ✅ Use per-player translations with
ShowAdminActivityLocalized - ✅ Handle player targeting and validation
- ✅ Implement proper cleanup on module unload
- ✅ Structure code using partial classes for organization
- ✅ Cache data for performance (weapons cache)
- ✅ Use configuration to enable/disable features
🎯 Features
This module provides fun admin commands:
- God Mode (
css_god) - Toggle god mode for players - No Clip (
css_noclip) - Enable no-clip mode - Freeze/Unfreeze (
css_freeze,css_unfreeze) - Freeze players in place - Respawn (
css_respawn) - Respawn dead players - Give Weapon (
css_give) - Give weapons to players - Strip Weapons (
css_strip) - Remove all player weapons - Set HP (
css_hp) - Set player health - Set Speed (
css_speed) - Modify player movement speed - Set Gravity (
css_gravity) - Change player gravity - Set Money (
css_money) - Set player money
📁 File Structure
CS2-SimpleAdmin_FunCommands/
├── CS2-SimpleAdmin_FunCommands.cs # Main plugin file - initialization, registration
├── Commands.cs # Command handlers
├── Actions.cs # Action methods (God, NoClip, Freeze, etc.)
├── Menus.cs # Menu creation using SimpleAdmin API
├── Config.cs # Configuration with command lists
└── lang/ # Translation files (13 languages)
├── en.json
├── pl.json
├── ru.json
└── ... (10 more languages)
🔍 Code Organization Explained
1. Main Plugin File (CS2-SimpleAdmin_FunCommands.cs)
Key Concepts Demonstrated:
public partial class CS2_SimpleAdmin_FunCommands : BasePlugin, IPluginConfig<Config>
{
// ✅ BEST PRACTICE: Use capability system to get API
private ICS2_SimpleAdminApi? _sharedApi;
private readonly PluginCapability<ICS2_SimpleAdminApi> _pluginCapability = new("simpleadmin:api");
// ✅ BEST PRACTICE: Cache expensive data
private static Dictionary<int, CsItem>? _weaponsCache;
// ✅ BEST PRACTICE: Track menu registration state
private bool _menusRegistered = false;
public override void OnAllPluginsLoaded(bool hotReload)
{
// Get the API
_sharedApi = _pluginCapability.Get();
// Register commands
RegisterFunCommands();
// ✅ BEST PRACTICE: Wait for SimpleAdmin to be ready before registering menus
_sharedApi.OnSimpleAdminReady += RegisterFunMenus;
RegisterFunMenus(); // Fallback for hot reload
}
}
Why partial classes?
- Separates concerns (commands, actions, menus)
- Makes code easier to navigate
- Each file has a specific purpose
2. Configuration (Config.cs)
Key Concept: Command lists for flexibility
public class Config : IBasePluginConfig
{
// ✅ BEST PRACTICE: Allow multiple command aliases
public List<string> NoclipCommands { get; set; } = ["css_noclip"];
public List<string> GodCommands { get; set; } = ["css_god"];
// ... more command lists
}
Benefits:
- Users can disable features by emptying the list
- Users can add command aliases (e.g.,
["css_god", "css_godmode"]) - Menus only register if commands exist
3. Commands (Commands.cs)
Key Concepts Demonstrated:
[CommandHelper(1, "<#userid or name>")]
[RequiresPermissions("@css/cheats")]
private void OnGodCommand(CCSPlayerController? caller, CommandInfo command)
{
// ✅ BEST PRACTICE: Use API to get targets (handles target syntax)
var targets = _sharedApi!.GetTarget(command);
if (targets == null) return;
// ✅ BEST PRACTICE: Filter for alive players
var playersToTarget = targets.Players.Where(player =>
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
// ✅ BEST PRACTICE: Check targeting permissions
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
God(caller, player);
}
});
// ✅ BEST PRACTICE: Always log commands
_sharedApi.LogCommand(caller, command);
}
4. Actions (Actions.cs)
Key Concepts Demonstrated:
private void God(CCSPlayerController? caller, CCSPlayerController player)
{
// Perform the action
if (!GodPlayers.Add(player.Slot))
{
GodPlayers.Remove(player.Slot);
}
// ✅ BEST PRACTICE: Use per-player language support
var activityArgs = new object[] { "CALLER", player.PlayerName };
if (caller == null || !_sharedApi!.IsAdminSilent(caller))
{
if (Localizer != null)
{
// Each player sees message in their configured language!
_sharedApi!.ShowAdminActivityLocalized(Localizer, "fun_admin_god_message", callerName, false, activityArgs);
}
}
// ✅ BEST PRACTICE: Log the action
_sharedApi!.LogCommand(caller, $"css_god {player.PlayerName}");
}
5. Menus (Menus.cs)
Key Concepts Demonstrated:
Simple Player Selection Menu
private object CreateGodModeMenu(CCSPlayerController admin)
{
// ✅ BEST PRACTICE: Use CreateMenuWithPlayers for simple player selection
return _sharedApi!.CreateMenuWithPlayers("God Mode", "fun", admin,
player => player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(player),
God); // Direct method reference
}
Nested Menu with Value Selection
private object CreateSetHpMenu(CCSPlayerController admin)
{
// ✅ BEST PRACTICE: Use CreateMenuWithBack for menus with back button
var menu = _sharedApi!.CreateMenuWithBack("Set HP", "fun", admin);
var players = _sharedApi.GetValidPlayers().Where(p =>
p.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(p));
foreach (var player in players)
{
// ✅ BEST PRACTICE: AddSubMenu automatically adds back button to submenu
_sharedApi.AddSubMenu(menu, playerName, p => CreateHpSelectionMenu(admin, player));
}
return menu;
}
private object CreateHpSelectionMenu(CCSPlayerController admin, CCSPlayerController target)
{
var hpMenu = _sharedApi!.CreateMenuWithBack($"Set HP: {target.PlayerName}", "fun", admin);
var hpValues = new[] { 1, 10, 25, 50, 100, 200, 500, 999 };
foreach (var hp in hpValues)
{
// ✅ BEST PRACTICE: AddMenuOption for simple actions
_sharedApi.AddMenuOption(hpMenu, $"{hp} HP", _ =>
{
// ✅ BEST PRACTICE: Always validate before executing
if (target.IsValid)
{
target.SetHp(hp);
LogAndShowActivity(admin, target, "fun_admin_hp_message", "css_hp", hp.ToString());
}
});
}
return hpMenu;
}
6. Translations
Key Concept: Module-specific translations
// lang/en.json
{
"fun_admin_god_message": "{lightred}{0}{default} changed god mode for {lightred}{1}{default}!",
"fun_admin_hp_message": "{lightred}{0}{default} changed {lightred}{1}{default} hp amount!"
}
Why module translations?
- Your module is independent from SimpleAdmin
- You can update translations without affecting main plugin
- Each player sees messages in their language automatically
🛠️ How to Use This as a Template
Step 1: Copy the Module
cp -r CS2-SimpleAdmin_FunCommands YourModuleName
Step 2: Rename Files
- Rename
.csprojfile - Rename all
.csfiles to match your module name - Update namespace in all files
Step 3: Update References
- Change
namespace CS2_SimpleAdmin_FunCommandstonamespace YourModuleName - Update plugin metadata (name, version, author, description)
Step 4: Modify Config
public class Config : IBasePluginConfig
{
public int Version { get; set; } = 1;
// Add your own command lists
public List<string> YourCommands { get; set; } = ["css_yourcommand"];
}
Step 5: Add Your Commands
Look at Commands.cs for examples of command handlers
Step 6: Add Your Menus
Look at Menus.cs for examples of menu creation
Step 7: Add Translations
Create language files in lang/{language}.json (e.g., lang/en.json, lang/pl.json)
📖 Learning Path
If you're new to module development, study files in this order:
- Config.cs - Understand configuration structure
- CS2-SimpleAdmin_FunCommands.cs - See initialization and API acquisition
- Commands.cs - Learn command registration and handling
- Actions.cs - Understand action methods and translations
- Menus.cs - Study menu creation patterns
🎓 Best Practices Demonstrated
✅ Command Registration
// Dynamic registration based on config
if (Config.GodCommands.Count > 0)
{
foreach (var command in Config.GodCommands)
{
_sharedApi.RegisterCommand(command, "Enable god mode", OnGodCommand);
}
}
✅ Target Validation
// Always check if player can be targeted
if (!caller.CanTarget(player)) return;
// Always validate player state
if (!player.IsValid) return;
✅ Translation Usage
// Use module's localizer for per-player language support
if (Localizer != null)
{
_sharedApi.ShowAdminActivityLocalized(Localizer, "translation_key", callerName, false, args);
}
✅ Cleanup on Unload
public override void Unload(bool hotReload)
{
// Unregister all commands
if (Config.GodCommands.Count > 0)
{
foreach (var command in Config.GodCommands)
{
_sharedApi.UnRegisterCommand(command);
}
}
// Unregister all menus
if (Config.GodCommands.Count > 0)
_sharedApi.UnregisterMenu("fun", "god");
// Remove event handlers
_sharedApi.OnSimpleAdminReady -= RegisterFunMenus;
}
✅ Data Caching
// Cache expensive operations
private static Dictionary<int, CsItem> GetWeaponsCache()
{
if (_weaponsCache != null) return _weaponsCache;
var weaponsArray = Enum.GetValues(typeof(CsItem));
_weaponsCache = new Dictionary<int, CsItem>();
// ... populate cache
return _weaponsCache;
}
🔗 Related Documentation
- MODULE_DEVELOPMENT.md - Complete API reference
- TRANSLATION_EXAMPLE.md - Translation usage guide
- CS2-SimpleAdminApi - API interface definitions
💡 Tips
- Start Simple - Begin with one command and one menu, then expand
- Test Thoroughly - Test with multiple players, different permissions, and languages
- Handle Errors - Always validate player state before actions
- Log Everything - Use
_sharedApi.LogCommand()for all admin actions - Use Partial Classes - Split your code into logical files
- Comment Your Code - Especially if others will use it as reference
🐛 Common Mistakes to Avoid
- ❌ Don't forget to check
IsValidbefore accessing player properties - ❌ Don't skip permission checks (
CanTarget,RequiresPermissions) - ❌ Don't forget to unregister commands/menus in
Unload() - ❌ Don't use SimpleAdmin's translations - create your own
- ❌ Don't hardcode command names - use config lists instead
🤝 Contributing
If you improve this module or find better patterns, please contribute back so others can learn!
📄 License
This module is provided as-is for educational purposes. Feel free to use it as a template for your own modules.