Minecraft – How to Replace All Chests with Ones with a Loot Table

minecraft-java-editionminecraft-java-edition-server

Hello so I have a hunger games map and a custom loot table I want to use to fill my chests. I want to be able to set these chests to a custom loot table on command. I have looked into the /fill command but I run into two limitations – orientations are not preserved upon replacing the old chests, and two that the arena is too large to load all at once so the command fails to work. Is there any solution to this? Preferably I would be able to fill the chests seamlessly and repeatedly so that I can easily reset the map for further use.

Best Answer

I'm currently working on something similar, and the best solution I've found so far is the following:

  1. Use Datapacks with functions, not command block chains, that saves tons of performance

  2. you have to create separate setblock functions for every kind of chests you have, multiply by four for orientation (six for barrels). You can separate spawning and filling using data merge, but you can't change block state of existing block, at least in vanilla, so you have to create a function for each orientation at least. The former is probably possible using WorldEdit since it exposes "cycler" tool which does exactly that.

  3. Your containers should be put at set places, since, to the best of my knowledge, you can't use a type selector for blocks. Unless you don't want to go crazy with spawning armor stands and testing for each block in the area, you should rather use execute positioned <pos> as entity @p[distance=..R] functions. You can use the chest position itself as pos, then R is the spawn trigger distance, which I set to the very possible minimum of 4 (as the chest is reachable from within the range of 3), and it's adjustable. Another option is to set pos to some enclosed area entrance (where R is the entrance size basically), which is impossible to bypass, and then trigger chest creation by another layer of execute positioned. Those checks should be running every tick, which you achieve either by using repeating command block or by putting your function into #minecraft:tick function tag.

  4. To prevent repetitive spawning you want to use Tags. That's a little bit verbose, but pretty efficient. Depending on your goals you can tag either a player or the chest at the moment it's filled. So it can be filled once per lifetime (totally multiplayer safe), once per every player approaching (not so much multiplayer safe), or, if you're crazy enough with state management, you can force a chest respawn once for every player but only if it's emptied. To do so, you should tag both a player and a chest on spawn event, and create another tick command which tracks previously tagged chests and removes that tag, so the state is fully reset for the chest (but not for the player). If you need too many tags, you could use code generation.

  5. Group up chests spawn whenever you can. For instance, if there's a room with 2 entrances and 15 chests, it's better to test for player presence in each of entrances rather than to do so around every chest. Whenever possible try to use redstone wiring (pressure plates, trapped chests, tripwires etc. ) with impulse command blocks to test for player approach rather then tick function, for performance reasons.

So the example code would be like

### Repeating command block or tick function
execute positioned X Y Z as entity @p[distance=..4,tag=!spawnChestUniqueIdentifier] run function datapack:chests/theSameUniqueIdentifier

### Function datapack:chests/theSameUniqueIdentifier

# The function is written specifically for the usage above, 
# as it assumes the location context ~ ~ ~ to be precisely at chest. 
# If you want to separate tracking and spawning, 
# put "execute positioned" here
# You have to wipe the chest first, since "setblock" doesn't refresh the loot table seed of existing blocks, sadly
fill ~ ~ ~ ~ ~ ~ air replace

# generally it's worth wiping whatever was dropped if the chest could have the remaining loot from previous spawn
# you may want to keep the items when several players approach the same chest so they get double items anyway
kill @e[type=item,distance=..3]

# And now you create a filled chest
setblock ~ ~ ~ chest[facing=south]{LootTable:"chests:masonry"} replace 

# Or creating template functions to avoid copy pasting
function utils:chests/masonry/south

# finally, you tag a player so he doesn't retrigger the spawn. Again, the context is dependant on the previous function
tag @s add spawnChestUniqueIdentifier

I have more examples here if you need them https://gitlab.com/octaharon/minecraft-quest/-/tree/master/data/chests