How to make a Rust auto smelt on gather plugin (ore to fragments)
Converts gathered metal ore into metal fragments, sulfur ore into sulfur and optionally wood into charcoal for players with a permission, using OnDispenserGather and OnDispenserBonus.
On most servers players spend a large part of the wipe standing at a furnace converting ore into fragments and sulfur. This plugin removes that step for chosen players: when they hit a node, the gathered ore is replaced with its smelted output before it enters their inventory, so no furnace and no fuel is involved.
The conversion happens in the OnDispenserGather and OnDispenserBonus hooks. Both receive the Item that is about to be handed to the player, so the plugin checks the shortname against the configured ratio table and rewrites item.amount and item.info by swapping the item definition for the smelted one. A permission gates the feature, per-resource toggles (metal, sulfur, wood, high quality metal) are controlled from the config, and a chat command lets players check whether the feature is active for them. The math rounds down, so a server never gives away more material than the ore would have produced.
The plugin code
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Auto Smelt Gather", "RustPluginGenerator", "1.0.0")]
[Description("Converts gathered ore into smelted resources for players with a permission.")]
public class AutoSmeltGather : RustPlugin
{
private const string PermUse = "autosmeltgather.use";
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Keep at least one item when a conversion rounds down to zero")]
public bool KeepMinimum { get; set; } = true;
[JsonProperty("Conversions (gathered shortname -> output shortname and ratio)")]
public Dictionary<string, Conversion> Conversions { get; set; } = new Dictionary<string, Conversion>
{
["metal.ore"] = new Conversion("metal.fragments", 1f, true),
["hq.metal.ore"] = new Conversion("metal.refined", 1f, true),
["sulfur.ore"] = new Conversion("sulfur", 1f, true),
["wood"] = new Conversion("charcoal", 0.5f, false)
};
}
private class Conversion
{
[JsonProperty("Output shortname")]
public string Output { get; set; }
[JsonProperty("Ratio")]
public float Ratio { get; set; }
[JsonProperty("Enabled")]
public bool Enabled { get; set; }
public Conversion()
{
}
public Conversion(string output, float ratio, bool enabled)
{
Output = output;
Ratio = ratio;
Enabled = enabled;
}
}
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>
{
["Smelt.Active"] = "Auto smelting is active for you. Converted resources: {0}",
["Smelt.Inactive"] = "Auto smelting is not enabled for you. Ask an admin about the autosmeltgather.use permission.",
["Smelt.None"] = "Auto smelting is active but no resource conversions are currently enabled."
}, 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(PermUse, this);
}
private object OnDispenserGather(ResourceDispenser resourceDispenser, BasePlayer entity, Item item)
{
if (entity == null || entity.IsNpc || entity.UserIDString == null) return null;
if (!permission.UserHasPermission(entity.UserIDString, PermUse)) return null;
Convert(item);
return null;
}
private object OnDispenserBonus(ResourceDispenser resourceDispenser, BasePlayer player, Item item)
{
if (player == null || player.IsNpc || player.UserIDString == null) return null;
if (!permission.UserHasPermission(player.UserIDString, PermUse)) return null;
Convert(item);
return null;
}
private void Convert(Item item)
{
if (item?.info == null || item.amount <= 0) return;
Conversion conversion;
if (!config.Conversions.TryGetValue(item.info.shortname, out conversion)) return;
if (conversion == null || !conversion.Enabled || conversion.Ratio <= 0f) return;
var outputDef = ItemManager.FindItemDefinition(conversion.Output);
if (outputDef == null) return;
int amount = Mathf.FloorToInt(item.amount * conversion.Ratio);
if (amount <= 0)
{
if (!config.KeepMinimum) return;
amount = 1;
}
item.info = outputDef;
item.amount = amount;
item.MarkDirty();
}
[ChatCommand("smelt")]
private void CmdSmelt(BasePlayer player, string command, string[] args)
{
if (player == null || string.IsNullOrEmpty(player.UserIDString)) return;
if (!permission.UserHasPermission(player.UserIDString, PermUse))
{
player.ChatMessage(Msg("Smelt.Inactive", player.UserIDString));
return;
}
var active = new List<string>();
foreach (var pair in config.Conversions)
{
if (pair.Value == null || !pair.Value.Enabled || pair.Value.Ratio <= 0f) continue;
active.Add(string.Format("{0} -> {1} (x{2:0.##})", pair.Key, pair.Value.Output, pair.Value.Ratio));
}
if (active.Count == 0)
{
player.ChatMessage(Msg("Smelt.None", player.UserIDString));
return;
}
player.ChatMessage(Msg("Smelt.Active", player.UserIDString, string.Join(", ", active.ToArray())));
}
}
}Hooks used: OnDispenserGather, OnDispenserBonus
Config file
Created automatically on first load at oxide/config/AutoSmeltGather.json.
{
"Keep at least one item when a conversion rounds down to zero": true,
"Conversions (gathered shortname -> output shortname and ratio)": {
"metal.ore": {
"Output shortname": "metal.fragments",
"Ratio": 1,
"Enabled": true
},
"hq.metal.ore": {
"Output shortname": "metal.refined",
"Ratio": 1,
"Enabled": true
},
"sulfur.ore": {
"Output shortname": "sulfur",
"Ratio": 1,
"Enabled": true
},
"wood": {
"Output shortname": "charcoal",
"Ratio": 0.5,
"Enabled": false
}
}
}Commands and permissions
| Command | What it does | Permission |
|---|---|---|
/smelt | Shows whether auto smelting is active for you and which resources are covered. | autosmeltgather.use |
| Permission | Grants |
|---|---|
autosmeltgather.use | Grants auto smelting of gathered resources for the player's account. |
How the code works
Hook the gather pipeline
OnDispenserGatherfires on every hit a player lands on a resource node, andOnDispenserBonusfires when a bonus yield is rolled. Both signatures give you theResourceDispenser, theBasePlayerand theItemthe game is about to grant. The plugin returnsnullfrom both so the normal inventory flow is untouched; all the work happens insideConvert, which modifies the item before it is handed over.Check permission and player state
The first guard is
entity == null || entity.IsNpcso scientists and bots never convert anything, followed bypermission.UserHasPermission(entity.UserIDString, PermUse). Both hooks also bail out whenitem?.infois null. Because the hooks fire several times per second during mining, nothing inside them writes to the console; the onlyPrintWarningcall sits inLoadConfigand runs once when the config file cannot be parsed.Replace the item definition
The config stores a dictionary mapping a gathered shortname to a
Conversionobject with an output shortname, a ratio and an enabled flag.ItemManager.FindItemDefinitionresolves the output, then the plugin assignsitem.info = outputDefand setsitem.amount = Mathf.FloorToInt(item.amount * conversion.Ratio). Overwritinginfodirectly avoids creating and moving a second item, which keeps the change atomic inside the gather call. A ratio of 0 disables a single line without touching code.Handle tiny stacks
With a ratio below 1 a single hit can produce
0fragments, which would silently delete ore. When the computed amount is zero and the config fieldKeepMinimumis true, the plugin sets the amount to 1 instead. On vanilla ore yields the base gather is large enough that this rarely triggers, but it prevents confusion on servers running gather multipliers over 1.Bonus yield gets the same treatment
OnDispenserBonusis called with the bonusItemseparately from the base gather, so it runs through the identicalConverthelper. That means a player with the permission gets smelted output from both the main hit and the random bonus drop, and the plugin does not need to touch any gather multiplier configured elsewhere on the server.Commands and messages
The only command is
/smelt, registered with[ChatCommand("smelt")]and gated by the sameautosmeltgather.usepermission. It readsconfig.Conversionsto build a list of active conversions and replies through the language file, so admins can translate the output. All player-facing text, including the inactive notice, goes throughlang.GetMessage.
Install it on your server
Save AutoSmeltGather.cs
Keep the file name exactly as the class name. Oxide matches them.Upload it to the plugins folder
Oxide:oxide/plugins/. Carbon:carbon/plugins/. The server compiles and loads it within a few seconds, no restart needed.Edit the config if you want
On first load the default config is written tooxide/config/AutoSmeltGather.json(Carbon:carbon/configs/AutoSmeltGather.json). After editing, reload it:oxide.reload AutoSmeltGather c.reload AutoSmeltGather # CarbonGrant the permission
Give it to every player, or swapdefaultfor a group such asvip:oxide.grant group default autosmeltgather.use c.grant group default autosmeltgather.use # Carbon
Variations
Each one opens the generator with a ready prompt you can tweak.
Chat notification on conversion
Tells the player in chat when ore was converted, throttled so mining does not spam the chat.
Generate this versionGroup based smelting tiers
Uses separate permission tiers so donors get a better ratio than regular players.
Generate this versionOxide permission plus fuel cost
Charges charcoal from the player's inventory for each conversion, mimicking a portable furnace.
Generate this version
FAQ
Does this work with gather rate multipliers?
Yes. The plugin runs after the server has calculated the gathered amount, so a x2 or x5 gather rate produces a larger stack that is then multiplied by the configured ratio. Keep KeepMinimum enabled if you use multipliers on servers where a single hit can produce small amounts.
Why does wood smelting default to disabled?
Wood is used as a building material far more often than as a furnace input. Converting it on gather surprises players who expect wood in their inventory. Set Enabled to true for the wood entry and pick a ratio that matches your economy if you want charcoal from trees as well.
Do I need a furnace or fuel?
No. The item's definition is replaced before it enters the player's inventory, so no entity is involved and no fuel is consumed. This also means the plugin works on resource nodes in monument zones and on quarries that use the same dispenser hooks.
How do I install it?
Drop the compiled .cs file into oxide/plugins on Oxide or carbon/plugins on Carbon; both load the same file. Grant autosmeltgather.use with oxide.grant user <name> autosmeltgather.use and edit oxide/config/AutoSmeltGather.json to change ratios or enable wood.
Customize it for your server
Change the reward, add a cooldown, restrict it to VIPs. Start from this prompt and edit anything.
Edit the prompt on the next screen. 2 free plugins a month, no card.
More gathering plugins
- Farming yield multiplierMultiplies the amount of crops a player receives when harvesting planter crops or wild plants, with a separate multiplier per item shortname.OnServerInitializedOnGrowableGathered
- Fishing catch bonusMultiplies the amount of each caught fish and rolls a configurable chance for bonus items after every successful catch, using the OnFishCaught hook.OnFishCaught
- Furnace smelting speedSpeeds up furnaces, large furnaces, refineries and other ovens by scheduling extra smelt cycles per cook tick, scaled per oven prefab from config.OnOvenCook
Last updated Oct 26, 2026.
Customize auto smelt on gather
Customize