OnCollectiblePickedup
OnCollectiblePickedup is called when a player picks a resource from a CollectibleEntity, right before the game gives the resulting item to that player.
void OnCollectiblePickedup(CollectibleEntity collectibleEntity, BasePlayer reciever, Item item)Parameters
| Parameter | Type | Description |
|---|---|---|
collectibleEntity | CollectibleEntity | The CollectibleEntity the player just picked from (hemp, mushroom, berry bush, pumpkin, corn and similar world spawns). Use its prefab name, position or net ID to identify the resource type. |
reciever | BasePlayer | The BasePlayer receiving the item. The hook fires before the item is handed over, so the item is still not in the player's inventory at this point. |
item | Item | The Item instance that will be given to the player. You can read info.shortname, amount and skin, and you can modify amount or remove it before it reaches the inventory. |
Return value
The return value is ignored. The game continues and always calls reciever.GiveItem with the item after the hook fires, so you cannot cancel a pickup by returning anything. To change what the player receives, mutate the item argument (amount, skin) instead.
How it works
The hook fires inside CollectibleEntity.DoPickup, after the game has built the item for the pickup and after it decides that a receiver exists, but before GiveItem is invoked. Only world collectibles use this path (hemp, mushrooms, berries, pumpkins, corn, seeds and similar), not ore nodes, trees or corpse loot. collectibleEntity identifies the source, reciever is the player and item is what is about to be handed over, so item.amount and item.skin can be changed here and the inventory receives the modified version.
Declaring reciever as BasePlayer is already the most specific useful type and does not narrow anything meaningful, since the game only calls this hook when a receiver exists; the drop branch without a receiver calls a different path. Because the hook runs on every pick, keep the body cheap: check shortnames against a small set rather than iterating the whole entity list, and avoid Puts or PrintWarning here. Cancelling is not possible: the hook's return value is ignored, so you influence the outcome only through the item object.
Example plugin: Bonus yield for hemp and berry pickups
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("Collectible Bonus Yield", "RustPluginGenerator", "1.0.0")]
[Description("Gives permissioned players extra yield from collectible pickups like hemp and berries.")]
public class CollectibleBonusYield : RustPlugin
{
private const string PermBonus = "collectiblebonusyield.bonus";
private PluginConfig config;
public class PluginConfig
{
[JsonProperty("Bonus multiplier")]
public float Multiplier = 2f;
[JsonProperty("Extra amount per collectible (optional override)")]
public int FlatBonus = 0;
[JsonProperty("Report bonus in chat")]
public bool NotifyPlayer = true;
[JsonProperty("Affected shortnames")]
public List<string> Shortnames = new List<string> { "hemp", "corn", "pumpkin", "clone.berry.blue", "clone.berry.green", "clone.berry.red", "clone.berry.white" };
}
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>
{
["Bonus"] = "Bonus yield: +{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(PermBonus, this);
}
private void OnCollectiblePickedup(CollectibleEntity collectibleEntity, BasePlayer reciever, Item item)
{
if (collectibleEntity == null || reciever == null || item == null || item.info == null) return;
if (!permission.UserHasPermission(reciever.UserIDString, PermBonus)) return;
if (!config.Shortnames.Contains(item.info.shortname)) return;
int original = item.amount;
int extra = config.FlatBonus > 0 ? config.FlatBonus : (int)Math.Round(original * (config.Multiplier - 1f));
if (extra <= 0) return;
item.amount = original + extra;
if (config.NotifyPlayer)
reciever.ChatMessage(Msg("Bonus", reciever.UserIDString, extra, item.info.displayName.english));
}
}
}The plugin keeps a config table mapping collectible shortnames (hemp, berry bushes, corn, pumpkin) to an extra amount. In OnCollectiblePickedup it checks the source prefab name, looks up the shortname in the config, and if a bonus is configured it multiplies the item's amount before the game gives it to the player. Players with the permission collectiblebonusyield.bonus get the extra yield; everyone else receives the normal stack. A chat message tells the player how many extra items they gained, sent through the language file so it can be translated. Since the hook fires before GiveItem, the increased amount ends up in the inventory directly rather than being dropped on the ground.
Common use cases
Server-wide harvest rate
Multiply
item.amountfor selected collectibles to double or triple the gathering rate on a farming-focused server, without touching ore dispensers or trees.VIP bonus pickups
Check
permission.UserHasPermission(reciever.UserIDString, ...)inside the hook and raise the amount only for permitted players.Resource tracking
Read
collectibleEntity.ShortPrefabNameanditem.info.shortnameto accumulate per-player statistics or feed a quest/economy plugin without changing the item.
Pitfalls
This is not OnCollectiblePickup
Oxide also exposes a
OnCollectiblePickuphook with a different signature. Use the one whose parameters match your intent; declaring the wrong set means the method is never called for this pickup.Null check the item and entity
collectibleEntity,recieveranditemare normally set, butitem.infocan still be a surprise if the entity was replaced; guarditem.infobefore readingshortnameto avoid exceptions during pickups.Not for ore, trees or loot
Mining nodes, chopping trees and looting containers do not reach this hook. Use
OnDispenserBonus/OnDispenserGatherfor ore andOnGrowableGatherfor planted crops instead.
Build a plugin with OnCollectiblePickedup
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 OnCollectiblePickedup
Generate