Skip to content
RustPluginGenerator

How to make a Rust recycler speed plugin (Oxide/Carbon)

Speeds up Recycler processing with a configurable seconds-per-cycle and a VIP permission for a faster rate. It uses OnRecyclerToggle to detect when a player starts a recycler and drives the recycler's own RecycleThink on a repeating timer.

Rust's recycler processes items on its own internal cadence; this plugin adds a repeating timer that calls the recycler's RecycleThink method at a configurable interval, starting when a player toggles the recycler on. It listens for OnRecyclerToggle, waits one tick so the recycler has flipped into its running state, then starts the loop. A permission route gives VIP players a faster rate. Both delays and the VIP check are config values. Timers are keyed by the recycler's network ID and destroyed in Unload, so a plugin reload does not leave orphaned loops running.

The plugin code

RecyclerSpeed.cs
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;

namespace Oxide.Plugins
{
    [Info("Recycler Speed", "RustPluginGenerator", "1.0.0")]
    [Description("Speeds up recycler processing and gives VIP players a faster cycle.")]
    public class RecyclerSpeed : RustPlugin
    {
        private const string PermVip = "recyclerspeed.vip";

        private PluginConfig config;
        private readonly Dictionary<ulong, Timer> activeTimers = new Dictionary<ulong, Timer>();

        private class PluginConfig
        {
            [JsonProperty("Seconds per cycle (default)")]
            public float SecondsPerCycle = 3f;

            [JsonProperty("Seconds per cycle (VIP permission)")]
            public float VipSecondsPerCycle = 1.5f;

            [JsonProperty("Require permission for VIP speed")]
            public bool RequireVipPermission = true;
        }

        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>
            {
                ["SpeedInfo"] = "Recycler speed: {0}s per cycle."
            }, 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(PermVip, this);
        }

        private object OnRecyclerToggle(Recycler recycler, BasePlayer player)
        {
            if (recycler == null || player == null || recycler.IsDestroyed) return null;

            StopTimer(recycler);

            // Hook fires before the recycler flips IsOn, so re-check next tick.
            NextTick(() =>
            {
                if (recycler == null || recycler.IsDestroyed) return;
                if (!recycler.IsOn()) return;
                StartTimer(recycler, player);
            });

            return null;
        }

        private void StartTimer(Recycler recycler, BasePlayer player)
        {
            float interval = config.SecondsPerCycle;
            bool isVip = !config.RequireVipPermission
                         || (player != null && permission.UserHasPermission(player.UserIDString, PermVip));
            if (isVip) interval = config.VipSecondsPerCycle;
            if (interval < 0.1f) interval = 0.1f;

            ulong id = recycler.net.ID.Value;
            activeTimers[id] = timer.Every(interval, () =>
            {
                if (recycler == null || recycler.IsDestroyed || !recycler.IsOn())
                {
                    StopTimer(recycler);
                    return;
                }
                recycler.RecycleThink();
            });
        }

        private void StopTimer(Recycler recycler)
        {
            if (recycler == null) return;
            ulong id = recycler.net.ID.Value;
            Timer t;
            if (activeTimers.TryGetValue(id, out t))
            {
                t.Destroy();
                activeTimers.Remove(id);
            }
        }

        private void Unload()
        {
            foreach (var kv in activeTimers)
            {
                kv.Value?.Destroy();
            }
            activeTimers.Clear();
        }
    }
}

Hooks used: OnRecyclerToggle

Config file

Created automatically on first load at oxide/config/RecyclerSpeed.json.

RecyclerSpeed.json
{
  "Seconds per cycle (default)": 3,
  "Seconds per cycle (VIP permission)": 1.5,
  "Require permission for VIP speed": true
}

Commands and permissions

No chat commands. The plugin works on its own.

Permissions
PermissionGrants
recyclerspeed.vipGrants the faster VIP seconds-per-cycle rate instead of the default rate.

How the code works

  1. Catch the toggle with OnRecyclerToggle

    object OnRecyclerToggle(Recycler recycler, BasePlayer player) fires when a player interacts with a recycler. Return null so vanilla behavior is not cancelled. Guard recycler, player and recycler.IsDestroyed first, then stop any existing timer for that recycler before starting a new one.

  2. Wait one tick for the on-state

    The toggle hook fires before the recycler flips IsOn(), so checking it directly here gives the old value. Use NextTick(() => ...) to re-check after the game has applied the toggle. If the recycler is now on, call StartTimer. If it is off, the stop call you already did leaves nothing running.

  3. Pick the interval and start the loop

    StartTimer chooses between Seconds per cycle (default) and Seconds per cycle (VIP permission) from the config. If Require permission for VIP speed is true, a player holding recyclerspeed.vip gets the VIP delay; if it is false, everyone gets the VIP delay. Values below 0.1 are raised to 0.1. Then register a timer.Every(interval, ...) callback and store it in a dictionary keyed by recycler.net.ID.Value.

  4. Drive RecycleThink inside the timer

    Inside the repeat callback, bail out if the recycler is destroyed or !IsOn(), and call StopTimer so the loop dies when the machine stops. Otherwise call recycler.RecycleThink() with no arguments. Because the timer fires at the configured interval, the recycler's own processing is triggered on that schedule while the plugin is active.

  5. Stop cleanly and avoid leaks

    StopTimer looks up the timer by network ID, calls Destroy() and removes the entry from the dictionary. It is safe to call when no timer exists. Unload destroys every timer in the map before the plugin is torn down, so callbacks do not keep ticking across a reload.

  6. Config, lang and permission setup

    The nested PluginConfig uses [JsonProperty] keys that read like plain English so admins can edit the JSON without looking at code. LoadConfig falls back to LoadDefaultConfig with a warning if the file is corrupt. LoadDefaultMessages registers a single SpeedInfo line; there is no command that uses it yet.

Install it on your server

  1. Save RecyclerSpeed.cs

    Keep the file name exactly as the class name. Oxide matches them.
  2. 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.
  3. Edit the config if you want

    On first load the default config is written to oxide/config/RecyclerSpeed.json (Carbon: carbon/configs/RecyclerSpeed.json). After editing, reload it:oxide.reload RecyclerSpeed c.reload RecyclerSpeed # Carbon
  4. Grant the permission

    Give it to every player, or swap default for a group such as vip:oxide.grant group default recyclerspeed.vip c.grant group default recyclerspeed.vip # Carbon

Variations

Each one opens the generator with a ready prompt you can tweak.

  • Show the current rate to the player

    Print the active seconds-per-cycle to the player when they open a recycler.

    Generate this version
  • Only speed up public recyclers

    Skip the speedup when the recycler is on building privilege owned by another player.

    Generate this version
  • Per-grade multiplier

    Make higher-tier recyclers run faster by reading a per-location multiplier from config.

    Generate this version

FAQ

How do I install this on Oxide or Carbon?

Drop RecyclerSpeed.cs into oxide/plugins/ on Oxide or the same folder on Carbon, then reload the plugin or restart the server. It will generate oxide/config/RecyclerSpeed.json and register the recyclerspeed.vip permission. Grant it with oxide.grant user <name> recyclerspeed.vip or to a group with oxide.group add vip followed by oxide.grant group vip recyclerspeed.vip.

Why does the recycler still feel slow sometimes?

The timer only starts on a toggle while this plugin is loaded. If a recycler was already running before the plugin loaded, the plugin's loop is not active until the next open. Turn the recycler off and on once.

Can I make VIP faster and non-VIP slower than vanilla?

Yes. Set Seconds per cycle (default) and Seconds per cycle (VIP permission) independently. Values below 0.1 are raised to 0.1 to avoid hammering the server. There is no upper clamp.

Does this work on other recycler-like entities?

No. The hook and the RecycleThink method are specific to the Recycler class. Loot barrels, quarries and ovens have different APIs and need their own plugins. A prefab that uses the Recycler component will work.

What happens if the player walks away mid-cycle?

The repeat callback checks IsOn() on every tick. When the player closes the recycler the game flips the machine off and the next tick sees that, destroys the timer and removes the entry from the internal map.

Customize it for your server

Change the reward, add a cooldown, restrict it to VIPs. Start from this prompt and edit anything.

Prompt: Create a Rust Oxide plugin called RecyclerSpeed that speeds up the vanilla Recycler by rescheduling RecycleThink on a repeating timer, with default and VIP seconds-per-cycle config values and a recyclerspeed.vip permission.
Customize for my server

Edit the prompt on the next screen. 2 free plugins a month, no card.

Last updated Oct 26, 2026.

Customize recycler speed

Customize