Skip to content

Implement CounterStrikeSharp port of MapDecals plugin from SwiftlyS2#1

Merged
erikpeik merged 5 commits intomainfrom
copilot/port-mapdecals-to-cssharp
Jan 13, 2026
Merged

Implement CounterStrikeSharp port of MapDecals plugin from SwiftlyS2#1
erikpeik merged 5 commits intomainfrom
copilot/port-mapdecals-to-cssharp

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 13, 2026

Complete port of the MapDecals plugin from SwiftlyS2 to CounterStrikeSharp (CS#), enabling CS2 server owners to place and manage persistent decals on maps through an interactive menu system with database persistence.

Core Implementation

Project Structure

  • Modular architecture: Config, Database, Commands, Events, Functions, Menus
  • Multi-database support: MySQL, PostgreSQL, SQLite with Dapper ORM
  • 1,322 lines across 11 C# files targeting .NET 8.0

Plugin System

  • Configuration via IPluginConfig<T> with JSON serialization
  • Command registration using CS# AddCommand() API with permission validation
  • Event hooks: EventRoundStart, EventPlayerConnectFull, EventPlayerPing
  • Interactive chat menus via MenuManager.OpenChatMenu()

Decal Management

  • Ping-based placement with eye angle calculation (2 units backward from ping)
  • Floor detection: steep downward angle (<-0.90) triggers floor placement with 0° pitch
  • Wall placement: applies 90° pitch rotation for vertical surfaces
  • CRUD operations with database persistence per-map

Database Schema

cc_mapdecals: id, map, decal_id, decal_name, position, angles, depth, width, height, force_on_vip, is_active
cc_mapdecals_preferences: steam_id, decals_enabled

API Adaptations

SwiftlyS2 → CounterStrikeSharp Mappings

  • BasePluginCounterStrikeSharp.API.Core.BasePlugin
  • SwiftlyS2 DB → Dapper with manual connection management
  • SwiftlyS2 commands → AddCommand() + [CommandHelper] attributes
  • Core.EntitySystem.CreateEntityByDesignerNameUtilities.CreateEntityByName<T>()
  • SwiftlyS2 menus → CS# MenuManager API

CI/CD and Releases

GitHub Actions Workflows

  • CI Build (.github/workflows/ci.yml): Automated builds on all pushes and pull requests in both Debug and Release configurations
  • Build and Release (.github/workflows/build-release.yml): Automated release creation when version tags are pushed
    • Builds plugin with all dependencies
    • Creates ZIP package with DLL, config, and documentation
    • Publishes GitHub release with automated release notes

Creating Releases
Simply push a version tag to trigger automated release:

git tag v1.0.0
git push origin v1.0.0

See RELEASE.md for detailed documentation on the CI/CD system, release creation, and version management.

Known Limitations

CS# entity API does not expose CEnvDecal properties (material, width, height, depth). Entity creation simplified to use only available base properties. Values stored in database for future API support.

Security

  • Parameterized queries via Dapper prevent SQL injection
  • Permission checks on all commands via AdminManager.PlayerHasPermissions()
  • Updated Npgsql to v8.0.5 (CVE mitigation)
Original prompt

Create a complete CounterStrikeSharp (CS#) port of the MapDecals plugin originally built for SwiftlyS2.

Plugin Overview

MapDecals allows CS2 server owners to place decals on maps at predefined locations. Players with admin permissions can place decals using ping locations, edit their properties (width, height, depth, position), and toggle visibility.

Core Features to Implement

1. Plugin Structure

  • Base plugin class inheriting from BasePlugin
  • Plugin metadata with version, author, description
  • Configuration system using JSON
  • Database support (MySQL/PostgreSQL/SQLite) with connection string in config

2. Configuration System

Create a JSON-based config with:

  • List of available decals with:
    • UniqId (string): Unique identifier for the decal
    • Name (string): Display name
    • Material (string): Material path (e.g., "materials/Example/exampleTexture.vmat")
    • ShowPermission (string): Optional permission required to see the decal
  • Command configuration for:
    • PlaceDecalCommands: Command and aliases for placing decals (default: "mapdecal", permission: "cc-mapdecals.admin")
    • AdToggleCommands: Command and aliases for toggling decal visibility (default: "decal", permission: "cc-mapdecals.vip")

3. Database Schema

Table: cc_mapdecals

  • id (BIGINT, PRIMARY KEY, AUTO_INCREMENT)
  • map (VARCHAR 64, NOT NULL, INDEXED)
  • decal_id (VARCHAR 64, NOT NULL) - references config UniqId
  • decal_name (VARCHAR 64, NOT NULL)
  • position (VARCHAR 64, NOT NULL) - stored as "X Y Z"
  • angles (VARCHAR 64, NOT NULL) - stored as "X Y Z"
  • depth (INT, NOT NULL, DEFAULT 12)
  • width (FLOAT, NOT NULL, DEFAULT 128)
  • height (FLOAT, NOT NULL, DEFAULT 128)
  • force_on_vip (BOOLEAN, NOT NULL, DEFAULT FALSE)
  • is_active (BOOLEAN, NOT NULL, DEFAULT TRUE)

4. Commands

Implement two commands:

  1. Place Decal Command (default: !mapdecal)

    • Opens menu to place and manage decals
    • Admin-only (configurable permission)
    • Must be alive to use
  2. Toggle Decal Command (default: !decal)

    • Toggles visibility of non-forced decals for the player
    • VIP-only (configurable permission)
    • Saves preference per player (using cookies/database)

5. Decal Placement System

  • Player places decal by pinging (right-click ping) while in placement mode
  • Decal placement logic:
    • Get player eye angles and position
    • Calculate decal position 2 units backward from ping
    • If looking down steeply (eyeZ < -0.90), place on floor
    • Otherwise place on wall with 90° pitch rotation
  • Automatically save to database after placement
  • Open edit menu immediately after placement

6. Decal Entity Management

Create env_decal entities with:

  • Material from config
  • Width, height, depth properties
  • Position and angle
  • Render mode: Normal
  • ProjectOnWorld: true
  • Per-player transmit based on:
    • ForceOnVIP flag
    • Player permissions
    • Player toggle preference

7. Menu System

Implement menus using a CS# menu library (e.g., WASDMenu or ChatMenu):

Main Menu (opened by place decal command):

  • "Place New Decal" - Shows list of available decals from config (filtered by permission)
  • "Edit Existing Decals" - Shows list of decals on current map

Place Decal Menu:

  • List all configured decals
  • Filter by ShowPermission
  • On select: Enter placement mode (wait for ping)
  • Show message: "Ping where you want to place the decal"

Edit Decals Menu:

  • List all decals on current map
  • Show decal name and active status
  • On select: Open Edit Decal Menu

Edit Decal Menu (for specific decal):

  • "Reposition" - Enter reposition mode (ping new location)
  • "Adjust Width" - Submenu with presets (64, 128, 256, 512, custom)
  • "Adjust Height" - Submenu with presets (64, 128, 256, 512, custom)
  • "Adjust Depth" - Submenu with presets (4, 8, 12, 16, 24, custom)
  • "Force on VIP" - Toggle boolean
  • "Enable/Disable" - Toggle is_active
  • "Delete Decal" - Delete from database and despawn entity

8. Event Handlers

  • OnMapStart: Load all decals for the map from database
  • OnRoundStart: Spawn all active decal entities
  • OnPlayerConnectFull: Load player decal visibility preferences
  • OnPlayerDisconnect: Save player decal visibility preferences
  • OnPlayerPing (EventPlayerPing): Handle decal placement/repositioning

9. Player Preferences System

  • Store per-player toggle state (decals on/off)
  • Use database or cookie system to persist across reconnects
  • Default: decals enabled
  • Apply to non-forced decals only

10. Transmit Control

  • Implement per-player transmit logic:
    • Force decals: Show only to players with ShowPermission
    • Non-force decals: Show based on player toggle + ShowPermission
  • Update transmit when:
    • Player connects
    • Player toggles visibility
    • Decal ForceOnVIP changes
    • Decal is enabled/disabled

Technical Requirements

Dependencies

  • CounterStrikeSharp.API
  • Dapper (for database operations)
  • Database drivers: MySqlConne...

This pull request was created from Copilot chat.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 3 commits January 13, 2026 12:15
Co-authored-by: erikpeik <52178013+erikpeik@users.noreply.github.com>
Co-authored-by: erikpeik <52178013+erikpeik@users.noreply.github.com>
Co-authored-by: erikpeik <52178013+erikpeik@users.noreply.github.com>
Copilot AI changed the title [WIP] Create CounterStrikeSharp port of MapDecals plugin Implement CounterStrikeSharp port of MapDecals plugin from SwiftlyS2 Jan 13, 2026
Copilot AI requested a review from erikpeik January 13, 2026 12:25
@erikpeik
Copy link
Copy Markdown
Member

@copilot Can you add github action which would make release and build version

Co-authored-by: erikpeik <52178013+erikpeik@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Jan 13, 2026

@copilot Can you add github action which would make release and build version

Added GitHub Actions workflows for automated builds and releases (commit f09fe1d):

CI Build (.github/workflows/ci.yml):

  • Runs on all pushes and pull requests
  • Builds in both Debug and Release configurations
  • Validates compilation with no errors

Build and Release (.github/workflows/build-release.yml):

  • Automatically triggered when you push version tags (e.g., v1.0.0)
  • Builds the plugin in Release mode
  • Creates ZIP package with DLL, dependencies, config, and docs
  • Publishes GitHub release with automated release notes

To create a release, simply:

git tag v1.0.0
git push origin v1.0.0

See RELEASE.md for full documentation on using the workflows and creating releases.

@erikpeik erikpeik marked this pull request as ready for review January 13, 2026 12:34
@erikpeik erikpeik merged commit d9cf39e into main Jan 13, 2026
4 checks passed
@erikpeik erikpeik deleted the copilot/port-mapdecals-to-cssharp branch January 13, 2026 12:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants