05 · Runtime model

Lifecycle: load once, update while active

The current loader calls Load after the mod is registered and its declared client/server flag passes the loader’s mode check. It then checks the mod’s active config value. A false-to-true transition calls Activate; while the config stays true, the loader calls Update repeatedly. A true-to-false transition calls Deactivate.

MethodUse it forDesign notes
GetMetaData()Stable identity and declared supportReturn consistent name, version, author, target API, and mode flags.
Load(context)One-time preparationValidate signatures/layouts, allocate resources, or prepare a hook. Fail closed.
Activate(context)Enable the featureInstall/enable prepared behavior only after validation succeeds.
Update(context)Poll settings or feature stateRuns repeatedly while enabled. Keep it cheap; cache unchanged settings.
Deactivate(context)Turn behavior offRestore patched bytes, unregister callbacks, release active state.
Unload(context)Free owned resourcesBe safe after partial load or failed activation. Avoid double-free and stale hooks.
Lifecycle robustness is your responsibility. In the current code path, Unload is an interface requirement, but don’t assume it runs for every process exit or failure path. Keep destructors and cleanup idempotent.
06 · Reference

ModMetaData

Metadata tells the loader and users how the DLL describes itself. Current source uses client/server flags to skip loading when the running mode is not declared. Flags are declarations, not test evidence.

struct ModMetaData {
    std::string name;
    std::string description;
    std::string version;
    std::string author;
    std::string targetShroudtopiaVersion;
    bool hasClientSupport;
    bool hasServerSupport;
};

targetShroudtopiaVersion is descriptive in the current source path inspected; do not assume it is enforced as a compatibility gate. Test your mod against each loader version you claim to support.

07 · Reference

ModContext API

Shroudtopia passes a pointer to one shared context object to lifecycle methods. The header exposes these facilities in SDK/API 0.1.1.

context->shroudtopia

version, config_file, log_file, and mod_folder identify framework version and common paths. Treat paths as runtime values; do not hard-code a user's install location.

context->game

version, isClient, and isServer describe the running environment. In this release source, version is initialized to the literal "SVN HERE"; it is a placeholder. The mode booleans are based on the executable-name heuristic.

context->config

Typed getters read one value from the named mod section and accept a fallback. Available scalar and array pairs:

  • GetBool / GetBoolArray
  • GetString / GetStringArray
  • GetInt / GetIntArray
  • GetFloat / GetFloatArray
  • GetUint64 / GetUint64Array

Each getter takes (modKey, key, defaultValue). Use your metadata name for modKey. Validate ranges and handle malformed or out-of-range values in your mod.

context->Log(const char*)

Write a message to the framework log. Prefix messages with your mod ID. Pass a stable null-terminated string, and avoid format characters if a consumer might interpret them.

registeredMods and loadedMods

The context exposes a map of registered mod objects and a set of loaded mod pointers. This is internal-style shared state: check lifetime and nullability, and avoid assuming iteration order or a cross-version ABI guarantee.

No universal Enshrouded gameplay API. The context exposes framework utilities and native process access. It does not provide a supported inventory, player, world, UI, or network abstraction.
08 · Reference

Configuration patterns

Read through the type that matches the JSON value. Supply a safe default and validate it before use. The loader’s update loop makes periodic reads possible; it does not make expensive work safe to repeat.

const int toggleKey = context->config.GetInt(
    "example_mod", "toggle_key", 117); // F6 virtual-key code
const float distance = context->config.GetFloat(
    "example_mod", "camera_distance", -0.15f);
const std::vector<uint64_t> ids = context->config.GetUint64Array(
    "example_mod", "steam_ids", {});

if (toggleKey < 0 || toggleKey > 255) {
    context->Log("[example_mod] invalid toggle_key; using default");
}

For live-adjustable settings, compare the newly read value with the last applied value, then update only the associated data safely. Avoid rewriting executable instructions every poll.

Config safety

  • Use explicit units and bounds in documentation.
  • Define behavior for missing, wrong-type, non-finite, and out-of-range values.
  • Do not mutate the config file from every Update call.
  • Preserve user config across framework upgrades.
  • Keep settings keys stable; if a rename is required, migrate old keys deliberately.
09 · Native helpers

Memory utilities and sharp edges

context->memory exposes process-memory helpers. They are low-level mechanisms, not game APIs. A successful helper call cannot prove that a signature still identifies the intended instruction.

FunctionCurrent header signaturePurpose
FindPatternuintptr_t(const char*, const char*, uintptr_t, size_t)Scan a bounded address range for a byte pattern and mask.
Read / Writebool(uintptr_t, buffer, size_t)Copy bytes to or from an address.
WriteToReadOnlyMemorybool(LPVOID, LPVOID, SIZE_T)Write through protected-memory handling.
WriteJumpvoid(LPVOID target, LPVOID destination)Write a jump at a target address.
AllocateMemoryNearAddressLPVOID(LPVOID, SIZE_T)Request nearby memory for relative branches.
GetModuleBaseAddressuintptr_t(const std::string&)Look up a loaded module base.
ApplyPatchbool(uintptr_t, uint8_t*, size_t)Apply byte patch data.
InjectShellcodebool(uint8_t*, size_t, uintptr_t, uintptr_t*)Inject bytes near a target and optionally return its address.
CreateDetourbool(LPVOID target, LPVOID detour, void** trampoline)Declared detour helper; see release limitation below.
Do not rely on CreateDetour in 0.2.1-shroudweave.1. Its current implementation returns true while the actual installation code is commented out. That return value does not mean a hook exists.

Safer patch design

  • Verify executable identity, image bounds, unique signature, expected original instructions, and all required data layouts.
  • Reject zero, multiple, malformed, or already modified matches. Never patch on a partial match.
  • Keep a copy of original bytes. Restore them on deactivation and every failure path.
  • Allocate and free trampolines deterministically. Flush the instruction cache when required.
  • Use synchronized access for values changed while game code reads them.
  • Test unsupported versions and missing signatures; expected result is a safe inactive feature.
  • Never treat a static scan, unit test, or successful build as proof of live gameplay behavior.
↑ Top