Skip to content
RustPluginGenerator

CanCombineDroppedItem

CanCombineDroppedItem is called when one dropped item entity lands on another and the game is about to merge their stacks. Returning any non-null value blocks the merge and leaves both entities separate.

Signature
object CanCombineDroppedItem(DroppedItem droppedItem, DroppedItem di)

Parameters

Hook parameters
ParameterTypeDescription
droppedItemDroppedItemThe DroppedItem entity that was dropped onto (the target stack on the ground). Its item is the stack that would grow.
diDroppedItemThe DroppedItem that was just dropped or moved onto the target. Its item is the stack that would be merged into droppedItem.

Return value

Returning null (or not implementing the hook) lets the merge continue normally. Returning any non-null value stops OnDroppedOn before the amounts are combined, so both dropped item entities stay separate. The returned object itself is discarded because the original method returns void.

How it works

The hook fires from DroppedItem.OnDroppedOn, which runs when a dropped item is placed on top of another dropped item (for example by physics settling, by a player dropping an item onto an existing stack, or when a stack is moved onto a ground item). The game first checks that both entities still own items, that neither has NeverCombine set and that item.CanStack(di.item) passes, then calls CanCombineDroppedItem before summing di.item.amount + item.amount.

droppedItem is the receiving entity; di is the incoming one. Read droppedItem.item and di.item to inspect shortnames, skins, amounts or GetOwnerPlayer(). Both arguments are already DroppedItem, so the declared types are as specific as Oxide can dispatch on; a null-check is still needed because either entity's item can be null in edge cases.

Use it to keep stacks apart for custom rules: block merging of different skins (the game already refuses different skins in CanStack, but you may want to segment by owner or category), or stop merging entirely on your server. The hook is followed by OnDroppedItemCombined only when the merge proceeds.

Example plugin: Prevent merging of player-dropped stacks by owner

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

namespace Oxide.Plugins
{
    [Info("No Dropped Stack Merge", "RustPluginGenerator", "1.0.0")]
    [Description("Prevents selected players' dropped items from merging with other stacks on the ground")]
    public class NoDroppedStackMerge : RustPlugin
    {
        private const string PermKeep = "nodroppedstackmerge.keep";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Block merging for all players")]
            public bool BlockForEveryone { get; set; } = false;
        }

        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>
            {
                ["Status"] = "Dropped stack merging - everyone blocked: {0}. Permission '{1}' holders are always exempt from merging.",
                ["Granted"] = "You now keep your dropped stacks separate.",
                ["Revoked"] = "Your dropped stacks can merge again."
            }, 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(PermKeep, this);
        }

        private object CanCombineDroppedItem(DroppedItem droppedItem, DroppedItem di)
        {
            if (droppedItem == null || di == null) return null;
            if (droppedItem.item == null || di.item == null) return null;

            if (config.BlockForEveryone) return true;

            if (IsProtected(droppedItem.item) || IsProtected(di.item)) return true;

            return null;
        }

        private bool IsProtected(Item item)
        {
            var owner = item.GetOwnerPlayer();
            if (owner == null) return false;
            return permission.UserHasPermission(owner.UserIDString, PermKeep);
        }

        [ChatCommand("mergestatus")]
        private void CmdMergeStatus(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;
            SendReply(player, Msg("Status", player.UserIDString, config.BlockForEveryone, PermKeep));
        }

        [ChatCommand("mergetoggle")]
        private void CmdMergeToggle(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;
            if (!player.IsAdmin) return;

            bool granted;
            if (permission.UserHasPermission(player.UserIDString, PermKeep))
            {
                permission.RevokeUserPermission(player.UserIDString, PermKeep);
                granted = false;
            }
            else
            {
                permission.GrantUserPermission(player.UserIDString, PermKeep, this);
                granted = true;
            }

            SendReply(player, Msg(granted ? "Granted" : "Revoked", player.UserIDString));
        }
    }
}

The plugin adds a permission that exempts a player's items from merging with anything else on the ground. CanCombineDroppedItem compares the owner of each stack via item.GetOwnerPlayer(); if either side belongs to a player holding nodroppedstackmerge.keep, it returns true, which blocks the merge. A config toggle lets admins disable merging for every player on the server instead. Because the hook can fire often on busy servers, nothing is logged inside it; the only output is the optional chat warning sent by a /mergestatus command that reports the current mode.

Common use cases

  • Keep premium skin stacks separate

    Block merging when either stack carries a skin ID you want to keep distinct, so collectors' skinned drops never absorb a plain stack.

  • Stop automatic stack merging server-wide

    Return true for every pair when a server wants fully discrete ground loot, for example on scarcity-focused wipes or custom loot economies.

  • Segment merging by owner

    Compare GetOwnerPlayer() on both items and refuse merges across different players so dropped loot keeps its original owner tag for return-on-death rules.

Pitfalls

  • Null item on either entity

    DroppedItem.item can be null if the entity's item was already taken or removed. Always check both droppedItem?.item and di?.item before calling into Item members.

  • Return value type does not matter

    The original method returns void, so only null versus non-null counts. Returning false still blocks the merge because it is not null.

  • Does not cover inventory merging

    This hook only handles dropped item entities bumping into each other. Merging inside containers is controlled by CanStackItem and container-level logic.

Build a plugin with CanCombineDroppedItem

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

Prompt: Keep premium skin stacks separate using the CanCombineDroppedItem 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 CanCombineDroppedItem

Generate