OnExperimentEnd
OnExperimentEnd is called on a Workbench when its research experiment finishes, right before the game checks the scrap cost and consumes the resources to hand out the blueprint.
object OnExperimentEnd(Workbench workbench)Parameters
| Parameter | Type | Description |
|---|---|---|
workbench | Workbench | The Workbench entity that finished its research cycle. Use workbench.OwnerID, workbench.transform.position or workbench.net.ID to identify who and where the experiment happened. |
Return value
Returning null (or nothing) lets the experiment continue normally: the game checks the scrap amount, removes the research cost and awards the blueprint. Returning any non-null object aborts the rest of ExperimentComplete, so the player gets no blueprint and no scrap or research item is consumed.
How it works
This hook fires inside Workbench.ExperimentComplete, the internal method a workbench runs when the experiment timer completes and the player is still using it. It runs after the pending blueprint has been selected but before the scrap check and the consumption of the research item, so it is the last point where the outcome can still be altered.
The only parameter is the Workbench entity itself. It gives you OwnerID for the player who started the experiment, net.ID for identifying the instance, and transform.position for proximity checks. From there you can look up the owner with BasePlayer.FindByID(workbench.OwnerID), read amounts with the inventory API or restrict experiments to specific zones.
Declaring the parameter as Workbench is already the most specific useful type; narrowing further is not possible because the game only passes workbenches. Practical rules: the workbench is still valid while the hook runs, so read its state but do not destroy or modify its inventory from here. Avoid log spam, since the hook fires once per completed experiment, per player.
Example plugin: Charge a custom item for experiments
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("Experiment Fee", "RustPluginGenerator", "1.0.0")]
[Description("Charges a configurable item before a workbench experiment can complete")]
public class ExperimentFee : RustPlugin
{
private const string PermBypass = "experimentfee.bypass";
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Required item shortname")]
public string ItemShortname = "blueprint";
[JsonProperty("Required amount")]
public int Amount = 5;
}
protected override void LoadDefaultConfig() => config = new PluginConfig();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<PluginConfig>();
if (config == null) throw new Exception();
}
catch
{
PrintWarning("Configuration file is invalid, using defaults");
LoadDefaultConfig();
}
SaveConfig();
}
protected override void SaveConfig() => Config.WriteObject(config);
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["Missing"] = "You need {0} x {1} to finish this experiment.",
["Charged"] = "{0} x {1} were consumed by the workbench."
}, this);
}
private string Msg(string key, string userId = null, params object[] args) => string.Format(lang.GetMessage(key, this, userId), args);
private void Init()
{
permission.RegisterPermission(PermBypass, this);
}
private object OnExperimentEnd(Workbench workbench)
{
if (workbench == null || workbench.IsDestroyed) return null;
BasePlayer player = BasePlayer.FindByID(workbench.OwnerID);
if (player == null || !player.IsConnected) return null;
if (permission.UserHasPermission(player.UserIDString, PermBypass)) return null;
ItemDefinition definition = ItemManager.FindItemDefinition(config.ItemShortname);
if (definition == null) return null;
int have = player.inventory.GetAmount(definition.itemid);
if (have < config.Amount)
{
player.ChatMessage(Msg("Missing", player.UserIDString, config.Amount, definition.displayName.english));
return false;
}
player.inventory.Take(null, definition.itemid, config.Amount);
player.ChatMessage(Msg("Charged", player.UserIDString, config.Amount, definition.displayName.english));
return null;
}
}
}The plugin registers the hook and finds the player who owns the workbench. If the player is not on the bypass permission, it checks how many blueprint items they carry and compares that to the configured fee. When the player has too few, the plugin cancels the experiment with a non-null return, tells the player why through the language file and does nothing else. When the fee is paid, the items are taken with inventory.Take and the hook returns null so the vanilla experiment completes as usual. All values live in the config, and the permission list is registered in Init.
Common use cases
Require a custom research currency
Cancel experiments for players who do not carry the configured number of an item (blueprints, scrap substitutes, event tokens) and remove the cost when they do.
Restrict experiments to certain zones or monuments
Read workbench.transform.position and cancel the experiment when the bench is outside an allowed radius of a monument or inside a PvP zone.
Log or reward completed research
Notify a Discord bridge plugin or grant a small bonus when a player finishes an experiment, using OwnerID to find the player.
Pitfalls
Cancelling without telling the player
A non-null return silently aborts the experiment. Always send a chat message or console reply so the player knows why no blueprint was produced.
Modifying the bench inventory
The hook runs mid-method and the game still touches experimentResourceItem and pendingBlueprint afterwards. Take or give items on the player inventory, not on the workbench, to avoid inconsistent state.
Assuming the hook covers all blueprint unlocks
OnExperimentEnd only fires for workbench research. Blueprints obtained through loot, tech tree or admin commands never reach this hook.
Build a plugin with OnExperimentEnd
Describe what should happen and get a compiled plugin that uses this hook.
Edit the prompt on the next screen. 2 free plugins a month, no card.
Last updated Oct 26, 2026. Found a mistake? Email support@rustplugingenerator.com.
Build a plugin with OnExperimentEnd
Generate