Skip to content
RustPluginGenerator

OnItemSplit

OnItemSplit is called from Item.SplitItem when a stack is about to be divided, before the game reduces the original stack and creates the new item. Returning an Item replaces the game's own split result.

Signature
object OnItemSplit(Item item, int splitAmount)

Parameters

Hook parameters
ParameterTypeDescription
itemItemThe stack being split. Use item.info, item.amount, item.skin and item.parent to decide whether the split should be handled specially. It is never null when the hook fires.
splitAmountintHow many units the caller wants to move out of the stack. It is always smaller than item.amount, because Item.SplitItem returns early when the requested amount is greater than or equal to the stack size.

Return value

Return null to let the game split the stack normally. Returning an Item makes SplitItem return that item immediately: the original stack keeps its full amount and no new item is created by the game, so you are responsible for any item you produce or hand out.

How it works

The hook fires inside Item.SplitItem, right after the method rejects requests where the split amount is greater than or equal to the stack size. It runs for every split, whether the split comes from right-clicking a stack in the inventory, dragging onto an empty slot, moving part of a stack into a container, or server code calling SplitItem directly. Both parameters are non-null: item is the source stack and splitAmount is the requested quantity. Because the hook fires before amount is reduced, item.amount still holds the full stack size at that point. The injection fires once per call, not per unit. Declaring the first parameter as a more specific type (for example Item) does not narrow anything here, since the argument is always an Item; there is no useful subtype to narrow to. The hook is a good place to veto or reshape splits for certain items, for example to keep a special stack intact or to hand back a custom item instead of a plain split. Keep the body cheap: it fires on routine inventory actions, so avoid logging or allocations.

Example plugin: Block splitting of a protected stack

NoSplitProtection.cs
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;

namespace Oxide.Plugins
{
    [Info("No Split Protection", "RustPluginGenerator", "1.0.0")]
    [Description("Prevents configured item stacks from being split by players or plugins.")]
    public class NoSplitProtection : RustPlugin
    {
        private const string PermManage = "nosplitprotection.manage";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Protected item shortnames")]
            public List<string> ProtectedItems = new List<string> { "scrap", "metal.fragments" };
        }

        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>
            {
                ["NoPermission"] = "You do not have permission to use this command.",
                ["Reloaded"] = "Protected item list reloaded: {0}",
                ["NoItems"] = "The protected item list is empty."
            }, 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(PermManage, this);
        }

        private object OnItemSplit(Item item, int splitAmount)
        {
            if (item?.info == null || config?.ProtectedItems == null) return null;
            if (!config.ProtectedItems.Contains(item.info.shortname)) return null;

            // Returning the source item makes Item.SplitItem stop and hand back the unchanged stack.
            return item;
        }

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

            LoadConfig();

            if (config.ProtectedItems.Count == 0)
            {
                player.ChatMessage(Msg("NoItems", player.UserIDString));
                return;
            }

            player.ChatMessage(Msg("Reloaded", player.UserIDString, string.Join(", ", config.ProtectedItems)));
        }
    }
}

The plugin keeps a configurable list of item shortnames that must not be split. OnItemSplit checks the item's shortname against that list; if it matches, the hook returns the source item itself, which makes SplitItem return early so the stack stays whole. Returning the same item does not duplicate anything: the game's split code is skipped entirely, the stack amount is unchanged, and the caller receives a reference to the stack it already had. When it does not match, the hook returns null and normal splitting continues. A chat command reloads the rule list at runtime, and the item list lives in the config file so admins can edit it without recompiling.

Common use cases

  • Keep stacks of a currency item whole

    Veto splits for an item that other plugins treat as an atomic token, so it can never be turned into two partial stacks that break counting logic.

  • Restrict splitting to the owner

    Return the item unchanged when a player tries to split a stack inside a container they do not own, while leaving normal splits for their own inventory.

  • Swap in a custom item on split

    Create your own item with a skin or renamed display name and return it so the caller receives that item instead of the default split result.

Pitfalls

  • Returning a new item leaves the source untouched

    When you return a different Item, the game does not reduce item.amount and does not create a normal split. If you created an item yourself you must place it in a container or give it to a player, otherwise it exists only in memory.

  • The hook does not fire for oversized requests

    SplitItem returns before the hook when splitAmount is greater than or equal to the stack size, so you cannot rely on OnItemSplit to catch every inventory move that looks like a split.

  • Do not recurse

    Calling SplitItem from inside OnItemSplit triggers the hook again. Compute your replacement item with ItemManager.CreateByName and MoveToContainer instead of splitting the same stack.

Build a plugin with OnItemSplit

Describe what should happen and get a compiled plugin that uses this hook.

Prompt: Keep stacks of a currency item whole using the OnItemSplit hook, with a config file and a permission
Generate this plugin

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 OnItemSplit

Generate