Debugging plugins
Where the server runs
The launcher starts the server as a child process of the same program:
LonelyIce.exe --server -c <server folder>\configs\worldserver.conf
with the server folder as working directory, the config by its absolute path and a few environment variables. The
core reads the module and plugin configs from the modules folder beside that config (configs/modules); the
launcher passes none of their values as AC_* variables. World and auth run in that one process, and it loads your
plugin. While it runs it holds plugins/.cache/server.lock locked, so --pkg and the launcher's Plugins page refuse
to change plugins until it stops. Its standard output is the log the launcher shows, plus control lines starting
with @@LI; its standard input takes console commands. The process layout is described in
Launcher architecture.
Logs
- Console tab: the live log of the running server.
- Files:
LogsDirofworldserver.conf,logsin the server folder by default:Server.log,Errors.log,Playerbots.log. The Maintenance page lists them.
Log from your plugin with the core's macros:
#include "Log.h"
LOG_INFO("module", "example.greeter: greeted {}", player->GetName());
LOG_DEBUG("module.greeter", "message was '{}'", message);
LOG_ERROR("module", "example.greeter: {}", error);
Loggers are hierarchical: module.greeter uses the module logger unless one of its own is configured. The default
worldserver.conf has Logger.module=4,Console Server (info). For debug output, set in worldserver.conf:
Logger.module=5,Console Server
The plugin loader logs under server.loading, the database layer under sql.driver and sql.updates.
Loader messages
A loaded plugin logs one line:
Plugin example.greeter 1.0.0 (Greeter)
Everything else means it was not loaded. The loader skips the plugin, and every plugin that depends on it:
| Message | Cause | Fix |
|---|---|---|
Plugin <folder>: unsupported manifest format 0 |
"format" missing or not 1. |
Add "format": 1. |
Plugin <folder>: manifest needs id and version |
Missing id or version. |
|
Plugin <folder>: bad plugin.json: ... |
Not valid JSON. | |
Plugin <folder>: id <id> is already used by <other> |
Two folders with the same id. |
Remove the copy. |
Plugin <id> <version> skipped: needs <dep> <range> |
A dependency is missing, or installed <version> is out of range. |
Install or update it. |
Plugin <id> <version> skipped: conflicts with <id> |
A plugin from conflicts is present. |
|
... not loaded: built for core <abi>, this server is <abi> |
core.abi in the manifest differs from the server's AC_PLUGIN_ABI. |
Build against the core of the release you run. |
... not loaded: no build for windows-x64 (<path>) |
No server/<platform>/<library> for this system, or server.library does not match the file name. |
|
... not loaded: LoadLibrary failed, error <n> |
Windows could not load the DLL. 126: a DLL it needs is missing (put it in RUNTIME_FILES); 127: a function it imports is missing, usually a core built from other sources. |
|
... not loaded: dlopen failed: <message> |
The same on Linux and macOS. | |
... not loaded: not a plugin library (missing exports) |
No entry macro in the library. | Add AC_PLUGIN, AC_PLUGIN_ON_LOAD or AC_PLUGIN_ENTRY. |
... not loaded: library built for <abi> <platform>, this server is <abi> <platform> |
The library's compiled-in ABI or platform differs, although the manifest matched. | Rebuild; do not edit core.abi by hand. |
... not loaded: this server is built without shared libraries ... |
A core built with static linking cannot load libraries. | Build the plugin into it. |
... not loaded: a dependency failed to load |
See the dependency's own line. |
A plugin whose server.apps does not include the running program is left out without a message.
About the ABI
The ABI string is a promise, not a check of the binary. The loader compares core.abi and the library's
AcorePlugin_Abi() with the server's AC_PLUGIN_ABI (lonelyice-ac-2 for current releases) and the platform
(windows-x64: MSVC with the dynamic release runtime). Two builds with the same string still differ if they were made
from different core sources or options. Crashes at load or in the first call into the core usually mean exactly that.
Build your plugin in a LonelyIce build tree of the same release you test with; see
ABI.
Running the server by hand
Stop the server in the launcher first: two servers cannot share the ports or the SQLite files. Then start the same command with the environment the launcher would give it (the full list is in Environment variables):
| Variable | Value |
|---|---|
AC_PLUGINS_DIR |
The plugins folder next to LonelyIce.exe. It overrides PluginsDir. |
LONELYICE_CLIENT |
The game folder, so client patches are built. |
LONELYICE_LOCALE |
The client locale to read game data from, e.g. enGB. |
LONELYICE_DATA=client |
When the disk cache is off (Settings → Storage): game data is read from the client. |
AC_DBC_FROM_DATABASE=1 |
When the disk cache is on, instead of LONELYICE_DATA. |
With a database server as storage, also the *DatabaseInfo overrides described in
Storage plugins.
Windows cmd, from the server folder:
cd /d D:\LonelyIce
set AC_PLUGINS_DIR=D:\LonelyIce\plugins
set LONELYICE_CLIENT=D:\Games\WoW335
set LONELYICE_LOCALE=enGB
set LONELYICE_DATA=client
start "" /b /wait LonelyIce.exe --server -c configs\worldserver.conf
LonelyIce.exe is a windowed program, so the shell does not wait for it on its own; start /b /wait keeps the
console for the server. Type console commands such as reload config or server shutdown 0; Ctrl+C stops the server
too. When its input ends, the server saves and stops, as it does when the launcher closes.
The server prints for the launcher, so a command's answer comes as @@LI out <text> lines, one per line of output,
followed by @@LI done ok or @@LI done fail:
account create tester secret
@@LI out Account created: tester
@@LI done ok
server restart <seconds> makes the process exit with code 2; the launcher starts it again, a shell does not. The
server run by hand holds plugins/.cache/server.lock as well: --pkg install, update, remove, enable,
disable and apply on that plugins folder refuse until it stops. -c may be relative (to the working directory);
the modules folder beside the config is used either way.
Linux and macOS:
cd ~/LonelyIce
AC_PLUGINS_DIR=$PWD/plugins LONELYICE_CLIENT=~/Games/WoW335 LONELYICE_DATA=client \
./LonelyIce --server -c configs/worldserver.conf
To apply plugin SQL and patches without starting the world, use
LonelyIce.exe --pkg apply -c configs\worldserver.conf --client <game folder>.
Attaching a debugger
Build RelWithDebInfo (or Debug). The .pdb files stay in the build output; --pkg pack and the release leave them
out. A debugger finds a plugin's .pdb next to the DLL or at the build path recorded in it.
Attach to the running server. Start the server from the launcher, then attach to the LonelyIce.exe process
whose command line contains --server (in Visual Studio: Debug → Attach to Process; the launcher's own process is the
other LonelyIce.exe). Breakpoints in your scripts are hit on the next event. On Linux: gdb -p <pid>.
Debug the start. Code in AC_PLUGIN_ON_LOAD, script constructors or start-up hooks has run before you can attach.
Start the server process under the debugger instead, with the arguments, working directory and environment from the
previous section:
- Visual Studio: open
LonelyIce.exeas a project (File → Open → Project/Solution), and set Arguments (--server -c configs\worldserver.conf), Working Directory and Environment in its debugging properties. - gdb:
gdb --args ./LonelyIce --server -c configs/worldserver.conf, with the variables exported in the shell.
The plugin DLL is loaded while the server starts; set breakpoints as pending or break on its load.
Switching a plugin off
When a plugin keeps the server from starting, or to rule it out:
- Launcher: Plugins page, disable it.
- Command line:
LonelyIce.exe --pkg disable <id>, later--pkg enable <id>. - By hand: move
plugins/<id>toplugins/.disabled/<id>; the server does not look there.
Disabling is refused while an enabled plugin depends on it, and enabling is refused while one of the plugin's own
dependencies is missing, disabled or out of range. Stop the server before changing plugins (--pkg and the launcher
refuse while it holds plugins/.cache/server.lock; moving the folder by hand is not checked). On the next start the
plugin's patches are uninstalled, but it keeps its named ids, so enabling it again gives it the same ones
(Client patches); its tables and rows stay in the databases. Its
Settings group disappears; its config file in configs/modules stays.
Common problems
| Symptom | Look at |
|---|---|
| Plugin SQL did not run after an edit | Was the server restarted? The SQL stamp hashes the files' contents, so any edit is applied on the next start; a database the plugin owns is updated by the plugin's own code, not by the stamp (Shipping SQL). |
| A setting has no effect | The key in modules/<name>.conf beside the config the server was started with (configs/modules), an AC_<KEY> variable in the environment that overrides it, the apply mode, and whether your code re-reads it (Plugin settings). |
--pkg says the server is running on this plugins folder |
A server process (from the launcher or by hand) still holds plugins/.cache/server.lock: stop it. |
| A named id is 0 | Log lines Plugin patches:; the query ran before the databases were open. |
| The client does not show a new spell | The launcher needs the game folder; check Data/<locale>/patch-<locale>-4.MPQ and restart the game. |
| The package is not offered in the catalog | core and platforms of the entry (Hosting your own catalog). |