Skip to content
Contractsv1.7.0

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"]
}
FieldTypeDescription
typestringExactly 'Loot'
titlestringShort, human-readable name. Must not be empty.
descriptionstringA blurb of 1 to 3 sentences. May be empty if the title is self-explanatory.
amountRequiredinteger (≥ 1)How many of the target items the player must loot.
cooldownobject | 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.
burstobject | 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.
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.
itemsstring[]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.
  • 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 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.