Script Hooks🔗
Available since
7.0.0
Script Hooks are a new way to mod scripts. Hooks allow you to add a custom Callable, which then calls further modded functions and the vanilla function at the end. The callable needs to fulfil specific requirements to work.
Hooks are slightly more complex to use and little less powerful than Script Extensions, so prefer using those if possible.
Info
The Mod Loader makes Script Hooks work by generating new GDScript files, which take the place of the original vanilla
scripts. The newly generated scripts then use the ModLoaderHookChain to call
modded callables one after the other. These callables need to always take the chain as first argument and also call
ModLoaderHookChain.execute_next()
once at some point
to properly work.
There are two ways in which mod hooks can take the place of vanilla scripts:
- Dynamic
- This does not work in the editor! See Hooks in the Editor below.
- Generated at runtime by the mod loader. This is automatic, you don't have to do anything.
- Any file can technically be converted, but the hook file can't be applied if the script is preloaded.
- Preprocessed/ built in
- These are created during export if the game developer has enabled the Mod Hook preprocessor export addon.
- You can always hook a preprocessed file, preloaded or not.
- You can check if a file has a comment similar to this "ModLoader Hooks - The following code has been automatically added by the Godot Mod Loader." to see if it is already hooked.
See also
API Reference: ModLoaderMod.add_hook()
and
API Reference: ModLoaderMod.install_script_hooks()
Hooks in the editor🔗
The dynamically generated script that our mod hook preprocessor creates cannot be applied in the editor
(at least until 4.4).
To work around this issue, you can use the mod tool file system context menu
and convert the vanilla file once. Afterward you will be able to test mod hooks as if there were no differences.
Features🔗
Multiple mods can mod the same functions and their changes will accumulate rather than replacing each other (depending on load order).
The main feature of mod hooks is that they can mod global classes - scripts which use class_name
to be globally accessible.
Global classes have a bug which prevents us from using extensions.
Hooks can modify member variables by accessing the ModLoaderHookChain.reference_object
.
Example: this hook method that is attached to the main node which contains the game's version.
Warning
This feature does not exist in Godot 3
Similarly to extensions, hooks can also change vanilla methods before and after they are called, which allows them to manipulate both the input parameters and the output value of a function.
Warning
This feature does not exist in Godot 3
As you may have noticed above, each one of the mod hook functions calls chain.execute_next()
. This is almost like
calling super()
in a script extension - it hands off the call to the next modded, or finally the vanilla method.
That means you can also completely replace the vanilla method by never calling chain.execute_next()
- but be careful
with this as it will likely break all compatibility with other mods that try to hook the same method.
Hooks can be applied to Autoloads, but you should really prefer extensions - since Autoloads can't have class_names in any case. It may be possible to hook autoloads earlier in the load order, but this hasn't been tested. Do note that in case it works it is impossible the stop the Autoload's _init from running since hooks are applied during the ModLoader's _ready
To install a single hook, call ModLoaderMod.add_hook()
from your mod's mod_main.gd
, in _init()
or in any function that gets called
by _init()
, like the install_script_hook_files()
functions we usually use by convention.
While a single script extension always extends a whole file, a single script hook only
affects a single method. For convenience, we added an "install-" method which applies all hooks from a file:
ModLoaderMod.install_script_hooks()
.
For single hooks, your function name does not matter, if you set it correctly in add_hook()
, it will be found.
However, when using ModLoaderMod.install_script_hooks()
with a whole file, all your custom callables need to
have the exact same name as the vanilla function, otherwise they will be ignored.
Warning
This feature does not exist in Godot 3
Common issues
- Always make sure you are calling
chain.execute_next()
! - In editor: Make sure the file is converted!
Limitations🔗
Script Hooks don't exist for Godot 3, they are a recent addition. They are also not required in 3 since even global classes can be modded with Script extensions in Godot 3.
Script Hooks suffer from the same limitation that Extensions do, they will not be applied to scripts that are
preload()
ed
in any way. This affects both scripts which are preloaded directly - preload("res://player.gd")
- and scripts which are
indirectly preloaded by being used in preloaded scenes - preload("res://player.tscn")
.
This is a Godot limitation we have yet to find a workaround for.
Hooks can only access the reference object indirectly to change values, which means they cannot add new variables to the class they are applied to.
Subclasses (declared with class
inside another class) can't be modded yet. Simply because we didn't add that
feature to the hook preprocessor yet since it's rarely used and was not needed yet. If you need support for this, please
let us know on this issue #516
There is technically a small hit to performance when using hooks compared to extensions, but it is negligible in most cases.