Skip to content

Fix AntiCheat plugin resolution and DLL load diagnostics#481

Open
IbrahimAlzaidi wants to merge 1 commit intoGeneralsOnlineDevelopmentTeam:pluginsfrom
IbrahimAlzaidi:plugin/fix_cheat_dll
Open

Fix AntiCheat plugin resolution and DLL load diagnostics#481
IbrahimAlzaidi wants to merge 1 commit intoGeneralsOnlineDevelopmentTeam:pluginsfrom
IbrahimAlzaidi:plugin/fix_cheat_dll

Conversation

@IbrahimAlzaidi
Copy link
Copy Markdown

Fix the AntiCheat startup path so old or incomplete settings can no longer produce the invalid DLL path plugins/.dll.

Previously, NGMP_OnlineServicesManager::Init() built the plugin DLL path inline from Settings.GetAnticheatPlugin():

plugins/{plugin}/{plugin}.dll

If plugins.anticheat was missing, empty, or invalid in settings.json, the settings object could return an empty string and startup attempted to load:

plugins/.dll

That made the real failure hard to diagnose and also relied on process current working directory behavior for DLL loading.

This change makes AntiCheat startup deterministic, self-diagnosing, and safer:

  • Add AnticheatPluginResolver

    • Centralizes AntiCheat plugin path resolution.
    • Defaults missing/empty settings to easyanticheat.
    • Resolves approved candidates from the game executable directory.
    • Restricts candidates to the <exe dir>/plugins directory.
    • Records selected path, tried paths, defaulting state, and diagnostic notes.
    • Produces both detailed log text and user-facing failure text.
  • Normalize AntiCheat settings

    • Add GenOnlineSettings::DEFAULT_ANTICHEAT_PLUGIN.
    • Default m_Plugins_Anticheat to easyanticheat.
    • Make GetAnticheatPlugin() return the default as a final safety net.
    • Normalize the value after loading settings.
    • Normalize again before saving so empty values are not persisted.
    • Track whether the setting was healed/defaulted so diagnostics can explain old or missing config files accurately.
  • Use resolver during online services startup

    • Ensure settings are initialized before resolving the plugin.
    • Replace inline std::format("plugins/{}/{}.dll", ...) path construction.
    • Log the complete AntiCheat resolution result.
    • Show a user-visible AntiCheat error if no approved plugin file is found.
    • Fail closed by quitting instead of allowing online play without AntiCheat.
  • Improve DLL loading

    • Change AnticheatPlugInterface::LoadPlugin() to return bool.
    • Add an optional failure-reason output string.
    • Load the plugin from the resolved absolute path with LoadLibraryExA.
    • Use LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR and LOAD_LIBRARY_SEARCH_DEFAULT_DIRS for controlled dependency search.
    • Capture GetLastError() and convert it with FormatMessageA.
    • Report missing required exports as explicit load failures.
    • Reset function pointers on failure.
    • Track the last load path and Win32 load error.
    • Unload any already-loaded plugin module before reloading.

The user now gets actionable failures instead of the old generic plugins/.dll load error. Missing files show the exact approved paths checked. DLL load failures show the resolved plugin path plus the formatted Windows error, which helps distinguish missing plugin files from missing dependency DLLs, architecture mismatches, quarantined files, or service/install issues.

Verified with:

cmake --preset win32 \
  -DRTS_BUILD_GENERALS=OFF \
  -DRTS_BUILD_ZEROHOUR=ON \
  -DRTS_BUILD_CORE_TOOLS=OFF \
  -DRTS_BUILD_ZEROHOUR_TOOLS=OFF \
  -DRTS_BUILD_CORE_EXTRAS=OFF \
  -DRTS_BUILD_ZEROHOUR_EXTRAS=OFF \
  -DRTS_INSTALL_PREFIX_ZEROHOUR="C:/sys/Zero Hour Standalone"

cmake --build build/win32 --config Release --target z_generals --parallel 8

Build completed successfully:

[1107/1107] Linking CXX executable GeneralsMD\Release\GeneralsOnlineZH.exe

Fix the AntiCheat startup path so old or incomplete settings can no longer
produce the invalid DLL path `plugins/.dll`.

Previously, `NGMP_OnlineServicesManager::Init()` built the plugin DLL path
inline from `Settings.GetAnticheatPlugin()`:

    plugins/{plugin}/{plugin}.dll

If `plugins.anticheat` was missing, empty, or invalid in `settings.json`, the
settings object could return an empty string and startup attempted to load:

    plugins/.dll

That made the real failure hard to diagnose and also relied on process current
working directory behavior for DLL loading.

This change makes AntiCheat startup deterministic, self-diagnosing, and safer:

- Add `AnticheatPluginResolver`
  - Centralizes AntiCheat plugin path resolution.
  - Defaults missing/empty settings to `easyanticheat`.
  - Resolves approved candidates from the game executable directory.
  - Restricts candidates to the `<exe dir>/plugins` directory.
  - Records selected path, tried paths, defaulting state, and diagnostic notes.
  - Produces both detailed log text and user-facing failure text.

- Normalize AntiCheat settings
  - Add `GenOnlineSettings::DEFAULT_ANTICHEAT_PLUGIN`.
  - Default `m_Plugins_Anticheat` to `easyanticheat`.
  - Make `GetAnticheatPlugin()` return the default as a final safety net.
  - Normalize the value after loading settings.
  - Normalize again before saving so empty values are not persisted.
  - Track whether the setting was healed/defaulted so diagnostics can explain
    old or missing config files accurately.

- Use resolver during online services startup
  - Ensure settings are initialized before resolving the plugin.
  - Replace inline `std::format("plugins/{}/{}.dll", ...)` path construction.
  - Log the complete AntiCheat resolution result.
  - Show a user-visible AntiCheat error if no approved plugin file is found.
  - Fail closed by quitting instead of allowing online play without AntiCheat.

- Improve DLL loading
  - Change `AnticheatPlugInterface::LoadPlugin()` to return `bool`.
  - Add an optional failure-reason output string.
  - Load the plugin from the resolved absolute path with `LoadLibraryExA`.
  - Use `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR` and
    `LOAD_LIBRARY_SEARCH_DEFAULT_DIRS` for controlled dependency search.
  - Capture `GetLastError()` and convert it with `FormatMessageA`.
  - Report missing required exports as explicit load failures.
  - Reset function pointers on failure.
  - Track the last load path and Win32 load error.
  - Unload any already-loaded plugin module before reloading.

The user now gets actionable failures instead of the old generic
`plugins/.dll` load error. Missing files show the exact approved paths checked.
DLL load failures show the resolved plugin path plus the formatted Windows error,
which helps distinguish missing plugin files from missing dependency DLLs,
architecture mismatches, quarantined files, or service/install issues.

Verified with:

    cmake --preset win32 \
      -DRTS_BUILD_GENERALS=OFF \
      -DRTS_BUILD_ZEROHOUR=ON \
      -DRTS_BUILD_CORE_TOOLS=OFF \
      -DRTS_BUILD_ZEROHOUR_TOOLS=OFF \
      -DRTS_BUILD_CORE_EXTRAS=OFF \
      -DRTS_BUILD_ZEROHOUR_EXTRAS=OFF \
      -DRTS_INSTALL_PREFIX_ZEROHOUR="C:/sys/Zero Hour Standalone"

    cmake --build build/win32 --config Release --target z_generals --parallel 8

Build completed successfully:

    [1107/1107] Linking CXX executable GeneralsMD\Release\GeneralsOnlineZH.exe
@IbrahimAlzaidi
Copy link
Copy Markdown
Author

@x64-dev @MrS-ibra

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.

1 participant