Skip to content

Writing a client plugin ​

The WildM client is built on GRW ScriptHook — the advanced fork by GameXueRen (GPL-3.0) of Phiality's original, patched for WildM. A plugin is a Windows DLL renamed to .asi, loaded by the framework (dinput8.dll) and calling its exported Sh* functions. The full function list is the generated ScriptHook API reference. The fork's own detailed guide is forks/gamexueren/docs/plugins.md.

WildM servers do not load third-party plugins

Plugins are part of the client build that the launcher installs (grw-mp/build/ → %USERPROFILE%\WildM\client\). There is no mechanism today for a server to push plugins to players: server resources are data files only. This guide is for people working on the WildM client itself.

Where plugins live ​

In WildM everything is resolved next to the framework DLL — the client folder — not next to GRW.exe:

text
%USERPROFILE%\WildM\client\
├── dinput8.dll              the framework
├── scripthook.ini           main config, created on first launch
├── logs\                    every log file
└── plugins\
    └── myplugin\
        ├── myplugin.asi
        ├── myplugin.ini     the plugin's own config (same base name)
        └── lang.ini         optional text overrides
  • The loader reads plugins\ and one level of folders under it. A plugin is named after its file (myplugin.asi → myplugin). In the WildM build, .asi files at the root of the client folder are no longer loaded.
  • A plugin's settings sit beside its .asi with the same base name; ShPluginIniPath() returns that path, ShPluginConfigGet*/Set* read and write it.
  • A plugin with no line in scripthook.ini's [plugins] section is loaded; myplugin=0 switches it off (next launch).
  • Use ShFrameworkDir() for the client folder and ShGameDir() only when you really need the game folder — and never write there.

A minimal plugin ​

The loader starts plugins from a worker thread, after dinput8.dll is initialised, so a plugin can link the import library and call the API directly. Keep DllMain short and do the work in a thread:

c
#include <windows.h>
#include "scripthook.h"

SH_REQUIRES_API(1);              /* lowest plugin API version you need */

static void OnNoon(uint32_t m, uint32_t i, int v, void *u) {
    if (v) ShSetTime(12.0f);
}

static DWORD WINAPI Init(LPVOID p) {
    uint32_t menu = ShMenuCreate("High noon");
    ShMenuToggle(menu, "Always noon", 0, OnNoon, NULL);
    return 0;
}

BOOL WINAPI DllMain(HINSTANCE inst, DWORD why, LPVOID res) {
    if (why == DLL_PROCESS_ATTACH) {
        DisableThreadLibraryCalls(inst);
        CreateThread(NULL, 0, Init, NULL, 0, NULL);
    }
    return TRUE;
}

(The example is the one from the fork's guide; see Menu and Weather and time.)

API rules worth knowing ​

  • Return values: every int function returns 1 on success and 0 on failure; ShLastError() then gives an ShError code (Core).
  • Units: ShVec3 in metres — x east, y north, z up. Headings in radians.
  • API version: SH_API_VERSION (currently 2) goes up whenever a plugin-visible change happens. Declare the lowest version you need with SH_REQUIRES_API(n); the loader refuses a plugin that needs more than it offers, with a line in logs\scripthook.log and a toast. wildm_net declares 2 because it uses the shared hook layer (ShHook*).
  • Optional features: to stay loadable on older frameworks, resolve newer exports with GetProcAddress(GetModuleHandleA("dinput8.dll"), "ShSomething") and skip the feature if it is NULL.
  • Game modes: call ShPluginBlacklist(SH_MODE_BLACKLIST_GHOST_WAR | SH_MODE_BLACKLIST_MERCENARIES) (or whichever modes apply) and check ShPluginAllowed(). Every WildM plugin must stay inactive in Ghost War and Mercenaries. See Plugin mode blacklist and Play mode selection.
  • Game state: ShGetGameState() / ShIsInGame() tell you whether the world is loaded (Game state).
  • Logging: ShLogPath("myplugin.log", …) gives logs\myplugin.log next to the framework; respect ShLogLevel().
  • Menu callbacks arrive on a thread the API owns; calling the API from a callback is safe.

WildM-specific rules ​

  • Never create, modify or delete files in the game folder.
  • No plugin that moves or teleports the player is shipped in the WildM client build.
  • No DRM or anti-cheat interference.
  • Keep pure logic in a header you can unit-test outside the game, as wildm_net does with wildm_proto.h and tests/test_proto.c.

Build ​

See Building the client. To add a plugin to the WildM build, it has to be added to the Build-Plugin list in forks/gamexueren/build_wildm.ps1 (owned by the client team).

WildM is an independent community project. It is not affiliated with or endorsed by Ubisoft. Ghost Recon Wildlands is a trademark of Ubisoft Entertainment.