Skip to content

Kiln 0.2.0-dev.26

Pre-release
Pre-release

Choose a tag to compare

@frostdev-ops frostdev-ops released this 12 Nov 22:10
d0fdb64

Kiln 0.2.0-dev.26

The first of the releases that complete the mod API: data that stays with one item, weapons with attacks of their own, typed reads of the game's data, offline tests for mods, and a launcher that can move its data folder to another drive. Almost all of it works on test servers and in Kiln's tests. One session in a real game (Linux, Proton, one player on a test server) has seen the launcher and the game start, item data, mod creatures and the sample weapons' own attacks; the rest waits for a game test (see "Known limits").

Mod authors

  • Lab and test members are a build error now (KILNX001). About 120 of Kiln's lab and test helpers that shipped in 0.2.0-dev.24 (Rules.Simulate, QuestInput, Knowledge.LabRecord, GameTree.Simulate and the Lab* members, CreatureRef.ForceReady and others) are now [Experimental("KILNX001")] and hidden from completion. A project that calls one no longer builds until it adds <NoWarn>$(NoWarn);KILNX001</NoWarn> to its project file. Mods that are already built are not affected and keep loading. In a mod players install, use the documented API instead. Details: docs/kiln/modding/analyzers.md#kilnx001.
  • The mod API is 0.2.1. Mods.RuntimeApiVersion says 0.2.1, and a mod built with this release's MDK asks for API 0.2.1 by default (KilnApiVersion), so it needs a 0.2.0-dev.26 runtime or newer; earlier Kilns refuse it. Mods that ask for 0.2 or 0.1 keep loading. Everything new is additive: no public member of 0.2.0-dev.25 was removed or changed, and Kiln's build now fails when shipped API is removed or changed. API marked [Experimental("KILNX…")] may still change. Mods.ApiVersion is obsolete (its value is copied into your mod at build time); read Mods.RuntimeApiVersion.

Data on one item

  • InstanceData.Key<T>("name") and item.Get(key) keep a value on one particular item through moves, saves, crashes and restored backups. Items in chests work on a test server; items in a player's inventory wait for the guided client pass. Sample mods/samples/ItemData.
  • When Kiln cannot tell which of two identical items a value belongs to, it sets the value aside and reports it (InstanceData.Parked, /instances). Two identical items swapped where Kiln did not see it (another world, an offline edit) trade their values.
  • Inventory reads and the results of give, take and move carry a Ref naming one item; Items.Held, Items.Equipped and item.Location() say where it is, and ItemMovedEvent reports moves.
  • Persist.Carried (values that travel with a character between worlds) is not in this release.

Player animation states and weapon moves

  • Weapons can have player animation states of their own: list them in assets/player-states.json, each placed like a game attack, block, dodge, cast or shot state, and Kiln adds every mod's states to the player's animation graph at start. Changes apply from the next start. PlayerAnimStates (new library Kiln.PlayerAnim) looks states up; kiln lint and the cook check the file. Sample mods/samples/PlayerStates.
  • A mod can no longer replace the player's animation graph: Assets.Override and Assets.Register throw for its key, and an assets.json entry for it is refused at boot with a kiln.log line.
  • Weapon blocks take Moves: per attack slot a state to play (a mod's state or a game state by name, or a GUID), the sequence it starts from, when the blow lands (HitAt) and how long it lasts, with primary and heavy chains. Wand attacks and staff spells take Animation and HitAt on their premade motion. Sample mods/samples/WeaponMoves.
  • The PlayerStates and WeaponMoves samples build each move on the game's own clip of the state it is placed like and replace only the sword arm ("from" plus "blend" with "bones"), so the body moves as in the game's attack; a clip that animates the arm alone leaves the rest of the body in the skeleton's bind pose. PlayerStates ships its motion as a glTF of its own rig, so its release zip cooks with one kiln cook and no other step. mods/samples/check_player_clips.py checks cooked clips against the game's.
  • A clip in cook.json can be made from another clip at cook time with "from": trim, reverse, speed, mirror, blend, joint offsets. A reworked game clip is made on each player's machine and never ships.
  • A look takes Animated: a skinned model with its graph, held in the class's hand. A dedicated server needs no cooked files for it; where nothing is cooked the rigid model stands in.
  • On a dedicated server that has not cooked your mod, items whose look names a model of your mod no longer log "is not a keen::Model the game has loaded".

The API contract

  • Kiln.Capabilities (Is, Get, Why, All) says what works on this game build and side, and why not; CapabilityChangedEvent reports changes, and Game.HasFeature asks the same table. Native plugins report rows of their own and follow changes through kiln.caps.
  • A manifest whose features name something unavailable is refused with the reason. Name only capabilities fixed at boot there; check the others with Capabilities.Is at run time.
  • EntityRef, ItemInstanceRef and PlayerRef remember which spawn an id belonged to, so IsAlive can tell when the game reused the id; they answer from the first tick, not in OnLoad. PlayerRef converts from Player, TalentHolder, QuestHolder and CharacterKey.
  • One error rule (docs/kiln/modding/contract.md): the game's refusal comes back as Ok = false with a reason, a bad argument throws ArgumentException, and a capability that is off throws KilnUnavailableException, which names it and is still a NotSupportedException.
  • [ServerOnly], [ClientOnly], [AnyThread], [RenderThread] and [WorkerThread] mark members. ItemId moved to Kiln.Api; mods built against older SDKs load unchanged.
  • Release builds carry their full version (0.2.0-dev.26), and the release folder holds the SDK as a NuGet feed folder (sdk/feed/), so a kiln dependency can name a dev build. The feed is published with this release for the first time: NuGet source https://frostdev.io/kiln/downloads/0.2.0-dev.26/sdk/feed/index.json.

Typed game data

  • Kiln.Game has typed structs for every ECS component and event of the game under Kiln.Ecs (Ecs.Health, Ecs.NpcCommon), generated from the game's own reflection. Put Game in KilnServices and read with Ecs.TryGet<Ecs.Health>(entity, out var h). Reads only; writes stay World.TryWrite for now.
  • Each type is checked against the running game before its first read; a mismatch reads as nothing with one warning (Ecs.WhyNot<T>()). A mod using the types gets kiln.gametypes.json at build and is refused by name at load when a type it uses changed. An enum constant the compiler folds into a number is not checked.
  • Kiln.GameTypes.Components, ComponentTypes and the nine old structs still work and are obsolete.

Testing

  • Test a mod under dotnet test with no game: dotnet new kiln-unit-test --mod ../MyMod/MyMod.csproj makes an xunit project on Kiln.TestKit, which loads the mod through Kiln's real loader, steps the server, fakes players and saved data and runs your [KilnTest] methods. A test that needs something the kit does not fake is skipped with its name; docs/kiln/modding/unit-tests.md lists what is real offline. A [KilnTest] that blocks the tick thread is stopped and reported with the stack where it blocked (tested on Linux).
  • Guided client checks: mark an async method [ClientCheck("area.slug", "Title")] and call ClientChecks.Register(this) in OnLoad. A player plays the check and the verdict lands in kiln/state/client-checks.json.

Advanced mods and hooks

  • These raw entry points now throw InvalidOperationException unless kiln.mod.json has "advanced": true: OriginalCode.Read, FindUnique, TryFindUnique, Keep.Block, Keep.Span, Arsenals.JobHandler, Is.Job, Act.Job, Abilities.Programs.Register, NativeCall.Bytes.
  • Re-installing a Hooks.Detour or Hooks.Mid by the same mod on the same function, also after a hot reload, reuses its link slot instead of using up a new one. A mod that has unloaded can no longer install hooks.
  • Arsenals.RulesState (ArsenalRulesStatus) replaces Arsenals.Status, whose type had the same name as Kiln.Rules' record; the old ones still work.
  • Kiln.SteamGuard (experimental, KILNX002) is meant to stop the game client reporting achievements to Steam while it is latched or held. It has not run in a real client.
  • Native plugins: on_unload is called once at a normal exit with code 0, last loaded first, with a fault guard and a 3 s limit. Works for a dedicated server stopped under Wine, and at a game client's quit under Proton (seen once: 11 handlers in 9 ms). On native Windows it is off unless kiln.json has "unloadAtExit": "on". It is not called when the process is killed or crashes.
  • After moving Kiln's data folder, delete a native mod project's build folder once: CMake remembers the old SDK path.

Fixes and docs

  • Creature blocks defined in OnLoad keep their own faction relations and sense filter again, and Factions.Relate and Blocks.SenseFilter calls made in OnLoad stay in force; 0.2.0-dev.24's first server step dropped them.
  • docs/kiln/modding/limits.md lists what a mod cannot do yet: missing and limited APIs, hard caps with their values, problems seen in the game, and the release each is planned for.
  • The API reference and the modder's guide build as a site into the release's sdk/api/, with each member's thread and side. It is published with this release for the first time: https://frostdev.io/kiln/downloads/0.2.0-dev.26/sdk/api/. New pages for capabilities (services/caps.md), player animation states (services/playeranim.md) and the achievement guard (services/steamguard.md).

Players

  • Move Kiln's data folder. Settings, Library, Data folder, Change moves everything Kiln keeps (mods, runtimes, modpacks, profiles, backups) to a folder or drive you choose, as asked for on Nexus. Kiln checks the copy before it switches and only then removes the old one; a failed or canceled move changes nothing. On Windows a drive root (D:\ becomes D:\Kiln) and paths with spaces or accents work; a place whose paths would be too long for the game is refused with the reason.
  • After moving the data folder, start only 0.2.0-dev.26 or newer: older launchers do not know the new place and start empty.
  • An interrupted move removes only the files Kiln can prove it copied; anything else stays and is listed. Kiln removes nothing when one folder is a link or a mount of the other.
  • A profile another launcher or data folder put into the game is shown as "Another data folder's profile is loaded in this game" and is no longer changed.
  • Notices about mod folders no longer nag. Kiln tells you once about a mod folder it leaves alone, files it kept and notes on your mods, instead of at every change to your mods. A notice you closed does not come back; one still open when you quit the launcher comes once more. Notes above your mods shows them again, and Settings, "Show notes about mod folders" turns them off; the one notice that still comes is the first about a folder that may still run a mod you switched off. A long notice shows its first lines and "Show all", and notices no longer cover the window. Asked for on Nexus.
  • ShroudForge settings of a mod in your own folder (a mod you had in ShroudForge's mods folder before Kiln) can be changed in the launcher. Kiln writes only the setting you change and leaves the folder otherwise as it is. When two of your folders hold the same package, the launcher names them and changes neither.
  • Mods can remember things about a single item, such as kills on one sword. If Kiln cannot tell two identical items apart, the value waits instead of jumping to the wrong item.
  • Mods can give weapons their own attacks, guards and casts, timing and chains, and parts that move while held. Such a mod has to be on the server and in your game. If a game update breaks this, Kiln keeps the game's own moves and says so in kiln.log; if your game has not cooked the mod's models you see the plain weapon. The sample swords' attacks and blocks now move the whole body as the game's own moves do, with the sample's motion on the sword arm only; in the first test they left the character standing stiff.
  • Mod creatures with their own friends and foes behave as their mod says again; in 0.2.0-dev.24 they fought by the game's normal factions.
  • kiln/state/capabilities.json lists what works on your game build and why something is off, also when Kiln stays idle on an unverified build. When a mod does not load because the game build lacks something, kiln.log and the mod list say what and why.
  • After a game update, a mod built against game types the update changed is meant to show as failed with the reason, instead of breaking during play.
  • For testers: Ctrl+Shift+F5 opens the list of your mods' guided checks; Ctrl+Shift+F6, F7 and F8 record pass, fail and skip. During such checks Kiln is meant to stop the game unlocking Steam achievements; this has not been tried in a real game, and a game without Kiln cannot be protected.
  • Release builds show their full build number (0.2.0-dev.26) instead of 0.2.0-dev.

Server operators

  • A mod with player animation states must be cooked on the dedicated server too. The server and every player's game merge the mods' player states into the player's animation graph, and the join compares the two. A state whose clips are not cooked on a machine is left out there, so a server that has not cooked the mod has a different graph, and players whose game cooked it are refused at join, with both sides' mod states in the reason. This also holds under modSync: warn; with modSync: off it is only a warning. Use the same mod version on both sides; player states change only with a restart.
  • kiln cook works against a dedicated server's files. kiln cook <server folder>/kiln/mods/<mod> --game <server folder> cooks what a server uses (animations, skeletons, graphs, templates) and skips the models, which only a player's game draws, with one line each. Before, a recipe with a model stopped at its first model and cooked nothing on a server. kiln cook also takes an installed mod's folder (the one with its kiln.mod.json), and the server then starts without a warning for each missing model file.
  • Cooking needs Python and Kiln's source. kiln cook runs the cook tools of a Kiln source checkout (KILN_REPO); the launcher and the server pack do not carry them yet. Without them, cook the mod on a PC with the same game update and copy the mod folder with its assets/cooked folder and assets/assets.json to the server: the cooked animations come out byte for byte the same from a server's and a game's files of one build. Cook again after every game update (docs/kiln/dedicated-servers.md, "Mods on the server").
  • Admin level, experience, skill-point, knowledge and quest commands of Kiln.Admin latch the achievement guard on the affected players' games first. /questadmin and quest rewards paid later to a player who was offline are not covered. Experimental, not run in a real client.
  • A mod that re-installs the same hook, or a hot reload, no longer uses up a new hook slot each time.
  • kiln/state/capabilities.json and the refusal reasons in the mod list help with support reports.

Fixed

  • Mods' own attack sequences work in a second world of the same game run. Quitting to the menu and loading a world
    again, or joining a server after playing single player, made Kiln forget the names of a mod's copied attack sequences:
    blocks that named them failed with "is neither a GUID nor the name of a template, sequence or perk", and on a hosted
    world the weapons fell back to the base attacks. A joined Kiln server's content sync covered it; a second hosted world
    did not. Present since the first Kiln release.

  • Animations authored on a game skeleton no longer tear the body apart. A clip cooked onto one of the game's skeletons
    ("skeleton": "@...", for example the player's) had every joint's whole chain baked into its own transform, so the
    character was drawn torn into spikes while the attack played. Re-cook such clips. A glTF whose joints are not nested as
    the game's skeleton nests them is now refused with a clear error. Joints a clip does not animate hold the skeleton's
    bind pose: build the clip on a game clip (blend with bones) to keep the rest of the body in a natural pose.

  • Data a mod keeps on an item in your inventory comes back when you rejoin. Kiln assumed the game recreates a
    character's items in slot order; it makes worn gear first, so every rejoin of a player wearing gear set the item data
    aside. The join is now checked link by link. Two identical items still in your inventory when you leave from the menu
    are kept but set aside, never swapped. Found in the first test with a real player.

  • Changing a setting in ShroudForge's own in-game menu no longer makes Kiln treat its copy of the mod as yours. ShroudForge's
    menu adds its default targets line to the mod's extended.mod.json; Kiln took that for a change by the player, kept the
    folder at every change to your mods ("Some mod folders were kept") and refused setting edits in the launcher.

GitHub issue 23

From a server's logs that a player sent in (thank you).

  • Kiln no longer mistakes another tool's POWRPROF.dll for Shrouded Engine Tweaks. A proxy loader that only forwards
    the system DLL (ProxyLoader on a server, for example) switched kiln-set off and stopped kiln.types' Systems runner. SET is
    now recognised by its own markers, never by the file name, so SET Lua mods and the Systems runner keep working next to it.
  • A Shroudtopia mod that another loader already started is not started a second time. Before, kiln-shroudtopia could
    create a mod again that EML or ProxyLoader had loaded, so its effect (a loot multiplier, say) applied twice. Kiln now
    leaves it to that loader and the launcher shows it as running under its own loader.
  • Two EML loaders in one game are reported. Each one applies the EML patches again at every start. Kiln's log and the
    launcher's Engines view now say so and tell you to keep one; on a Kiln server, remove them all (Kiln runs the EML mods).
  • A chain.ini in kiln\chain\ is reported in proxy.log. The file belongs directly in the kiln folder. The server
    pack's KILN-SERVER-README now says so, and to keep one mod loader (Kiln replaces ProxyLoader and EML's own
    dinput8/dbghelp loaders on a Kiln server).

Known limits

One session in a real game has seen part of this release: one player (Linux, Proton) on a dedicated test server on the same machine, with the player's usual mods. Seen there:

  • The launcher and the game start with this release's launcher and runtime.
  • Item data on an item in the backpack and in a chest, through a server restart and through leaving and rejoining (the rejoin after the fix under "Fixed").
  • Mod creatures from blocks: spawned, scaled, with a faction of their own; an ally followed and guarded the player and fought hostile creatures.
  • Player animation states and weapon moves: the join check passed with the same graph on both sides, and the sample weapons played their own states. The samples' motion was reworked after that session and has not been seen in a game since.
  • Plugin on_unload at the game's quit: every handler was called, none faulted.

Open from that session:

  • A mod with a cook recipe needs the cook tools on every machine. The launcher does not cook: a player who adds such a mod sees the plain weapon instead of its models, and a mod with player animation states is refused at join unless the player's game and the server both cooked it (kiln cook, Python and Kiln's source). Cooking from the launcher is planned.
  • A mod item with a model of its own shows its base item's model while it lies on the ground.
  • A creature keeps its old target for about ten seconds after its faction changes, and an attack order does not replace the target a boss already has.
  • Two identical items with data in the backpack when a player leaves from the menu are set aside at the next join (kept, not swapped).
  • A cooked mod's files differ between a server and a game (the cook report), so after a refused join the launcher can list such a mod as missing or changed although both sides have the same version.

Everything else has only run on test servers (dedicated lab servers under Wine, some with a fake client) and in Kiln's tests; the guided client pass has to confirm:

  • Item data in a player's inventory beyond the above: the action bar and worn items, a crash, crafting with a tagged item (the 21 checks in mods/tests/instances-test).
  • Player animation states: how a state looks and blends, a second player seeing it, hits landing in time, root motion, a cast overlay; the join refusal as a player sees it (the seven checks in mods/samples/PlayerStates).
  • Weapon moves and held models: the six checks blocks.moves-* and blocks.held-animated*; a cooked client keeping its own model and graph; a staff cast with moves, a held block or parry, a shield.
  • The achievement guard (guard.proof, on an account that already has the level achievements) and the guided check runner itself: the screen, the HUD line, the keys, the note field, hosted and joined sessions.
  • Typed reads on a client (typegen.typed-reads), and the refusal of a mod whose game types changed; the refusal path was not run with a real mod list.
  • Player refs with a real player in a new session after a slot is reused. They have only run with simulated players.
  • Plugin on_unload on native Windows with "unloadAtExit": "on".
  • The launcher's notes about mod folders (once only, the Notes dialog, the switch, ShroudForge settings in your own folder) ran in the launcher's tests and in a browser against a stand-in for its backend, not in the installed launcher; the fix for ShroudForge's menu follows ShroudForge's source, not a real menu save.
  • The data folder move was tested by the launcher's own tests, not in the real launcher window, on Linux or on Windows (another drive and back, a drive root, long paths, held and read-only files, an older launcher after a move).
  • The SDK feed and the API reference site are published for the first time; a restore from the feed over https was not tried before this release. A locally built tree still says 0.2.0-dev.
  • Cooking on a real dedicated server: tested against a test server's files; that a server skips the marked model entries quietly was read in the code, not seen in a log.
  • Nothing in this release ran on native Windows or on a Steam Deck. Everything in 0.2.0-dev.25's known limits still applies.