Skip to content
RustPluginGenerator

How to make a Rust farming yield plugin (per-crop multipliers)

Multiplies the amount of crops a player receives when harvesting planter crops or wild plants, with a separate multiplier per item shortname. It uses OnGrowableGathered to change the harvested Item amount before it enters the player's inventory.

Planter boxes and wild hemp patches are the main source of cloth and food on most servers, and tuning that output usually means wiping and hand-editing loot tables or installing a full farming overhaul. This plugin does one job: it scales the number of items you get from a single harvest, per crop.

The OnGrowableGathered hook fires after the game has created the harvested Item but before it is handed to the player, so changing item.amount there is all that is needed. A config dictionary maps item shortnames such as cloth, corn, pumpkin, berry.blue or clone.hemp to a float multiplier, plus a global fallback and an optional cap so a 5x hemp setting cannot overflow the stack in one pickup. Admin chat commands let you change a multiplier or reload the config without restarting the server.

The plugin code

FarmingYieldMultiplier.cs
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Farming Yield Multiplier", "RustPluginGenerator", "1.0.0")]
    [Description("Multiplies crops harvested from planters and wild plants with per-item multipliers.")]
    public class FarmingYieldMultiplier : RustPlugin
    {
        private const string PermAdmin = "farmingyieldmultiplier.admin";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Default multiplier")]
            public float DefaultMultiplier = 1f;

            [JsonProperty("Max amount per harvest")]
            public int MaxAmountPerHarvest = 1000;

            [JsonProperty("Notify player on bonus")]
            public bool NotifyPlayer = true;

            [JsonProperty("Item multipliers (shortname)")]
            public Dictionary<string, float> Multipliers = new Dictionary<string, float>
            {
                ["cloth"] = 1f,
                ["corn"] = 1f,
                ["pumpkin"] = 1f,
                ["mushroom"] = 1f,
                ["clone.hemp"] = 1f,
                ["berry.blue"] = 1f,
                ["berry.red"] = 1f,
                ["berry.white"] = 1f,
                ["berry.yellow"] = 1f,
                ["berry.green"] = 1f
            };
        }

        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();
            }
            if (config.Multipliers == null) config.Multipliers = new Dictionary<string, float>();
            SaveConfig();
        }

        protected override void SaveConfig() => Config.WriteObject(config);

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["NoPermission"] = "You do not have permission to use this command.",
                ["Usage"] = "Usage: /farmyield <shortname> <multiplier>, /farmyield list, /farmyield reload",
                ["Set"] = "Multiplier for {0} set to {1}x.",
                ["InvalidItem"] = "'{0}' is not a valid item shortname.",
                ["InvalidNumber"] = "'{0}' is not a valid multiplier.",
                ["List"] = "Configured multipliers: {0}",
                ["ListEmpty"] = "No multipliers are configured.",
                ["Reloaded"] = "Configuration reloaded.",
                ["Bonus"] = "+{0}x {1}"
            }, 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(PermAdmin, this);
        }

        private void OnServerInitialized()
        {
            foreach (var pair in config.Multipliers)
            {
                if (ItemManager.FindItemDefinition(pair.Key) == null)
                    PrintWarning($"Configured shortname '{pair.Key}' is not a valid item and will be ignored");
            }
        }

        private void OnGrowableGathered(GrowableEntity growableEntity, Item item, BasePlayer player)
        {
            if (item == null || item.info == null) return;

            float multiplier;
            if (!config.Multipliers.TryGetValue(item.info.shortname, out multiplier))
                multiplier = config.DefaultMultiplier;

            if (multiplier <= 0f || Mathf.Approximately(multiplier, 1f)) return;

            int baseAmount = item.amount;
            int amount = Mathf.RoundToInt(baseAmount * multiplier);
            if (amount < 1) amount = 1;

            int cap = config.MaxAmountPerHarvest;
            if (cap > 0 && amount > cap) amount = cap;
            if (item.info.stackable > 0 && amount > item.info.stackable) amount = item.info.stackable;

            if (amount == baseAmount) return;

            item.amount = amount;
            item.MarkDirty();

            if (config.NotifyPlayer && player != null && amount > baseAmount)
                SendReply(player, Msg("Bonus", player.UserIDString, amount - baseAmount, item.info.displayName.english));
        }

        [ChatCommand("farmyield")]
        private void CmdFarmYield(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;
            if (!permission.UserHasPermission(player.UserIDString, PermAdmin))
            {
                player.ChatMessage(Msg("NoPermission", player.UserIDString));
                return;
            }

            if (args == null || args.Length == 0)
            {
                player.ChatMessage(Msg("Usage", player.UserIDString));
                return;
            }

            if (args[0].Equals("list", StringComparison.OrdinalIgnoreCase))
            {
                if (config.Multipliers.Count == 0)
                {
                    player.ChatMessage(Msg("ListEmpty", player.UserIDString));
                    return;
                }

                var entries = new List<string>();
                foreach (var pair in config.Multipliers)
                    entries.Add($"{pair.Key}: {pair.Value}x");
                entries.Sort(StringComparer.OrdinalIgnoreCase);
                player.ChatMessage(Msg("List", player.UserIDString, string.Join(", ", entries)));
                return;
            }

            if (args[0].Equals("reload", StringComparison.OrdinalIgnoreCase))
            {
                LoadConfig();
                player.ChatMessage(Msg("Reloaded", player.UserIDString));
                return;
            }

            if (args.Length < 2)
            {
                player.ChatMessage(Msg("Usage", player.UserIDString));
                return;
            }

            string shortname = args[0].ToLowerInvariant();
            if (ItemManager.FindItemDefinition(shortname) == null)
            {
                player.ChatMessage(Msg("InvalidItem", player.UserIDString, shortname));
                return;
            }

            float multiplier;
            if (!float.TryParse(args[1], out multiplier) || multiplier <= 0f)
            {
                player.ChatMessage(Msg("InvalidNumber", player.UserIDString, args[1]));
                return;
            }

            config.Multipliers[shortname] = multiplier;
            SaveConfig();
            player.ChatMessage(Msg("Set", player.UserIDString, shortname, multiplier));
        }
    }
}

Hooks used: OnServerInitialized, OnGrowableGathered

Config file

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

FarmingYieldMultiplier.json
{
  "Default multiplier": 1,
  "Max amount per harvest": 1000,
  "Notify player on bonus": true,
  "Item multipliers (shortname)": {
    "cloth": 1,
    "corn": 1,
    "pumpkin": 1,
    "mushroom": 1,
    "clone.hemp": 1,
    "berry.blue": 1,
    "berry.red": 1,
    "berry.white": 1,
    "berry.yellow": 1,
    "berry.green": 1
  }
}

Commands and permissions

Chat and console commands
CommandWhat it doesPermission
/farmyield <shortname> <multiplier>Sets the multiplier for one crop shortname and saves the config.farmingyieldmultiplier.admin
/farmyield listLists every shortname with a configured multiplier.farmingyieldmultiplier.admin
/farmyield reloadReloads the config file from disk so edits take effect without a reload.farmingyieldmultiplier.admin
Permissions
PermissionGrants
farmingyieldmultiplier.adminGrants access to the /farmyield commands.

How the code works

  1. Hook signature and dispatch

    The plugin declares private void OnGrowableGathered(GrowableEntity growableEntity, Item item, BasePlayer player). Oxide only calls a hook method when every argument fits the declared parameter type, so all three parameters must be the exact game types. GrowableEntity is the plant, Item is the freshly created harvest that is about to be given to the player, and player is the harvester, which can be null for non-player harvesting.

  2. Reading the multiplier

    config.Multipliers is a Dictionary<string, float> keyed by item shortname. The hook reads item.info.shortname, lowercases it, and looks it up. If the shortname is not present it falls back to config.DefaultMultiplier, so new crop types added by Facepunch are still handled. A multiplier of exactly 1.0 returns early so vanilla harvesting costs nothing in CPU.

  3. Applying and capping the amount

    The new amount is Mathf.RoundToInt(item.amount * multiplier), clamped to at least 1 and to config.MaxAmountPerHarvest so a high multiplier cannot produce a single stack larger than the item's stackable value. The hook mutates item.amount in place, which is safe because the item has not been moved into a container yet; calling item.MarkDirty() keeps any client-side preview in sync.

  4. Feedback and logging

    When config.NotifyPlayer is true and the multiplier is above 1, the player gets a short SendReply built from the lang message 'Bonus', formatted with the extra count and the crop's display name. Damage-style hooks are not involved, but the hook still fires for every wild hemp pickup, so there are no Puts calls inside it; all warnings happen at load time when a configured shortname resolves to null via ItemManager.FindItemDefinition.

  5. Config and validation

    LoadConfig reads the file and falls back to LoadDefaultConfig on any parse error, printing one warning. The defaults ship multipliers of 1.0 for corn, pumpkin, cloth, mushrooms and each berry colour so admins edit values rather than guess shortnames. SaveConfig writes the dictionary back, preserving whatever the admin added with /farmyield.

  6. Commands and permissions

    Three chat commands share the farmingyieldmultiplier.admin permission, registered in Init. /farmyield <shortname> <multiplier> validates the shortname with ItemManager.FindItemDefinition, parses the float, writes it into config.Multipliers, calls SaveConfig and confirms in chat. /farmyield list prints sorted entries and /farmyield reload re-reads the file in place.

Install it on your server

  1. Save FarmingYieldMultiplier.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/FarmingYieldMultiplier.json (Carbon: carbon/configs/FarmingYieldMultiplier.json). After editing, reload it:oxide.reload FarmingYieldMultiplier c.reload FarmingYieldMultiplier # Carbon
  4. Grant the permission

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

Variations

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

FAQ

Does this work on wild hemp and berry bushes, or only planter boxes?

Both. GrowableEntity covers planter crops and the wild plants the game spawns, so a hemp bush picked by hand fires the same hook. If you want to boost only planter crops, add a check that growableEntity.GetParentEntity() is a planter or that the growable is inside a building block.

Which shortnames do I put in the config?

Use the item shortname from the Rust item list, for example cloth, corn, pumpkin, mushroom, berry.blue, berry.red, berry.white, berry.yellow and clone.hemp for the planted hemp clone. Run /farmyield list to see the defaults, and /farmyield <shortname> 2.5 to add one.

Why does my multiplier not give exactly that number?

The result is rounded to a whole integer with Mathf.RoundToInt and clamped to the item's stack size and to your Max amount per harvest setting. A 0.4 multiplier on a 1-item harvest still yields 1, because the plugin never removes items from a player.

Can I disable the bonus chat message?

Yes, set Notify player on bonus to false in oxide/config/FarmingYieldMultiplier.json. The message text itself lives in the lang file, so you can also reword it there without touching the plugin code.

How do I install it on Oxide or Carbon?

Drop FarmingYieldMultiplier.cs into oxide/plugins, run oxide.reload FarmingYieldMultiplier from the server console, then edit oxide/config/FarmingYieldMultiplier.json and use /farmyield reload. Carbon loads the same file unchanged.

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 that multiplies crop harvest amounts from OnGrowableGathered using a per-shortname multiplier dictionary, a default multiplier, a max amount cap and /farmyield admin commands.
Customize for my server

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

Last updated Oct 26, 2026.

Customize farming yield multiplier

Customize