Theme
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,.asifiles at the root of the client folder are no longer loaded. - A plugin's settings sit beside its
.asiwith 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=0switches it off (next launch). - Use
ShFrameworkDir()for the client folder andShGameDir()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
intfunction returns1on success and0on failure;ShLastError()then gives anShErrorcode (Core). - Units:
ShVec3in metres — x east, y north, z up. Headings in radians. - API version:
SH_API_VERSION(currently2) goes up whenever a plugin-visible change happens. Declare the lowest version you need withSH_REQUIRES_API(n); the loader refuses a plugin that needs more than it offers, with a line inlogs\scripthook.logand a toast.wildm_netdeclares2because 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 isNULL. - Game modes: call
ShPluginBlacklist(SH_MODE_BLACKLIST_GHOST_WAR | SH_MODE_BLACKLIST_MERCENARIES)(or whichever modes apply) and checkShPluginAllowed(). 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", …)giveslogs\myplugin.lognext to the framework; respectShLogLevel(). - 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_netdoes withwildm_proto.handtests/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).