Skip to content

Native plugin support (Rust and C++) with an in-game plugin manager - #124

Open
baarsik wants to merge 14 commits into
CS-NextClient:mainfrom
baarsik:feature/plugin-support
Open

baarsik wants to merge 14 commits into
CS-NextClient:mainfrom
baarsik:feature/plugin-support

Conversation

@baarsik

@baarsik baarsik commented Oct 4, 2026

Copy link
Copy Markdown
  • Add an in-game Plugins menu for enabling modules, approving permissions, configuring load order, and reviewing compatibility issues
  • Bind approvals to plugin contents, validate dependencies and conflicts, and provide -noplugins recovery mode
  • Expose game events, client state, player input, HUD rendering, console commands, cvars, chat, connection controls, and persistent plugin storage
  • Support custom settings pages, windows, message filters, background tasks, and communication between plugins
  • Bundle a sample Life Stats plugin to report damage, kills, and death details locally in chat and the console. It's just an example, really, and should be replaced for something meaningful in the future
  • Add English and Russian UI text, SDK documentation, and example plugins
  • Extend regression coverage and CI to build and test the plugin runtime, client bridge, and Rust SDK
01-main-menu 02-plugins 03-plugins-enable 04-plugins-restart-pending

Please note it explicitly doesn't support Linux. Plugins use bcrypt, shell32, win headers and dll exports. And I don't have a Linux machine to fix and test this. Linux will not have plugins for now.

- Add an in-game Plugins menu for enabling modules, approving permissions, configuring load order, and reviewing compatibility issues
- Bind approvals to plugin contents, validate dependencies and conflicts, and provide -noplugins recovery mode
- Expose game events, client state, player input, HUD rendering, console commands, cvars, chat, connection controls, and persistent plugin storage
- Support custom settings pages, windows, message filters, background tasks, and communication between plugins
- Bundle a sample Life Stats plugin to report damage, kills, and death details locally in chat and the console. It's just an example, really, and should be replaced for something meaningful in the future
- Add English and Russian UI text, SDK documentation, and example plugins
- Extend regression coverage and CI to build and test the plugin runtime, client bridge, and Rust SDK
- Plugins use bcrypt, shell32, win headers and dll exports. And I don't have a Linux machine to fix and test this. Linux will not have plugins for now
- Added safe-start recovery after unclean sessions, session metadata and bounded callback traces
- Attached current and previous plugin session diagnostics to Windows crash reports
- Added shared host-memory accounting, resource statistics and callback timing
- Introduced global callback budgets and rotating event, frame and drawing dispatch
- Migrated settings into individual plugin files with validation, backups and recovery
- Added retained service completions with status, cancellation and acknowledgement operations
- Added cooperative worker cancellation and earlier resource cleanup when plugins retire
- Added a developer-only Diagnostics tab.
@baarsik

baarsik commented Oct 5, 2026

Copy link
Copy Markdown
Author
  • Added safe-start recovery after unclean sessions, session metadata and bounded callback traces
  • Attached current and previous plugin session diagnostics to Windows crash reports
  • Added shared host-memory accounting, resource statistics and callback timing
  • Introduced global callback budgets and rotating event, frame and drawing dispatch
  • Migrated settings into individual plugin files with validation, backups and recovery
  • Added retained service completions with status, cancellation and acknowledgement operations
  • Added cooperative worker cancellation and earlier resource cleanup when plugins retire
  • Added a developer-only Diagnostics tab

This is how the tabs look when -dev mode is enabled. When dev is disabled, tabs are hidden - only the description is visible.
image

- Restored Windows 7 compatibility for shipped plugin DLLs (who cares)
- Fixed plugin menu crashes, dropdown input, Unicode text limits, and large-font/image clipping
- Corrected load-order warnings and added actionable English/Russian package errors
- Consolidated duplicated runtime/SDK logic and reduced repeated UI work
@Polarhigh

Copy link
Copy Markdown
Contributor

This is a powerful feature. However, I’m concerned that native plugins run inside the game process without memory isolation, allowing them to access engine state directly and bypass restrictions imposed by the plugin API. This significantly lowers the barrier to developing cheats. Someone can already fork the client and implement cheats directly in it, but I would prefer not to make this level of access part of the supported plugin system.

Have you considered a plugin system that isolates plugins from the host process’s memory, for example WebAssembly or an embedded scripting language with a restricted API?

@baarsik

baarsik commented Oct 5, 2026

Copy link
Copy Markdown
Author

To be honest, I don’t think native plugin support changes much for someone who already wants to develop cheats. Injecting a library into a game without a/c isn’t particularly hard, and NitroAPI already provides hooking capabilities. Native plugins make loading and integration more convenient, but that’s already the easiest part of the job that requires no coding skills.

Basically a sandbox would restrict what plugins can do through the supported system. However, a cheat developer could simply use native injection, or modify the client itself. So while sandboxing has benefits for running untrusted plugins, I’m not convinced it would make a significant difference specifically for cheat development in this environment.

As a side note -- I was also thinking about allowing servers to request a list of loaded plugins, including their hashes and declared permissions. Fell out of scope, though - already too big to thoroughly review. Server admins could then maintain an allowlist, require certain plugins, or ask players to disable plugins they don’t allow (based on name, hash, required permissions). A modified client could still falsify that information, but at this point we're back at "they could just do the hacks old way, which would be actually even easier".

tl;dr: Native plugin support adds convenience, but the underlying access is already available through existing tools. I see server-controlled plugin policies as a way to mitigate risk.

@baarsik

baarsik commented Oct 5, 2026

Copy link
Copy Markdown
Author

To further clarify, I disagree on "significantly lowers the barrier to developing cheats" part. Same effort needed, plugin system only allows to inject code (which is the entire point of native plugins -- I wanted this access to be present). And an injector can be downloaded for free from the internet. So someone will have to hook into the VMTs and implement actual cheat logic. At this point no matter what you do, having an open-source client is the biggest concern, NitroAPI is second risk and only then comes the native plugin support. I don't think people will be writing cheats just for this plugin system + this particular SDK, when there is a significantly more convenient way to do it. A much more likely scenario is that someone will take existing open-source cheats and rewrite it using a public mapper, since other injection methods are far more versatile and would apply to other game clients as well.

@baarsik

baarsik commented Oct 5, 2026

Copy link
Copy Markdown
Author

There's even a better example: there's metahook, which is partially what I was looking at (but the idea was to get rid of hlsdk and allow plugins to be super lightweight, user-friendly, configurable from the UI and much more integrated into the client itself). As far as I know, there aren't cheats that utilize metahook. Same reasoning here: there are easier ways to make cheats, than use this system.

- UTF-8: Reject malformed host strings and contain Options JSON decoding and schema failures
- Message subscriptions: Use the existing client-table wrappers to observe registered consumers. Reject subscriptions and filters for unobserved messages without changing engine registration. Registrations through other tables remain unsupported
- Optional scoreboard messages: Remove fallback HealthInfo and Account registrations. These updates now require a consumer registered through a wrapped table
- Round state: Detect ordinary-player transitions through ResetHUD/RoundTime, clear stale round results, and suppress duplicate starts during active-round refreshes
- Damaged plugins: Exclude invalid metadata from compatibility checks while retaining diagnostic catalog entries
- Window buttons: Keep stateless button actions out of persisted widget values, preventing unnecessary window recreation and loss of position and size
- Discovery: Cache unchanged package metadata. Force fresh discovery for approval and retain final locked hash verification before loading. Discovery remains synchronous
- Shared adapters: Consolidate GameUI control creation, value extraction, and synchronization; share bounded-string retrieval in the Rust SDK
- Cleanup: Extract snapshot serialization, consolidate game-state resets, remove unused CvarStorage::archive, and condition the client JSON dependency on plugin support
- Capped dropdown options and accounted for their UI memory
- Fixed pending store deletes and cancelled/timed-out service callbacks
- Preserved empty list selections; corrected signed sliders and range validation
- Added Options → Plugins immediately after Game, visible but disabled when no active plugin contributes controls
- Added localized plugin sections in load order, with down/right triangle icons. The first opens by default; only one can remain expanded, and all can be collapsed
- Added permission-free, owner-scoped settings helpers for C++ and Rust. Custom tabs and additions to other settings pages still require ui.settings
- Added Life Stats Output choices: Console by default or Console + Chat. The choice persists, and switching to Console clears queued chat
- Added Rust Life Stats with matching C++ metadata and behavior. It now ships as the default plugins/life_stats.dll; C++ remains a reference
@baarsik

baarsik commented Oct 9, 2026

Copy link
Copy Markdown
Author

Plugin settings
image

User-friendly alternative to -noplugins after crash
image

@baarsik

baarsik commented Oct 10, 2026

Copy link
Copy Markdown
Author

A 'More' button that shows all tabs that didn't have enough space in Options. Fixes a bug where an Options window becomes resized when settings are applied. Also specifically important because a new tab was introduced and there is support for plugin-specific tabs as well.
image

Integrate upstream 278e926, preserving the Windows plugin runtime, SDK,
settings, translations, and CI checks alongside the new radar and NitroApi
source layout. Resolve HUD visibility and shutdown ordering across both
branches, and migrate plugin consumers and fixtures to goldsrc ABI types.

Preserve networking snapshots with maintained Windows x86 net_api_t and
net_status_t declarations. Compiler-measured layouts and signatures come
from SDK c9f95e6f91f67d79002a79df13244a7538ab799f using the recorded MSVC x86
probe; plugin_facts_windows.json records the extraction. Add layout checks
and a network snapshot integration test without changing the public SDK.

Validation: Windows x86 Release BUILD_ALL packaging and test build passed;
CTest 1036 passed, 1 optional map test skipped; Rust SDK and Life Stats
17 passed; ABI tooling 355 passed, 2 skipped; provenance gate passed.
The ABI negative control rejects an incorrect latency field type.
No game launch or Linux runtime validation was performed.
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