Skip to content
Contractsv1.16.1

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": {},
"rewards": {},
"items": ["scrap"],
"containers": []
}
FieldTypeDescription
typestringExactly 'Loot'
itemsstring[]Rust item shortNames, lowercase dotted (e.g. rifle.ak, rifle.bolt, wood, stones, metal.fragments, scrap, hqm, lowgradefuel). Add :skin with the skin's workshop ID to match only that skinned item (paper:3731371060). A bare short name matches any skin, and :0 matches only the unskinned item.
containersstring[]Rust entity short prefab names of the containers the items must come out of, or @preset refs (e.g. supply_drop for the airdrop, crate_normal for a military crate, @Hackable Crates, @Barrels, scientist2.corpse for a scientist body). Empty means any source, the ground included. Use the body's name, not the living NPC's. Names are not case sensitive.
Shared fields (10)
FieldTypeDescription
titlestringShort, human-readable name. Must not be empty.
titleTranslationsmap | nullPer-language overrides for title, mapping a language code (like "fr" or "zh-CN") to the translated text. Players whose language has no entry (matched case-insensitively, no region fallback) or a blank one see title instead. null when there are none.
descriptionstringA blurb of 1 to 3 sentences. May be empty if the title is self-explanatory.
descriptionTranslationsmap | nullPer-language overrides for description, mapping a language code (like "fr" or "zh-CN") to the translated text. Players whose language has no entry (matched case-insensitively, no region fallback) or a blank one see description instead. null when there are none.
amountRequiredinteger (min 1)How many of the target items the player must loot.
cooldown ⭐ Premiumobject | nullOptional pacing limit on how fast progress adds up: at most maxAmount units of progress count in any sliding window of windowSeconds. Absent means progress adds up freely. While the window's budget is used up, further progress is ignored and the player sees a countdown to the next available moment.
burst ⭐ Premiumobject | nullOptional clustering requirement: actions only count when they arrive clustered, at least amount units within one trailing window of windowSeconds forming one burst. When present, amountRequired counts completed bursts instead of raw actions. Actions that age out of the window without completing a burst never count. Absent means no clustering.
conditionsmapOptional 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.
rewards ⭐ PremiummapRewards claimed when this objective completes, instead of at the end of the contract. Each entry is a full reward, the same shape the contract's own rewards use. Use an empty map for none.
hideDetailsbooleanWhen true, hides this objective's in-game condition strip and details popup, so the description is the only explanation players get. Title, description and progress still show. Absent means false.
  • This objective type is not recommended for use. Read the “Not recommended” section below for details.
  • 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.

Use containers to specify the list of containers that items must have come out of. Leave it empty for the objective to count an item no matter where it came from: a crate, a barrel, a body, or the ground. It takes entity names or @preset references, the same as the Kill objective’s entities list. Case doesn’t matter.

{
"type": "Loot",
"title": "Loot scrap from the airdrop",
"description": "",
"amountRequired": 100,
"conditions": {},
"rewards": {},
"items": ["scrap"],
"containers": ["supply_drop"]
}

Leave items empty instead and you get any item from specific containers: "items": [] with "containers": ["@Hackable Crates"] is “loot any 10 items out of a hackable crate”.

Four presets ship with the plugin so you don’t have to list the prefab names yourself:

Preset Covers
@Normal Crates The brown wooden crates, including the ones on train wagons
@Military Crates The green military crates
@Hackable Crates All five hackable crate variants (oil rig, ghost ship, the rest)
@Barrels Road barrels, oil barrels and diesel barrels
  • Ground pickups stop counting. An item lying on the ground has no container to match against, so it never counts for that objective. Same for berries, mushrooms and other collectibles.
  • Barrels count what they spill. A barrel dumps its loot on the ground as it breaks, so there is nothing to open. The plugin remembers which barrel each item fell out of until you walk over and pick it up, so @Barrels counts the way you’d expect.
  • Bodies go by the corpse name, not the NPC’s name. Every scientist type leaves the same scientist2.corpse and a scarecrow leaves murderer_corpse. @Scientists names the living NPC, so it does nothing here (unless you add scientist2.corpse to it, which the default preset doesn’t have).
  • Player boxes and player bodies still never count, with or without a containers list. This is to prevent exploits. See the tainting system below.
  • Scrap Yield tea gives you bonus scrap that was never inside the barrel, so a barrel filter skips it.
  • A body that despawns leaves an item_drop_backpack bag behind. Items taken out of the bag match the bag’s name, not the corpse’s.
  • Some crates share a short name across monuments: crate_elite spawns in radtown and in underwater labs and a containers list can’t tell them apart. Pair it with a Monument condition if you need to.

The containers list is the one thing only Loot can do, since Turn In has no idea where an item came from. For everything else, Turn In is the safer pick. The Loot objective sounds simple on paper, but it is amongst one of 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. Whenever any player picks up a tainted item, the plugin skips progressing the loot objective and “untaints” the item to free memory.

The plugin already does a lot to untaint items as soon as possible, but it is not perfect:

  • Picking up a tainted item from the world or a world container untaints it immediately. The plugin doesn’t need to track items not in world containers, because they can’t be used for progress anyway.
  • When stacking two tainted items together, the resulting stack stays tainted, but the source stack (which no longer exists) is untainted.
  • Tainted items that despawn from the world are untainted.
    • However, if the tainted item is in a world container (e.g. a chest) and that container despawns, the taint will remain in memory indefinitely. This is arguably the biggest source of lingering tainted items.

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 items Approx. RAM What it means
up to ~100k up to ~3 MB Normal. Nothing to do.
~100k to ~1M ~3 to 30 MB Fine, but a sign of long uptime or heavy item churn.
millions, climbing each save tens to hundreds of MB Worth 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.

Known limitations that will not be addressed

Section titled “Known limitations that will not be addressed”

The loot objective has some known limitations that are accepted and will not be fixed, either because the fix would involve worse memory/performance tradeoffs or because it would open up potential over-count exploits. The loot objective aims to prevent over-counting, even if that means under-counting in some edge cases.

  • This objective will only progress for items that are moved from the world or a world-owned container to a player inventory. This means looting an item from a player body or a player-owned container will not progress the objective. This is intentional to prevent over-counting progress exploits by moving items between player-owned containers.
  • If a player drags a stack of their items onto a world container’s stack of the same item (that is untainted), then the entire stack becomes tainted, because any item that leaves the player inventory must become tainted. This entire stack will be ineligible for loot progress. Fixing this would require tracking the stack’s network ID AND the tainted amount, which would be a significant increase in memory footprint compared to only tracking the network ID.
    • E.g: Say a scientist’s body has an untainted stack of 10 scrap. If you grab the 10 scraps, then it counts. However, if you drag 1 scrap from your inventory onto that stack of 10, it becomes a tainted stack of 11. If you then loot from that stack, it does not count, even if you only take 1 scrap.

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.