OnNpcGiveSoldItem
OnNpcGiveSoldItem is called on an NPC vending machine right after a purchase completes, before the item is transferred to the buyer. Returning a non-null value suppresses the delivery.
object OnNpcGiveSoldItem(NPCVendingMachine vendingMachine, Item soldItem, BasePlayer buyer)Parameters
| Parameter | Type | Description |
|---|---|---|
vendingMachine | NPCVendingMachine | The NPCVendingMachine that is handing out the purchased item. Use it for its position, net.ID or to inspect what the machine sells. |
soldItem | Item | The Item instance about to be handed to the buyer. Its info.shortname, amount and skin identify what was purchased, and you can modify it before it reaches the player. |
buyer | BasePlayer | The BasePlayer who paid for the item. Can be null-checked and used for messages, inventory checks or permission checks. |
Return value
Returning null (or nothing, since the original method returns void) lets the purchase proceed: item ownership is set and the item is given to the buyer. Returning any non-null value stops the original code path, so ownership is never assigned and the item is never handed to the player — the purchase is effectively cancelled after payment.
How it works
The hook fires inside NPCVendingMachine.GiveSoldItem, the method the game runs when a player has paid for an item in an NPC (bandit camp / outpost) vending machine. It runs before item ownership is reassigned and before the base GiveSoldItem hands the item to the buyer, so it is the last point where you can inspect or alter the transaction.
soldItem is the already-created Item that is about to be delivered; buyer is the player who bought it. This lets you read the shortname, amount and skin to decide what should happen next. The vendingMachine parameter identifies which machine sold it, which matters if your server gives different treatment to Outpost, Bandit Camp or custom NPC vendors.
Declaring the machine parameter as the exact type NPCVendingMachine is fine, but declaring buyer more narrowly (for example BasePlayer is already the narrowest useful type here) does not help narrow much — this signature only fires with a real player buyer. If the argument were ever not an instance of your declared type, Oxide would skip your method.
Since this hook fires once per purchase rather than per tick, it is safe to send feedback messages from it.
Example plugin: Cancel NPC vending machine sales to players who cannot carry the item
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("Npc Vendor Carry Check", "RustPluginGenerator", "1.0.0")]
[Description("Cancels NPC vending machine sales when the buyer has no room for the purchased item")]
public class NpcVendorCarryCheck : RustPlugin
{
private const string PermBypass = "npcvendorcarrycheck.bypass";
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Notify player when sale is blocked")]
public bool NotifyPlayer { get; set; } = 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>
{
["NoRoom"] = "The NPC vendor could not give you {0}: make room in your inventory first."
}, 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);
}
private object OnNpcGiveSoldItem(NPCVendingMachine vendingMachine, Item soldItem, BasePlayer buyer)
{
if (soldItem == null || soldItem.info == null || buyer == null || buyer.inventory == null)
return null;
if (permission.UserHasPermission(buyer.UserIDString, PermBypass))
return null;
if (HasRoomFor(buyer, soldItem.info))
return null;
if (config.NotifyPlayer)
buyer.ChatMessage(Msg("NoRoom", buyer.UserIDString, soldItem.info.displayName.english));
return true;
}
private bool HasRoomFor(BasePlayer player, ItemDefinition info)
{
if (info.stackable > 0 && player.inventory.GetAmount(info.itemid) > 0)
{
foreach (var item in player.inventory.containerMain.itemList)
{
if (item.info.itemid == info.itemid && item.amount < info.stackable)
return true;
}
}
return !player.inventory.containerMain.IsFull() || !player.inventory.containerBelt.IsFull();
}
}
}The plugin intercepts every NPC vending machine sale and checks that the buyer has inventory space for the purchased item. It counts free slots in the main and belt containers; if there is no room, the hook returns a non-null value so the item is never given, and the player gets a chat message. The machine still charges the player, since this hook fires after the purchase completes, so the message tells the buyer to free space. All text goes through the language file, and the hook is called only once per transaction, so the chat feedback is cheap.
Common use cases
Block sales when inventory is full
Return non-null when the buyer has no valid destination slot, preventing the sold item from being handed out and lost on the ground.
Reward purchases with extras
Read soldItem.info.shortname, award a bonus item through ItemManager and still return null so the original delivery happens.
Log vendor economy
Record who bought what from which machine (net.ID) so admins can audit Outpost and Bandit Camp trades.
Pitfalls
Payment is not refunded
The hook runs after the purchase is confirmed, so returning non-null cancels the item hand-off but does not give currency back. Handle refunds yourself if you cancel.
Not a server-wide vendor hook
This fires only on NPCVendingMachine. Player-run vending machines with a MarketTerminal do not pass through it, so do not rely on it for all vending activity.
Do not assume buyer is non-null
While the game normally passes a valid player, guard against null to avoid a NullReferenceException breaking the transaction.
Build a plugin with OnNpcGiveSoldItem
Describe what should happen and get a compiled plugin that uses this hook.
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 OnNpcGiveSoldItem
Generate