Skip to content
These docs reflect Contracts v0.6.0, the latest release. Older servers may differ.

Loot

Requires the player to loot specified items from the world or non-player-owned containers. Counts the number of items obtained.

{
"type": "Loot",
"title": "Loot scrap from crates",
"description": "",
"amountRequired": 100,
"conditions": {},
"items": ["scrap"]
}
Field Type Description
type string Exactly 'Loot'
title string Short, human-readable name. Must not be empty.
description string A blurb of 1 to 3 sentences. May be empty if the title is self-explanatory.
amountRequired integer (≥ 1) How many units must be completed. What a unit means depends on the objective type. Minimum 1.
conditions map Optional conditions that restrict when progress counts. Progress is recorded only when the objective's main requirement and all of these conditions are met. Use an empty map for none.
items string[] Rust item shortNames, lowercase dotted (e.g. rifle.ak, rifle.bolt, wood, stones, metal.fragments, scrap, hqm, lowgradefuel).
  • This objective type is not recommended for use. Read the “Not recommended” section below for details.
  • Counts items acquired into the player’s inventory from the world or non-player-owned containers: crates, barrels, the ground, NPC corpses. Pulling items out of your own base storage does not count.
  • Progress follows the items as they move (including stack splits), crediting the player who received them.
  • Loot overlaps intentionally with Harvest: picking a wild pumpkin is both a Loot and a Harvest.

The Loot objective sounds simple on paper, but it is arguably the most technically complex objective of the plugin and the most likely to have exploits or edge cases. The complexity comes from the mechanisms required to prevent a player from removing and re-adding the same item to a container to farm progress. There are also a bunch of ways for a player to get items (dragging a stack to their inventory, splitting a stack into their inventory, picking up an item from the world, etc.). All of these actions fire on different Rust events, include different payloads and required me to implement a whole transfer and tainting system just to track item movements.

In the simplest terms, the system works like this: when an item is moved from a player container to a non-player-owned container or the world, the plugin “taints” (remembers) that item in memory (yep, it consumes RAM for this! See “How many tainted items is ‘a lot’?” below). Whenever any player picks up a tainted item, the plugin skips progressing the loot objective and “untaints” the item to save memory.

Each tainted item is a single network ID (8 bytes) in a hashset, so figure roughly 30 bytes each once you account for overhead (of the hashset data structure). The count only matters relative to RAM, and that bar is high:

Tainted itemsApprox. RAMWhat it means
up to ~100kup to ~3 MBNormal. Nothing to do.
~100k to ~1M~3 to 30 MBFine, but a sign of long uptime or heavy item churn.
millions, climbing each savetens to hundreds of MBWorth clearing.

The number resets on every server/plugin restart, so a normal wipe cycle keeps it well inside the first row. Read it as a growth signal, not a memory threat: if it’s climbing by hundreds of thousands every save, restart or run contracts.debug.clear_tainted_items. The absolute number on its own is rarely a reason to act.

On my PVE server, which restarts every 24 hours, the count usually sits at around 250 tainted items at the end of the day.

For all these reasons, it’s recommended to use the Turn In objective instead of Loot whenever possible. Because the Turn In objective takes away the item from the player, it avoids all the complexities of tracking items in the world and inventory, and is much more straightforward to implement and manage.