Toggling Hooks and Patches
Note: In this section, we'll be making the following assumptions:
Plugin name:
"Tutorial Plugin"
Plugin ID:
"com.tutorial.plugin"
DLL Name:
"tutorial-plugin.dll"
In this section, we'll be getting your new function hook running.
Changing the enable/disable/setting_changed_bool functions
To let our plugin handle settings for us, we'll need to make some small changes to the enable
, disable
, and setting_changed_bool
functions.
pub extern "C" fn enable() {
FIRST_RUN.get_or_init(|| {
pretty_env_logger::formatted_builder()
.filter_level(LevelFilter::Debug)
.init();
true
});
// Add these lines
let plugin = plugin::get();
if let Err(error) = plugin.on_enable() {
error!("Error while enabling plugin: {:?}", error);
}
info!("Tutorial plugin enabled");
}
#[no_mangle]
pub extern "C" fn disable() {
// Add these lines
let plugin = plugin::get();
if let Err(error) = plugin.on_disable() {
error!("Error while disabling plugin: {:?}", error);
}
info!("Tutorial plugin disabled");
}
#[no_mangle]
pub unsafe extern "C" fn setting_changed_bool(name: char_p::Box, value: bool) {
// Replace the contents with this:
let plugin = plugin::get();
plugin.on_setting_changed_bool(name, value, |key, value| {
debug!("Setting changed: {} = {}", key, value);
});
}
Automating Settings Changes
When you create a hook with #[hook_signature]
or #[hook_pointer]
, it will assign it an ID of hook::your_hook_name
in the plugin instance.
Similarly, when you create a patch with #[patch_signature]
or #[patch_pointer]
, it will assign it an ID of patch::your_patch_name
.
The plugin.on_enable()
method will loop through all of your hooks and patches, and check if your plugin has a setting for it. If it does, it will check if it's true
or false
.
If the setting value is true
, the hook/patch will be enabled. If false
, or if it's not a boolean, the hook/patch will be disabled.
The plugin.on_disable()
method will loop through all of them and turn them all off, regardless of the user configuration.
The plugin.on_setting_changed_bool(name, value, callback)
method will check if the setting name matches a patch or hook name, and will enable/disable it depending on the new value.
The name and value are then passed to the callback, which you can do anything you want with. If you want it to do nothing, you can do this instead:
plugin.on_setting_changed_bool(name, value, |key, value| {});
Updating config.toml
By default, if a hook/patch doesn't have an entry in the config.toml
, it will be enabled when the plugin is enabled, and disabled when the plugin is disabled.
If you want to de-couple this from your plugin's enabled/disabled states, you can add it as a setting to your config.toml
.
[[setting]]
name = "Enable God Mode"
id = "hook::proc_dmg_and_stamina" # If it was a patch instead of a hook, the id would be patch::proc_dmg_and_stamina
description = "Enables God Mode"
default = true
This setting will now show up in the in-game overlay in the settings page for your plugin.
Multiple Hooks/Patches under one toggle
It's common that you might want multiple patches or hooks to be managed from one setting.
For this purpose, we provide a #[link_setting("setting_name")]
macro. It's used like so:
#[plugin(id = "com.tutorial.plugin")]
mod plugin {
#[patch_signature]
#[link_setting("patches_enabled")]
fn my_first_patch(_address: *mut u8) -> Vec<u8> {
register!("DE AD BE EF ?? ?? ?? ??");
vec![0x90, 0x90, 0x90]
}
#[patch_signature]
#[link_setting("patches_enabled")]
fn my_second_patch(_address: *mut u8) -> Vec<u8> {
register!("?? ?? ?? ?? EF EB AD DE");
vec![0x90, 0x12, 0x90, 0x15, 0x00, 0x00, 0x00]
}
}
In this example, while both patches would be given the settings patch::my_first_patch
and patch::my_second_patch
, they're both also assigned to patches_enabled
.
This means that in your config.toml
, you could toggle both of them with one setting like so:
[[setting]]
name = "Enable Patches"
id = "patches_enabled"
description = "enable both patches!"
default = true