Skip to content

KenshiLua v0.3.5-alpha

Choose a tag to compare

@Genpretz Genpretz released this 04 Oct 09:55
· 155 commits to main since this release

Overview

KenshiLua v0.3.5-alpha expands the Lua scripting API with arithmetic and comparison operator overloading, new engine lifecycle constructor events, MyGUI widget factories, and broader gameplay class method bindings.

This release also introduces a dedicated backward compatibility layer in src/Compatibility/LegacyCompat.cpp. This layer addresses breaking changes introduced in recent updates regarding event names, enum tables, and MyGUI APIs, ensuring existing scripts continue running while providing clear paths for migration.


Backward Compatibility & API Migration (LegacyCompat.cpp)

Recent releases introduced changes to event registration, enum naming conventions, and MyGUI widget access. To prevent breaking existing scripts, src/Compatibility/LegacyCompat.cpp automatically bridges legacy script invocations to their modern equivalents at runtime.

How Existing Scripts Are Affected

  • Non-Breaking Execution: Older scripts run without crashing or requiring immediate code edits. Lookups for legacy event names, deprecated enum tables, and older MyGUI convenience helpers are intercepted and resolved transparently.
  • Advisory Deprecation Logs: Accessing legacy interfaces emits an advisory warning in kenshilua.log detailing the deprecated call and the exact replacement syntax.
  • Migration Timeline: These backward compatibility shims are temporary. Script authors should update their code to canonical APIs, as legacy shims will be phased out in subsequent releases.

1. Event Subscription Migration (registerHandler to Events.on)

Event handling across engine systems has been unified under the global Events table with standard on and off syntax, replacing global registration functions and redundant aliases.

  • Legacy Functions:
    • registerHandler(eventName, handler) (global function)
    • unregisterHandler(handlerId) (global function)
  • Canonical API:
    • Events.on(eventName, handler): Registers an event listener and returns an integer handlerId.
    • Events.off(handlerId): Unregisters the listener by its ID.
  • Event Naming Migration:
    • Legacy shorthand: "onCharacterDeath", "onKeyDown", "onPlayerRecruit", "onBuildingConstructed".
    • Canonical scoped symbols: "Character::declareDead", "InputHandler::keyDownEvent", "PlayerInterface::recruit", "Building::_CONSTRUCTOR".
  • Backward Compatibility: Global registerHandler and unregisterHandler remain available as compatibility wrappers. Deprecated shorthand strings are intercepted by LegacyCompat::ResolveEventName as a fallback, translating them to their canonical C++ symbols and logging an advisory warning.
-- ============================================================
-- Engine Events: Before & After
-- ============================================================

-- Legacy Syntax:
local handlerId = registerHandler("onKeyDown", function(inputHandler, keyCode)
    print("Key pressed:", keyCode)
end)
unregisterHandler(handlerId)

-- Modern Canonical Syntax:
local handlerId = Events.on("InputHandler::keyDownEvent", function(inputHandler, keyCode)
    print("Key pressed:", keyCode)
end)
Events.off(handlerId)

2. MyGUI Widget Callback Migration (registerCallback to widget:on)

UI event handling has undergone the exact same standardization: all widget listener management is now unified on widget:on and widget:off.

  • Legacy Methods:
    • widget:registerCallback(eventType, handler) (deprecated)
  • Canonical API:
    • widget:on(eventType, handler): Attaches an event listener to the widget and returns an integer callbackId.
    • widget:off(idOrEvent): Detaches the listener by its integer callback ID, or detaches all listeners for a given event type string (e.g. widget:off("eventMouseButtonClick")).
  • Canonical Event Names (1:1 with C++ Delegate Members):
    • Widget event names are now strictly 1:1 with MyGUI's C++ delegate member variables (e.g. "eventMouseButtonClick", "eventEditTextChange", "eventWindowButtonPressed", "eventKeyButtonPressed").
  • Backward Compatibility:
    • All legacy shorthand names and aliases (such as "OnClick", "click", "MouseButtonClick", "change", "OnTextChanged", "keyDown") are deprecated and automatically resolved via LegacyCompat::ResolveWidgetEventName, logging an advisory warning in kenshilua.log that points to the canonical event* name.
-- ============================================================
-- MyGUI Widget Events: Before & After
-- ============================================================

-- Legacy Syntax (deprecated, emits log warning):
local button = window:createWidget("Button", "Button", 10, 10, 100, 30, "Align", "MyButton")
local callbackId = button:registerCallback("OnClick", function(sender)
    print("Button clicked!")
end)

-- Modern Canonical Syntax:
local button = window:createWidget("Button", "Button", 10, 10, 100, 30, "Align", "MyButton")
local callbackId = button:on("eventMouseButtonClick", function(sender)
    print("Button clicked!")
end)

-- Detach listener when no longer needed:
button:off(callbackId)
-- Or detach all click listeners on that widget:
button:off("eventMouseButtonClick")

3. Enum Scoping and Constant Naming

Enums have been aligned 1:1 with literal C++ enum types and scoped constants declared in the KenshiLib SDK.

  • Table Names: Scoped under their declaring classes or full C++ enum typenames (e.g. CrimeEnum, CharacterTypeEnum, Character.CharMessage, BuildingPlacementGroundType.Enum).
  • Member Constants: Retain original C++ prefixes to prevent collisions (e.g. CRIME_MURDER instead of MURDER, OT_CIVILIAN instead of CIVILIAN, BF_RESEARCH instead of RESEARCH).
  • Backward Compatibility: LegacyCompat::InstallEnumCompatibility generates legacy global tables and installs __index metatable forwarders on canonical tables. Legacy accesses resolve to canonical values and log a one-time migration warning.
-- ============================================================
-- Enum Scoping: Before & After
-- ============================================================

-- Legacy (deprecated, emits log warning):
local crime = Crime.MURDER
local charType = CharacterType.CIVILIAN
local func = BuildingFunction.RESEARCH

-- Canonical API:
local crime = CrimeEnum.CRIME_MURDER
local charType = CharacterTypeEnum.OT_CIVILIAN
local func = BuildingFunction.BF_RESEARCH

4. MyGUI Helpers & Widget Properties

MyGUI bindings have been refactored to align with the underlying MyGUI C++ class structure and ForgottenGUI widget creation systems.

  • Global Helpers: Restored legacy global convenience functions on the MyGUI table (MyGUI.resetKeyFocus(), MyGUI.setPointerVisible(), MyGUI.isResourceExist(), MyGUI.loadResource(), and legacy parameter layouts for MyGUI.createWidget()).
  • Widget Properties: Metatable bridges restore property accessors (setFontName, setEditReadOnly, setMinSize, setImageTexture, setPositionReal, setCoordReal, attachToWidget, detachFromWidget) directly across widget instances.

5. Why These Changes Were Made

During the alpha phase, KenshiLua's API has transitioned from experimental, handwritten shorthands to strict 1:1 parity with Kenshi's underlying C++ SDK (KenshiLib) and MyGUI:

  • Predictability & 1:1 SDK Parity: Replacing ad-hoc event names ("onKeyDown") and widget callbacks ("OnClick") with literal C++ symbols ("InputHandler::keyDownEvent", "eventMouseButtonClick") eliminates guesswork. Developers can inspect KenshiLib headers and reference docs directly to know the exact event and delegate names.
  • Collision Prevention & Disambiguation: Scoping enum tables under their true C++ types and retaining SDK prefixes prevents constant collisions and eliminates subtle naming bugs. In legacy tables where prefixes and underscores were stripped, distinct engine values collided into fragile casing differences—such as legacy Stats.FARMING (STAT_FARMING skill) versus Stats.Farming (_Farming work-rate multiplier). Canonical identifiers like StatsEnumerated.STAT_FARMINGand StatsEnumerated._Farming guarantee 1:1 clarity with KenshiLib and eliminate global namespace clutter.
  • Unified Event Lifecycle: Standardizing both engine hooks and widget delegates on symmetrical on and off methods (Events.on/Events.off and widget:on/widget:off) replaces fragmented registration helpers with a single consistent API pattern.
  • Clear Terminology (register vs. on): The previous use of "register" (registerHandler, widget:registerCallback) created confusion. In the Lua C API and throughout KenshiLua's own codebase, "register" exclusively refers to exposing C++ classes, metatables, and functions to the Lua runtime (e.g. luaL_register, registerBinding, registerConstructor). Overloading the word "register" to also mean dynamic event subscription conflated engine setup with runtime script logic. Transitioning to Events.on / Events.off establishes a clear distinction between binding infrastructure and event subscription, while adopting the standard .on() / .off() listener pattern widely used across Node.js (EventEmitter), modern Lua event libraries, and event-driven frameworks.

New Features

  • Operator Overloads & Math Bindings: Implemented arithmetic (+, -, *, /, unary -), comparison (<, <=, >, >=), equality (==), and explicit operator_bool across 40+ core math and engine classes (including NxVec3, hkVector4f, TimeOfDay, hand, InstanceID, and CPerfTimer).
  • Engine Lifecycle Hooks: Added _CONSTRUCTOR event hooks across major subsystems (GameData, Tasker, GameWorld, FactionManager, ZoneManager, DialogueWindow, InventoryGUI, RootObject, and others in src/Callbacks.h), plus constructor bindings for value-type objects.
  • MyGUI Factories & Window Callbacks: Added ForgottenGUI widget creation factory methods (createWidget, createWidgetReal) and event callbacks across interface windows (BuildModeWindow, CharacterEditWindow, InventoryGUI, LevelEditor, tooltips, and layout classes).
  • Extended Gameplay Bindings: Implemented unmapped methods for Armour, Building, Crossbow, Sword, TagsClass, ZoneManager, and inventory layout classes.
  • Binary Version Detection: Startup logging in src/Plugin.cpp now reports whether the running game is the GOG or Steam executable along with the binary version string.

Fixes

  • Metatable Inheritance: Corrected class inheritance wiring in src/Lua/RegisterBindings.cpp for ResourceLoader -> ThreadClass and hkContainerHeapAllocator::Allocator -> hkMemoryAllocator.
  • Doc Tooling Path Fix: Resolved script path resolution in tools/docs/generate_enums_reference.py, restoring missing entries in docs/EnumsReference.md.
  • Documentation Typo: Corrected documentation filename typo from UnboundRefrence.md to docs/UnboundReference.md.

Refactors & Documentation

  • Example Scripts Modernization: Updated all bundled example scripts and example mods to use canonical event subscriptions and modern API calls.
  • Directory Cleanup: Renamed src/Compatability to src/Compatibility and relocated SDKCompat.h to src/Compatibility/TypeShims.h.
  • Project Maintenance: Added a Visual Studio Debug|x64 profile, added GitHub issue templates, and regenerated docs/BindingsReference.md and docs/CallbacksReference.md.