From a4b571ccbcb8d8d387f334f9e7940cd8bbf8f11b Mon Sep 17 00:00:00 2001 From: Daniel Mills Date: Mon, 20 Jan 2020 14:38:07 -0500 Subject: [PATCH] Parallax Object Generation --- .../java/ninja/bytecode/iris/Settings.java | 5 +- .../iris/controller/ExecutionController.java | 19 +- .../iris/controller/PackController.java | 1 + .../iris/generator/IrisGenerator.java | 81 ++--- .../iris/generator/genobject/GenObject.java | 30 +- .../genobject/GenObjectDecorator.java | 152 ++++----- .../generator/genobject/GenObjectGroup.java | 36 +++ .../iris/generator/layer/GenLayerCarving.java | 17 +- .../iris/generator/layer/GenLayerCaverns.java | 17 +- .../iris/generator/layer/GenLayerCaves.java | 27 +- .../placer/AtomicParallaxPlacer.java | 41 +++ .../iris/generator/placer/AtomicPlacer.java | 45 --- .../bytecode/iris/pack/CompiledDimension.java | 59 ++++ .../bytecode/iris/util/AtomicChunkData.java | 184 +++++++++++ .../bytecode/iris/util/AtomicMCAData.java | 80 +++++ .../bytecode/iris/util/AtomicWorldData.java | 157 +++++++++ .../ninja/bytecode/iris/util/ChunkPlan.java | 34 +- .../bytecode/iris/util/GlowingBlock.java | 1 - .../bytecode/iris/util/IrisWorldData.java | 253 +++++++++++++++ .../bytecode/iris/util/ParallaxAnchor.java | 44 +++ .../bytecode/iris/util/ParallaxCache.java | 98 ++++++ .../iris/util/ParallaxWorldGenerator.java | 190 +++++++++++ .../iris/util/ParallelChunkGenerator.java | 102 +++--- .../{ChunkedVector.java => SChunkVector.java} | 14 +- .../ninja/bytecode/iris/util/SMCAVector.java | 61 ++++ src/main/java/org/jnbt/ByteArrayTag.java | 82 +++++ src/main/java/org/jnbt/ByteTag.java | 74 +++++ src/main/java/org/jnbt/CompoundTag.java | 83 +++++ src/main/java/org/jnbt/DoubleTag.java | 74 +++++ src/main/java/org/jnbt/EndTag.java | 60 ++++ src/main/java/org/jnbt/FloatTag.java | 74 +++++ src/main/java/org/jnbt/IntArrayTag.java | 76 +++++ src/main/java/org/jnbt/IntTag.java | 74 +++++ src/main/java/org/jnbt/ListTag.java | 99 ++++++ src/main/java/org/jnbt/LongTag.java | 74 +++++ src/main/java/org/jnbt/NBTConstants.java | 77 +++++ src/main/java/org/jnbt/NBTInputStream.java | 205 ++++++++++++ src/main/java/org/jnbt/NBTOutputStream.java | 301 ++++++++++++++++++ src/main/java/org/jnbt/NBTUtils.java | 165 ++++++++++ src/main/java/org/jnbt/ShortTag.java | 74 +++++ src/main/java/org/jnbt/StringTag.java | 74 +++++ src/main/java/org/jnbt/Tag.java | 73 +++++ 42 files changed, 3190 insertions(+), 297 deletions(-) create mode 100644 src/main/java/ninja/bytecode/iris/generator/placer/AtomicParallaxPlacer.java delete mode 100644 src/main/java/ninja/bytecode/iris/generator/placer/AtomicPlacer.java create mode 100644 src/main/java/ninja/bytecode/iris/util/AtomicMCAData.java create mode 100644 src/main/java/ninja/bytecode/iris/util/AtomicWorldData.java create mode 100644 src/main/java/ninja/bytecode/iris/util/IrisWorldData.java create mode 100644 src/main/java/ninja/bytecode/iris/util/ParallaxAnchor.java create mode 100644 src/main/java/ninja/bytecode/iris/util/ParallaxCache.java create mode 100644 src/main/java/ninja/bytecode/iris/util/ParallaxWorldGenerator.java rename src/main/java/ninja/bytecode/iris/util/{ChunkedVector.java => SChunkVector.java} (79%) create mode 100644 src/main/java/ninja/bytecode/iris/util/SMCAVector.java create mode 100644 src/main/java/org/jnbt/ByteArrayTag.java create mode 100644 src/main/java/org/jnbt/ByteTag.java create mode 100644 src/main/java/org/jnbt/CompoundTag.java create mode 100644 src/main/java/org/jnbt/DoubleTag.java create mode 100644 src/main/java/org/jnbt/EndTag.java create mode 100644 src/main/java/org/jnbt/FloatTag.java create mode 100644 src/main/java/org/jnbt/IntArrayTag.java create mode 100644 src/main/java/org/jnbt/IntTag.java create mode 100644 src/main/java/org/jnbt/ListTag.java create mode 100644 src/main/java/org/jnbt/LongTag.java create mode 100644 src/main/java/org/jnbt/NBTConstants.java create mode 100644 src/main/java/org/jnbt/NBTInputStream.java create mode 100644 src/main/java/org/jnbt/NBTOutputStream.java create mode 100644 src/main/java/org/jnbt/NBTUtils.java create mode 100644 src/main/java/org/jnbt/ShortTag.java create mode 100644 src/main/java/org/jnbt/StringTag.java create mode 100644 src/main/java/org/jnbt/Tag.java diff --git a/src/main/java/ninja/bytecode/iris/Settings.java b/src/main/java/ninja/bytecode/iris/Settings.java index ffa633898..bf9197dfe 100644 --- a/src/main/java/ninja/bytecode/iris/Settings.java +++ b/src/main/java/ninja/bytecode/iris/Settings.java @@ -9,7 +9,7 @@ public class Settings public static class PerformanceSettings { - public PerformanceMode performanceMode = PerformanceMode.UNLIMITED; + public PerformanceMode performanceMode = PerformanceMode.HALF_CPU; public boolean fastDecoration = true; public int threadPriority = Thread.MAX_PRIORITY; public int threadCount = 4; @@ -17,6 +17,7 @@ public class Settings public int decorationAccuracy = 2; public boolean interpolation = true; public boolean surfaceNoise = true; + public boolean noObjectFail = false; public boolean verbose = false; public int placeHistoryLimit = 8192; } @@ -37,7 +38,7 @@ public class Settings public double heightScale = 0.56; public double baseHeight = 0.065; public int seaLevel = 63; - public double caveDensity = 4; + public double caveDensity = 5; public double caveScale = 1.45; public double biomeScale = 0.65; public boolean flatBedrock = true; diff --git a/src/main/java/ninja/bytecode/iris/controller/ExecutionController.java b/src/main/java/ninja/bytecode/iris/controller/ExecutionController.java index 833f319c3..b40c9d903 100644 --- a/src/main/java/ninja/bytecode/iris/controller/ExecutionController.java +++ b/src/main/java/ninja/bytecode/iris/controller/ExecutionController.java @@ -8,9 +8,9 @@ import ninja.bytecode.shuriken.collections.GMap; import ninja.bytecode.shuriken.execution.TaskExecutor; public class ExecutionController implements IrisController -{ +{ GMap executors; - + @Override public void onStart() { @@ -20,16 +20,19 @@ public class ExecutionController implements IrisController @Override public void onStop() { - + for(TaskExecutor i : executors.v()) + { + i.close(); + } } - - public TaskExecutor getExecutor(World world) + + public TaskExecutor getExecutor(World world, String f) { - TaskExecutor x = new TaskExecutor(getTC(), Iris.settings.performance.threadPriority, "Iris Generator (" + world.getName() + ")"); - executors.put(world.getWorldFolder().getAbsolutePath() + " (" + world + ")", x); + TaskExecutor x = new TaskExecutor(getTC(), Iris.settings.performance.threadPriority, "Iris " + f); + executors.put(world.getWorldFolder().getAbsolutePath() + " (" + world + ") " + f, x); return x; } - + private int getTC() { switch(Iris.settings.performance.performanceMode) diff --git a/src/main/java/ninja/bytecode/iris/controller/PackController.java b/src/main/java/ninja/bytecode/iris/controller/PackController.java index 736f8fad9..35474bbcb 100644 --- a/src/main/java/ninja/bytecode/iris/controller/PackController.java +++ b/src/main/java/ninja/bytecode/iris/controller/PackController.java @@ -142,6 +142,7 @@ public class PackController implements IrisController for(String i : compiledDimensions.k()) { CompiledDimension d = compiledDimensions.get(i); + d.computeObjectSize(); L.i(ChatColor.GREEN + i + ChatColor.WHITE + " (" + d.getEnvironment().toString().toLowerCase() + ")"); L.i(ChatColor.DARK_GREEN + " Biomes: " + ChatColor.GRAY + F.f(d.getBiomes().size())); L.i(ChatColor.DARK_GREEN + " Objects: " + ChatColor.GRAY + F.f(d.countObjects())); diff --git a/src/main/java/ninja/bytecode/iris/generator/IrisGenerator.java b/src/main/java/ninja/bytecode/iris/generator/IrisGenerator.java index 24bd8dde3..508cf3488 100644 --- a/src/main/java/ninja/bytecode/iris/generator/IrisGenerator.java +++ b/src/main/java/ninja/bytecode/iris/generator/IrisGenerator.java @@ -1,13 +1,11 @@ package ninja.bytecode.iris.generator; -import java.util.List; import java.util.Random; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Biome; -import org.bukkit.generator.BlockPopulator; import org.bukkit.util.NumberConversions; import mortar.util.text.C; @@ -30,14 +28,15 @@ import ninja.bytecode.iris.util.AtomicChunkData; import ninja.bytecode.iris.util.ChunkPlan; import ninja.bytecode.iris.util.IrisInterpolation; import ninja.bytecode.iris.util.MB; -import ninja.bytecode.iris.util.ParallelChunkGenerator; +import ninja.bytecode.iris.util.ParallaxWorldGenerator; +import ninja.bytecode.iris.util.SChunkVector; import ninja.bytecode.shuriken.collections.GList; import ninja.bytecode.shuriken.logging.L; import ninja.bytecode.shuriken.math.CNG; import ninja.bytecode.shuriken.math.M; import ninja.bytecode.shuriken.math.RNG; -public class IrisGenerator extends ParallelChunkGenerator +public class IrisGenerator extends ParallaxWorldGenerator { //@builder public static final GList ROCK = new GList().add(new MB[] { @@ -75,7 +74,6 @@ public class IrisGenerator extends ParallelChunkGenerator private GenLayerCliffs glCliffs; private RNG rTerrain; private CompiledDimension dim; - private World world; public IrisGenerator() { @@ -113,7 +111,6 @@ public class IrisGenerator extends ParallelChunkGenerator } //@builder - this.world = world; rTerrain = new RNG(world.getSeed()); glLNoise = new GenLayerLayeredNoise(this, world, random, rTerrain.nextParallelRNG(2)); glBiome = new GenLayerBiome(this, world, random, rTerrain.nextParallelRNG(4), dim.getBiomes()); @@ -124,6 +121,7 @@ public class IrisGenerator extends ParallelChunkGenerator glCliffs = new GenLayerCliffs(this, world, random, rTerrain.nextParallelRNG(9)); scatterCache = new double[16][][]; scatter = new CNG(rTerrain.nextParallelRNG(52), 1, 1).scale(10); + god = new GenObjectDecorator(this); //@done for(int i = 0; i < 16; i++) { @@ -242,11 +240,25 @@ public class IrisGenerator extends ParallelChunkGenerator } @Override - public Biome genColumn(int wxxf, int wzxf, int x, int z, ChunkPlan plan) + public void onGenParallax(int x, int z, Random random) + { + try + { + god.decorateParallax(x, z, random); + } + + catch(Throwable e) + { + e.printStackTrace(); + } + } + + @Override + public Biome onGenColumn(int wxxf, int wzxf, int x, int z, ChunkPlan plan, AtomicChunkData data) { if(disposed) { - setBlock(x, 0, z, Material.MAGENTA_GLAZED_TERRACOTTA); + data.setBlock(x, 0, z, Material.MAGENTA_GLAZED_TERRACOTTA); return Biome.VOID; } @@ -296,7 +308,7 @@ public class IrisGenerator extends ParallelChunkGenerator for(int j = 0; j < snowHeight; j++) { highest = j == snowHeight - 1 ? highest < j ? j : highest : highest < j + 1 ? j + 1 : highest; - setBlock(x, i + j + 1, z, j == snowHeight - 1 ? Material.SNOW : Material.SNOW_BLOCK, j == snowHeight - 1 ? (byte) layers : (byte) 0); + data.setBlock(x, i + j + 1, z, j == snowHeight - 1 ? Material.SNOW : Material.SNOW_BLOCK, j == snowHeight - 1 ? (byte) layers : (byte) 0); } } @@ -307,24 +319,24 @@ public class IrisGenerator extends ParallelChunkGenerator if(!mbx.material.equals(Material.AIR)) { highest = i > highest ? i : highest; - setBlock(x, i + 1, z, mbx.material, mbx.data); + data.setBlock(x, i + 1, z, mbx.material, mbx.data); } } } highest = i > highest ? i : highest; - setBlock(x, i, z, mb.material, mb.data); + data.setBlock(x, i, z, mb.material, mb.data); } - glCaves.genCaves(wxx, wzx, x, z, height, this); - glCarving.genCarves(wxx, wzx, x, z, height, this, biome); - glCaverns.genCaverns(wxx, wzx, x, z, height, this, biome); + glCaves.genCaves(wxx, wzx, x, z, height, this, data); + glCarving.genCarves(wxx, wzx, x, z, height, this, biome, data); + glCaverns.genCaverns(wxx, wzx, x, z, height, this, biome, data); int hw = 0; int hl = 0; for(int i = highest; i > 0; i--) { - Material t = getType(x, i, z); + Material t = data.getType(x, i, z); hw = i > seaLevel && hw == 0 && (t.equals(Material.WATER) || t.equals(Material.STATIONARY_WATER)) ? i : hw; hl = hl == 0 && !t.equals(Material.AIR) ? i : hl; } @@ -336,31 +348,12 @@ public class IrisGenerator extends ParallelChunkGenerator return biome.getRealBiome(); } - @Override - public void decorateColumn(int wx, int wz, int x, int z, ChunkPlan plan) - { - - } - @Override public void onPostChunk(World world, int x, int z, Random random, AtomicChunkData data, ChunkPlan plan) { } - @Override - public List getDefaultPopulators(World world) - { - GList p = new GList<>(); - - if(Iris.settings.gen.genObjects) - { - p.add(god = new GenObjectDecorator(this)); - } - - return p; - } - private double getBiomedHeight(int x, int z, ChunkPlan plan) { double xh = plan.getHeight(x, z); @@ -377,11 +370,6 @@ public class IrisGenerator extends ParallelChunkGenerator return xh; } - public World getWorld() - { - return world; - } - public RNG getRTerrain() { return rTerrain; @@ -398,7 +386,7 @@ public class IrisGenerator extends ParallelChunkGenerator { return; } - L.w(C.YELLOW + "Disposed Iris World " + C.RED + world.getName()); + L.w(C.YELLOW + "Disposed Iris World " + C.RED + getWorld().getName()); disposed = true; dim = null; glLNoise = null; @@ -407,6 +395,7 @@ public class IrisGenerator extends ParallelChunkGenerator glCaverns = null; glSnow = null; glCliffs = null; + god.dispose(); } public boolean isDisposed() @@ -439,4 +428,16 @@ public class IrisGenerator extends ParallelChunkGenerator { return god.randomObject(string); } + + @Override + protected SChunkVector getParallaxSize() + { + return dim.getMaxChunkSize(); + } + + @Override + protected void onUnload() + { + dispose(); + } } \ No newline at end of file diff --git a/src/main/java/ninja/bytecode/iris/generator/genobject/GenObject.java b/src/main/java/ninja/bytecode/iris/generator/genobject/GenObject.java index ef72524d2..14aa1ce3b 100644 --- a/src/main/java/ninja/bytecode/iris/generator/genobject/GenObject.java +++ b/src/main/java/ninja/bytecode/iris/generator/genobject/GenObject.java @@ -98,7 +98,7 @@ public class GenObject } mountHeight = avg(avy); - mount = new BlockVector(avg(avx), 0, avg(avz)); + mount = new BlockVector(0, 0, 0); } private int avg(double[] v) @@ -255,7 +255,7 @@ public class GenObject public Location place(int wx, int wy, int wz, IPlacer placer) { - Location start = new Location(placer.getWorld(), wx, wy, wz).clone().add(sh(w), sh(h) + 1, sh(d)); + Location start = new Location(placer.getWorld(), wx, wy, wz).clone().add(0, sh(h) + 1, 0); if(mount == null) { @@ -279,21 +279,25 @@ public class GenObject MB b = getSchematic().get(i); Location f = start.clone().add(i.toBlockVector()); - Material m = placer.get(f.clone().subtract(0, 1, 0)).material; - if(i.getY() == mountHeight && (m.equals(Material.WATER) || m.equals(Material.STATIONARY_WATER) || m.equals(Material.LAVA) || m.equals(Material.STATIONARY_LAVA))) + if(!Iris.settings.performance.noObjectFail) { - for(Location j : undo.k()) - { - placer.set(j, undo.get(j)); - } + Material m = placer.get(f.clone().subtract(0, 1, 0)).material; - if(Iris.settings.performance.verbose) + if(i.getY() == mountHeight && (m.equals(Material.WATER) || m.equals(Material.STATIONARY_WATER) || m.equals(Material.LAVA) || m.equals(Material.STATIONARY_LAVA))) { - L.w(C.WHITE + "Object " + C.YELLOW + getName() + C.WHITE + " failed to place in " + C.YELLOW + m.toString().toLowerCase() + C.WHITE + " at " + C.YELLOW + F.f(f.getBlockX()) + " " + F.f(f.getBlockY()) + " " + F.f(f.getBlockZ())); - } + for(Location j : undo.k()) + { + placer.set(j, undo.get(j)); + } - failures++; - return null; + if(Iris.settings.performance.verbose) + { + L.w(C.WHITE + "Object " + C.YELLOW + getName() + C.WHITE + " failed to place in " + C.YELLOW + m.toString().toLowerCase() + C.WHITE + " at " + C.YELLOW + F.f(f.getBlockX()) + " " + F.f(f.getBlockY()) + " " + F.f(f.getBlockZ())); + } + + failures++; + return null; + } } try diff --git a/src/main/java/ninja/bytecode/iris/generator/genobject/GenObjectDecorator.java b/src/main/java/ninja/bytecode/iris/generator/genobject/GenObjectDecorator.java index f6ecc1f2d..81ceb33e4 100644 --- a/src/main/java/ninja/bytecode/iris/generator/genobject/GenObjectDecorator.java +++ b/src/main/java/ninja/bytecode/iris/generator/genobject/GenObjectDecorator.java @@ -2,41 +2,34 @@ package ninja.bytecode.iris.generator.genobject; import java.util.Collections; import java.util.Random; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import org.bukkit.Bukkit; -import org.bukkit.Chunk; import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.generator.BlockPopulator; import mortar.logic.format.F; import mortar.util.text.C; import net.md_5.bungee.api.ChatColor; import ninja.bytecode.iris.Iris; import ninja.bytecode.iris.generator.IrisGenerator; -import ninja.bytecode.iris.generator.placer.BukkitPlacer; -import ninja.bytecode.iris.generator.placer.NMSPlacer; +import ninja.bytecode.iris.generator.placer.AtomicParallaxPlacer; import ninja.bytecode.iris.pack.IrisBiome; import ninja.bytecode.iris.util.IPlacer; +import ninja.bytecode.iris.util.MB; +import ninja.bytecode.iris.util.ParallaxCache; +import ninja.bytecode.iris.util.SMCAVector; import ninja.bytecode.shuriken.collections.GList; import ninja.bytecode.shuriken.collections.GMap; import ninja.bytecode.shuriken.collections.GSet; import ninja.bytecode.shuriken.execution.ChronoLatch; import ninja.bytecode.shuriken.logging.L; import ninja.bytecode.shuriken.math.M; +import ninja.bytecode.shuriken.math.RNG; -public class GenObjectDecorator extends BlockPopulator +public class GenObjectDecorator { private GList placeHistory; private GMap> orderCache; private GMap> populationCache; private IPlacer placer; - private Executor ex; private IrisGenerator g; private ChronoLatch cl = new ChronoLatch(250); @@ -46,7 +39,6 @@ public class GenObjectDecorator extends BlockPopulator placeHistory = new GList<>(); populationCache = new GMap<>(); orderCache = new GMap<>(); - ex = Executors.newSingleThreadExecutor(); for(IrisBiome i : generator.getDimension().getBiomes()) { @@ -100,25 +92,24 @@ public class GenObjectDecorator extends BlockPopulator L.i("Population Cache is " + populationCache.size()); } - @Override - public void populate(World world, Random rnotusingyou, Chunk source) + public void decorateParallax(int cx, int cz, Random random) { - if(g.isDisposed()) + try { - placeHistory.clear(); - return; - } + if(g.isDisposed()) + { + placeHistory.clear(); + return; + } - ex.execute(() -> - { - Random random = new Random(((source.getX() - 32) * (source.getZ() + 54)) + world.getSeed()); + ParallaxCache cache = new ParallaxCache(g); GSet hits = new GSet<>(); for(int i = 0; i < Iris.settings.performance.decorationAccuracy; i++) { - int x = (source.getX() << 4) + random.nextInt(16); - int z = (source.getZ() << 4) + random.nextInt(16); - IrisBiome biome = g.getBiome((int) g.getOffsetX(x), (int) g.getOffsetX(z)); + int x = (cx << 4) + random.nextInt(16); + int z = (cz << 4) + random.nextInt(16); + IrisBiome biome = cache.getBiome(x, z); if(hits.contains(biome)) { @@ -133,20 +124,19 @@ public class GenObjectDecorator extends BlockPopulator } hits.add(biome); - - populate(world, random, source, biome, orderCache.get(biome)); + populate(cx, cz, random, biome, cache); } + } - if(Iris.settings.performance.verbose) - { - L.flush(); - } - }); + catch(Throwable e) + { + e.printStackTrace(); + } } - private void populate(World world, Random random, Chunk source, IrisBiome biome, GList order) + private void populate(int cx, int cz, Random random, IrisBiome biome, ParallaxCache cache) { - for(GenObjectGroup i : order) + for(GenObjectGroup i : orderCache.get(biome)) { if(biome.getSchematicGroups().get(i.getName()) == null) { @@ -158,60 +148,65 @@ public class GenObjectDecorator extends BlockPopulator { if(M.r(Iris.settings.gen.objectDensity)) { - int x = (source.getX() << 4) + random.nextInt(16); - int z = (source.getZ() << 4) + random.nextInt(16); - Block b = world.getHighestBlockAt(x, z).getRelative(BlockFace.DOWN); - Material t = b.getType(); + GenObject go = i.getSchematics().get(random.nextInt(i.getSchematics().size())); + int x = (cx << 4) + random.nextInt(16); + int z = (cz << 4) + random.nextInt(16); - if(!t.isSolid() || !biome.isSurface(t)) + if(i.getWorldChance() >= 0D) { - if(Iris.settings.performance.verbose) - { - L.w(C.WHITE + "Object " + C.YELLOW + i.getName() + "/*" + C.WHITE + " failed to place in " + C.YELLOW + t.toString().toLowerCase() + C.WHITE + " at " + C.YELLOW + F.f(b.getX()) + " " + F.f(b.getY()) + " " + F.f(b.getZ())); - } + int rngx = (int) Math.floor(x / (double) (i.getWorldRadius() == 0 ? 32 : i.getWorldRadius())); + int rngz = (int) Math.floor(z / (double) (i.getWorldRadius() == 0 ? 32 : i.getWorldRadius())); - continue; - } - - if(placer == null) - { - if(Iris.settings.performance.fastDecoration) - { - placer = new NMSPlacer(world); - } - - else - { - placer = new BukkitPlacer(world, false); - } - } - - GenObject g = i.getSchematics().get(random.nextInt(i.getSchematics().size())); - Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> - { - Location start = g.place(x, b.getY(), z, placer); - - if(start != null) + if(new RNG(new SMCAVector(rngx, rngz).hashCode()).nextDouble() < i.getWorldChance()) { if(Iris.settings.performance.verbose) { - L.v(C.GRAY + "Placed " + C.DARK_GREEN + i.getName() + C.WHITE + "/" + C.DARK_GREEN + g.getName() + C.GRAY + " at " + C.DARK_GREEN + F.f(start.getBlockX()) + " " + F.f(start.getBlockY()) + " " + F.f(start.getBlockZ())); + L.w(C.WHITE + "Object " + C.YELLOW + i.getName() + "/*" + C.WHITE + " failed to place due to a world chance."); } - if(Iris.settings.performance.debugMode) - { - placeHistory.add(new PlacedObject(start.getBlockX(), start.getBlockY(), start.getBlockZ(), i.getName() + ":" + g.getName())); + break; + } + } - if(placeHistory.size() > Iris.settings.performance.placeHistoryLimit) + int by = cache.getHeight(x, z); + MB mb = cache.get(x, by, z); + + if(!Iris.settings.performance.noObjectFail) + { + if(!mb.material.isSolid() || !biome.isSurface(mb.material)) + { + if(Iris.settings.performance.verbose) + { + L.w(C.WHITE + "Object " + C.YELLOW + i.getName() + "/*" + C.WHITE + " failed to place in " + C.YELLOW + mb.material.toString().toLowerCase() + C.WHITE + " at " + C.YELLOW + F.f(x) + " " + F.f(by) + " " + F.f(z)); + } + + return; + } + } + + placer = new AtomicParallaxPlacer(g, cache); + Location start = go.place(x, by, z, placer); + + if(start != null) + { + if(Iris.settings.performance.verbose) + { + L.v(C.GRAY + "Placed " + C.DARK_GREEN + i.getName() + C.WHITE + "/" + C.DARK_GREEN + go.getName() + C.GRAY + " at " + C.DARK_GREEN + F.f(start.getBlockX()) + " " + F.f(start.getBlockY()) + " " + F.f(start.getBlockZ())); + } + + if(Iris.settings.performance.debugMode) + { + placeHistory.add(new PlacedObject(start.getBlockX(), start.getBlockY(), start.getBlockZ(), i.getName() + ":" + go.getName())); + + if(placeHistory.size() > Iris.settings.performance.placeHistoryLimit) + { + while(placeHistory.size() > Iris.settings.performance.placeHistoryLimit) { - while(placeHistory.size() > Iris.settings.performance.placeHistoryLimit) - { - placeHistory.remove(0); - } + placeHistory.remove(0); } } } - }); + } } } } @@ -249,6 +244,11 @@ public class GenObjectDecorator extends BlockPopulator return placeHistory; } + public void dispose() + { + + } + public PlacedObject randomObject(String string) { GList v = new GList<>(); diff --git a/src/main/java/ninja/bytecode/iris/generator/genobject/GenObjectGroup.java b/src/main/java/ninja/bytecode/iris/generator/genobject/GenObjectGroup.java index 77995d7ec..f31f2e11e 100644 --- a/src/main/java/ninja/bytecode/iris/generator/genobject/GenObjectGroup.java +++ b/src/main/java/ninja/bytecode/iris/generator/genobject/GenObjectGroup.java @@ -21,6 +21,8 @@ public class GenObjectGroup private GList flags; private String name; private int priority; + private double worldChance; + private int worldRad; public GenObjectGroup(String name) { @@ -28,6 +30,8 @@ public class GenObjectGroup this.flags = new GList<>(); this.name = name; priority = Integer.MIN_VALUE; + worldChance = Integer.MIN_VALUE; + worldRad = 32; } public void read(DataInputStream din) throws IOException @@ -295,4 +299,36 @@ public class GenObjectGroup return true; } + public double getWorldChance() + { + if(worldChance == Integer.MIN_VALUE) + { + for(String i : flags) + { + if(i.startsWith("world chance ")) + { + worldChance = Double.valueOf(i.split("\\Q \\E")[2]); + } + } + } + + return worldChance; + } + + public double getWorldRadius() + { + if(worldRad == Integer.MIN_VALUE) + { + for(String i : flags) + { + if(i.startsWith("world radius ")) + { + worldRad = Integer.valueOf(i.split("\\Q \\E")[2]); + } + } + } + + return worldRad; + } + } diff --git a/src/main/java/ninja/bytecode/iris/generator/layer/GenLayerCarving.java b/src/main/java/ninja/bytecode/iris/generator/layer/GenLayerCarving.java index 79cb40831..cad4c5be6 100644 --- a/src/main/java/ninja/bytecode/iris/generator/layer/GenLayerCarving.java +++ b/src/main/java/ninja/bytecode/iris/generator/layer/GenLayerCarving.java @@ -8,6 +8,7 @@ import org.bukkit.World; import ninja.bytecode.iris.Iris; import ninja.bytecode.iris.generator.IrisGenerator; import ninja.bytecode.iris.pack.IrisBiome; +import ninja.bytecode.iris.util.AtomicChunkData; import ninja.bytecode.iris.util.GenLayer; import ninja.bytecode.iris.util.IrisInterpolation; import ninja.bytecode.iris.util.MB; @@ -68,7 +69,7 @@ public class GenLayerCarving extends GenLayer return carver.noise(x + fx, y - fy, z + fz); } - public void genCarves(double wxx, double wzx, int x, int z, int s, IrisGenerator g, IrisBiome biome) + public void genCarves(double wxx, double wzx, int x, int z, int s, IrisGenerator g, IrisBiome biome, AtomicChunkData data) { if(!Iris.settings.gen.genCarving) { @@ -103,7 +104,7 @@ public class GenLayerCarving extends GenLayer if(carve(wxx, i, wzx) < IrisInterpolation.lerpBezier(0, ch, hill)) { carved++; - g.setBlock(x, i, z, Material.AIR); + data.setBlock(x, i, z, Material.AIR); } } @@ -113,7 +114,7 @@ public class GenLayerCarving extends GenLayer for(int i = Iris.settings.gen.maxCarvingHeight; i > Iris.settings.gen.minCarvingHeight; i--) { - Material m = g.getType(x, i, z); + Material m = data.getType(x, i, z); if(!m.equals(Material.AIR)) { hit++; @@ -126,7 +127,7 @@ public class GenLayerCarving extends GenLayer { for(int j = i; j > i - 5; j--) { - if(g.getType(x, j, z).equals(Material.AIR)) + if(data.getType(x, j, z).equals(Material.AIR)) { fail = true; break; @@ -137,12 +138,12 @@ public class GenLayerCarving extends GenLayer if(!fail) { MB mb = biome.getSurface(wxx, wzx, g.getRTerrain()); - g.setBlock(x, i, z, mb.material, mb.data); + data.setBlock(x, i, z, mb.material, mb.data); } else { - g.setBlock(x, i, z, Material.AIR); + data.setBlock(x, i, z, Material.AIR); } } @@ -151,12 +152,12 @@ public class GenLayerCarving extends GenLayer if(!fail) { MB mb = biome.getSubSurface(wxx, i, wzx, g.getRTerrain()); - g.setBlock(x, i, z, mb.material, mb.data); + data.setBlock(x, i, z, mb.material, mb.data); } else { - g.setBlock(x, i, z, Material.AIR); + data.setBlock(x, i, z, Material.AIR); } } } diff --git a/src/main/java/ninja/bytecode/iris/generator/layer/GenLayerCaverns.java b/src/main/java/ninja/bytecode/iris/generator/layer/GenLayerCaverns.java index ed0d20926..ef7fec51d 100644 --- a/src/main/java/ninja/bytecode/iris/generator/layer/GenLayerCaverns.java +++ b/src/main/java/ninja/bytecode/iris/generator/layer/GenLayerCaverns.java @@ -8,6 +8,7 @@ import org.bukkit.World; import ninja.bytecode.iris.Iris; import ninja.bytecode.iris.generator.IrisGenerator; import ninja.bytecode.iris.pack.IrisBiome; +import ninja.bytecode.iris.util.AtomicChunkData; import ninja.bytecode.iris.util.GenLayer; import ninja.bytecode.iris.util.IrisInterpolation; import ninja.bytecode.iris.util.MB; @@ -68,7 +69,7 @@ public class GenLayerCaverns extends GenLayer return carver.noise(x + fx, y - fy, z + fz); } - public void genCaverns(double wxx, double wzx, int x, int z, int s, IrisGenerator g, IrisBiome biome) + public void genCaverns(double wxx, double wzx, int x, int z, int s, IrisGenerator g, IrisBiome biome, AtomicChunkData data) { if(!Iris.settings.gen.genCaverns) { @@ -103,7 +104,7 @@ public class GenLayerCaverns extends GenLayer if(cavern(wxx, i, wzx) < IrisInterpolation.lerpBezier(0, ch, hill)) { carved++; - g.setBlock(x, i, z, Material.AIR); + data.setBlock(x, i, z, Material.AIR); } } @@ -113,7 +114,7 @@ public class GenLayerCaverns extends GenLayer for(int i = Iris.settings.gen.maxCavernHeight; i > Iris.settings.gen.minCavernHeight; i--) { - Material m = g.getType(x, i, z); + Material m = data.getType(x, i, z); if(!m.equals(Material.AIR)) { hit++; @@ -126,7 +127,7 @@ public class GenLayerCaverns extends GenLayer { for(int j = i; j > i - 5; j--) { - if(g.getType(x, j, z).equals(Material.AIR)) + if(data.getType(x, j, z).equals(Material.AIR)) { fail = true; break; @@ -137,12 +138,12 @@ public class GenLayerCaverns extends GenLayer if(!fail) { MB mb = biome.getSurface(wxx, wzx, g.getRTerrain()); - g.setBlock(x, i, z, mb.material, mb.data); + data.setBlock(x, i, z, mb.material, mb.data); } else { - g.setBlock(x, i, z, Material.AIR); + data.setBlock(x, i, z, Material.AIR); } } @@ -151,12 +152,12 @@ public class GenLayerCaverns extends GenLayer if(!fail) { MB mb = biome.getSubSurface(wxx, i, wzx, g.getRTerrain()); - g.setBlock(x, i, z, mb.material, mb.data); + data.setBlock(x, i, z, mb.material, mb.data); } else { - g.setBlock(x, i, z, Material.AIR); + data.setBlock(x, i, z, Material.AIR); } } } diff --git a/src/main/java/ninja/bytecode/iris/generator/layer/GenLayerCaves.java b/src/main/java/ninja/bytecode/iris/generator/layer/GenLayerCaves.java index 4edd67100..bab7a1e63 100644 --- a/src/main/java/ninja/bytecode/iris/generator/layer/GenLayerCaves.java +++ b/src/main/java/ninja/bytecode/iris/generator/layer/GenLayerCaves.java @@ -7,6 +7,7 @@ import org.bukkit.World; import ninja.bytecode.iris.Iris; import ninja.bytecode.iris.generator.IrisGenerator; +import ninja.bytecode.iris.util.AtomicChunkData; import ninja.bytecode.iris.util.GenLayer; import ninja.bytecode.iris.util.PolygonGenerator; import ninja.bytecode.shuriken.math.CNG; @@ -18,7 +19,7 @@ public class GenLayerCaves extends GenLayer private CNG caveGirth; private CNG caveClamp; private PolygonGenerator caveVeins; - + public GenLayerCaves(IrisGenerator iris, World world, Random random, RNG rng) { super(iris, world, random, rng); @@ -27,47 +28,47 @@ public class GenLayerCaves extends GenLayer caveClamp = new CNG(rng.nextParallelRNG(-10000), 1D, 3).scale(0.1422); caveVeins = new PolygonGenerator(rng.nextParallelRNG(-99999), 4, 0.002 * Iris.settings.gen.caveScale, 1, (g) -> g.fractureWith(new CNG(rng.nextParallelRNG(-5555), 1D, 4).scale(0.02), 70)); } - - public void genCaves(double wxx, double wzx, int x, int z, int s, IrisGenerator g) + + public void genCaves(double wxx, double wzx, int x, int z, int s, IrisGenerator g, AtomicChunkData data) { if(!Iris.settings.gen.genCaves) { return; } - + for(double itr = 0; itr < 0.1 * Iris.settings.gen.caveDensity; itr += 0.1) { double thickness = 0.25 + itr + (0.5 * caveClamp.noise(wxx, wzx)); - double size = 3.88D * thickness; + double size = (3.88D * thickness); double variance = 8.34D * thickness; double w = size + (variance * caveGirth.noise(wxx, wzx)); double h = size + (variance * caveGirth.noise(wzx, wxx)); double width = 0; double height = h; double elevation = (caveHeight.noise(wxx + (19949D * itr), wzx - (19949D * itr)) * (350)) - 80; + while(width <= w && height > 1D) { - width+=2; - height-=2; + width += 2; + height -= 2; if(caveVeins.hasBorder(3, width, wxx - (19949D * itr), wzx + (19949D * itr))) { double r = (((caveGirth.noise(wxx, wzx, width)) * variance) + height) / 2D; - int f = 3; - for(int i = (int) -r; i < r && f >= 0; i++) + for(int i = (int) -r; i < r; i++) { if(i + height > s) { break; } - - Material t = g.getType(x, (int) (elevation + i) - 55, z); + + Material t = data.getType(x, (int) (elevation + i) - 55, z); if(t.equals(Material.BEDROCK) || t.equals(Material.WATER) || t.equals(Material.STATIONARY_WATER)) { continue; } - - g.setBlock(x, (int) (elevation + i) - 55, z, Material.AIR); + + data.setBlock(x, (int) (elevation + i) - 55, z, Material.AIR); } } } diff --git a/src/main/java/ninja/bytecode/iris/generator/placer/AtomicParallaxPlacer.java b/src/main/java/ninja/bytecode/iris/generator/placer/AtomicParallaxPlacer.java new file mode 100644 index 000000000..004e7ef38 --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/generator/placer/AtomicParallaxPlacer.java @@ -0,0 +1,41 @@ +package ninja.bytecode.iris.generator.placer; + +import org.bukkit.Location; + +import ninja.bytecode.iris.generator.IrisGenerator; +import ninja.bytecode.iris.util.IrisWorldData; +import ninja.bytecode.iris.util.MB; +import ninja.bytecode.iris.util.ParallaxCache; +import ninja.bytecode.iris.util.Placer; + +public class AtomicParallaxPlacer extends Placer +{ + private IrisWorldData data; + private ParallaxCache cache; + + public AtomicParallaxPlacer(IrisGenerator g, ParallaxCache cache) + { + super(g.getWorld()); + this.data = g.getWorldData(); + this.cache = cache; + } + + @Override + public MB get(Location l) + { + return cache.get(l.getBlockX(), l.getBlockY(), l.getBlockZ()); + } + + @SuppressWarnings("deprecation") + @Override + public void set(Location l, MB mb) + { + data.setBlock(l.getBlockX(), l.getBlockY(), l.getBlockZ(), mb.material.getId(), mb.data); + } + + @Override + public int getHighestY(Location l) + { + return cache.getHeight(l.getBlockX(), l.getBlockZ()); + } +} diff --git a/src/main/java/ninja/bytecode/iris/generator/placer/AtomicPlacer.java b/src/main/java/ninja/bytecode/iris/generator/placer/AtomicPlacer.java deleted file mode 100644 index 871aa4964..000000000 --- a/src/main/java/ninja/bytecode/iris/generator/placer/AtomicPlacer.java +++ /dev/null @@ -1,45 +0,0 @@ -package ninja.bytecode.iris.generator.placer; - -import org.bukkit.Location; -import org.bukkit.World; - -import ninja.bytecode.iris.util.AtomicChunkData; -import ninja.bytecode.iris.util.ChunkPlan; -import ninja.bytecode.iris.util.MB; -import ninja.bytecode.iris.util.Placer; - -public class AtomicPlacer extends Placer -{ - private AtomicChunkData data; - private ChunkPlan plan; - - public AtomicPlacer(World world) - { - super(world); - } - - public void bind(AtomicChunkData data, ChunkPlan plan) - { - this.data = data; - this.plan = plan; - } - - @Override - public MB get(Location l) - { - return MB.of(data.getType(l.getBlockX(), l.getBlockY(), l.getBlockZ()), data.getData(l.getBlockX(), l.getBlockY(), l.getBlockZ())); - } - - @SuppressWarnings("deprecation") - @Override - public void set(Location l, MB mb) - { - data.setBlock(l.getBlockX(), l.getBlockY(), l.getBlockZ(), mb.material.getId(), mb.data); - } - - @Override - public int getHighestY(Location l) - { - return plan.getRealHeight(l.getBlockX(), l.getBlockZ()); - } -} diff --git a/src/main/java/ninja/bytecode/iris/pack/CompiledDimension.java b/src/main/java/ninja/bytecode/iris/pack/CompiledDimension.java index d60550940..3ace9596b 100644 --- a/src/main/java/ninja/bytecode/iris/pack/CompiledDimension.java +++ b/src/main/java/ninja/bytecode/iris/pack/CompiledDimension.java @@ -13,7 +13,10 @@ import org.bukkit.World.Environment; import org.bukkit.block.Biome; import net.md_5.bungee.api.ChatColor; +import ninja.bytecode.iris.generator.genobject.GenObject; import ninja.bytecode.iris.generator.genobject.GenObjectGroup; +import ninja.bytecode.iris.util.SChunkVector; +import ninja.bytecode.iris.util.SBlockVector; import ninja.bytecode.shuriken.collections.GList; import ninja.bytecode.shuriken.collections.GMap; import ninja.bytecode.shuriken.io.CustomOutputStream; @@ -29,6 +32,8 @@ public class CompiledDimension private GList biomes; private GMap biomeCache; private GMap objects; + private SBlockVector maxSize; + private SChunkVector maxChunkSize; public CompiledDimension(IrisDimension dimension) { @@ -36,6 +41,8 @@ public class CompiledDimension biomes = new GList<>(); biomeCache = new GMap<>(); objects = new GMap<>(); + maxSize = new SBlockVector(0, 0, 0); + maxChunkSize = new SChunkVector(0, 0); } public void read(InputStream in) throws IOException @@ -182,4 +189,56 @@ public class CompiledDimension objects.clear(); } + + public void computeObjectSize() + { + int maxWidth = 0; + int maxHeight = 0; + int maxDepth = 0; + + for(GenObjectGroup i : objects.values()) + { + for(GenObject j : i.getSchematics().copy()) + { + maxWidth = j.getW() > maxWidth ? j.getW() : maxWidth; + maxHeight = j.getH() > maxHeight ? j.getH() : maxHeight; + maxDepth = j.getD() > maxDepth ? j.getD() : maxDepth; + } + } + + maxSize = new SBlockVector(maxWidth, maxHeight, maxDepth); + maxChunkSize = new SChunkVector(Math.ceil((double) (maxWidth) / 16D), Math.ceil((double) (maxDepth) / 16D)); + L.i("Max Object Bound is " + maxWidth + ", " + maxHeight + ", " + maxDepth); + L.i("Max Object Region is " + maxChunkSize.getX() + " by " + maxChunkSize.getZ() + " Chunks"); + } + + public static IrisBiome getTheVoid() + { + return theVoid; + } + + public IrisDimension getDimension() + { + return dimension; + } + + public GMap getBiomeCache() + { + return biomeCache; + } + + public GMap getObjects() + { + return objects; + } + + public SBlockVector getMaxSize() + { + return maxSize; + } + + public SChunkVector getMaxChunkSize() + { + return maxChunkSize; + } } diff --git a/src/main/java/ninja/bytecode/iris/util/AtomicChunkData.java b/src/main/java/ninja/bytecode/iris/util/AtomicChunkData.java index 63841ef46..b96d38a9a 100644 --- a/src/main/java/ninja/bytecode/iris/util/AtomicChunkData.java +++ b/src/main/java/ninja/bytecode/iris/util/AtomicChunkData.java @@ -1,5 +1,10 @@ package ninja.bytecode.iris.util; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.lang.reflect.Field; import java.util.concurrent.locks.ReentrantLock; @@ -10,6 +15,8 @@ import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator.ChunkData; import org.bukkit.material.MaterialData; +import mortar.compute.math.M; + public final class AtomicChunkData implements ChunkGenerator.ChunkData { private static final Field t; @@ -35,11 +42,125 @@ public final class AtomicChunkData implements ChunkGenerator.ChunkData private char[] s15; private char[][] m; private World w; + private long lastUse; + private int bits; public AtomicChunkData(World world) { this.maxHeight = world.getMaxHeight(); this.w = world; + bits = 0; + lastUse = M.ms(); + } + + public long getTimeSinceLastUse() + { + return M.ms() - lastUse; + } + + public void read(InputStream in) throws IOException + { + read(in, true); + } + + public void read(InputStream in, boolean ignoreAir) throws IOException + { + DataInputStream din = new DataInputStream(in); + int bits = din.readInt(); + + for(int i = 0; i < 16; i++) + { + int bit = getBit(i); + if((bits & bit) == bit) + { + char[] section = getChunkSection(i << 4, true); + + for(int j = 0; j < section.length; j++) + { + char c = din.readChar(); + + if(c == 0 && ignoreAir) + { + continue; + } + + section[j] = c; + } + } + } + + din.close(); + } + + public void write(OutputStream out) throws IOException + { + DataOutputStream dos = new DataOutputStream(out); + dos.writeInt(getDataBits()); + + for(int i = 0; i < 16; i++) + { + if(hasDataBit(i)) + { + char[] section = getChunkSection(i << 4, false); + for(int j = 0; j < section.length; j++) + { + dos.writeChar(section[j]); + } + } + } + + dos.close(); + } + + public boolean hasDataBit(int section) + { + int b = getBit(section); + return (bits & b) == b; + } + + public void clearDataBits() + { + bits = 0; + } + + public void addDataBit(int section) + { + bits |= getBit(section); + } + + public void removeDataBit(int section) + { + bits ^= getBit(section); + } + + public int getDataBits() + { + return bits; + } + + public int getBit(int index) + { + return (int) (index < 0 ? -1 : Math.pow(2, index)); + } + + public int computeDataBits() + { + int bits = 0; + + for(int i = 0; i < 16; i++) + { + try + { + bits |= sections[i].get(this) != null ? getBit(i) : 0; + } + + catch(Throwable e) + { + + } + } + + return bits; } @Override @@ -80,6 +201,7 @@ public final class AtomicChunkData implements ChunkGenerator.ChunkData @Override public Material getType(int x, int y, int z) { + lastUse = M.ms(); return Material.getMaterial(getTypeId(x, y, z)); } @@ -87,18 +209,27 @@ public final class AtomicChunkData implements ChunkGenerator.ChunkData @Override public MaterialData getTypeAndData(int x, int y, int z) { + lastUse = M.ms(); return getType(x, y, z).getNewData(getData(x, y, z)); } + @SuppressWarnings("deprecation") + public void setBlock(int x, int y, int z, Material blockId, byte data) + { + setBlock(x, y, z, blockId.getId(), data); + } + @Override public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, int blockId) { + lastUse = M.ms(); setRegion(xMin, yMin, zMin, xMax, yMax, zMax, blockId, (byte) 0); } @Override public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, int blockId, int data) { + lastUse = M.ms(); throw new UnsupportedOperationException("AtomicChunkData does not support setting regions"); } @@ -114,6 +245,31 @@ public final class AtomicChunkData implements ChunkGenerator.ChunkData setBlock(x, y, z, (char) (blockId << 4 | data)); } + @SuppressWarnings("deprecation") + public MB getMB(int x, int y, int z) + { + if(x != (x & 0xf) || y < 0 || y >= maxHeight || z != (z & 0xf)) + { + lastUse = M.ms(); + return MB.of(Material.AIR); + } + + char[] section = getChunkSection(y, false); + + if(section == null) + { + lastUse = M.ms(); + return MB.of(Material.AIR); + } + + else + { + lastUse = M.ms(); + char xf = section[(y & 0xf) << 8 | z << 4 | x]; + return MB.of(Material.getMaterial(xf >> 4), xf & 0xf); + } + } + @Override public int getTypeId(int x, int y, int z) { @@ -131,6 +287,7 @@ public final class AtomicChunkData implements ChunkGenerator.ChunkData else { + lastUse = M.ms(); return section[(y & 0xf) << 8 | z << 4 | x] >> 4; } } @@ -140,6 +297,7 @@ public final class AtomicChunkData implements ChunkGenerator.ChunkData { if(x != (x & 0xf) || y < 0 || y >= maxHeight || z != (z & 0xf)) { + lastUse = M.ms(); return (byte) 0; } @@ -147,11 +305,13 @@ public final class AtomicChunkData implements ChunkGenerator.ChunkData if(section == null) { + lastUse = M.ms(); return (byte) 0; } else { + lastUse = M.ms(); return (byte) (section[(y & 0xf) << 8 | z << 4 | x] & 0xf); } } @@ -163,6 +323,7 @@ public final class AtomicChunkData implements ChunkGenerator.ChunkData return; } + lastUse = M.ms(); ReentrantLock l = locks[y >> 4]; l.lock(); getChunkSection(y, true)[(y & 0xf) << 8 | z << 4 | x] = type; @@ -181,6 +342,7 @@ public final class AtomicChunkData implements ChunkGenerator.ChunkData { sf.set(this, new char[h]); section = (char[]) sf.get(this); + addDataBit(s); } return section; @@ -263,4 +425,26 @@ public final class AtomicChunkData implements ChunkGenerator.ChunkData t = x; } + + public void inject(AtomicChunkData data) + { + for(int i = 0; i < 16; i++) + { + if(hasDataBit(i)) + { + char[] fromSection = getChunkSection(i << 4, false); + char[] toSection = data.getChunkSection(i << 4, true); + + for(int j = 0; j < fromSection.length; j++) + { + char x = fromSection[j]; + + if(x != 0) + { + toSection[j] = x; + } + } + } + } + } } \ No newline at end of file diff --git a/src/main/java/ninja/bytecode/iris/util/AtomicMCAData.java b/src/main/java/ninja/bytecode/iris/util/AtomicMCAData.java new file mode 100644 index 000000000..cabef8a5c --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/util/AtomicMCAData.java @@ -0,0 +1,80 @@ +package ninja.bytecode.iris.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.bukkit.World; +import org.jnbt.ByteArrayTag; +import org.jnbt.CompoundTag; +import org.jnbt.NBTInputStream; +import org.jnbt.NBTOutputStream; +import org.jnbt.Tag; + +import ninja.bytecode.shuriken.collections.GMap; + +public class AtomicMCAData +{ + private final World world; + private GMap tag; + + public AtomicMCAData(World world) + { + this.world = world; + tag = new GMap<>(); + } + + public void read(InputStream in) throws IOException + { + NBTInputStream nin = new NBTInputStream(in); + tag = new GMap<>(); + tag.putAll(((CompoundTag) nin.readTag()).getValue()); + nin.close(); + } + + public void write(OutputStream out) throws IOException + { + NBTOutputStream nos = new NBTOutputStream(out); + nos.writeTag(new CompoundTag("imca", tag)); + nos.close(); + } + + public boolean contains(int rx, int rz) + { + return tag.containsKey(rx + "." + rz); + } + + public void delete(int rx, int rz) + { + tag.remove(rx + "." + rz); + } + + public void set(int rx, int rz, AtomicChunkData data) throws IOException + { + ByteArrayOutputStream boas = new ByteArrayOutputStream(); + data.write(boas); + tag.put(rx + "." + rz, new ByteArrayTag(rx + "." + rz, boas.toByteArray())); + } + + public AtomicChunkData get(int rx, int rz) throws IOException + { + if(!contains(rx, rz)) + { + return null; + } + + AtomicChunkData data = new AtomicChunkData(world); + ByteArrayTag btag = (ByteArrayTag) tag.get(rx + "." + rz); + ByteArrayInputStream in = new ByteArrayInputStream(btag.getValue()); + data.read(in); + + return data; + } + + public World getWorld() + { + return world; + } +} diff --git a/src/main/java/ninja/bytecode/iris/util/AtomicWorldData.java b/src/main/java/ninja/bytecode/iris/util/AtomicWorldData.java new file mode 100644 index 000000000..a64514954 --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/util/AtomicWorldData.java @@ -0,0 +1,157 @@ +package ninja.bytecode.iris.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import org.bukkit.World; + +import ninja.bytecode.shuriken.collections.GList; +import ninja.bytecode.shuriken.collections.GMap; + +public class AtomicWorldData +{ + private World world; + private GMap loadedSections; + + public AtomicWorldData(World world) + { + this.world = world; + loadedSections = new GMap<>(); + getSubregionFolder().mkdirs(); + } + + public GList getLoadedRegions() + { + return loadedSections.k(); + } + + public AtomicMCAData getSubregion(int x, int z) throws IOException + { + if(!isSectionLoaded(x, z)) + { + loadedSections.put(new SMCAVector(x, z), loadSection(x, z)); + } + + AtomicMCAData f = loadedSections.get(new SMCAVector(x, z)); + + return f; + } + + public void saveAll() throws IOException + { + for(SMCAVector i : loadedSections.keySet()) + { + saveSection(i); + } + } + + public void unloadAll(boolean save) throws IOException + { + for(SMCAVector i : loadedSections.keySet()) + { + unloadSection(i, save); + } + } + + public void deleteSection(int x, int z) throws IOException + { + unloadSection(x, z, false); + getSubregionFile(x, z).delete(); + } + + public boolean isSectionLoaded(int x, int z) + { + return isSectionLoaded(new SMCAVector(x, z)); + } + + public boolean isSectionLoaded(SMCAVector s) + { + return loadedSections.containsKey(s); + } + + public boolean unloadSection(int x, int z, boolean save) throws IOException + { + return unloadSection(new SMCAVector(x, z), save); + } + + public boolean unloadSection(SMCAVector s, boolean save) throws IOException + { + if(!isSectionLoaded(s)) + { + return false; + } + + if(save) + { + saveSection(s); + } + + loadedSections.remove(s); + return true; + } + + public boolean saveSection(int x, int z) throws IOException + { + return saveSection(new SMCAVector(x, z)); + } + + public boolean saveSection(SMCAVector s) throws IOException + { + if(!isSectionLoaded(s.getX(), s.getZ())) + { + return false; + } + + AtomicMCAData data = loadedSections.get(s); + FileOutputStream fos = new FileOutputStream(getSubregionFile(s.getX(), s.getZ())); + data.write(fos); + fos.close(); + return true; + } + + public AtomicMCAData loadSection(int x, int z) throws IOException + { + if(isSectionLoaded(x, z)) + { + return loadedSections.get(new SMCAVector(x, z)); + } + + File file = getSubregionFile(x, z); + + if(!file.exists()) + { + return createSection(x, z); + } + + FileInputStream fin = new FileInputStream(file); + AtomicMCAData data = new AtomicMCAData(world); + data.read(fin); + fin.close(); + return data; + } + + public AtomicMCAData createSection(int x, int z) + { + if(isSectionLoaded(x, z)) + { + return loadedSections.get(new SMCAVector(x, z)); + } + + AtomicMCAData data = new AtomicMCAData(world); + loadedSections.put(new SMCAVector(x, z), data); + + return data; + } + + public File getSubregionFile(int x, int z) + { + return new File(getSubregionFolder(), "sr." + x + "." + z + ".smca"); + } + + public File getSubregionFolder() + { + return new File(world.getWorldFolder(), "subregion"); + } +} diff --git a/src/main/java/ninja/bytecode/iris/util/ChunkPlan.java b/src/main/java/ninja/bytecode/iris/util/ChunkPlan.java index 2dd5e8b31..8738486b2 100644 --- a/src/main/java/ninja/bytecode/iris/util/ChunkPlan.java +++ b/src/main/java/ninja/bytecode/iris/util/ChunkPlan.java @@ -5,10 +5,10 @@ import ninja.bytecode.shuriken.collections.GMap; public class ChunkPlan { - private final GMap realHeightCache; - private final GMap realWaterHeightCache; - private final GMap heightCache; - private final GMap biomeCache; + private final GMap realHeightCache; + private final GMap realWaterHeightCache; + private final GMap heightCache; + private final GMap biomeCache; public ChunkPlan() { @@ -20,17 +20,17 @@ public class ChunkPlan public IrisBiome getBiome(int x, int z) { - return biomeCache.get(new ChunkedVector(x, z)); + return biomeCache.get(new SChunkVector(x, z)); } public void setBiome(int x, int z, IrisBiome cng) { - biomeCache.put(new ChunkedVector(x, z), cng); + biomeCache.put(new SChunkVector(x, z), cng); } public double getHeight(int x, int z) { - ChunkedVector c = new ChunkedVector(x, z); + SChunkVector c = new SChunkVector(x, z); if(hasHeight(c)) { return heightCache.get(c); @@ -41,7 +41,7 @@ public class ChunkPlan public int getRealHeight(int x, int z) { - ChunkedVector c = new ChunkedVector(x, z); + SChunkVector c = new SChunkVector(x, z); if(realHeightCache.containsKey(c)) { return realHeightCache.get(c); @@ -52,7 +52,7 @@ public class ChunkPlan public int getRealWaterHeight(int x, int z) { - ChunkedVector c = new ChunkedVector(x, z); + SChunkVector c = new SChunkVector(x, z); if(realWaterHeightCache.containsKey(c)) { @@ -62,43 +62,43 @@ public class ChunkPlan return 0; } - public boolean hasHeight(ChunkedVector c) + public boolean hasHeight(SChunkVector c) { return heightCache.containsKey(c); } public boolean hasHeight(int x, int z) { - return hasHeight(new ChunkedVector(x, z)); + return hasHeight(new SChunkVector(x, z)); } - public void setHeight(ChunkedVector c, double h) + public void setHeight(SChunkVector c, double h) { heightCache.put(c, h); } - public void setRealHeight(ChunkedVector c, int h) + public void setRealHeight(SChunkVector c, int h) { realHeightCache.put(c, h); } public void setRealHeight(int x, int z, int h) { - setRealHeight(new ChunkedVector(x, z), h); + setRealHeight(new SChunkVector(x, z), h); } - public void setRealWaterHeight(ChunkedVector c, int h) + public void setRealWaterHeight(SChunkVector c, int h) { realWaterHeightCache.put(c, h); } public void setRealWaterHeight(int x, int z, int h) { - setRealWaterHeight(new ChunkedVector(x, z), h); + setRealWaterHeight(new SChunkVector(x, z), h); } public void setHeight(int x, int z, double h) { - setHeight(new ChunkedVector(x, z), h); + setHeight(new SChunkVector(x, z), h); } } diff --git a/src/main/java/ninja/bytecode/iris/util/GlowingBlock.java b/src/main/java/ninja/bytecode/iris/util/GlowingBlock.java index 08b5592e8..1f00bb7cb 100644 --- a/src/main/java/ninja/bytecode/iris/util/GlowingBlock.java +++ b/src/main/java/ninja/bytecode/iris/util/GlowingBlock.java @@ -10,7 +10,6 @@ import org.bukkit.util.Vector; import mortar.api.nms.NMP; import mortar.api.world.MaterialBlock; import mortar.compute.math.M; -import mortar.util.text.C; public class GlowingBlock { diff --git a/src/main/java/ninja/bytecode/iris/util/IrisWorldData.java b/src/main/java/ninja/bytecode/iris/util/IrisWorldData.java new file mode 100644 index 000000000..218b19eb8 --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/util/IrisWorldData.java @@ -0,0 +1,253 @@ +package ninja.bytecode.iris.util; + +import java.io.IOException; + +import org.bukkit.Bukkit; +import org.bukkit.World; + +import mortar.logic.format.F; +import mortar.util.text.C; +import ninja.bytecode.iris.Iris; +import ninja.bytecode.shuriken.collections.GList; +import ninja.bytecode.shuriken.collections.GMap; +import ninja.bytecode.shuriken.execution.ChronoLatch; +import ninja.bytecode.shuriken.logging.L; + +public class IrisWorldData +{ + private final World world; + private final AtomicWorldData data; + private final GMap loadedChunks; + + public IrisWorldData(World world) + { + this.world = world; + data = new AtomicWorldData(world); + loadedChunks = new GMap<>(); + Bukkit.getScheduler().scheduleSyncRepeatingTask(Iris.instance, this::softUnloadWorld, 200, 20); + } + + private void softUnloadWorld() + { + L.i("Load: " + F.f(getLoadedChunks().size()) + " Chunks in " + F.f(getLoadedRegions().size()) + " Regions"); + + for(SMCAVector i : getLoadedChunks()) + { + try + { + AtomicChunkData d = getChunk(i.getX(), i.getZ()); + if(d.getTimeSinceLastUse() > 10000) + { + unloadChunk(i.getX(), i.getZ(), true); + } + } + + catch(Throwable e) + { + e.printStackTrace(); + } + } + + for(SMCAVector i : getLoadedRegions()) + { + softUnloadRegion(i.getX(), i.getZ()); + } + } + + private boolean softUnloadRegion(int rx, int rz) + { + for(SMCAVector i : loadedChunks.keySet()) + { + if(i.getX() >> 5 == rx && i.getZ() >> 5 == rz) + { + return false; + } + } + + try + { + data.unloadSection(rx, rz, true); + return true; + } + + catch(IOException e) + { + e.printStackTrace(); + L.f(C.RED + "Failed to save Iris Subregion " + C.YELLOW + rx + " " + rz); + } + + return false; + } + + public boolean deleteChunk(int x, int z) + { + if(isChunkLoaded(x, z)) + { + unloadChunk(x, z, false); + } + + try + { + AtomicMCAData region = data.getSubregion(x >> 5, z >> 5); + region.delete(x & 31, z & 31); + return true; + } + + catch(IOException e) + { + L.f(C.RED + "Failed delete chunk " + C.YELLOW + x + " " + z + C.RED.toString() + " -> Failed to get Region " + C.YELLOW + (x >> 5) + " " + (z >> 5)); + e.printStackTrace(); + } + + return false; + } + + public boolean unloadChunk(int x, int z, boolean save) + { + if(!isChunkLoaded(x, z)) + { + return false; + } + + if(save) + { + saveChunk(x, z); + } + + loadedChunks.remove(new SMCAVector(x, z)); + return true; + } + + public boolean saveChunk(int x, int z) + { + if(!isChunkLoaded(x, z)) + { + return false; + } + + try + { + AtomicMCAData region = data.getSubregion(x >> 5, z >> 5); + region.set(x & 31, z & 31, getChunk(x, z)); + return true; + } + + catch(IOException e) + { + L.f(C.RED + "Failed save chunk " + C.YELLOW + x + " " + z + C.RED.toString() + " -> Failed to get Region " + C.YELLOW + (x >> 5) + " " + (z >> 5)); + e.printStackTrace(); + } + + return false; + } + + public AtomicChunkData getOnly(int x, int z) + { + if(!isChunkLoaded(x, z)) + { + return null; + } + + return getChunk(x, z); + } + + public AtomicChunkData getChunk(int x, int z) + { + if(!isChunkLoaded(x, z)) + { + try + { + AtomicMCAData region = data.getSubregion(x >> 5, z >> 5); + + if(region.contains(x & 31, z & 31)) + { + AtomicChunkData chunk = region.get(x & 31, z & 31); + loadedChunks.put(new SMCAVector(x, z), chunk); + } + + else + { + AtomicChunkData data = new AtomicChunkData(world); + loadedChunks.put(new SMCAVector(x, z), data); + } + } + + catch(IOException e) + { + L.f(C.RED + "Failed load chunk " + C.YELLOW + x + " " + z + C.RED.toString() + " -> Failed to get Region " + C.YELLOW + (x >> 5) + " " + (z >> 5)); + e.printStackTrace(); + } + } + + return loadedChunks.get(new SMCAVector(x, z)); + } + + public boolean isChunkLoaded(int x, int z) + { + return loadedChunks.containsKey(new SMCAVector(x, z)); + } + + public void inject(int x, int z, AtomicChunkData data) + { + getChunk(x, z).inject(data); + } + + public boolean exists(int x, int z) + { + try + { + return isChunkLoaded(x, z) || data.getSubregion(x >> 5, z >> 5).contains(x & 31, z & 31); + } + + catch(IOException e) + { + L.f(C.RED + "Failed check chunk " + C.YELLOW + x + " " + z + C.RED.toString() + " -> Failed to get Region " + C.YELLOW + (x >> 5) + " " + (z >> 5)); + e.printStackTrace(); + } + + return false; + } + + public GList getLoadedChunks() + { + return loadedChunks.k(); + } + + public GList getLoadedRegions() + { + return data.getLoadedRegions(); + } + + public void saveAll() + { + for(SMCAVector i : loadedChunks.k()) + { + saveChunk(i.getX(), i.getZ()); + } + + try + { + data.saveAll(); + } + + catch(IOException e) + { + e.printStackTrace(); + } + } + + public void setBlock(int x, int y, int z, int id, byte data) + { + getChunk(x >> 4, z >> 4).setBlock(x & 15, y, z & 15, id, data); + } + + public void dispose() + { + for(SMCAVector i : getLoadedChunks()) + { + unloadChunk(i.getX(), i.getZ(), true); + } + + softUnloadWorld(); + } +} diff --git a/src/main/java/ninja/bytecode/iris/util/ParallaxAnchor.java b/src/main/java/ninja/bytecode/iris/util/ParallaxAnchor.java new file mode 100644 index 000000000..ed7fe2575 --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/util/ParallaxAnchor.java @@ -0,0 +1,44 @@ +package ninja.bytecode.iris.util; + +import ninja.bytecode.iris.pack.IrisBiome; + +public class ParallaxAnchor +{ + private final int height; + private final int water; + private final IrisBiome biome; + private final AtomicChunkData data; + + public ParallaxAnchor(int height, int water, IrisBiome biome, AtomicChunkData data) + { + this.height = height; + this.water = water; + this.biome = biome; + this.data = data; + } + + public AtomicChunkData getData() + { + return data; + } + + public int getWater() + { + return water; + } + + public int getHeight() + { + return height; + } + + public int getWaterHeight() + { + return water; + } + + public IrisBiome getBiome() + { + return biome; + } +} diff --git a/src/main/java/ninja/bytecode/iris/util/ParallaxCache.java b/src/main/java/ninja/bytecode/iris/util/ParallaxCache.java new file mode 100644 index 000000000..073532aa1 --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/util/ParallaxCache.java @@ -0,0 +1,98 @@ +package ninja.bytecode.iris.util; + +import ninja.bytecode.iris.generator.IrisGenerator; +import ninja.bytecode.iris.pack.IrisBiome; +import ninja.bytecode.shuriken.collections.GMap; +import ninja.bytecode.shuriken.collections.GSet; + +public class ParallaxCache +{ + private GMap cachePlan; + private GMap cacheData; + private GSet contains; + private IrisGenerator gen; + + public ParallaxCache(IrisGenerator gen) + { + this.gen = gen; + cacheData = new GMap<>(); + cachePlan = new GMap<>(); + contains = new GSet<>(); + } + + public MB get(int x, int y, int z) + { + SMCAVector s = new SMCAVector(x, z); + SMCAVector c = new SMCAVector(x >> 4, z >> 4); + + if(contains.contains(s) && cacheData.containsKey(c) && cachePlan.containsKey(c) ) + { + return cacheData.get(c).getMB(x & 15, y, z & 15); + } + + createData(x, z, s, c); + + return cacheData.get(c).getMB(x & 15, y, z & 15); + } + + public IrisBiome getBiome(int x, int z) + { + SMCAVector s = new SMCAVector(x, z); + SMCAVector c = new SMCAVector(x >> 4, z >> 4); + + if(contains.contains(s) && cacheData.containsKey(c) && cachePlan.containsKey(c) ) + { + return cachePlan.get(c).getBiome(x & 15, z & 15); + } + + createData(x, z, s, c); + + return cachePlan.get(c).getBiome(x & 15, z & 15); + } + + public int getWaterHeight(int x, int z) + { + SMCAVector s = new SMCAVector(x, z); + SMCAVector c = new SMCAVector(x >> 4, z >> 4); + + if(contains.contains(s) && cacheData.containsKey(c) && cachePlan.containsKey(c) ) + { + return cachePlan.get(c).getRealWaterHeight(x & 15, z & 15); + } + + createData(x, z, s, c); + + return cachePlan.get(c).getRealWaterHeight(x & 15, z & 15); + } + + public int getHeight(int x, int z) + { + SMCAVector s = new SMCAVector(x, z); + SMCAVector c = new SMCAVector(x >> 4, z >> 4); + + if(contains.contains(s) && cacheData.containsKey(c) && cachePlan.containsKey(c) ) + { + return cachePlan.get(c).getRealHeight(x & 15, z & 15); + } + + createData(x, z, s, c); + + return cachePlan.get(c).getRealHeight(x & 15, z & 15); + } + + private void createData(int x, int z, SMCAVector s, SMCAVector c) + { + if(!cacheData.containsKey(c)) + { + cacheData.put(c, new AtomicChunkData(gen.getWorld())); + } + + if(!cachePlan.containsKey(c)) + { + cachePlan.put(c, new ChunkPlan()); + } + + gen.computeAnchor(x, z, cachePlan.get(c), cacheData.get(c)); + contains.add(s); + } +} diff --git a/src/main/java/ninja/bytecode/iris/util/ParallaxWorldGenerator.java b/src/main/java/ninja/bytecode/iris/util/ParallaxWorldGenerator.java new file mode 100644 index 000000000..ec0c5b0f3 --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/util/ParallaxWorldGenerator.java @@ -0,0 +1,190 @@ +package ninja.bytecode.iris.util; + +import java.util.Random; + +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.event.world.WorldSaveEvent; +import org.bukkit.event.world.WorldUnloadEvent; + +import mortar.api.nms.NMP; +import ninja.bytecode.iris.Iris; +import ninja.bytecode.iris.controller.TimingsController; +import ninja.bytecode.shuriken.bench.PrecisionStopwatch; +import ninja.bytecode.shuriken.collections.GSet; +import ninja.bytecode.shuriken.execution.ChronoLatch; +import ninja.bytecode.shuriken.execution.TaskExecutor.TaskGroup; +import ninja.bytecode.shuriken.format.F; +import ninja.bytecode.shuriken.math.RNG; + +public abstract class ParallaxWorldGenerator extends ParallelChunkGenerator implements Listener +{ + private World world; + private IrisWorldData data; + private RNG rMaster; + private AtomicChunkData buffer; + private GSet fix; + private ChronoLatch cl; + + @Override + public final void init(World world, Random random) + { + this.world = world; + cl = new ChronoLatch(3000); + fix = new GSet<>(); + buffer = new AtomicChunkData(world); + this.data = new IrisWorldData(world); + this.rMaster = new RNG(world.getSeed() + 1); + onInit(world, rMaster.nextParallelRNG(1)); + Bukkit.getPluginManager().registerEvents(this, Iris.instance); + } + + @EventHandler + public void on(ChunkLoadEvent e) + { + if(e.getWorld().equals(world)) + { + NMP.host.relight(e.getChunk()); + Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> fix.add(e.getChunk()), 20); + + if(cl.flip()) + { + for(Chunk i : fix) + { + for(Player f : e.getWorld().getPlayers()) + { + NMP.CHUNK.refreshIgnorePosition(f, i); + } + } + + fix.clear(); + } + } + } + + @EventHandler + public void on(WorldUnloadEvent e) + { + if(e.getWorld().equals(world)) + { + getWorldData().dispose(); + onUnload(); + } + } + + @EventHandler + public void on(WorldSaveEvent e) + { + if(e.getWorld().equals(world)) + { + getWorldData().saveAll(); + } + } + + public ParallaxAnchor computeAnchor(int wx, int wz, ChunkPlan heightBuffer, AtomicChunkData data) + { + onGenColumn(wx, wz, wx & 15, wz & 15, heightBuffer, data); + + return new ParallaxAnchor(heightBuffer.getRealHeight(wx & 15, wz & 15), heightBuffer.getRealWaterHeight(wx & 15, wz & 15), heightBuffer.getBiome(wx & 15, wz & 15), data); + } + + public ParallaxAnchor computeAnchor(int wx, int wz) + { + ChunkPlan heightBuffer = new ChunkPlan(); + onGenColumn(wx, wz, wx & 15, wz & 15, heightBuffer, buffer); + + return new ParallaxAnchor(heightBuffer.getRealHeight(wx & 15, wz & 15), heightBuffer.getRealWaterHeight(wx & 15, wz & 15), heightBuffer.getBiome(wx & 15, wz & 15), buffer); + } + + @Override + public final ChunkPlan initChunk(World world, int x, int z, Random random) + { + PrecisionStopwatch ps = PrecisionStopwatch.start(); + TaskGroup g = startWork(); + int gg = 0; + int gx = 0; + for(int ii = -(getParallaxSize().getX() / 2) - 1; ii < (getParallaxSize().getX() / 2) + 1; ii++) + { + int i = ii; + + for(int jj = -(getParallaxSize().getZ() / 2) - 1; jj < (getParallaxSize().getZ() / 2) + 1; jj++) + { + gx++; + int j = jj; + int cx = x + i; + int cz = z + j; + + if(!getWorldData().exists(cx, cz)) + { + g.queue(() -> + { + onGenParallax(cx, cz, getRMaster(cx, cz, -59328)); + getWorldData().getChunk(cx, cz); + }); + + gg++; + } + } + } + + double a = ps.getMilliseconds(); + double b = g.execute().timeElapsed; + + System.out.println("MS: " + F.duration(Iris.getController(TimingsController.class).getResult("terrain"), 2) + " \tQMS: " + F.duration(a, 2) + " " + " \tEMS: " + F.duration(b, 2) + "\tSCG: " + gg + " / " + gx + " (" + F.pc(((double) gg / (double) gx)) + ") " + " \tTC: " + F.f(getWorldData().getLoadedChunks().size()) + " \tTR: " + getWorldData().getLoadedRegions().size()); + + return onInitChunk(world, x, z, random); + } + + @Override + public final void postChunk(World world, int x, int z, Random random, AtomicChunkData data, ChunkPlan plan) + { + getWorldData().inject(x, z, data); + onPostChunk(world, x, z, random, data, plan); + } + + @Override + public final Biome genColumn(int wx, int wz, int x, int z, ChunkPlan plan, AtomicChunkData data) + { + return onGenColumn(wx, wz, x, z, plan, data); + } + + public World getWorld() + { + return world; + } + + public IrisWorldData getWorldData() + { + return data; + } + + public RNG getRMaster() + { + return rMaster; + } + + public RNG getRMaster(int x, int z, int signature) + { + return rMaster.nextParallelRNG((int) (signature + x * z + z + x * 2.12)); + } + + protected abstract void onUnload(); + + protected abstract SChunkVector getParallaxSize(); + + public abstract void onGenParallax(int x, int z, Random random); + + public abstract void onInit(World world, Random random); + + public abstract ChunkPlan onInitChunk(World world, int x, int z, Random random); + + public abstract Biome onGenColumn(int wx, int wz, int x, int z, ChunkPlan plan, AtomicChunkData data2); + + public abstract void onPostChunk(World world, int x, int z, Random random, AtomicChunkData data, ChunkPlan plan); +} diff --git a/src/main/java/ninja/bytecode/iris/util/ParallelChunkGenerator.java b/src/main/java/ninja/bytecode/iris/util/ParallelChunkGenerator.java index 7fc6a7907..5a60156e6 100644 --- a/src/main/java/ninja/bytecode/iris/util/ParallelChunkGenerator.java +++ b/src/main/java/ninja/bytecode/iris/util/ParallelChunkGenerator.java @@ -8,7 +8,6 @@ import org.bukkit.World; import org.bukkit.block.Biome; import org.bukkit.generator.ChunkGenerator; -import mortar.logic.queue.ChronoLatch; import ninja.bytecode.iris.Iris; import ninja.bytecode.iris.controller.ExecutionController; import ninja.bytecode.iris.controller.TimingsController; @@ -24,48 +23,64 @@ public abstract class ParallelChunkGenerator extends ChunkGenerator private int j; private int wx; private int wz; - private AtomicChunkData data; private ReentrantLock biomeLock; private TaskGroup tg; - private ChronoLatch el = new ChronoLatch(5000); private boolean ready = false; int cg = 0; private RollingSequence rs = new RollingSequence(512); private World world; private TaskExecutor genPool; + private TaskExecutor genPar; public World getWorld() { return world; } - public Biome generateFullColumn(int a, int b, int c, int d, ChunkPlan p) + public Biome generateFullColumn(int a, int b, int c, int d, ChunkPlan p, AtomicChunkData data) { - return genColumn(a, b, c, d, p); + return genColumn(a, b, c, d, p, data); + } + + public TaskGroup startParallaxWork() + { + if(genPar == null) + { + genPar = Iris.getController(ExecutionController.class).getExecutor(world, "Parallax"); + } + + return genPar.startWork(); + } + + public TaskGroup startWork() + { + if(genPool == null) + { + genPool = Iris.getController(ExecutionController.class).getExecutor(world, "Generator"); + } + + return genPool.startWork(); } public ChunkData generateChunkData(World world, Random random, int x, int z, BiomeGrid biome) { + AtomicChunkData data = new AtomicChunkData(world); + try { Iris.getController(TimingsController.class).started("terrain"); - if(genPool == null) - { - genPool = Iris.getController(ExecutionController.class).getExecutor(world); - } this.world = world; - data = new AtomicChunkData(world); + if(!ready) { biomeLock = new ReentrantLock(); - onInit(world, random); + init(world, random); ready = true; } - tg = genPool.startWork(); + tg = startWork(); O plan = new O(); - for(i = 0; i < 16; i++) { wx = (x << 4) + i; @@ -79,7 +94,7 @@ public abstract class ParallelChunkGenerator extends ChunkGenerator int d = j; tg.queue(() -> { - Biome f = generateFullColumn(a, b, c, d, plan.get()); + Biome f = generateFullColumn(a, b, c, d, plan.get(), data); biomeLock.lock(); biome.setBiome(c, d, f); biomeLock.unlock(); @@ -87,9 +102,9 @@ public abstract class ParallelChunkGenerator extends ChunkGenerator } } - plan.set(onInitChunk(world, x, z, random)); + plan.set(initChunk(world, x, z, random)); TaskResult r = tg.execute(); - onPostChunk(world, x, z, random, data, plan.get()); + postChunk(world, x, z, random, data, plan.get()); rs.put(r.timeElapsed); cg++; Iris.getController(TimingsController.class).stopped("terrain"); @@ -97,61 +112,32 @@ public abstract class ParallelChunkGenerator extends ChunkGenerator catch(Throwable e) { - for(int i = 0; i < 16; i++) + try { - for(int j = 0; j < 16; j++) + for(int i = 0; i < 16; i++) { - data.setBlock(i, 0, j, Material.RED_GLAZED_TERRACOTTA); + for(int j = 0; j < 16; j++) + { + data.setBlock(i, 0, j, Material.RED_GLAZED_TERRACOTTA); + } } } - if(el.flip()) + catch(Throwable ex) { - e.printStackTrace(); + } + e.printStackTrace(); } return data.toChunkData(); } - public abstract void onInit(World world, Random random); + public abstract void init(World world, Random random); - public abstract ChunkPlan onInitChunk(World world, int x, int z, Random random); + public abstract ChunkPlan initChunk(World world, int x, int z, Random random); - public abstract void onPostChunk(World world, int x, int z, Random random, AtomicChunkData data, ChunkPlan plan); + public abstract void postChunk(World world, int x, int z, Random random, AtomicChunkData data, ChunkPlan plan); - public abstract Biome genColumn(int wx, int wz, int x, int z, ChunkPlan plan); - - public abstract void decorateColumn(int wx, int wz, int x, int z, ChunkPlan plan); - - public void setBlock(int x, int y, int z, Material b) - { - setBlock(x, y, z, b, (byte) 0); - } - - @SuppressWarnings("deprecation") - public void setBlock(int x, int y, int z, Material b, byte d) - { - setBlock(x, y, z, b.getId(), d); - } - - public void setBlock(int x, int y, int z, int b) - { - setBlock(x, y, z, b, (byte) 0); - } - - public void setBlock(int x, int y, int z, int b, byte d) - { - data.setBlock(x, y, z, b, d); - } - - public Material getType(int x, int y, int z) - { - return data.getType(x, y, z); - } - - public byte getData(int x, int y, int z) - { - return data.getData(x, y, z); - } + public abstract Biome genColumn(int wx, int wz, int x, int z, ChunkPlan plan, AtomicChunkData data); } \ No newline at end of file diff --git a/src/main/java/ninja/bytecode/iris/util/ChunkedVector.java b/src/main/java/ninja/bytecode/iris/util/SChunkVector.java similarity index 79% rename from src/main/java/ninja/bytecode/iris/util/ChunkedVector.java rename to src/main/java/ninja/bytecode/iris/util/SChunkVector.java index 5bf2bc789..31e1c080a 100644 --- a/src/main/java/ninja/bytecode/iris/util/ChunkedVector.java +++ b/src/main/java/ninja/bytecode/iris/util/SChunkVector.java @@ -1,28 +1,28 @@ package ninja.bytecode.iris.util; -public class ChunkedVector +public class SChunkVector { private byte x; private byte z; - public ChunkedVector(int x, int z) + public SChunkVector(int x, int z) { this.x = (byte) (x); this.z = (byte) (z); } - public ChunkedVector(byte x, byte z) + public SChunkVector(byte x, byte z) { this.x = x; this.z = z; } - public ChunkedVector(double x, double z) + public SChunkVector(double x, double z) { this((int) Math.round(x), (int) Math.round(z)); } - public ChunkedVector() + public SChunkVector() { this((byte) 0, (byte) 0); } @@ -66,13 +66,11 @@ public class ChunkedVector return false; if(getClass() != obj.getClass()) return false; - ChunkedVector other = (ChunkedVector) obj; + SChunkVector other = (SChunkVector) obj; if(x != other.x) return false; if(z != other.z) return false; return true; } - - } diff --git a/src/main/java/ninja/bytecode/iris/util/SMCAVector.java b/src/main/java/ninja/bytecode/iris/util/SMCAVector.java new file mode 100644 index 000000000..1becfc7d1 --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/util/SMCAVector.java @@ -0,0 +1,61 @@ +package ninja.bytecode.iris.util; + +import java.util.Objects; + +public class SMCAVector +{ + private int x; + private int z; + + public SMCAVector(int x, int z) + { + this.x = x; + this.z = z; + } + + public SMCAVector() + { + this(0, 0); + } + + public int getX() + { + return x; + } + + public void setX(int x) + { + this.x = x; + } + + public int getZ() + { + return z; + } + + public void setZ(int z) + { + this.z = z; + } + + @Override + public int hashCode() + { + return Objects.hash(x, z); + } + + @Override + public boolean equals(Object obj) + { + if(this == obj) + { + return true; + } + if(!(obj instanceof SMCAVector)) + { + return false; + } + SMCAVector other = (SMCAVector) obj; + return x == other.x && z == other.z; + } +} diff --git a/src/main/java/org/jnbt/ByteArrayTag.java b/src/main/java/org/jnbt/ByteArrayTag.java new file mode 100644 index 000000000..9b88ae90e --- /dev/null +++ b/src/main/java/org/jnbt/ByteArrayTag.java @@ -0,0 +1,82 @@ +package org.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * The TAG_Byte_Array tag. + * + * @author Graham Edgecombe + * + */ +public final class ByteArrayTag extends Tag { + + /** + * The value. + */ + private final byte[] value; + + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public ByteArrayTag(String name, byte[] value) { + super(name); + this.value = value; + } + + @Override + public byte[] getValue() { + return value; + } + + @Override + public String toString() { + StringBuilder hex = new StringBuilder(); + for (byte b : value) { + String hexDigits = Integer.toHexString(b).toUpperCase(); + if (hexDigits.length() == 1) { + hex.append("0"); + } + hex.append(hexDigits).append(" "); + } + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + return "TAG_Byte_Array" + append + ": " + hex.toString(); + } + +} diff --git a/src/main/java/org/jnbt/ByteTag.java b/src/main/java/org/jnbt/ByteTag.java new file mode 100644 index 000000000..f259f9b2a --- /dev/null +++ b/src/main/java/org/jnbt/ByteTag.java @@ -0,0 +1,74 @@ +package org.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * The TAG_Byte tag. + * + * @author Graham Edgecombe + * + */ +public final class ByteTag extends Tag { + + /** + * The value. + */ + private final byte value; + + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public ByteTag(String name, byte value) { + super(name); + this.value = value; + } + + @Override + public Byte getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + return "TAG_Byte" + append + ": " + value; + } + +} diff --git a/src/main/java/org/jnbt/CompoundTag.java b/src/main/java/org/jnbt/CompoundTag.java new file mode 100644 index 000000000..30225f8dd --- /dev/null +++ b/src/main/java/org/jnbt/CompoundTag.java @@ -0,0 +1,83 @@ +package org.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +import java.util.Collections; +import java.util.Map; + +/** + * The TAG_Compound tag. + * + * @author Graham Edgecombe + * + */ +public final class CompoundTag extends Tag { + + /** + * The value. + */ + private final Map value; + + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public CompoundTag(String name, Map value) { + super(name); + this.value = Collections.unmodifiableMap(value); + } + + @Override + public Map getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + StringBuilder bldr = new StringBuilder(); + bldr.append("TAG_Compound" + append + ": " + value.size() + " entries\r\n{\r\n"); + for (Map.Entry entry : value.entrySet()) { + bldr.append(" " + entry.getValue().toString().replaceAll("\r\n", "\r\n ") + "\r\n"); + } + bldr.append("}"); + return bldr.toString(); + } + +} diff --git a/src/main/java/org/jnbt/DoubleTag.java b/src/main/java/org/jnbt/DoubleTag.java new file mode 100644 index 000000000..31327f0c7 --- /dev/null +++ b/src/main/java/org/jnbt/DoubleTag.java @@ -0,0 +1,74 @@ +package org.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * The TAG_Double tag. + * + * @author Graham Edgecombe + * + */ +public final class DoubleTag extends Tag { + + /** + * The value. + */ + private final double value; + + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public DoubleTag(String name, double value) { + super(name); + this.value = value; + } + + @Override + public Double getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + return "TAG_Double" + append + ": " + value; + } + +} diff --git a/src/main/java/org/jnbt/EndTag.java b/src/main/java/org/jnbt/EndTag.java new file mode 100644 index 000000000..0420ccf83 --- /dev/null +++ b/src/main/java/org/jnbt/EndTag.java @@ -0,0 +1,60 @@ +package org.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * The TAG_End tag. + * + * @author Graham Edgecombe + * + */ +public final class EndTag extends Tag { + + /** + * Creates the tag. + */ + public EndTag() { + super(""); + } + + @Override + public Object getValue() { + return null; + } + + @Override + public String toString() { + return "TAG_End"; + } + +} diff --git a/src/main/java/org/jnbt/FloatTag.java b/src/main/java/org/jnbt/FloatTag.java new file mode 100644 index 000000000..fc7e4cb2a --- /dev/null +++ b/src/main/java/org/jnbt/FloatTag.java @@ -0,0 +1,74 @@ +package org.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * The TAG_Float tag. + * + * @author Graham Edgecombe + * + */ +public final class FloatTag extends Tag { + + /** + * The value. + */ + private final float value; + + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public FloatTag(String name, float value) { + super(name); + this.value = value; + } + + @Override + public Float getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + return "TAG_Float" + append + ": " + value; + } + +} diff --git a/src/main/java/org/jnbt/IntArrayTag.java b/src/main/java/org/jnbt/IntArrayTag.java new file mode 100644 index 000000000..d5ef03ebd --- /dev/null +++ b/src/main/java/org/jnbt/IntArrayTag.java @@ -0,0 +1,76 @@ +package org.jnbt; + +import java.util.Arrays; + +/* + * JNBT License + * + * Copyright (c) 2015 Neil Wightman + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * The TAG_Int_Array tag. + * + * @author Neil Wightman + * + */ +public final class IntArrayTag extends Tag { + + /** + * The value. + */ + private final int[] value; + + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public IntArrayTag(String name, int[] value) { + super(name); + this.value = value; + } + + @Override + public int[] getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + return "TAG_Int_Array" + append + ": " + Arrays.toString(value); + } + +} diff --git a/src/main/java/org/jnbt/IntTag.java b/src/main/java/org/jnbt/IntTag.java new file mode 100644 index 000000000..798f8e0e2 --- /dev/null +++ b/src/main/java/org/jnbt/IntTag.java @@ -0,0 +1,74 @@ +package org.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * The TAG_Int tag. + * + * @author Graham Edgecombe + * + */ +public final class IntTag extends Tag { + + /** + * The value. + */ + private final int value; + + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public IntTag(String name, int value) { + super(name); + this.value = value; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + return "TAG_Int" + append + ": " + value; + } + +} diff --git a/src/main/java/org/jnbt/ListTag.java b/src/main/java/org/jnbt/ListTag.java new file mode 100644 index 000000000..e12eb54c4 --- /dev/null +++ b/src/main/java/org/jnbt/ListTag.java @@ -0,0 +1,99 @@ +package org.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +import java.util.Collections; +import java.util.List; + +/** + * The TAG_List tag. + * + * @author Graham Edgecombe + * + */ +public final class ListTag extends Tag { + + /** + * The type. + */ + private final Class type; + + /** + * The value. + */ + private final List value; + + /** + * Creates the tag. + * + * @param name The name. + * @param type The type of item in the list. + * @param value The value. + */ + public ListTag(String name, Class type, List value) { + super(name); + this.type = type; + this.value = Collections.unmodifiableList(value); + } + + /** + * Gets the type of item in this list. + * + * @return The type of item in this list. + */ + public Class getType() { + return type; + } + + @Override + public List getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + StringBuilder bldr = new StringBuilder(); + bldr.append("TAG_List" + append + ": " + value.size() + " entries of type " + NBTUtils.getTypeName(type) + "\r\n{\r\n"); + for (Tag t : value) { + bldr.append(" " + t.toString().replaceAll("\r\n", "\r\n ") + "\r\n"); + } + bldr.append("}"); + return bldr.toString(); + } + +} diff --git a/src/main/java/org/jnbt/LongTag.java b/src/main/java/org/jnbt/LongTag.java new file mode 100644 index 000000000..a7d4d7939 --- /dev/null +++ b/src/main/java/org/jnbt/LongTag.java @@ -0,0 +1,74 @@ +package org.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * The TAG_Long tag. + * + * @author Graham Edgecombe + * + */ +public final class LongTag extends Tag { + + /** + * The value. + */ + private final long value; + + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public LongTag(String name, long value) { + super(name); + this.value = value; + } + + @Override + public Long getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + return "TAG_Long" + append + ": " + value; + } + +} diff --git a/src/main/java/org/jnbt/NBTConstants.java b/src/main/java/org/jnbt/NBTConstants.java new file mode 100644 index 000000000..4696afcc5 --- /dev/null +++ b/src/main/java/org/jnbt/NBTConstants.java @@ -0,0 +1,77 @@ +package org.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2015 Neil Wightman + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +import java.nio.charset.Charset; + +/** + * Changes : Neil Wightman - Support 19133 Tag_Int_Array tag + */ +/** + * A class which holds constant values. + * + * @author Graham Edgecombe + * + */ +public final class NBTConstants { + + /** + * The character set used by NBT (UTF-8). + */ + public static final Charset CHARSET = Charset.forName("UTF-8"); + + /** + * Tag type constants. + */ + public static final int TYPE_END = 0, + TYPE_BYTE = 1, + TYPE_SHORT = 2, + TYPE_INT = 3, + TYPE_LONG = 4, + TYPE_FLOAT = 5, + TYPE_DOUBLE = 6, + TYPE_BYTE_ARRAY = 7, + TYPE_STRING = 8, + TYPE_LIST = 9, + TYPE_COMPOUND = 10, + TYPE_INT_ARRAY = 11; + + /** + * Default private constructor. + */ + private NBTConstants() { + + } + +} diff --git a/src/main/java/org/jnbt/NBTInputStream.java b/src/main/java/org/jnbt/NBTInputStream.java new file mode 100644 index 000000000..676666bd4 --- /dev/null +++ b/src/main/java/org/jnbt/NBTInputStream.java @@ -0,0 +1,205 @@ +package org.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2015 Neil Wightman + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +import java.io.Closeable; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.GZIPInputStream; + +/** + * Changes : + * Neil Wightman - Support 19133 Tag_Int_Array tag + */ + +/** + *

+ * This class reads NBT, or + * Named Binary Tag streams, and produces an object graph of subclasses of the Tag object.

+ * + *

+ * The NBT format was created by Markus Persson, and the specification may be found at + * http://www.minecraft.net/docs/NBT.txt.

+ * + * @author Graham Edgecombe + * + */ +public final class NBTInputStream implements Closeable { + + /** + * The data input stream. + */ + private final DataInputStream is; + + /** + * Create a new NBTInputStream, which will source its data from the specified input stream. + * @param is The output stream + */ + public NBTInputStream(DataInputStream is) { + this.is = is; + } + + /** + * Creates a new NBTInputStream, which will source its data from the specified input stream. + * The stream will be decompressed using GZIP. + * + * @param is The input stream. + * @throws IOException if an I/O error occurs. + */ + public NBTInputStream(InputStream is) throws IOException { + this.is = new DataInputStream(new GZIPInputStream(is)); + } + + /** + * Reads an NBT tag from the stream. + * + * @return The tag that was read. + * @throws IOException if an I/O error occurs. + */ + public Tag readTag() throws IOException { + return readTag(0); + } + + /** + * Reads an NBT from the stream. + * + * @param depth The depth of this tag. + * @return The tag that was read. + * @throws IOException if an I/O error occurs. + */ + private Tag readTag(int depth) throws IOException { + int type = is.readByte() & 0xFF; + + String name; + if (type != NBTConstants.TYPE_END) { + int nameLength = is.readShort() & 0xFFFF; + byte[] nameBytes = new byte[nameLength]; + is.readFully(nameBytes); + name = new String(nameBytes, NBTConstants.CHARSET); + } else { + name = ""; + } + + return readTagPayload(type, name, depth); + } + + /** + * Reads the payload of a tag, given the name and type. + * + * @param type The type. + * @param name The name. + * @param depth The depth. + * @return The tag. + * @throws IOException if an I/O error occurs. + */ + private Tag readTagPayload(int type, String name, int depth) throws IOException { + switch (type) { + case NBTConstants.TYPE_END: + if (depth == 0) { + throw new IOException("TAG_End found without a TAG_Compound/TAG_List tag preceding it."); + } else { + return new EndTag(); + } + case NBTConstants.TYPE_BYTE: + return new ByteTag(name, is.readByte()); + case NBTConstants.TYPE_SHORT: + return new ShortTag(name, is.readShort()); + case NBTConstants.TYPE_INT: + return new IntTag(name, is.readInt()); + case NBTConstants.TYPE_LONG: + return new LongTag(name, is.readLong()); + case NBTConstants.TYPE_FLOAT: + return new FloatTag(name, is.readFloat()); + case NBTConstants.TYPE_DOUBLE: + return new DoubleTag(name, is.readDouble()); + case NBTConstants.TYPE_BYTE_ARRAY: + int length = is.readInt(); + byte[] bytes = new byte[length]; + is.readFully(bytes); + return new ByteArrayTag(name, bytes); + case NBTConstants.TYPE_STRING: + length = is.readShort(); + bytes = new byte[length]; + is.readFully(bytes); + return new StringTag(name, new String(bytes, NBTConstants.CHARSET)); + case NBTConstants.TYPE_LIST: + int childType = is.readByte(); + length = is.readInt(); + + List tagList = new ArrayList(); + for (int i = 0; i < length; i++) { + Tag tag = readTagPayload(childType, "", depth + 1); + if (tag instanceof EndTag) { + throw new IOException("TAG_End not permitted in a list."); + } + tagList.add(tag); + } + + return new ListTag(name, NBTUtils.getTypeClass(childType), tagList); + case NBTConstants.TYPE_COMPOUND: + Map tagMap = new HashMap(); + while (true) { + Tag tag = readTag(depth + 1); + if (tag instanceof EndTag) { + break; + } else { + tagMap.put(tag.getName(), tag); + } + } + + return new CompoundTag(name, tagMap); + case NBTConstants.TYPE_INT_ARRAY: + length = is.readInt(); + int[] value = new int[length]; + for (int i = 0; i < length; i++) { + value[i] = is.readInt(); + } + return new IntArrayTag(name, value); + default: + throw new IOException("Invalid tag type: " + type + "."); + } + } + + @Override + public void close() throws IOException { + is.close(); + } + +} diff --git a/src/main/java/org/jnbt/NBTOutputStream.java b/src/main/java/org/jnbt/NBTOutputStream.java new file mode 100644 index 000000000..b7db3f745 --- /dev/null +++ b/src/main/java/org/jnbt/NBTOutputStream.java @@ -0,0 +1,301 @@ +package org.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2015 Neil Wightman + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +import java.io.Closeable; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.zip.GZIPOutputStream; +/** + * Changes : Neil Wightman - Support 19133 Tag_Int_Array tag + */ + +/** + *

+ * This class writes NBT, or + * Named Binary Tag Tag objects to an underlying OutputStream.

+ * + *

+ * The NBT format was created by Markus Persson, and the specification may be found at + * http://www.minecraft.net/docs/NBT.txt.

+ * + * @author Graham Edgecombe + * + */ +public final class NBTOutputStream implements Closeable { + + /** + * The output stream. + */ + private final DataOutputStream os; + + /** + * Create a new NBTOutputStream, which will write data to the specified underlying output stream. + * @param os The output stream + */ + public NBTOutputStream(DataOutputStream os) { + this.os = os; + } + + /** + * Creates a new NBTOutputStream, which will write data to the specified underlying output stream. + * the stream will be compressed using GZIP. + * + * @param os The output stream. + * @throws IOException if an I/O error occurs. + */ + public NBTOutputStream(OutputStream os) throws IOException { + this.os = new DataOutputStream(new GZIPOutputStream(os)); + } + + /** + * Writes a tag. + * + * @param tag The tag to write. + * @throws IOException if an I/O error occurs. + */ + public void writeTag(Tag tag) throws IOException { + int type = NBTUtils.getTypeCode(tag.getClass()); + String name = tag.getName(); + byte[] nameBytes = name.getBytes(NBTConstants.CHARSET); + + os.writeByte(type); + os.writeShort(nameBytes.length); + os.write(nameBytes); + + if (type == NBTConstants.TYPE_END) { + throw new IOException("Named TAG_End not permitted."); + } + + writeTagPayload(tag); + } + + /** + * Writes tag payload. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeTagPayload(Tag tag) throws IOException { + int type = NBTUtils.getTypeCode(tag.getClass()); + switch (type) { + case NBTConstants.TYPE_END: + writeEndTagPayload((EndTag) tag); + break; + case NBTConstants.TYPE_BYTE: + writeByteTagPayload((ByteTag) tag); + break; + case NBTConstants.TYPE_SHORT: + writeShortTagPayload((ShortTag) tag); + break; + case NBTConstants.TYPE_INT: + writeIntTagPayload((IntTag) tag); + break; + case NBTConstants.TYPE_LONG: + writeLongTagPayload((LongTag) tag); + break; + case NBTConstants.TYPE_FLOAT: + writeFloatTagPayload((FloatTag) tag); + break; + case NBTConstants.TYPE_DOUBLE: + writeDoubleTagPayload((DoubleTag) tag); + break; + case NBTConstants.TYPE_BYTE_ARRAY: + writeByteArrayTagPayload((ByteArrayTag) tag); + break; + case NBTConstants.TYPE_STRING: + writeStringTagPayload((StringTag) tag); + break; + case NBTConstants.TYPE_LIST: + writeListTagPayload((ListTag) tag); + break; + case NBTConstants.TYPE_COMPOUND: + writeCompoundTagPayload((CompoundTag) tag); + break; + case NBTConstants.TYPE_INT_ARRAY: + writeIntArrayTagPayload((IntArrayTag) tag); + break; + default: + throw new IOException("Invalid tag type: " + type + "."); + } + } + + /** + * Writes a TAG_Byte tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeByteTagPayload(ByteTag tag) throws IOException { + os.writeByte(tag.getValue()); + } + + /** + * Writes a TAG_Byte_Array tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeByteArrayTagPayload(ByteArrayTag tag) throws IOException { + byte[] bytes = tag.getValue(); + os.writeInt(bytes.length); + os.write(bytes); + } + + + /** + * Writes a TAG_Compound tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeCompoundTagPayload(CompoundTag tag) throws IOException { + for (Tag childTag : tag.getValue().values()) { + writeTag(childTag); + } + os.writeByte((byte) 0); // end tag - better way? + } + + /** + * Writes a TAG_List tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeListTagPayload(ListTag tag) throws IOException { + Class clazz = tag.getType(); + List tags = tag.getValue(); + int size = tags.size(); + + os.writeByte(NBTUtils.getTypeCode(clazz)); + os.writeInt(size); + for (int i = 0; i < size; i++) { + writeTagPayload(tags.get(i)); + } + } + + /** + * Writes a TAG_String tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeStringTagPayload(StringTag tag) throws IOException { + byte[] bytes = tag.getValue().getBytes(NBTConstants.CHARSET); + os.writeShort(bytes.length); + os.write(bytes); + } + + /** + * Writes a TAG_Double tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeDoubleTagPayload(DoubleTag tag) throws IOException { + os.writeDouble(tag.getValue()); + } + + /** + * Writes a TAG_Float tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeFloatTagPayload(FloatTag tag) throws IOException { + os.writeFloat(tag.getValue()); + } + + /** + * Writes a TAG_Long tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeLongTagPayload(LongTag tag) throws IOException { + os.writeLong(tag.getValue()); + } + + /** + * Writes a TAG_Int tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeIntTagPayload(IntTag tag) throws IOException { + os.writeInt(tag.getValue()); + } + + /** + * Writes a TAG_Short tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeShortTagPayload(ShortTag tag) throws IOException { + os.writeShort(tag.getValue()); + } + + /** + * Writes a TAG_Empty tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeEndTagPayload(EndTag tag) { + /* empty */ + } + + /** + * Writes a TAG_Int_Array tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeIntArrayTagPayload(IntArrayTag tag) throws IOException { + final int[] values = tag.getValue(); + os.writeInt(values.length); + for(final int value : values) { + os.writeInt(value); + } + } + + @Override + public void close() throws IOException { + os.close(); + } + +} diff --git a/src/main/java/org/jnbt/NBTUtils.java b/src/main/java/org/jnbt/NBTUtils.java new file mode 100644 index 000000000..c4879af10 --- /dev/null +++ b/src/main/java/org/jnbt/NBTUtils.java @@ -0,0 +1,165 @@ +package org.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2015 Neil Wightman + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * Changes : Neil Wightman - Support 19133 Tag_Int_Array tag + */ +/** + * A class which contains NBT-related utility methods. This currently supports reading 19133 but only writing 19132. + * + * @author Graham Edgecombe + * + */ +public final class NBTUtils { + + /** + * Gets the type name of a tag. + * + * @param clazz The tag class. + * @return The type name. + */ + public static String getTypeName(Class clazz) { + if (clazz.equals(ByteArrayTag.class)) { + return "TAG_Byte_Array"; + } else if (clazz.equals(ByteTag.class)) { + return "TAG_Byte"; + } else if (clazz.equals(CompoundTag.class)) { + return "TAG_Compound"; + } else if (clazz.equals(DoubleTag.class)) { + return "TAG_Double"; + } else if (clazz.equals(EndTag.class)) { + return "TAG_End"; + } else if (clazz.equals(FloatTag.class)) { + return "TAG_Float"; + } else if (clazz.equals(IntTag.class)) { + return "TAG_Int"; + } else if (clazz.equals(ListTag.class)) { + return "TAG_List"; + } else if (clazz.equals(LongTag.class)) { + return "TAG_Long"; + } else if (clazz.equals(ShortTag.class)) { + return "TAG_Short"; + } else if (clazz.equals(StringTag.class)) { + return "TAG_String"; + } else if (clazz.equals(IntArrayTag.class)) { + return "TAG_Int_Array"; + } else { + throw new IllegalArgumentException("Invalid tag classs (" + clazz.getName() + ")."); + } + } + + /** + * Gets the type code of a tag class. + * + * @param clazz The tag class. + * @return The type code. + * @throws IllegalArgumentException if the tag class is invalid. + */ + public static int getTypeCode(Class clazz) { + if (clazz.equals(ByteArrayTag.class)) { + return NBTConstants.TYPE_BYTE_ARRAY; + } else if (clazz.equals(ByteTag.class)) { + return NBTConstants.TYPE_BYTE; + } else if (clazz.equals(CompoundTag.class)) { + return NBTConstants.TYPE_COMPOUND; + } else if (clazz.equals(DoubleTag.class)) { + return NBTConstants.TYPE_DOUBLE; + } else if (clazz.equals(EndTag.class)) { + return NBTConstants.TYPE_END; + } else if (clazz.equals(FloatTag.class)) { + return NBTConstants.TYPE_FLOAT; + } else if (clazz.equals(IntTag.class)) { + return NBTConstants.TYPE_INT; + } else if (clazz.equals(ListTag.class)) { + return NBTConstants.TYPE_LIST; + } else if (clazz.equals(LongTag.class)) { + return NBTConstants.TYPE_LONG; + } else if (clazz.equals(ShortTag.class)) { + return NBTConstants.TYPE_SHORT; + } else if (clazz.equals(StringTag.class)) { + return NBTConstants.TYPE_STRING; + } else if (clazz.equals(IntArrayTag.class)) { + return NBTConstants.TYPE_INT_ARRAY; + } else { + throw new IllegalArgumentException("Invalid tag classs (" + clazz.getName() + ")."); + } + } + + /** + * Gets the class of a type of tag. + * + * @param type The type. + * @return The class. + * @throws IllegalArgumentException if the tag type is invalid. + */ + public static Class getTypeClass(int type) { + switch (type) { + case NBTConstants.TYPE_END: + return EndTag.class; + case NBTConstants.TYPE_BYTE: + return ByteTag.class; + case NBTConstants.TYPE_SHORT: + return ShortTag.class; + case NBTConstants.TYPE_INT: + return IntTag.class; + case NBTConstants.TYPE_LONG: + return LongTag.class; + case NBTConstants.TYPE_FLOAT: + return FloatTag.class; + case NBTConstants.TYPE_DOUBLE: + return DoubleTag.class; + case NBTConstants.TYPE_BYTE_ARRAY: + return ByteArrayTag.class; + case NBTConstants.TYPE_STRING: + return StringTag.class; + case NBTConstants.TYPE_LIST: + return ListTag.class; + case NBTConstants.TYPE_COMPOUND: + return CompoundTag.class; + case NBTConstants.TYPE_INT_ARRAY: + return IntArrayTag.class; + default: + throw new IllegalArgumentException("Invalid tag type : " + type + "."); + } + } + + /** + * Default private constructor. + */ + private NBTUtils() { + + } + +} diff --git a/src/main/java/org/jnbt/ShortTag.java b/src/main/java/org/jnbt/ShortTag.java new file mode 100644 index 000000000..cf5448114 --- /dev/null +++ b/src/main/java/org/jnbt/ShortTag.java @@ -0,0 +1,74 @@ +package org.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * The TAG_Short tag. + * + * @author Graham Edgecombe + * + */ +public final class ShortTag extends Tag { + + /** + * The value. + */ + private final short value; + + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public ShortTag(String name, short value) { + super(name); + this.value = value; + } + + @Override + public Short getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + return "TAG_Short" + append + ": " + value; + } + +} diff --git a/src/main/java/org/jnbt/StringTag.java b/src/main/java/org/jnbt/StringTag.java new file mode 100644 index 000000000..80e8ba1d6 --- /dev/null +++ b/src/main/java/org/jnbt/StringTag.java @@ -0,0 +1,74 @@ +package org.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * The TAG_String tag. + * + * @author Graham Edgecombe + * + */ +public final class StringTag extends Tag { + + /** + * The value. + */ + private final String value; + + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public StringTag(String name, String value) { + super(name); + this.value = value; + } + + @Override + public String getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + return "TAG_String" + append + ": " + value; + } + +} diff --git a/src/main/java/org/jnbt/Tag.java b/src/main/java/org/jnbt/Tag.java new file mode 100644 index 000000000..80c7ff6f9 --- /dev/null +++ b/src/main/java/org/jnbt/Tag.java @@ -0,0 +1,73 @@ +package org.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * Represents a single NBT tag. + * + * @author Graham Edgecombe + * + */ +public abstract class Tag { + + /** + * The name of this tag. + */ + private final String name; + + /** + * Creates the tag with the specified name. + * + * @param name The name. + */ + public Tag(String name) { + this.name = name; + } + + /** + * Gets the name of this tag. + * + * @return The name of this tag. + */ + public final String getName() { + return name; + } + + /** + * Gets the value of this tag. + * + * @return The value of this tag. + */ + public abstract Object getValue(); + +}