Skip to content
RustPluginGenerator

CanMoveItem

CanMoveItem is called from PlayerInventory.MoveItem before any item relocation is executed, and returning a non-null value cancels the move entirely.

Signature
object CanMoveItem(Item item, PlayerInventory playerInventory, ItemContainerId itemContainerId, int targetPosition, int amount, ItemMoveModifier itemMoveModifier)

Parameters

Hook parameters
ParameterTypeDescription
itemItemThe Item being moved. Check item.info.shortname to identify what is being moved and item.parent for its current container.
playerInventoryPlayerInventoryThe PlayerInventory the move is performed on. Use playerInventory.player to reach the BasePlayer and its permissions.
itemContainerIdItemContainerIdThe ItemContainerId of the target container. Match it against playerInventory.containerMain.uid, containerBelt.uid and containerWear.uid to know which destination slot group is used.
targetPositionintThe target slot index inside the destination container. -1 usually means the game picks the first free slot.
amountintHow many units of the stack are being moved. Compare with item.amount to detect partial splits.
itemMoveModifierItemMoveModifierThe ItemMoveModifier describing the move (for example a quick move or a shift-click). Useful when you only want to block specific kinds of moves.

Return value

Returning null lets the game continue the move normally. Returning any non-null object (for example true) causes MoveItem to return early, so the item stays where it was and no split, stacking or container change happens.

How it works

The hook fires inside PlayerInventory.MoveItem, right after the game has validated the destination container and before the item is detached, split or inserted. Every inventory move routes through this method: dragging a stack in the inventory UI, shift-click quick transfers between belt, main and wear slots, moving items into a lootable container, and plugin-driven moves that call MoveItem on a player inventory.

The parameters give you the whole context. item is the exact Item instance, playerInventory owns the source and lets you reach playerInventory.player, itemContainerId identifies the destination container by its network id and targetPosition is the destination slot. amount is the quantity being moved, which differs from item.amount on split moves, and itemMoveModifier tells you what kind of move the client requested. Match itemContainerId against playerInventory.containerMain.uid, containerBelt.uid and containerWear.uid to classify the destination.

The signature only has one overload. Returning a value stops the move, so this is a filtering hook: use it for rules like forbidding a shortname in the belt, or restricting quick moves of certain items. There is no way to redirect the move from here; cancel and do the relocation yourself with MoveToContainer if you need that.

Example plugin: Keep restricted items out of the belt

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

namespace Oxide.Plugins
{
    [Info("Belt Item Restriction", "RustPluginGenerator", "1.0.0")]
    [Description("Blocks configured items from being moved into a player's belt container")]
    public class BeltItemRestriction : RustPlugin
    {
        private const string PermBypass = "beltitemrestriction.bypass";

        private PluginConfig config;
        private HashSet<string> blockedShortnames;

        private class PluginConfig
        {
            [JsonProperty("Blocked item shortnames")]
            public List<string> BlockedShortnames = new List<string> { "rocket.launcher", "explosive.satchel", "explosive.timed" };
        }

        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>
            {
                ["Blocked"] = "You cannot place {0} in your belt."
            }, 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(PermBypass, this);
            blockedShortnames = new HashSet<string>(config.BlockedShortnames, StringComparer.OrdinalIgnoreCase);
        }

        private object CanMoveItem(Item item, PlayerInventory playerInventory, ItemContainerId itemContainerId, int targetPosition, int amount, ItemMoveModifier itemMoveModifier)
        {
            if (item?.info == null || playerInventory?.containerBelt == null) return null;
            if (!blockedShortnames.Contains(item.info.shortname)) return null;
            if (itemContainerId.Value != playerInventory.containerBelt.uid.Value) return null;

            BasePlayer player = playerInventory.containerBelt.playerOwner;
            if (player == null || player.IsAdmin) return null;
            if (permission.UserHasPermission(player.UserIDString, PermBypass)) return null;

            player.ChatMessage(Msg("Blocked", player.UserIDString, item.info.displayName.english));
            return true;
        }
    }
}

The plugin reads a configurable list of shortnames and blocks moves of those items into the player's belt container. The hook resolves the destination by comparing itemContainerId with playerInventory.containerBelt.uid and checks the shortname against the config set. When a blocked move is detected it returns true to cancel it and sends a localized chat message to the player. Admins and players with the beltitemrestriction.bypass permission are ignored, so staff can still move anything. The default config restricts explosives and rocket launchers, which keeps raiding gear out of the hotbar on PvP servers.

Common use cases

  • Block raid tools in the hotbar

    Prevent rocket launchers, C4 and satchels from being dragged into belt slots while still allowing them in the main inventory.

  • Custom inventory rules per rank

    Give certain permission groups extra allowed shortnames in the wear or belt containers and cancel moves that exceed their allowance.

  • Stop quick-move from loot containers

    Inspect itemMoveModifier to cancel only fast transfers, forcing players to drag items manually from crates or corpses.

Pitfalls

  • Comparing containers by reference

    The destination arrives as an ItemContainerId, not an ItemContainer. Compare it against the uid of the player's containers, not against the container objects themselves.

  • Cancelling without feedback

    A cancelled move looks like a UI glitch to the player. Send a short chat or console message so they know the action was blocked on purpose.

  • Hook fires on every move

    This is a hot path for inventory-heavy actions. Keep the logic to a set lookup and avoid allocations, logging or physics queries inside the hook.

Plugin recipes that use CanMoveItem

Build a plugin with CanMoveItem

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

Prompt: Block raid tools in the hotbar using the CanMoveItem 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 CanMoveItem

Generate