From 2500fa54e4a832b36c6908f409a5d01768d360de Mon Sep 17 00:00:00 2001 From: Daniel Mills Date: Sat, 22 Aug 2020 09:51:54 -0400 Subject: [PATCH] Parallax 2 --- .../volmit/iris/command/CommandIrisLoot.java | 8 +- .../iris/gen/ParallaxChunkGenerator.java | 60 +---- .../volmit/iris/gen/atomics/AtomicSliver.java | 51 +++-- .../iris/gen/atomics/AtomicSliverMap.java | 78 ++++++- .../volmit/iris/gen/layer/GenLayerUpdate.java | 98 +++++++- .../iris/gen/parallax/ParallaxChunk.java | 157 +++++++++++++ .../iris/gen/parallax/ParallaxRegion.java | 128 +++++++++++ .../iris/gen/parallax/ParallaxSection.java | 103 +++++++++ .../iris/gen/parallax/ParallaxWorld.java | 121 ++++++++++ .../volmit/iris/object/IrisLootReference.java | 6 + .../com/volmit/iris/object/IrisLootTable.java | 14 +- .../com/volmit/iris/object/IrisObject.java | 36 +-- .../iris/object/IrisStructurePlacement.java | 2 +- .../volmit/iris/object/IrisStructureTile.java | 1 - .../com/volmit/iris/util/DataPalette.java | 125 +++++++++++ .../com/volmit/iris/util/NibbleArray.java | 212 ++++++++++++++++++ .../java/com/volmit/iris/util/Writable.java | 12 + 17 files changed, 1107 insertions(+), 105 deletions(-) create mode 100644 src/main/java/com/volmit/iris/gen/parallax/ParallaxChunk.java create mode 100644 src/main/java/com/volmit/iris/gen/parallax/ParallaxRegion.java create mode 100644 src/main/java/com/volmit/iris/gen/parallax/ParallaxSection.java create mode 100644 src/main/java/com/volmit/iris/gen/parallax/ParallaxWorld.java create mode 100644 src/main/java/com/volmit/iris/util/DataPalette.java create mode 100644 src/main/java/com/volmit/iris/util/NibbleArray.java create mode 100644 src/main/java/com/volmit/iris/util/Writable.java diff --git a/src/main/java/com/volmit/iris/command/CommandIrisLoot.java b/src/main/java/com/volmit/iris/command/CommandIrisLoot.java index a018f056e..32a6acec9 100644 --- a/src/main/java/com/volmit/iris/command/CommandIrisLoot.java +++ b/src/main/java/com/volmit/iris/command/CommandIrisLoot.java @@ -8,7 +8,7 @@ import org.bukkit.inventory.Inventory; import com.volmit.iris.Iris; import com.volmit.iris.object.InventorySlotType; import com.volmit.iris.object.IrisLootTable; -import com.volmit.iris.util.KSet; +import com.volmit.iris.util.KList; import com.volmit.iris.util.MortarCommand; import com.volmit.iris.util.MortarSender; import com.volmit.iris.util.O; @@ -30,9 +30,9 @@ public class CommandIrisLoot extends MortarCommand if(sender.isPlayer()) { Player p = sender.player(); - KSet tables = Iris.proj.getCurrentProject().getGlUpdate().getLootTables(p.getLocation().getBlock()); + KList tables = Iris.proj.getCurrentProject().getGlUpdate().getLootTables(RNG.r, p.getLocation().getBlock()); Inventory inv = Bukkit.createInventory(null, 27 * 2); - Iris.proj.getCurrentProject().getGlUpdate().addItems(true, inv, RNG.r, tables, InventorySlotType.STORAGE, p.getLocation().getBlockX(), p.getLocation().getBlockY(), p.getLocation().getBlockZ()); + Iris.proj.getCurrentProject().getGlUpdate().addItems(true, inv, RNG.r, tables, InventorySlotType.STORAGE, p.getLocation().getBlockX(), p.getLocation().getBlockY(), p.getLocation().getBlockZ(), 1); p.openInventory(inv); for(IrisLootTable i : tables) @@ -74,7 +74,7 @@ public class CommandIrisLoot extends MortarCommand inv.clear(); } - Iris.proj.getCurrentProject().getGlUpdate().addItems(true, inv, new RNG(RNG.r.imax()), tables, InventorySlotType.STORAGE, p.getLocation().getBlockX(), p.getLocation().getBlockY(), p.getLocation().getBlockZ()); + Iris.proj.getCurrentProject().getGlUpdate().addItems(true, inv, new RNG(RNG.r.imax()), tables, InventorySlotType.STORAGE, p.getLocation().getBlockX(), p.getLocation().getBlockY(), p.getLocation().getBlockZ(), 1); }, 0, fast ? 5 : 35)); return true; diff --git a/src/main/java/com/volmit/iris/gen/ParallaxChunkGenerator.java b/src/main/java/com/volmit/iris/gen/ParallaxChunkGenerator.java index dc36b244b..3d8338674 100644 --- a/src/main/java/com/volmit/iris/gen/ParallaxChunkGenerator.java +++ b/src/main/java/com/volmit/iris/gen/ParallaxChunkGenerator.java @@ -17,7 +17,6 @@ import com.volmit.iris.object.IrisBiomeMutation; import com.volmit.iris.object.IrisObjectPlacement; import com.volmit.iris.object.IrisRegion; import com.volmit.iris.object.IrisStructurePlacement; -import com.volmit.iris.object.TileResult; import com.volmit.iris.util.BiomeMap; import com.volmit.iris.util.CaveResult; import com.volmit.iris.util.ChunkPosition; @@ -27,6 +26,7 @@ import com.volmit.iris.util.IrisLock; import com.volmit.iris.util.IrisStructureResult; import com.volmit.iris.util.KList; import com.volmit.iris.util.KMap; +import com.volmit.iris.util.NBTInputStream; import com.volmit.iris.util.NastyRunnable; import com.volmit.iris.util.PrecisionStopwatch; import com.volmit.iris.util.RNG; @@ -106,7 +106,7 @@ public abstract class ParallaxChunkGenerator extends TerrainChunkGenerator imple return h; } - +NBTInputStream return h; } @@ -218,61 +218,7 @@ public abstract class ParallaxChunkGenerator extends TerrainChunkGenerator imple public IrisStructureResult getStructure(int x, int y, int z) { - IrisBiome b = sampleTrueBiome(x, z).getBiome(); - IrisRegion r = sampleRegion(x, z); - RNG ro = getMasterRandom().nextParallelRNG(496888 + (x >> 4) + (z >> 4)); - int h = (int) Math.round(getTerrainHeight(x, z)); - KList p = new KList<>(); - - for(IrisStructurePlacement i : r.getStructures()) - { - if(i.getHeight() > -1) - { - if(y >= i.getHeight() && y <= i.getHeight() + (i.getStructure(this).getGridHeight() * i.getStructure(this).getMaxLayers())) - { - p.add(i); - } - } - - else if(y >= h && y <= i.getStructure(this).getGridHeight() + h) - { - p.add(i); - } - } - - for(IrisStructurePlacement i : b.getStructures()) - { - if(i.getHeight() > -1) - { - if(y >= i.getHeight() && y <= i.getHeight() + (i.getStructure(this).getGridHeight() * i.getStructure(this).getMaxLayers())) - { - p.add(i); - } - } - - else if(y >= h && y <= i.getStructure(this).getGridHeight() + h) - { - p.add(i); - } - } - - for(IrisStructurePlacement i : p) - { - if(!i.hasStructure(ro, x, y, z)) - { - continue; - } - - int hv = (i.getHeight() == -1 ? 0 : i.getHeight()) + (Math.floorDiv(y, i.getStructure(this).getGridHeight()) * i.getStructure(this).getGridHeight()); - TileResult tile = i.getStructure(this).getTile(ro, Math.floorDiv(i.gridSize(this), x) * i.gridSize(this), hv, Math.floorDiv(i.gridSize(this), z) * i.gridSize(this)); - - if(tile != null && tile.getTile() != null) - { - return new IrisStructureResult(tile.getTile(), i.getStructure(this)); - } - } - - return null; + return getParallaxChunk(x >> 4, z >> 4).getStructure(this, y); } protected void onGenerateParallax(RNG random, int x, int z) diff --git a/src/main/java/com/volmit/iris/gen/atomics/AtomicSliver.java b/src/main/java/com/volmit/iris/gen/atomics/AtomicSliver.java index 9880deae4..35d7e228b 100644 --- a/src/main/java/com/volmit/iris/gen/atomics/AtomicSliver.java +++ b/src/main/java/com/volmit/iris/gen/atomics/AtomicSliver.java @@ -25,24 +25,24 @@ import lombok.Data; public class AtomicSliver { public static final BlockData AIR = B.getBlockData("AIR"); + private transient KMap truebiome; + private transient KMap biome; + private transient IrisLock lock = new IrisLock("Sliver"); + private transient int highestBiome = 0; + private transient long last = M.ms(); + private transient int x; + private transient int z; + private transient boolean modified = false; private KMap block; - private KMap truebiome; - private KMap biome; - private KSet update; - private IrisLock lock = new IrisLock("Sliver"); + private KSet blockUpdates; private int highestBlock = 0; - private int highestBiome = 0; - private long last = M.ms(); - private int x; - private int z; - boolean modified = false; public AtomicSliver(int x, int z) { lock.setDisabled(true); this.x = x; this.z = z; - update = new KSet<>(); + blockUpdates = new KSet<>(); this.block = new KMap<>(); this.biome = new KMap<>(); this.truebiome = new KMap<>(); @@ -55,17 +55,17 @@ public class AtomicSliver public KSet getUpdatables() { - return update; + return blockUpdates; } public void update(int y) { - update.add(y); + blockUpdates.add(y); } public void dontUpdate(int y) { - update.remove(y); + blockUpdates.remove(y); } public BlockData get(int h) @@ -81,6 +81,19 @@ public class AtomicSliver return b; } + public BlockData getOrNull(int h) + { + BlockData b = block.get(h); + last = M.ms(); + + if(b.getMaterial().equals(Material.AIR)) + { + return null; + } + + return b; + } + public void set(int h, BlockData d) { setSilently(h, d); @@ -190,6 +203,8 @@ public class AtomicSliver { lock.lock(); this.block = new KMap(); + + // Block Palette int h = din.readByte() - Byte.MIN_VALUE; int p = din.readByte() - Byte.MIN_VALUE; int u = din.readByte() - Byte.MIN_VALUE; @@ -202,11 +217,13 @@ public class AtomicSliver palette.add(B.getBlockData(din.readUTF())); } + // Blocks for(int i = 0; i <= h; i++) { block.put(i, palette.get(din.readByte() - Byte.MIN_VALUE).clone()); } + // Updates for(int i = 0; i <= u; i++) { update(din.readByte() - Byte.MIN_VALUE); @@ -220,6 +237,8 @@ public class AtomicSliver { lock.lock(); dos.writeByte(highestBlock + Byte.MIN_VALUE); + + // Block Palette KList palette = new KList<>(); for(int i = 0; i <= highestBlock; i++) @@ -234,13 +253,14 @@ public class AtomicSliver } dos.writeByte(palette.size() + Byte.MIN_VALUE); - dos.writeByte(update.size() + Byte.MIN_VALUE); + dos.writeByte(blockUpdates.size() + Byte.MIN_VALUE); for(String i : palette) { dos.writeUTF(i); } + // Blocks for(int i = 0; i <= highestBlock; i++) { BlockData dat = block.get(i); @@ -248,6 +268,7 @@ public class AtomicSliver dos.writeByte(palette.indexOf(d) + Byte.MIN_VALUE); } + // Updates for(Integer i : getUpdatables()) { dos.writeByte(i + Byte.MIN_VALUE); @@ -296,6 +317,6 @@ public class AtomicSliver public void inject(KSet updatables) { - update.addAll(updatables); + blockUpdates.addAll(updatables); } } diff --git a/src/main/java/com/volmit/iris/gen/atomics/AtomicSliverMap.java b/src/main/java/com/volmit/iris/gen/atomics/AtomicSliverMap.java index 7ee88ee3f..f128b29ef 100644 --- a/src/main/java/com/volmit/iris/gen/atomics/AtomicSliverMap.java +++ b/src/main/java/com/volmit/iris/gen/atomics/AtomicSliverMap.java @@ -9,7 +9,13 @@ import java.io.OutputStream; import org.bukkit.generator.ChunkGenerator.BiomeGrid; import org.bukkit.generator.ChunkGenerator.ChunkData; +import com.volmit.iris.gen.DimensionChunkGenerator; +import com.volmit.iris.object.IrisStructure; +import com.volmit.iris.object.IrisStructureTile; import com.volmit.iris.util.HeightMap; +import com.volmit.iris.util.IrisStructureResult; +import com.volmit.iris.util.KList; +import com.volmit.iris.util.KMap; import lombok.Data; @@ -17,11 +23,13 @@ import lombok.Data; public class AtomicSliverMap { private final AtomicSliver[] slivers; + private KMap structures; private boolean parallaxGenerated; private boolean worldGenerated; public AtomicSliverMap() { + structures = new KMap<>(); parallaxGenerated = false; worldGenerated = false; slivers = new AtomicSliver[256]; @@ -43,6 +51,32 @@ public class AtomicSliverMap } } + public void setStructure(int y, IrisStructure s, IrisStructureTile t) + { + structures.put(y, s.getLoadKey() + "." + s.getTiles().indexOf(t)); + } + + public IrisStructureResult getStructure(DimensionChunkGenerator g, int y) + { + String v = structures.get(y); + + if(v == null) + { + return null; + } + + String[] a = v.split("\\Q.\\E"); + + IrisStructure s = g.getData().getStructureLoader().load(a[0]); + + if(s == null) + { + return null; + } + + return new IrisStructureResult(s.getTiles().get(Integer.valueOf(a[1])), s); + } + public void write(OutputStream out) throws IOException { DataOutputStream dos = new DataOutputStream(out); @@ -53,6 +87,33 @@ public class AtomicSliverMap slivers[i].write(dos); } + KList structurePalette = new KList<>(); + + for(Integer i : structures.k()) + { + String struct = structures.get(i); + + if(!structurePalette.contains(struct)) + { + structurePalette.add(struct); + } + } + + dos.writeByte(structurePalette.size() + Byte.MIN_VALUE); + + for(String i : structurePalette) + { + dos.writeUTF(i); + } + + dos.writeByte(structures.size() + Byte.MIN_VALUE); + + for(Integer i : structures.k()) + { + dos.writeByte(i + Byte.MIN_VALUE); + dos.writeByte(structurePalette.indexOf(structures.get(i)) + Byte.MIN_VALUE); + } + dos.flush(); } @@ -74,6 +135,21 @@ public class AtomicSliverMap } } + + int spc = din.readByte() - Byte.MIN_VALUE; + KList spal = new KList<>(); + for(int i = 0; i < spc; i++) + { + spal.add(din.readUTF()); + } + + int smc = din.readByte() - Byte.MIN_VALUE; + structures.clear(); + + for(int i = 0; i < smc; i++) + { + structures.put(din.readByte() - Byte.MIN_VALUE, spal.get(din.readByte() - Byte.MIN_VALUE)); + } } public AtomicSliver getSliver(int x, int z) @@ -122,7 +198,7 @@ public class AtomicSliverMap for(int j = 0; j < 16; j++) { getSliver(i, j).inject(map.getSliver(i, j).getUpdatables()); - } + } } } } diff --git a/src/main/java/com/volmit/iris/gen/layer/GenLayerUpdate.java b/src/main/java/com/volmit/iris/gen/layer/GenLayerUpdate.java index 750b323e9..08719e29d 100644 --- a/src/main/java/com/volmit/iris/gen/layer/GenLayerUpdate.java +++ b/src/main/java/com/volmit/iris/gen/layer/GenLayerUpdate.java @@ -24,7 +24,6 @@ import com.volmit.iris.object.LootMode; import com.volmit.iris.util.B; import com.volmit.iris.util.IrisStructureResult; import com.volmit.iris.util.KList; -import com.volmit.iris.util.KSet; import com.volmit.iris.util.PrecisionStopwatch; import com.volmit.iris.util.RNG; @@ -81,7 +80,7 @@ public class GenLayerUpdate extends BlockPopulator } } - public void injectTables(KSet list, IrisLootReference r) + public void injectTables(KList list, IrisLootReference r) { if(r.getMode().equals(LootMode.CLEAR) || r.getMode().equals(LootMode.REPLACE)) { @@ -91,15 +90,16 @@ public class GenLayerUpdate extends BlockPopulator list.addAll(r.getLootTables(gen)); } - public KSet getLootTables(Block b) + public KList getLootTables(RNG rng, Block b) { int rx = b.getX(); int rz = b.getZ(); IrisRegion region = gen.sampleRegion(rx, rz); IrisBiome biomeSurface = gen.sampleTrueBiome(rx, rz).getBiome(); IrisBiome biomeUnder = gen.sampleTrueBiome(rx, b.getY(), rz).getBiome(); - KSet tables = new KSet<>(); + KList tables = new KList(); IrisStructureResult structure = gen.getStructure(rx, b.getY(), rz); + double multiplier = 1D * gen.getDimension().getLoot().getMultiplier() * region.getLoot().getMultiplier() * biomeSurface.getLoot().getMultiplier() * biomeUnder.getLoot().getMultiplier(); injectTables(tables, gen.getDimension().getLoot()); injectTables(tables, region.getLoot()); injectTables(tables, biomeSurface.getLoot()); @@ -109,20 +109,38 @@ public class GenLayerUpdate extends BlockPopulator { injectTables(tables, structure.getStructure().getLoot()); injectTables(tables, structure.getTile().getLoot()); + multiplier *= structure.getStructure().getLoot().getMultiplier() * structure.getTile().getLoot().getMultiplier(); + } + + if(tables.isNotEmpty()) + { + int target = (int) Math.round(tables.size() * multiplier); + + while(tables.size() < target && tables.isNotEmpty()) + { + tables.add(tables.get(rng.i(tables.size() - 1))); + } + + while(tables.size() > target && tables.isNotEmpty()) + { + tables.remove(rng.i(tables.size() - 1)); + } } return tables; } - public void addItems(boolean debug, Inventory inv, RNG rng, KSet tables, InventorySlotType slot, int x, int y, int z) + public void addItems(boolean debug, Inventory inv, RNG rng, KList tables, InventorySlotType slot, int x, int y, int z, int mgf) { KList items = new KList<>(); for(int t = 0; t < gen.getDimension().getLootTries(); t++) { + int b = 4; for(IrisLootTable i : tables) { - items.addAll(i.getLoot(debug, rng.nextParallelRNG(345911 * -t), slot, x, y, z)); + b++; + items.addAll(i.getLoot(debug, rng.nextParallelRNG(345911 * -t), slot, x, y, z, t + b + b, mgf + b)); } for(ItemStack i : items) @@ -135,6 +153,8 @@ public class GenLayerUpdate extends BlockPopulator break; } } + + scramble(inv, rng); } public void updateStorage(Block b, BlockData data, int rx, int rz, RNG rng) @@ -148,12 +168,12 @@ public class GenLayerUpdate extends BlockPopulator if(slot != null) { - KSet tables = getLootTables(b); + KList tables = getLootTables(rng.nextParallelRNG(4568111), b); try { InventoryHolder m = (InventoryHolder) b.getState(); - addItems(false, m.getInventory(), rng, tables, slot, rx, b.getY(), rz); + addItems(false, m.getInventory(), rng, tables, slot, rx, b.getY(), rz, 15); } catch(Throwable e) @@ -164,6 +184,68 @@ public class GenLayerUpdate extends BlockPopulator } } + private void scramble(Inventory inventory, RNG rng) + { + KList v = new KList<>(); + + for(ItemStack i : inventory.getContents()) + { + if(i == null) + { + continue; + } + + v.add(i); + } + + inventory.clear(); + int sz = inventory.getSize(); + int tr = 5; + + while(v.isNotEmpty()) + { + int slot = rng.i(0, sz - 1); + + if(inventory.getItem(slot) == null) + { + tr = tr < 5 ? tr + 1 : tr; + int pick = rng.i(0, v.size() - 1); + ItemStack g = v.get(pick); + + if(g.getAmount() == 1) + { + v.remove(pick); + inventory.setItem(pick, g); + } + + else + { + int portion = rng.i(1, g.getAmount() - 1); + ItemStack port = g.clone(); + port.setAmount(portion); + g.setAmount(g.getAmount() - portion); + v.add(g); + inventory.setItem(slot, port); + } + } + + else + { + tr--; + } + + if(tr <= 0) + { + break; + } + } + + for(ItemStack i : v) + { + inventory.addItem(i); + } + } + public void updateLight(Block b, BlockData data) { b.setType(Material.AIR, false); diff --git a/src/main/java/com/volmit/iris/gen/parallax/ParallaxChunk.java b/src/main/java/com/volmit/iris/gen/parallax/ParallaxChunk.java new file mode 100644 index 000000000..c74c4446f --- /dev/null +++ b/src/main/java/com/volmit/iris/gen/parallax/ParallaxChunk.java @@ -0,0 +1,157 @@ +package com.volmit.iris.gen.parallax; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.bukkit.Material; +import org.bukkit.block.data.BlockData; +import org.bukkit.generator.ChunkGenerator.ChunkData; + +import com.volmit.iris.Iris; +import com.volmit.iris.gen.atomics.AtomicSliver; +import com.volmit.iris.util.Writable; + +public class ParallaxChunk implements Writable +{ + private static final ParallaxSection EMPTY = new ParallaxSection(); + private final ParallaxSection[] sections; + private boolean parallaxGenerated; + private boolean worldGenerated; + + public ParallaxChunk(DataInputStream in) throws IOException + { + this(); + read(in); + } + + public ParallaxChunk() + { + parallaxGenerated = false; + worldGenerated = false; + sections = new ParallaxSection[16]; + } + + public boolean isParallaxGenerated() + { + return parallaxGenerated; + } + + public void setParallaxGenerated(boolean parallaxGenerated) + { + this.parallaxGenerated = parallaxGenerated; + } + + public boolean isWorldGenerated() + { + return worldGenerated; + } + + public void setWorldGenerated(boolean worldGenerated) + { + this.worldGenerated = worldGenerated; + } + + public void export(ChunkData d) + { + for(ParallaxSection i : sections) + { + if(i != null) + { + for(int x = 0; x < 16; x++) + { + for(int y = 0; y < 16; y++) + { + for(int z = 0; z < 16; z++) + { + BlockData b = get(x, y, z); + + if(b == null || b.getMaterial().equals(Material.AIR)) + { + continue; + } + + d.setBlock(x, y, z, b); + } + } + } + } + } + } + + public void injectUpdates(AtomicSliver sliver, int x, int z) + { + for(Integer i : sliver.getUpdatables()) + { + if(i > 255 || i < 0) + { + Iris.warn("Block Update out of bounds: " + i); + } + + getSection(i >> 4, true).update(x, i, z); + } + } + + @Override + public void write(DataOutputStream o) throws IOException + { + o.writeBoolean(isParallaxGenerated()); + o.writeBoolean(isWorldGenerated()); + + for(int i = 15; i > 0; i--) + { + ParallaxSection c = sections[i]; + + if(c != null) + { + o.writeBoolean(true); + c.write(o); + } + + else + { + o.writeBoolean(false); + } + } + } + + @Override + public void read(DataInputStream i) throws IOException + { + setParallaxGenerated(i.readBoolean()); + setWorldGenerated(i.readBoolean()); + + for(int iv = 15; iv > 0; iv--) + { + if(i.readBoolean()) + { + sections[iv] = new ParallaxSection(i); + } + } + } + + public BlockData get(int x, int y, int z) + { + return getSection(y >> 4, false).getBlock(x, y & 15, z); + } + + public void set(int x, int y, int z, BlockData d) + { + getSection(y >> 4, true).setBlock(x, y & 15, z, d); + } + + private final ParallaxSection getSection(int y, boolean create) + { + if(sections[y] == null) + { + if(create) + { + sections[y] = new ParallaxSection(); + } + + return EMPTY; + } + + return sections[y]; + } +} diff --git a/src/main/java/com/volmit/iris/gen/parallax/ParallaxRegion.java b/src/main/java/com/volmit/iris/gen/parallax/ParallaxRegion.java new file mode 100644 index 000000000..eb6e45b8c --- /dev/null +++ b/src/main/java/com/volmit/iris/gen/parallax/ParallaxRegion.java @@ -0,0 +1,128 @@ +package com.volmit.iris.gen.parallax; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.zip.GZIPInputStream; + +import org.bukkit.block.data.BlockData; + +import com.volmit.iris.IrisSettings; +import com.volmit.iris.util.CustomOutputStream; +import com.volmit.iris.util.M; +import com.volmit.iris.util.Writable; + +public class ParallaxRegion implements Writable +{ + private static final ParallaxChunk EMPTY = new ParallaxChunk(); + private ParallaxChunk[] chunks; + private transient long last; + + public ParallaxRegion(File i) throws IOException + { + this(); + + if(i.exists()) + { + FileInputStream in = new FileInputStream(i); + GZIPInputStream vin = new GZIPInputStream(in); + DataInputStream min = new DataInputStream(vin); + read(min); + min.close(); + } + } + + public ParallaxRegion(DataInputStream i) throws IOException + { + this(); + read(i); + } + + public ParallaxRegion() + { + last = M.ms(); + chunks = new ParallaxChunk[1024]; + } + + @Override + public void write(DataOutputStream o) throws IOException + { + int c = 0; + + for(ParallaxChunk i : chunks) + { + if(i != null) + { + c++; + } + } + + o.writeShort(c); + + for(int i = 0; i < 1024; i++) + { + ParallaxChunk ch = chunks[i]; + if(ch != null) + { + ch.write(o); + } + } + } + + public void write(File file) throws IOException + { + file.getParentFile().mkdirs(); + FileOutputStream o = new FileOutputStream(file); + CustomOutputStream g = new CustomOutputStream(o, IrisSettings.get().parallaxCompressionLevel); + DataOutputStream d = new DataOutputStream(g); + write(d); + d.close(); + } + + @Override + public void read(DataInputStream i) throws IOException + { + int v = i.readShort(); + + for(int b = 0; b < v; b++) + { + chunks[b] = new ParallaxChunk(i); + } + } + + public boolean isOlderThan(long time) + { + return M.ms() - time > last; + } + + public void set(int x, int y, int z, BlockData d) + { + getChunk(x >> 4, z >> 4, true).set(x & 15, y, z & 15, d); + } + + public BlockData get(int x, int y, int z) + { + return getChunk(x >> 4, z >> 4, false).get(x & 15, y, z & 15); + } + + private final ParallaxChunk getChunk(int x, int z, boolean create) + { + last = M.ms(); + int v = (z << 5) | x; + + if(chunks[v] == null) + { + if(create) + { + chunks[v] = new ParallaxChunk(); + } + + return EMPTY; + } + + return chunks[v]; + } +} diff --git a/src/main/java/com/volmit/iris/gen/parallax/ParallaxSection.java b/src/main/java/com/volmit/iris/gen/parallax/ParallaxSection.java new file mode 100644 index 000000000..1902b093f --- /dev/null +++ b/src/main/java/com/volmit/iris/gen/parallax/ParallaxSection.java @@ -0,0 +1,103 @@ +package com.volmit.iris.gen.parallax; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.bukkit.block.data.BlockData; + +import com.volmit.iris.util.B; +import com.volmit.iris.util.DataPalette; +import com.volmit.iris.util.KSet; +import com.volmit.iris.util.Writable; + +public class ParallaxSection implements Writable +{ + private final DataPalette block; + private final KSet updates; + + public ParallaxSection(DataInputStream in) throws IOException + { + this(); + read(in); + } + + public ParallaxSection() + { + updates = new KSet(); + this.block = new DataPalette(B.get("AIR")) + { + @Override + public void writeType(BlockData t, DataOutputStream o) throws IOException + { + o.writeUTF(t.getAsString(true)); + } + + @Override + public BlockData readType(DataInputStream i) throws IOException + { + return B.get(i.readUTF()); + } + }; + } + + public void clearUpdates() + { + updates.clear(); + } + + public void update(int x, int y, int z) + { + updates.add((short) (y << 8 | z << 4 | x)); + } + + public void dontUpdate(int x, int y, int z) + { + updates.remove((short) (y << 8 | z << 4 | x)); + } + + public void setBlock(int x, int y, int z, BlockData d) + { + block.set(x, y, z, d); + + if(B.isUpdatable(d)) + { + update(x, y, z); + } + + else + { + dontUpdate(x, y, z); + } + } + + public BlockData getBlock(int x, int y, int z) + { + return block.get(x, y, z); + } + + @Override + public void write(DataOutputStream o) throws IOException + { + block.write(o); + o.writeShort(updates.size()); + + for(Short i : updates) + { + o.writeShort(i); + } + } + + @Override + public void read(DataInputStream i) throws IOException + { + block.read(i); + updates.clear(); + int m = i.readShort(); + + for(int v = 0; v < m; v++) + { + updates.add(i.readShort()); + } + } +} diff --git a/src/main/java/com/volmit/iris/gen/parallax/ParallaxWorld.java b/src/main/java/com/volmit/iris/gen/parallax/ParallaxWorld.java new file mode 100644 index 000000000..3414c6b5d --- /dev/null +++ b/src/main/java/com/volmit/iris/gen/parallax/ParallaxWorld.java @@ -0,0 +1,121 @@ +package com.volmit.iris.gen.parallax; + +import java.io.File; +import java.io.IOException; + +import org.bukkit.block.data.BlockData; + +import com.volmit.iris.Iris; +import com.volmit.iris.util.KMap; + +public class ParallaxWorld +{ + private final KMap loadedRegions; + private final File dataFolder; + + public ParallaxWorld(File dataFolder) + { + loadedRegions = new KMap<>(); + this.dataFolder = dataFolder; + } + + public void unloadAll() + { + for(long i : loadedRegions.k()) + { + try + { + unload(i); + } + + catch(IOException e) + { + Iris.error("Failed to save region " + i); + e.printStackTrace(); + } + } + } + + public void clean(long time) + { + for(long i : loadedRegions.k()) + { + ParallaxRegion r = loadedRegions.get(i); + + if(r.isOlderThan(time)) + { + try + { + unload(i); + } + + catch(IOException e) + { + Iris.error("Failed to save region " + i); + e.printStackTrace(); + } + + break; + } + } + } + + private void unload(long i) throws IOException + { + ParallaxRegion r = loadedRegions.get(i); + r.write(new File(dataFolder, i + ".plx")); + loadedRegions.remove(i); + } + + public BlockData getBlock(int x, int y, int z) + { + if(y > 255 || y < 0) + { + throw new IllegalArgumentException(y + " exceeds 0-255"); + } + + return getRegion(x >> 5, z >> 5).get(x & 511, y, z & 511); + } + + public void setBlock(int x, int y, int z, BlockData d) + { + if(d == null) + { + throw new IllegalArgumentException("Block data cannot be null"); + } + + if(y > 255 || y < 0) + { + throw new IllegalArgumentException(y + " exceeds 0-255"); + } + + getRegion(x >> 5, z >> 5).set(x & 511, y, z & 511, d); + } + + public ParallaxRegion getRegion(int x, int z) + { + Long vb = (((long) x) << 32) | (z & 0xffffffffL); + File ff = new File(dataFolder, vb + ".plx"); + + return loadedRegions.compute(vb, (k, v) -> + { + if(k == null || v == null) + { + try + { + return new ParallaxRegion(ff); + } + + catch(IOException e) + { + Iris.error("Failed to load parallax file: " + ff.getAbsolutePath() + " Assuming empty region!"); + ff.deleteOnExit(); + ff.delete(); + return new ParallaxRegion(); + } + } + + return v; + }); + } +} diff --git a/src/main/java/com/volmit/iris/object/IrisLootReference.java b/src/main/java/com/volmit/iris/object/IrisLootReference.java index 5971c9e36..d024ec6ad 100644 --- a/src/main/java/com/volmit/iris/object/IrisLootReference.java +++ b/src/main/java/com/volmit/iris/object/IrisLootReference.java @@ -6,6 +6,7 @@ import com.volmit.iris.util.ArrayType; import com.volmit.iris.util.Desc; import com.volmit.iris.util.DontObfuscate; import com.volmit.iris.util.KList; +import com.volmit.iris.util.MinNumber; import com.volmit.iris.util.RegistryListLoot; import lombok.Data; @@ -24,6 +25,11 @@ public class IrisLootReference @Desc("Add loot table registries here") private KList tables = new KList<>(); + @MinNumber(0) + @DontObfuscate + @Desc("Increase the chance of loot in this area") + private double multiplier = 1D; + private transient AtomicCache> tt = new AtomicCache<>(); public IrisLootReference() diff --git a/src/main/java/com/volmit/iris/object/IrisLootTable.java b/src/main/java/com/volmit/iris/object/IrisLootTable.java index fb5004ddf..7a2b86319 100644 --- a/src/main/java/com/volmit/iris/object/IrisLootTable.java +++ b/src/main/java/com/volmit/iris/object/IrisLootTable.java @@ -27,12 +27,17 @@ public class IrisLootTable extends IrisRegistrant @Desc("The rarity as in 1 in X chance") private int rarity = 1; + @MinNumber(1) + @DontObfuscate + @Desc("The maximum amount of loot that can be picked in this table at a time.") + private int maxPicked = 3; + @DontObfuscate @Desc("The loot in this table") @ArrayType(min = 1, type = IrisLoot.class) private KList loot = new KList<>(); - public KList getLoot(boolean debug, RNG rng, InventorySlotType slot, int x, int y, int z) + public KList getLoot(boolean debug, RNG rng, InventorySlotType slot, int x, int y, int z, int gg, int ffs) { KList lootf = new KList<>(); @@ -42,7 +47,7 @@ public class IrisLootTable extends IrisRegistrant { if(i.getSlotTypes().equals(slot)) { - ItemStack item = i.get(debug, this, rng.nextParallelRNG(294788 + x + y - z * z + (m * -4125)), x, y, z); + ItemStack item = i.get(debug, this, rng, x, y, z); if(item != null) { @@ -51,6 +56,11 @@ public class IrisLootTable extends IrisRegistrant } m++; + + if(m > maxPicked) + { + break; + } } return lootf; diff --git a/src/main/java/com/volmit/iris/object/IrisObject.java b/src/main/java/com/volmit/iris/object/IrisObject.java index 8fc0fa01f..3dab3fb6d 100644 --- a/src/main/java/com/volmit/iris/object/IrisObject.java +++ b/src/main/java/com/volmit/iris/object/IrisObject.java @@ -8,6 +8,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.function.Consumer; import org.bukkit.Location; import org.bukkit.Material; @@ -17,6 +18,7 @@ import org.bukkit.block.data.type.Leaves; import org.bukkit.util.BlockVector; import com.volmit.iris.util.B; +import com.volmit.iris.util.BlockPosition; import com.volmit.iris.util.ChunkPosition; import com.volmit.iris.util.IObjectPlacer; import com.volmit.iris.util.KMap; @@ -127,7 +129,12 @@ public class IrisObject extends IrisRegistrant place(x, -1, z, placer, config, rng); } - public void place(int x, int yv, int z, IObjectPlacer placer, IrisObjectPlacement config, RNG rng) + public int place(int x, int yv, int z, IObjectPlacer placer, IrisObjectPlacement config, RNG rng) + { + return place(x, yv, z, placer, config, rng, null); + } + + public int place(int x, int yv, int z, IObjectPlacer placer, IrisObjectPlacement config, RNG rng, Consumer listener) { int spinx = rng.imax() / 1000; int spiny = rng.imax() / 1000; @@ -135,7 +142,6 @@ public class IrisObject extends IrisRegistrant int rty = config.getRotation().rotate(new BlockVector(0, getCenter().getBlockY(), 0), spinx, spiny, spinz).getBlockY(); int ty = config.getTranslate().translate(new BlockVector(0, getCenter().getBlockY(), 0), config.getRotation(), spinx, spiny, spinz).getBlockY(); int y = -1; - KMap paintmap = null; if(yv < 0) { @@ -233,7 +239,6 @@ public class IrisObject extends IrisRegistrant else if(config.getMode().equals(ObjectPlaceMode.PAINT)) { y = placer.getHighest(x, z, config.isUnderwater()) + rty; - paintmap = new KMap<>(); } } @@ -253,18 +258,18 @@ public class IrisObject extends IrisRegistrant { if(!config.isUnderwater() && !config.isOnwater() && placer.isUnderwater(x, z)) { - return; + return -1; } } if(config.isUnderwater() && y + rty + ty >= placer.getFluidHeight()) { - return; + return -1; } if(!config.getClamp().canPlace(y + rty + ty, y - rty + ty)) { - return; + return -1; } if(config.isBore()) @@ -309,17 +314,9 @@ public class IrisObject extends IrisRegistrant int yy = y + (int) Math.round(i.getY()); int zz = z + (int) Math.round(i.getZ()); - if(config.getMode().equals(ObjectPlaceMode.PAINT)) + if(yv < 0 && config.getMode().equals(ObjectPlaceMode.PAINT)) { - yy = (int) Math.round(i.getY()) + Math.floorDiv(h, 2) + paintmap.compute(new ChunkPosition(xx, zz), (k, v) -> - { - if(k == null || v == null) - { - return placer.getHighest(xx, zz, config.isUnderwater()); - } - - return v; - }); + yy = (int) Math.round(i.getY()) + Math.floorDiv(h, 2) + placer.getHighest(xx, zz, config.isUnderwater()); } if(heightmap != null) @@ -347,6 +344,11 @@ public class IrisObject extends IrisRegistrant ((Waterlogged) data).setWaterlogged(true); } + if(listener != null) + { + listener.accept(new BlockPosition(xx, yy, zz)); + } + placer.set(xx, yy, zz, data); } @@ -373,6 +375,8 @@ public class IrisObject extends IrisRegistrant } } } + + return y; } public void place(Location at) diff --git a/src/main/java/com/volmit/iris/object/IrisStructurePlacement.java b/src/main/java/com/volmit/iris/object/IrisStructurePlacement.java index cbc7c26d8..4003222f6 100644 --- a/src/main/java/com/volmit/iris/object/IrisStructurePlacement.java +++ b/src/main/java/com/volmit/iris/object/IrisStructurePlacement.java @@ -147,7 +147,7 @@ public class IrisStructurePlacement } o = o != null ? o : load(g, t.getTile().getObjects().get(rnp.nextInt(t.getTile().getObjects().size()))); - o.place(Math.floorDiv(i, s) * s, height == -1 ? -1 : h, Math.floorDiv(j, s) * s, g, t.getPlacement(), rng); + o.place(Math.floorDiv(i, s) * s, height == -1 ? -1 : h, Math.floorDiv(j, s) * s, g, t.getPlacement(), rng, (gg) -> g.getParallaxChunk(gg.getChunkX(), gg.getChunkZ()).setStructure(gg.getY(), t.getStructure(), t.getTile())); } } diff --git a/src/main/java/com/volmit/iris/object/IrisStructureTile.java b/src/main/java/com/volmit/iris/object/IrisStructureTile.java index d787d8b4b..79565a310 100644 --- a/src/main/java/com/volmit/iris/object/IrisStructureTile.java +++ b/src/main/java/com/volmit/iris/object/IrisStructureTile.java @@ -63,7 +63,6 @@ public class IrisStructureTile private KList objects = new KList<>(); @RegistryListObject - @Required @ArrayType(min = 1, type = IrisRareObject.class) @DontObfuscate @Desc("List of objects to place centered in this tile but with rarity. These items only place some of the time so specify objects for common stuff too.") diff --git a/src/main/java/com/volmit/iris/util/DataPalette.java b/src/main/java/com/volmit/iris/util/DataPalette.java new file mode 100644 index 000000000..028328d9d --- /dev/null +++ b/src/main/java/com/volmit/iris/util/DataPalette.java @@ -0,0 +1,125 @@ +package com.volmit.iris.util; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public abstract class DataPalette implements Writable +{ + private static final int DEFAULT_BITS_PER_BLOCK = 4; + private static final int CAPACITY = 4096; + private int bpb; + private NibbleArray data; + private KList palette; + + public DataPalette(T defaultValue) + { + palette = new KList<>(); + bpb = DEFAULT_BITS_PER_BLOCK; + data = new NibbleArray(bpb, CAPACITY); + data.setAll(Byte.MIN_VALUE); + getPaletteId(defaultValue); + } + + public abstract T readType(DataInputStream i) throws IOException; + + public abstract void writeType(T t, DataOutputStream o) throws IOException; + + @Override + public void write(DataOutputStream o) throws IOException + { + o.writeByte(bpb + Byte.MIN_VALUE); + o.writeByte(palette.size() + Byte.MIN_VALUE); + + for(T i : palette) + { + writeType(i, o); + } + + data.write(o); + } + + @Override + public void read(DataInputStream i) throws IOException + { + bpb = i.readByte() - Byte.MIN_VALUE; + palette = new KList<>(); + int v = i.readByte() - Byte.MIN_VALUE; + + for(int j = 0; j < v; j++) + { + palette.add(readType(i)); + } + + data = new NibbleArray(CAPACITY, i); + } + + private final void expand() + { + if(bpb < 8) + { + changeBitsPerBlock(bpb + 1); + } + + else + { + throw new IndexOutOfBoundsException("The Data Palette can only handle at most 256 block types per 16x16x16 region. We cannot use more than 8 bits per block!"); + } + } + + public final void optimize() + { + int targetBits = bpb; + int needed = palette.size(); + + for(int i = 1; i < bpb; i++) + { + if(Math.pow(2, i) > needed) + { + targetBits = i; + break; + } + } + + changeBitsPerBlock(targetBits); + } + + private final void changeBitsPerBlock(int bits) + { + bpb = bits; + data = new NibbleArray(bpb, CAPACITY, data); + } + + public final void set(int x, int y, int z, T d) + { + data.set(getCoordinateIndex(x, y, z), getPaletteId(d)); + } + + public final T get(int x, int y, int z) + { + return palette.get(data.get(getCoordinateIndex(x, y, z))); + } + + private final int getPaletteId(T d) + { + int index = palette.indexOf(d); + + if(index == -1) + { + index = palette.size(); + palette.add(d); + + if(palette.size() > Math.pow(2, bpb)) + { + expand(); + } + } + + return index + Byte.MIN_VALUE; + } + + private final int getCoordinateIndex(int x, int y, int z) + { + return y << 8 | z << 4 | x; + } +} diff --git a/src/main/java/com/volmit/iris/util/NibbleArray.java b/src/main/java/com/volmit/iris/util/NibbleArray.java new file mode 100644 index 000000000..bcc986e1f --- /dev/null +++ b/src/main/java/com/volmit/iris/util/NibbleArray.java @@ -0,0 +1,212 @@ +package com.volmit.iris.util; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteOrder; +import java.util.Arrays; +import java.util.StringJoiner; + +public class NibbleArray implements Writable +{ + private byte[] data; + private int depth; + private final int size; + private byte mask; + private final Object lock = new Object(); + + public NibbleArray(int capacity, DataInputStream in) throws IOException + { + size = capacity; + read(in); + } + + public NibbleArray(int nibbleDepth, int capacity) + { + if(nibbleDepth > 8 || nibbleDepth < 1) + { + throw new IllegalArgumentException(); + } + + int neededBits = nibbleDepth * capacity; + + size = capacity; + depth = nibbleDepth; + data = new byte[(neededBits + neededBits % 8) / 8]; + mask = (byte) maskFor(nibbleDepth); + } + + public NibbleArray(int nibbleDepth, int capacity, NibbleArray existing) + { + if(nibbleDepth > 8 || nibbleDepth < 1) + { + throw new IllegalArgumentException(); + } + + int neededBits = nibbleDepth * capacity; + size = capacity; + depth = nibbleDepth; + data = new byte[(neededBits + neededBits % 8) / 8]; + mask = (byte) maskFor(nibbleDepth); + + for(int i = 0; i < Math.min(size, existing.size()); i++) + { + set(i, existing.get(i)); + } + } + + @Override + public void write(DataOutputStream o) throws IOException + { + o.writeByte(depth + Byte.MIN_VALUE); + o.write(data); + } + + @Override + public void read(DataInputStream i) throws IOException + { + depth = i.readByte() - Byte.MIN_VALUE; + int neededBits = depth * size; + data = new byte[(neededBits + neededBits % 8) / 8]; + mask = (byte) maskFor(depth); + i.read(data); + } + + public int size() + { + return size; + } + + public byte get(int index) + { + synchronized(lock) + { + bitIndex = index * depth; + byteIndex = bitIndex >> 3; + bitInByte = bitIndex & 7; + int value = data[byteIndex] >> bitInByte; + + if(bitInByte + depth > 8) + { + value |= data[byteIndex + 1] << bitInByte; + } + + return (byte) (value & mask); + } + } + + public byte getAsync(int index) + { + int bitIndex = index * depth; + int byteIndex = bitIndex >> 3; + int bitInByte = bitIndex & 7; + int value = data[byteIndex] >> bitInByte; + + if(bitInByte + depth > 8) + { + value |= data[byteIndex + 1] << bitInByte; + } + + return (byte) (value & mask); + } + + private transient int bitIndex, byteIndex, bitInByte; + + public void set(int index, int nibble) + { + set(index, (byte) nibble); + } + + public void set(int index, byte nybble) + { + synchronized(lock) + { + bitIndex = index * depth; + byteIndex = bitIndex >> 3; + bitInByte = bitIndex & 7; + data[byteIndex] = (byte) (((~(data[byteIndex] & (mask << bitInByte)) & data[byteIndex]) | ((nybble & mask) << bitInByte)) & 0xff); + + if(bitInByte + depth > 8) + { + data[byteIndex + 1] = (byte) (((~(data[byteIndex + 1] & MASKS[bitInByte + depth - 8]) & data[byteIndex + 1]) | ((nybble & mask) >> (8 - bitInByte))) & 0xff); + } + } + } + + public String toBitsString() + { + return toBitsString(ByteOrder.BIG_ENDIAN); + } + + public String toBitsString(ByteOrder byteOrder) + { + StringJoiner joiner = new StringJoiner(" "); + + for(int i = 0; i < data.length; i++) + { + joiner.add(binaryString(data[i], byteOrder)); + } + + return joiner.toString(); + } + + public void clear() + { + Arrays.fill(data, (byte) 0); + } + + public void setAll(byte nibble) + { + for(int i = 0; i < size; i++) + { + set(i, nibble); + } + } + + public void setAll(int nibble) + { + for(int i = 0; i < size; i++) + { + set(i, (byte) nibble); + } + } + + public static int maskFor(int amountOfBits) + { + return powerOfTwo(amountOfBits) - 1; + } + + public static int powerOfTwo(int power) + { + int result = 1; + + for(int i = 0; i < power; i++) + { + result *= 2; + } + + return result; + } + + private static final int[] MASKS = new int[8]; + + static + { + for(int i = 0; i < MASKS.length; i++) + { + MASKS[i] = maskFor(i); + } + } + + public static String binaryString(byte b, ByteOrder byteOrder) + { + String str = String.format("%8s", Integer.toBinaryString(b & 0xff)).replace(' ', '0'); + + return byteOrder.equals(ByteOrder.BIG_ENDIAN) ? str : reverse(str); + } + + public static String reverse(String str) + { + return new StringBuilder(str).reverse().toString(); + } +} \ No newline at end of file diff --git a/src/main/java/com/volmit/iris/util/Writable.java b/src/main/java/com/volmit/iris/util/Writable.java new file mode 100644 index 000000000..2c02046f1 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/Writable.java @@ -0,0 +1,12 @@ +package com.volmit.iris.util; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public interface Writable +{ + public void write(DataOutputStream o) throws IOException; + + public void read(DataInputStream i) throws IOException; +}