Modding: Difference between revisions

From Lancer Tactics Wiki
Add security section
add link to godot
 
(One intermediate revision by the same user not shown)
Line 5: Line 5:
To make mods, you'll first need to get your hands on the source of the game. It’s possible to decompile any game made in Godot which is what bootleg (affectionate) modders have been doing up til now, but we’re making the source available through official channels for convenience. Lancer’s TTRPG creator ecosystem is based on a belief that it should be hackable & extensible, and we want to carry that culture forward.
To make mods, you'll first need to get your hands on the source of the game. It’s possible to decompile any game made in Godot which is what bootleg (affectionate) modders have been doing up til now, but we’re making the source available through official channels for convenience. Lancer’s TTRPG creator ecosystem is based on a belief that it should be hackable & extensible, and we want to carry that culture forward.


We (Wickworks) are talking to a lawyer to make sure we have all our hatches buttoned down legally to make sure we maintain ownership of the game code itself (and maybe like watermark some assets?), but once we get that squared away we’ll add the modkit alongside the rest of the downloads on itch. Until then, you can request early access to it on [https://discord.com/invite/2t7Efg2Fbj LT’s Discord server].
We (Wickworks) are talking to a lawyer to make sure we have all our hatches buttoned down legally to make sure we maintain ownership of the game code itself (and maybe like watermark some assets?), but once we get that squared away we’ll add the modkit alongside the rest of the downloads on itch. Until then, you can request early access to it on [https://discord.com/invite/2t7Efg2Fbj Lancer Tactic’s Discord server].
 
Finally, before getting started with the modkit, please be sure you've reviewed and agree to the [[Modding EULA]].


== Background — how do Godot mods work? ==
== Background — how do Godot mods work? ==
Lancer Tactics is made in the [https://godotengine.org/ Godot] game engine; at time of writing on version 4.7. In order to make mods for anything besides asset packs, you'll need to become comfortable working in its editor. I hope this doesn't scare you off; of all the major game engines, it has a reputation of being easy to pick up.


Godot projects have an internal filesystem much like the one on your computer; a series of folders and files. The root is referred to as <code>res://</code>
Godot projects have an internal filesystem much like the one on your computer; a series of folders and files. The root is referred to as <code>res://</code>

Latest revision as of 07:28, 5 July 2026

Get help: wick.itch.io/lancer-tactics or Discord

Modkit

[edit]

To make mods, you'll first need to get your hands on the source of the game. It’s possible to decompile any game made in Godot which is what bootleg (affectionate) modders have been doing up til now, but we’re making the source available through official channels for convenience. Lancer’s TTRPG creator ecosystem is based on a belief that it should be hackable & extensible, and we want to carry that culture forward.

We (Wickworks) are talking to a lawyer to make sure we have all our hatches buttoned down legally to make sure we maintain ownership of the game code itself (and maybe like watermark some assets?), but once we get that squared away we’ll add the modkit alongside the rest of the downloads on itch. Until then, you can request early access to it on Lancer Tactic’s Discord server.

Finally, before getting started with the modkit, please be sure you've reviewed and agree to the Modding EULA.

Background — how do Godot mods work?

[edit]

Lancer Tactics is made in the Godot game engine; at time of writing on version 4.7. In order to make mods for anything besides asset packs, you'll need to become comfortable working in its editor. I hope this doesn't scare you off; of all the major game engines, it has a reputation of being easy to pick up.

Godot projects have an internal filesystem much like the one on your computer; a series of folders and files. The root is referred to as res://

TODO INSERT IMAGE

However, to run the game Godot needs assets (images, music, etc) to be in a different format than you’d find with them sitting in your filesystem. It compiles these files into a big pile of files that look like this, all jumbled together in one folder:

TODO INSERT IMAGE

To keep knowing where things are, it maintains a skeleton of the original filesystem with all the orignal files swapped out with “.remap” placeholders that just point to where in that big pile the asset they represent went. Furthermore, whenever the game attempts to load a specific asset at a location (e.g. res://assets/icons/mech.svg), it caches whatever it loaded into memory. Whenever we ask it to load that asset again, it doesn’t have to go ask the .remap file where the original one went and load it up — it just returns whatever it ended up loading from there last time. This cache is like a ghost sitting on top of a skeleton.

For modding, this gives us an opportunity. Instead of needing to actually copy in modded files and override the originals, we can manipulate this ghost-filesystem-cache by lying about what files are at what locations. Here’s what a mod looks like for Lancer Tactics (using a modified system from Godot Mod Loader):

TODO INSERT IMAGE

We have a special folder where we put the actual mod files: “unpacked”, then another folder with the specific mod’s identifier “Wickworks-Sawhorse”. Within that, we have a “res” folder, and that’s where the lying starts. When loading up the mod, we can grab everything inside of the mod’s “res” and tell the ghost-cache that the mod’s files are the ones that are actually at that location. In this example, by the end of it when we ask Godot to load res://assets/frames/horus/mf_sawhorse.png, it will return the file that’s actually over in res://unpacked/Wickworks-Sawhorse/res/assets/frames/horus/mf_sawhorse.png.

Now that we have the power to insert files as we’d like, Godot Mod Loader additionally has some functions that through dark magic allows multiple mods to all alter the same script instead of overriding it. I’m a little fuzzy on these details, but the shape of it is that while loading each successive script that extends the same file actually extends the previous one that also extended that script so all the changes end up getting applied.

In order to change what mods are active, the out-of-the-box Godot Mod Loader addon expects you to restart the game so it can start work with a clean slate each time. For Lancer Tactics, I modified this so it tracks all files inserted so we can go and undo all of our lies and reset the ghost-cache to files’ original locations. This was important because I’m expecting people to want to be able to make maps and modules with bundled-in mods that substantially alter the content of the game, and requiring a restart whenever you load one up would be a terrible experience.


Mod Loader

[edit]

Lancer Tactics is using the Godot Mod Loader addon, but has made some changes to it:

Implicit Overrides

[edit]

Instead of having to manually declare each asset you want to override in code (via an overwrites.gd that the Mod Tools would generate and put into an extensions/ or overwrites/ sub-directory), assets and resources are overridden implicitly by matching file paths and names from a mod's res/ directory. See the "replacing resources" section below for details. This should allow people to make texture asset pack mods fully outside the Godot editor by downloading a template, dropping images into a folder, and zipping it up.

Hooks and class_name scripts

[edit]

Unfortunately, it ended up not being feasible to support their script Hook system, which means it's not possible to change class_name scripts (or is unreliable to do so). Hooks were designed to work around a bug in Godot that has been unaddressed since 4.3 and do some crazy preprocessing of scripts that involves decompiling them, editing names of functions, and swapping in the copies. For the time being, we decided to instead convert the most likely targets of mods (UnitCondition, UnitTile, UnitAction, etc) into autoloads so they can be accessed by normal script Extensions instead. This will provide better backwards compatibility for mods once the bug is addressed than having to go back and tear out all the Hook code.

TL;DR: You are NOT able to currently extend a script with a class_name. If you want to make a mod that extends a class_name script and it looks like it could be an Autoload, let me know and I'll see if I can convert it!

The engine bug necessitating hooks: github.com/godotengine/godot/issues/83542

A possible fix to the godot engine that'll solve all this: github.com/godotengine/godot/pull/116556

Formatting and Paths

[edit]

Additionally, we've made some path and formatting tweaks for styling and ease of use:

  • We're using res://unpacked/ instead of res://mods-unpacked/ for the directory in the godot project that mods are unpacked into. I don't like hypens in filenames, it's a bit shorter, and will be sorted alphabetically to the end of the project folders. Plus mod zips will be structured like unpacked/Author-ModName/manifest.json which is a bit cleaner than having an extra "mods-" at the start of it.
  • Our manifest.json doesn't have the "extra": {"godot: {...}} keys, opting instead to just put all that stuff at the root.
  • Mod config files are stored in user://mod_configs/ instead of straight user:// for mod-tool-plugin-save.json, mod_user_profiles.json, and mod_loader_cache.json. Keeps the user directory cleaner.


Mod Types & Security

[edit]

Mods are code that some rando wrote that the game downloads and then runs on your machine. Godot does not have built-in sandboxing for gdscript, so this code has just as much access as the game does, ie, can read or write any file on your computer and talk to the internet. Uh oh!

To be fair, this is a issue with most mods for most games. It’s a very trust-based at-your-own-risk sort of country. This is fine if mods aren’t tightly integrated, but as soon as you as a developer start removing barriers and letting people accidentally stumble into downloading harmful code by just pressing buttons in-game, you start taking on the responsibility in the case of something bad happening.

Slay The Spire 2, another game made in Godot, handles this by putting a big warning screen that it forces you to click through before you can start installing mods. We’ve done something similar:

TODO INSERT IMAGE

To help manage this, Lancer Tactics distinguishes between three mod types: Asset Packs, Content Packs and "Substantial" mods:

  • Asset Pack: just images and tres files. Doesn't even have a mod_main.gd; just a json manifest and images in various places to get picked up by the appropriate resources. These can be required by combats/modules and they'll be treated as safe since not having scripts makes them risk-free. We allow tres files to be in asset packs as long as they do not contain any inline scripts.
    • Mods that do not contain any .gd files (including mod_main.gd) will be treated as Asset Packs.
  • Content Pack: can have scripts and a mod_main.gd to do non-standard things, but are responsible for making sure anything off-standard they do is reversible without a restart. Since these allow arbitrary code execution, they will prompt additional warnings to the player before they are enabled.
    • Mods that contain .gd files and are marked as requires_restart as "false" in their manifest will be treated as Content Packs.
  • Substantial Mod: as above, but the changes are not cleanly reversible so require a restart to turn off or on.
    • Mods that contain .gd files and are marked as requires_restart as "true" in their manifest will be treated as Substantial Mods.

An area for future growth, given substantially more resources, would be to add the ability to script game content in a nice sandboxed environment by setting up an internal .lua API for new abilities to use. This would allow new mechs and whatnot to count as asset packs, but the time-benefit tradeoff isn’t favorable given our current time and labor constraints.