add vanilla loottables

This commit is contained in:
Julian Krings 2024-12-22 16:39:41 +01:00
parent 3265447536
commit e189b2389c
No known key found for this signature in database
GPG Key ID: 208C6E08C3B718D2
11 changed files with 293 additions and 69 deletions

View File

@ -26,7 +26,6 @@ import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.project.IrisProject;
import com.volmit.iris.core.service.ConversionSVC;
import com.volmit.iris.core.service.StudioSVC;
import com.volmit.iris.core.tools.IrisConverter;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.*;
@ -55,7 +54,6 @@ import com.volmit.iris.util.noise.CNG;
import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.plugin.VolmitSender;
import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.O;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
@ -64,7 +62,6 @@ import io.papermc.lib.PaperLib;
import org.bukkit.*;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.BlockVector;
import org.bukkit.util.Vector;
@ -79,7 +76,6 @@ import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
@ -306,7 +302,7 @@ public class CommandStudio implements DecreeExecutor {
Inventory inv = Bukkit.createInventory(null, 27 * 2);
try {
engine().addItems(true, inv, RNG.r, tables, InventorySlotType.STORAGE, player().getLocation().getBlockX(), player().getLocation().getBlockY(), player().getLocation().getBlockZ(), 1);
engine().addItems(true, inv, RNG.r, tables, InventorySlotType.STORAGE, player().getWorld(), player().getLocation().getBlockX(), player().getLocation().getBlockY(), player().getLocation().getBlockZ(), 1);
} catch (Throwable e) {
Iris.reportError(e);
sender().sendMessage(C.RED + "Cannot add items to virtual inventory because of: " + e.getMessage());
@ -329,7 +325,7 @@ public class CommandStudio implements DecreeExecutor {
inv.clear();
}
engine().addItems(true, inv, new RNG(RNG.r.imax()), tables, InventorySlotType.STORAGE, player().getLocation().getBlockX(), player().getLocation().getBlockY(), player().getLocation().getBlockZ(), 1);
engine().addItems(true, inv, new RNG(RNG.r.imax()), tables, InventorySlotType.STORAGE, player().getWorld(), player().getLocation().getBlockX(), player().getLocation().getBlockY(), player().getLocation().getBlockZ(), 1);
}, 0, fast ? 5 : 35));
sender().sendMessage(C.GREEN + "Opening inventory now!");

View File

@ -21,7 +21,6 @@ package com.volmit.iris.core.nms.v1X;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.container.BlockPos;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
@ -32,7 +31,6 @@ import com.volmit.iris.util.nbt.mca.palette.MCAPaletteAccess;
import com.volmit.iris.util.nbt.tag.CompoundTag;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.entity.Dolphin;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.event.entity.CreatureSpawnEvent;
@ -40,8 +38,8 @@ import org.bukkit.generator.ChunkGenerator;
import org.bukkit.generator.structure.Structure;
import org.bukkit.inventory.ItemStack;
import java.awt.*;
import java.awt.Color;
import java.util.stream.StreamSupport;
public class NMSBinding1X implements INMSBinding {
private static final boolean supportsCustomHeight = testCustomHeight();
@ -113,7 +111,7 @@ public class NMSBinding1X implements INMSBinding {
@Override
public KList<String> getStructureKeys() {
var list = Registry.STRUCTURE.stream()
var list = StreamSupport.stream(Registry.STRUCTURE.spliterator(), false)
.map(Structure::getKey)
.map(NamespacedKey::toString)
.toList();

View File

@ -64,10 +64,7 @@ import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import com.volmit.iris.util.stream.ProceduralStream;
import io.papermc.lib.PaperLib;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
@ -78,7 +75,7 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import java.awt.*;
import java.awt.Color;
import java.util.Arrays;
import java.util.Set;
import java.util.UUID;
@ -417,7 +414,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
if (tables.isEmpty())
return;
InventoryHolder m = (InventoryHolder) block.getState();
addItems(false, m.getInventory(), rx, tables, slot, x, y, z, 15);
addItems(false, m.getInventory(), rx, tables, slot, c.getWorld(), x, y, z, 15);
} catch (Throwable e) {
Iris.reportError(e);
@ -526,16 +523,16 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
}
@Override
default void addItems(boolean debug, Inventory inv, RNG rng, KList<IrisLootTable> tables, InventorySlotType slot, int x, int y, int z, int mgf) {
default void addItems(boolean debug, Inventory inv, RNG rng, KList<IrisLootTable> tables, InventorySlotType slot, World world, int x, int y, int z, int mgf) {
KList<ItemStack> items = new KList<>();
int b = 4;
for (IrisLootTable i : tables) {
if (i == null)
continue;
b++;
items.addAll(i.getLoot(debug, rng, slot, x, y, z));
items.addAll(i.getLoot(debug, rng, slot, world, x, y, z));
}
if (IrisLootEvent.callLootEvent(items, inv, world, x, y, z))
return;
if (PaperLib.isPaper() && getWorld().hasRealWorld()) {
PaperLib.getChunkAtAsync(getWorld().realWorld(), x >> 4, z >> 4).thenAccept((c) -> {

View File

@ -1,32 +1,109 @@
package com.volmit.iris.engine.framework;
import com.volmit.iris.Iris;
import com.volmit.iris.engine.object.InventorySlotType;
import com.volmit.iris.engine.object.IrisLootTable;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.scheduling.J;
import lombok.Getter;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.event.world.LootGenerateEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.loot.LootContext;
import org.bukkit.loot.LootTable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Random;
@Getter
public class IrisLootEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private static final LootTable EMPTY = new LootTable() {
@NotNull
@Override
public NamespacedKey getKey() {
return new NamespacedKey(Iris.instance, "empty");
}
@NotNull
@Override
public Collection<ItemStack> populateLoot(@Nullable Random random, @NotNull LootContext context) {
return List.of();
}
@Override
public void fillInventory(@NotNull Inventory inventory, @Nullable Random random, @NotNull LootContext context) {
}
};
private final Engine engine;
private final Block block;
private final InventorySlotType slot;
private final KList<IrisLootTable> tables;
/**
* Constructor for IrisLootEvent with mode selection.
*
* @param engine The engine instance.
* @param block The block associated with the event.
* @param slot The inventory slot type.
* @param tables The list of IrisLootTables. (mutable*)
*/
public IrisLootEvent(Engine engine, Block block, InventorySlotType slot, KList<IrisLootTable> tables) {
this.engine = engine;
this.block = block;
this.slot = slot;
this.tables = tables;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
/**
* Required method to get the HandlerList for this event.
*
* @return The HandlerList.
*/
public static HandlerList getHandlerList() {
return handlers;
}
}
/**
* Triggers the corresponding Bukkit loot event.
* This method integrates your custom IrisLootTables with Bukkit's LootGenerateEvent,
* allowing other plugins to modify or cancel the loot generation.
*
* @return true when the event was canceled
*/
public static boolean callLootEvent(KList<ItemStack> loot, Inventory inv, World world, int x, int y, int z) {
InventoryHolder holder = inv.getHolder();
Location loc = new Location(world, x, y, z);
if (holder == null) {
holder = new InventoryHolder() {
@NotNull
@Override
public Inventory getInventory() {
return inv;
}
};
}
LootContext context = new LootContext.Builder(loc).build();
LootGenerateEvent event = new LootGenerateEvent(world, null, holder, EMPTY, context, loot, true);
if (!Bukkit.isPrimaryThread())
J.sfut(() -> Bukkit.getPluginManager().callEvent(event)).join();
else Bukkit.getPluginManager().callEvent(event);
return !event.isCancelled();
}
}

View File

@ -23,6 +23,7 @@ import com.volmit.iris.engine.object.IrisLootReference;
import com.volmit.iris.engine.object.IrisLootTable;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.math.RNG;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.inventory.Inventory;
@ -33,5 +34,5 @@ public interface LootProvider {
KList<IrisLootTable> getLootTables(RNG rng, Block b);
void addItems(boolean debug, Inventory inv, RNG rng, KList<IrisLootTable> tables, InventorySlotType slot, int x, int y, int z, int mgf);
void addItems(boolean debug, Inventory inv, RNG rng, KList<IrisLootTable> tables, InventorySlotType slot, World world, int x, int y, int z, int mgf);
}

View File

@ -20,8 +20,6 @@ import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.TileState;
import org.bukkit.block.data.BlockData;
import org.bukkit.inventory.InventoryHolder;
@ -74,7 +72,7 @@ public class WorldObjectPlacer implements IObjectPlacer {
if (tables.isEmpty())
return;
InventoryHolder m = (InventoryHolder) block.getState();
engine.addItems(false, m.getInventory(), rx, tables, slot, x, y, z, 15);
engine.addItems(false, m.getInventory(), rx, tables, slot, world, x, y, z, 15);
} catch (Throwable e) {
Iris.reportError(e);
}

View File

@ -271,7 +271,7 @@ public class IrisEntity extends IrisRegistrant {
for (String fi : getLoot().getTables()) {
IrisLootTable i = gen.getData().getLootLoader().load(fi);
items.addAll(i.getLoot(gen.isStudio(), rng.nextParallelRNG(345911), InventorySlotType.STORAGE, finalAt.getBlockX(), finalAt.getBlockY(), finalAt.getBlockZ()));
items.addAll(i.getLoot(gen.isStudio(), rng.nextParallelRNG(345911), InventorySlotType.STORAGE, finalAt.getWorld(), finalAt.getBlockX(), finalAt.getBlockY(), finalAt.getBlockZ()));
}
return items;

View File

@ -33,6 +33,7 @@ import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.inventory.ItemStack;
@Accessors(chain = true)
@ -67,7 +68,7 @@ public class IrisLootTable extends IrisRegistrant {
@ArrayType(min = 1, type = IrisLoot.class)
private KList<IrisLoot> loot = new KList<>();
public KList<ItemStack> getLoot(boolean debug, RNG rng, InventorySlotType slot, int x, int y, int z) {
public KList<ItemStack> getLoot(boolean debug, RNG rng, InventorySlotType slot, World world, int x, int y, int z) {
KList<ItemStack> lootf = new KList<>();
int m = 0;

View File

@ -34,9 +34,14 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.TreeType;
import org.bukkit.block.data.BlockData;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
@Snippet("object-placer")
@EqualsAndHashCode()
@ -125,6 +130,8 @@ public class IrisObjectPlacement {
@ArrayType(min = 1, type = IrisObjectLoot.class)
@Desc("The loot tables to apply to these objects")
private KList<IrisObjectLoot> loot = new KList<>();
@Desc("The vanilla loot tables to apply to these objects")
private KList<IrisObjectVanillaLoot> vanillaLoot = new KList<>();
@Desc("Whether the given loot tables override any and all other loot tables available in the dimension, region or biome.")
private boolean overrideGlobalLoot = false;
@Desc("This object / these objects override the following trees when they grow...")
@ -140,7 +147,8 @@ public class IrisObjectPlacement {
private KList<String> forbiddenCollisions = new KList<>();
@Desc("Ignore any placement restrictions for this object")
private boolean forcePlace = false;
private transient AtomicCache<TableCache> cache = new AtomicCache<>();
private transient AtomicCache<TableCache<IrisLootTable>> cache = new AtomicCache<>();
private transient AtomicCache<TableCache<IrisVanillaLootTable>> vanillaCache = new AtomicCache<>();
public IrisObjectPlacement toPlacement(String... place) {
IrisObjectPlacement p = new IrisObjectPlacement();
@ -211,48 +219,62 @@ public class IrisObjectPlacement {
return (int) Math.round(densityStyle.get(rng, x, z, data));
}
private TableCache getCache(IrisData manager) {
return cache.aquire(() -> {
TableCache tc = new TableCache();
private TableCache<IrisLootTable> getCache(IrisData manager) {
return cache.aquire(() -> getCache(manager, manager.getLootLoader()::load));
}
for (IrisObjectLoot loot : getLoot()) {
if (loot == null)
continue;
IrisLootTable table = manager.getLootLoader().load(loot.getName());
if (table == null) {
Iris.warn("Couldn't find loot table " + loot.getName());
continue;
private TableCache<IrisVanillaLootTable> getVanillaCache(IrisData manager) {
return vanillaCache.aquire(() -> getCache(manager, IrisObjectPlacement::getVanillaTable));
}
private <T> TableCache<T> getCache(IrisData manager, Function<String, T> loader) {
TableCache<T> tc = new TableCache<>();
for (IrisObjectLoot loot : getLoot()) {
if (loot == null)
continue;
T table = loader.apply(loot.getName());
if (table == null) {
Iris.warn("Couldn't find loot table " + loot.getName());
continue;
}
if (loot.getFilter().isEmpty()) //Table applies to all containers
{
tc.global.put(table, loot.getWeight());
} else if (!loot.isExact()) //Table is meant to be by type
{
for (BlockData filterData : loot.getFilter(manager)) {
if (!tc.basic.containsKey(filterData.getMaterial())) {
tc.basic.put(filterData.getMaterial(), new WeightedRandom<>());
}
tc.basic.get(filterData.getMaterial()).put(table, loot.getWeight());
}
if (loot.getFilter().isEmpty()) //Table applies to all containers
{
tc.global.put(table, loot.getWeight());
} else if (!loot.isExact()) //Table is meant to be by type
{
for (BlockData filterData : loot.getFilter(manager)) {
if (!tc.basic.containsKey(filterData.getMaterial())) {
tc.basic.put(filterData.getMaterial(), new WeightedRandom<>());
}
tc.basic.get(filterData.getMaterial()).put(table, loot.getWeight());
} else //Filter is exact
{
for (BlockData filterData : loot.getFilter(manager)) {
if (!tc.exact.containsKey(filterData.getMaterial())) {
tc.exact.put(filterData.getMaterial(), new KMap<>());
}
} else //Filter is exact
{
for (BlockData filterData : loot.getFilter(manager)) {
if (!tc.exact.containsKey(filterData.getMaterial())) {
tc.exact.put(filterData.getMaterial(), new KMap<>());
}
if (!tc.exact.get(filterData.getMaterial()).containsKey(filterData)) {
tc.exact.get(filterData.getMaterial()).put(filterData, new WeightedRandom<>());
}
tc.exact.get(filterData.getMaterial()).get(filterData).put(table, loot.getWeight());
if (!tc.exact.get(filterData.getMaterial()).containsKey(filterData)) {
tc.exact.get(filterData.getMaterial()).put(filterData, new WeightedRandom<>());
}
tc.exact.get(filterData.getMaterial()).get(filterData).put(table, loot.getWeight());
}
}
return tc;
});
}
return tc;
}
@Nullable
private static IrisVanillaLootTable getVanillaTable(String name) {
NamespacedKey key = NamespacedKey.fromString(name);
if (key == null)
return null;
return new IrisVanillaLootTable(Bukkit.getLootTable(key));
}
/**
@ -263,10 +285,16 @@ public class IrisObjectPlacement {
* @return The loot table it should use.
*/
public IrisLootTable getTable(BlockData data, IrisData dataManager) {
TableCache cache = getCache(dataManager);
IrisLootTable table = pickTable(data, getVanillaCache(dataManager));
if (table == null) {
table = pickTable(data, getCache(dataManager));
}
return table;
}
private <T> T pickTable(BlockData data, TableCache<T> cache) {
if (B.isStorageChest(data)) {
IrisLootTable picked = null;
T picked = null;
if (cache.exact.containsKey(data.getMaterial()) && cache.exact.get(data.getMaterial()).containsKey(data)) {
picked = cache.exact.get(data.getMaterial()).get(data).pullRandom();
} else if (cache.basic.containsKey(data.getMaterial())) {
@ -281,9 +309,9 @@ public class IrisObjectPlacement {
return null;
}
private static class TableCache {
final transient WeightedRandom<IrisLootTable> global = new WeightedRandom<>();
final transient KMap<Material, WeightedRandom<IrisLootTable>> basic = new KMap<>();
final transient KMap<Material, KMap<BlockData, WeightedRandom<IrisLootTable>>> exact = new KMap<>();
private static class TableCache<T> {
final transient WeightedRandom<T> global = new WeightedRandom<>();
final transient KMap<Material, WeightedRandom<T>> basic = new KMap<>();
final transient KMap<Material, KMap<BlockData, WeightedRandom<T>>> exact = new KMap<>();
}
}

View File

@ -0,0 +1,48 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.object.annotations.*;
import com.volmit.iris.util.collection.KList;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.bukkit.block.data.BlockData;
@Snippet("object-vanilla-loot")
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@Desc("Represents vanilla loot within this object or jigsaw piece")
@Data
public class IrisObjectVanillaLoot {
private final transient AtomicCache<KList<BlockData>> filterCache = new AtomicCache<>();
@ArrayType(min = 1, type = IrisBlockData.class)
@Desc("The list of blocks this loot table should apply to")
private KList<IrisBlockData> filter = new KList<>();
@Desc("Exactly match the block data or not")
private boolean exact = false;
@Desc("The vanilla loot table key")
@Required
private String name;
@Desc("The weight of this loot table being chosen")
private int weight = 1;
public KList<BlockData> getFilter(IrisData rdata) {
return filterCache.aquire(() ->
{
KList<BlockData> b = new KList<>();
for (IrisBlockData i : filter) {
BlockData bx = i.getBlockData(rdata);
if (bx != null) {
b.add(bx);
}
}
return b;
});
}
}

View File

@ -0,0 +1,80 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.math.RNG;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.inventory.ItemStack;
import org.bukkit.loot.LootContext;
import org.bukkit.loot.LootTable;
import java.io.File;
@Data
@EqualsAndHashCode(callSuper = false)
public class IrisVanillaLootTable extends IrisLootTable {
private final LootTable lootTable;
@Override
public String getName() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public int getRarity() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public int getMaxPicked() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public int getMinPicked() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public int getMaxTries() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public KList<IrisLoot> getLoot() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public KList<ItemStack> getLoot(boolean debug, RNG rng, InventorySlotType slot, World world, int x, int y, int z) {
return new KList<>(lootTable.populateLoot(rng, new LootContext.Builder(new Location(world, x, y, z)).build()));
}
@Override
public String getFolderName() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public String getTypeName() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public File getLoadFile() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public IrisData getLoader() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public KList<String> getPreprocessors() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
}