Contributing hooks
Details about contributing hooks to Oxide
This guide currently concerns only Oxide hooks, not uMod hooks.
How to contribute hooks
Thank you for taking an interest in contributing to Oxide! Most hooks are contributed by community members such as yourself. Adding hooks can be very rewarding to the community as it allows plugins to change game behavior in ways that were not previously possible. Please follow the below steps to get started. Keep in mind that it does take some setup work, especially the first time.
- Read the decompiled source of the game using a tool such as dnSpy or ILSpy to figure out which class methods you would like to hook, and to make sure new hooks are either the only way to solve your use case, or a significant improvement over using existing hooks
- Check out a local copy of the game's Oxide repository on GitHub, such as Oxide.Rust
- Use the Oxide Patcher to add hooks in the corresponding methods, following the conventions outlined later in this guide
- Test your hooks, ideally developing a prototype plugin alongside them to validate that they will work for your use case
- Commit the hook in Git and submit a pull request to the game repository from the earlier steps, with documentation of the hook signatures and use cases included
- Subscribe to the pull request and respond to questions and comments
- Wait patiently for the hook to be reviewed, ideally merged, and made available in the next Oxide update for that game
Note: Please do not skip testing your hooks. Nobody knows your use-case better than you. If the hook doesn't work as you expect, finding out when the next Oxide update is released means you may have to wait a month or longer for the next Oxide update in order to use the updated hook.
Hook conventions
When contributing hooks, please follow the below conventions. Keep in mind that these conventions haven't always existed, so some hooks that exist today don't follow them.
There are two main types of hooks: Pre hooks and post hooks.
Pre hooks
Pre hooks run before an event occurs, or when a player action is about to processed. The purpose of a pre hook is usually to allow plugins to block an event from occurring. A plugin might want to block an event for several reasons, such as to disallow a player action, or to replace the vanilla behavior with custom behavior that the plugin will perform after blocking the event.
There are two types of pre hooks: Can hooks and On hooks.
Can hooks
Can hooks are intended to allow plugins to choose between allowing, disallowing or simply observing an event. Can hooks are typically placed in methods that have the bool return type.
For example, the CanCraft hook in Rust allows plugins to return true to forcibly allow an item to be crafted, false to disallow the item from being crafted, or null to let the game (or another plugin) decide.
Naming convention: Can[Action] or Can[Action][Subject], such as CanMoveItem.
Return behavior: Returning a boolean in a subscribed method should cause the hooked method to return that boolean. Non-boolean values should be be ignored, resulting in default behavior.
Here's an example of code that could be injected into a game method using a Can hook. Keep in mind that your decompiler of choice may display this several different ways.
object obj = Interface.CallHook("CanCraft", this, blueprint, quantity, free);
if (obj is bool)
{
return (bool)obj;
}
On hooks
On pre hooks are intended to allow plugins to choose between blocking or simply observing an event. On pre hooks are typically placed in methods that have the void return type.
For example, the OnEngineStart hook in Rust is called when a player tries to start a vehicle engine. It allows plugins to return a non-null value (most commonly false) to prevent the engine from being started, or null to let the game (or another plugin) decide.
Naming convention: On[Subject][Action], such as OnEngineStart.
Return behavior: Usually depends on return type of the method being hooked.
void: Returning non-null should prevent the default behavior, usually by returning early, or sometimes by skipping a section of the method. Returning null should result in the default behavior.int/object/etc.: Returning a value of the corresponding type should prevent the default behavior and either exit the method early by returning that value, or sometimes by assigning that value to a variable that is later used in the method. Returning null should result in the default behavior.bool: Not recommended; use a Can hook instead
Here's an example of code that could be injected into a game method using an On pre hook. Keep in mind that your decompiler of choice may display this several different ways.
if (Interface.CallHook("OnEngineStart", vehicle, player) != null)
{
return;
}
Post hooks
Post hooks run after an event has occurred, or after a player action has been processed. Post hooks always use the same naming convention and are intended to allow plugins to simply observe an event. While post hooks do not directly allow plugins to alter the behavior of the method where the hook is placed, plugins can still alter the game in response to events.
For example, the OnEngineStarted hook in Rust is called after a player has started a vehicle engine. A plugin could use this to conceivably alter the speed of the vehicle based on the permissions of the player that started the engine.
Naming convention: On[Subject][Action]ed (may replace "ed" with a more appropriate past-tense verb)
Return behavior: No return behavior. Oxide should ignore the return value of subscribed methods.
Here's an example of code that could be injected into a game method using an On post hook.
Interface.CallHook("OnEngineStarted", vehicle, player);