diff --git a/src/main/java/ninja/bytecode/iris/Iris.java b/src/main/java/ninja/bytecode/iris/Iris.java index 98eff85c7..4a2f04f58 100644 --- a/src/main/java/ninja/bytecode/iris/Iris.java +++ b/src/main/java/ninja/bytecode/iris/Iris.java @@ -4,29 +4,25 @@ import java.util.UUID; import java.util.function.Function; import org.bukkit.Bukkit; -import org.bukkit.ChatColor; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.WorldCreator; -import org.bukkit.block.Biome; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.generator.ChunkGenerator; +import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.util.Vector; import ninja.bytecode.iris.generator.IrisGenerator; -import ninja.bytecode.iris.util.RealBiome; import ninja.bytecode.shuriken.bench.Profiler; import ninja.bytecode.shuriken.collections.GMap; import ninja.bytecode.shuriken.collections.GSet; -import ninja.bytecode.shuriken.execution.J; import ninja.bytecode.shuriken.execution.TaskExecutor; -import ninja.bytecode.shuriken.format.F; -import ninja.bytecode.shuriken.math.M; public class Iris extends JavaPlugin implements Listener { @@ -46,6 +42,7 @@ public class Iris extends JavaPlugin implements Listener gen = new IrisGenerator(); genPool = new TaskExecutor(getTC(), settings.performance.threadPriority, "Iris Generator"); getServer().getPluginManager().registerEvents((Listener) this, this); + new WandManager(); // Debug world regens GSet ws = new GSet<>(); @@ -90,6 +87,7 @@ public class Iris extends JavaPlugin implements Listener public void onDisable() { genPool.close(); + HandlerList.unregisterAll((Plugin) this); } @Override @@ -101,63 +99,17 @@ public class Iris extends JavaPlugin implements Listener @EventHandler public void on(PlayerCommandPreprocessEvent e) { - if(e.getMessage().toLowerCase().equals("/iris")) + if(e.getMessage().toLowerCase().equals("/iris gen")) { + e.setCancelled(true); World wold = e.getPlayer().getWorld(); World w = createIrisWorld(); e.getPlayer().teleport(new Location(w, 0, 256, 0)); e.getPlayer().setFlying(true); e.getPlayer().setGameMode(GameMode.CREATIVE); - e.setCancelled(true); wold.setAutoSave(false); Bukkit.unloadWorld(wold, false); } - - if(e.getMessage().toLowerCase().equals("/iris info")) - { - e.setCancelled(true); - sendInfo(e.getPlayer()); - - for(Biome i : Biome.values()) - { - J.attempt(() -> System.out.print(new RealBiome(i))); - } - } - } - - private void sendInfo(Player player) - { - for(int i = 0; i < 18; i++) - { - player.sendMessage(""); - } - - GMap> w = values.get(player.getWorld().getName()); - for(String i : w.k()) - { - double value = w.get(i).apply(player.getLocation().toVector()); - String p = i.substring(0, 2); - String v = value + ""; - - if(p.startsWith("%")) - { - v = F.pc(value, Integer.valueOf(p.substring(1))); - } - - if(p.startsWith("D")) - { - v = F.f(value, Integer.valueOf(p.substring(1))); - } - - else if(p.startsWith("^")) - { - double c = M.lerpInverse(-11, 37, value); - double f = 32 + (c * (1.8)); - v = F.f(c, Integer.valueOf(p.substring(1))) + " \u00B0C / " + F.f(f, Integer.valueOf(p.substring(1))) + " \u00B0F"; - } - - player.sendMessage(ChatColor.GREEN + i.substring(2) + ": " + ChatColor.RESET + ChatColor.WHITE + ChatColor.BOLD + v); - } } private World createIrisWorld() diff --git a/src/main/java/ninja/bytecode/iris/Settings.java b/src/main/java/ninja/bytecode/iris/Settings.java index c86a98634..6afcb56ef 100644 --- a/src/main/java/ninja/bytecode/iris/Settings.java +++ b/src/main/java/ninja/bytecode/iris/Settings.java @@ -9,8 +9,8 @@ public class Settings public static class PerformanceSettings { - public PerformanceMode performanceMode = PerformanceMode.HALF_CPU; - public int threadCount = 4; + public PerformanceMode performanceMode = PerformanceMode.MATCH_CPU; + public int threadCount = 12; public int threadPriority = Thread.MAX_PRIORITY; } diff --git a/src/main/java/ninja/bytecode/iris/WandManager.java b/src/main/java/ninja/bytecode/iris/WandManager.java new file mode 100644 index 000000000..903707f6d --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/WandManager.java @@ -0,0 +1,101 @@ +package ninja.bytecode.iris; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; + +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; + +import ninja.bytecode.iris.schematic.Schematic; +import ninja.bytecode.iris.util.WandUtil; + +public class WandManager implements Listener +{ + public WandManager() + { + Bukkit.getPluginManager().registerEvents(this, Iris.instance); + } + + @EventHandler + public void on(PlayerCommandPreprocessEvent e) + { + if(e.getMessage().startsWith("/isave ")) + { + e.setCancelled(true); + Schematic s = WandUtil.createSchematic(e.getPlayer().getInventory().getItemInMainHand(), e.getPlayer().getLocation()); + File f = new File(Iris.instance.getDataFolder(), "schematics/" + e.getMessage().split("\\Q \\E")[1] + ".ish"); + f.getParentFile().mkdirs(); + try + { + FileOutputStream fos = new FileOutputStream(f); + s.write(fos); + e.getPlayer().sendMessage("Done!"); + } + + catch(Throwable e1) + { + e1.printStackTrace(); + } + } + + if(e.getMessage().startsWith("/iload ")) + { + e.setCancelled(true); + Schematic s = new Schematic(1, 1, 1, 1, 1, 1); + File f = new File(Iris.instance.getDataFolder(), "schematics/" + e.getMessage().split("\\Q \\E")[1] + ".ish"); + if(!f.exists()) + { + e.getPlayer().sendMessage("Not Found"); + return; + } + + try + { + FileInputStream fin = new FileInputStream(f); + s.read(fin); + WandUtil.pasteSchematic(s, e.getPlayer().getLocation()); + e.getPlayer().sendMessage("Done!"); + } + + catch(Throwable e1) + { + e1.printStackTrace(); + } + } + + if(e.getMessage().equals("/iris wand")) + { + e.setCancelled(true); + e.getPlayer().getInventory().addItem(WandUtil.createWand()); + } + } + + @EventHandler + public void on(PlayerInteractEvent e) + { + if(e.getHand().equals(EquipmentSlot.HAND) && WandUtil.isWand(e.getPlayer().getInventory().getItemInMainHand())) + { + if(e.getAction().equals(Action.LEFT_CLICK_BLOCK)) + { + e.setCancelled(true); + e.getPlayer().getInventory().setItemInMainHand(WandUtil.update(true, e.getPlayer().getLocation(), e.getPlayer().getInventory().getItemInMainHand())); + + e.getPlayer().updateInventory(); + } + + else if(e.getAction().equals(Action.RIGHT_CLICK_BLOCK)) + { + e.setCancelled(true); + e.getPlayer().getInventory().setItemInMainHand(WandUtil.update(false, e.getPlayer().getLocation(), e.getPlayer().getInventory().getItemInMainHand())); + + e.getPlayer().updateInventory(); + } + } + } +} diff --git a/src/main/java/ninja/bytecode/iris/generator/IrisGenerator.java b/src/main/java/ninja/bytecode/iris/generator/IrisGenerator.java index e527848f5..bea636895 100644 --- a/src/main/java/ninja/bytecode/iris/generator/IrisGenerator.java +++ b/src/main/java/ninja/bytecode/iris/generator/IrisGenerator.java @@ -1,5 +1,6 @@ package ninja.bytecode.iris.generator; +import java.io.File; import java.util.List; import java.util.Random; @@ -12,15 +13,19 @@ import ninja.bytecode.iris.Iris; import ninja.bytecode.iris.generator.biome.IrisBiome; import ninja.bytecode.iris.generator.layer.GenLayerBase; import ninja.bytecode.iris.generator.layer.GenLayerBiome; +import ninja.bytecode.iris.generator.layer.GenLayerCaves; import ninja.bytecode.iris.generator.layer.GenLayerLayeredNoise; import ninja.bytecode.iris.generator.layer.GenLayerRidge; -import ninja.bytecode.iris.generator.populator.PopulatorTrees; +import ninja.bytecode.iris.generator.populator.BiomeBiasSchematicPopulator; +import ninja.bytecode.iris.schematic.Schematic; 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.shuriken.collections.GList; +import ninja.bytecode.shuriken.execution.J; +import ninja.bytecode.shuriken.logging.L; import ninja.bytecode.shuriken.math.M; import ninja.bytecode.shuriken.math.RNG; @@ -32,8 +37,8 @@ public class IrisGenerator extends ParallelChunkGenerator private GenLayerLayeredNoise glLNoise; private GenLayerRidge glRidge; private GenLayerBiome glBiome; + private GenLayerCaves glCaves; private RNG rTerrain; - private RNG rScatter; private World world; @Override @@ -45,11 +50,7 @@ public class IrisGenerator extends ParallelChunkGenerator glLNoise = new GenLayerLayeredNoise(this, world, random, rTerrain.nextParallelRNG(2)); glRidge = new GenLayerRidge(this, world, random, rTerrain.nextParallelRNG(3)); glBiome = new GenLayerBiome(this, world, random, rTerrain.nextParallelRNG(4)); - } - - public World getWorld() - { - return world; + glCaves = new GenLayerCaves(this, world, random, rTerrain.nextParallelRNG(-1)); } @Override @@ -69,8 +70,10 @@ public class IrisGenerator extends ParallelChunkGenerator hv += glLNoise.generateLayer(hv, wxx, wzx); hv -= glRidge.generateLayer(hv, wxx, wzx); int height = (int) Math.round(M.clip(hv, 0D, 1D) * 253); + int max = Math.max(height, seaLevel); + IrisBiome override = null; - for(int i = 0; i < Math.max(height, seaLevel); i++) + for(int i = 0; i < max; i++) { MB mb = new MB(Material.STONE); boolean underwater = i >= height && i < seaLevel; @@ -88,10 +91,43 @@ public class IrisGenerator extends ParallelChunkGenerator if(i == height - 1) { + if(height > 61 && height < glBase.scatterInt(x, i, z, 4) + 65) + { + override = IrisBiome.BEACH; + } + + else if(height < 63) + { + if(i < 36) + { + override = IrisBiome.DEEP_OCEAN; + } + + else if(i < 50) + { + override = IrisBiome.OCEAN; + } + + else + { + override = IrisBiome.LAKE; + } + } + + if(override != null) + { + biome = override; + } + mb = biome.getSurface(wx, wz, rTerrain); } - if(Iris.settings.gen.flatBedrock ? i == 0 : i < glBase.scatterInt(x, i, z, 3)) + if(i == 0) + { + mb = BEDROCK; + } + + if(Iris.settings.gen.flatBedrock ? i == 1 : i < glBase.scatterInt(x, i, z, 3)) { mb = BEDROCK; } @@ -99,6 +135,13 @@ public class IrisGenerator extends ParallelChunkGenerator setBlock(x, i, z, mb.material, mb.data); } + glCaves.genCaves(wxx, wzx, x, z, height, this); + + if(override != null) + { + return override.getRealBiome(); + } + return biome.getRealBiome(); } @@ -114,24 +157,73 @@ public class IrisGenerator extends ParallelChunkGenerator } + @Override + public List getDefaultPopulators(World world) + { + GList p = new GList<>(); + int b = 0; + for(IrisBiome i : IrisBiome.getBiomes()) + { + b++; + L.i("Processing Populators for Biome " + i.getName()); + + for(String j : i.getSchematicGroups().keySet()) + { + p.add(new BiomeBiasSchematicPopulator(i.getSchematicGroups().get(j), i, loadSchematics(j))); + } + } + + J.attempt(() -> p.add(new BiomeBiasSchematicPopulator(5, IrisBiome.JUNGLE, loadSchematics("")))); + + L.i("Initialized " + b + " Biomes and " + p.size() + " Populators"); + L.flush(); + + return p; + } + + private Schematic[] loadSchematics(String folder) + { + File f = new File(Iris.instance.getDataFolder(), "objects/" + folder); + GList s = new GList<>(); + + try + { + if(f.exists() && f.isDirectory()) + { + for(File i : f.listFiles()) + { + if(i.isFile() && i.getName().endsWith(".ish")) + { + s.add(Schematic.load(i)); + } + } + } + } + + catch(Throwable e) + { + + } + + return s.toArray(new Schematic[s.size()]); + } + private double getBiomedHeight(int x, int z, ChunkPlan plan) { - return plan.getHeight(x, z, () -> { + return plan.getHeight(x, z, () -> + { int wx = (int) Math.round((double) x * Iris.settings.gen.horizontalZoom); int wz = (int) Math.round((double) z * Iris.settings.gen.horizontalZoom); IrisBiome biome = glBiome.getBiome(wx * Iris.settings.gen.biomeScale, wz * Iris.settings.gen.biomeScale); double h = Iris.settings.gen.baseHeight + biome.getHeight(); h += (glBase.getHeight(wx, wz) * biome.getAmp()) - (0.33 * biome.getAmp()); - + return h; }); } - @Override - public List getDefaultPopulators(World world) + public World getWorld() { - GList p = new GList(); - p.add(new PopulatorTrees()); - return p; + return world; } } \ No newline at end of file diff --git a/src/main/java/ninja/bytecode/iris/generator/biome/IrisBiome.java b/src/main/java/ninja/bytecode/iris/generator/biome/IrisBiome.java index cb253125e..28b324c4f 100644 --- a/src/main/java/ninja/bytecode/iris/generator/biome/IrisBiome.java +++ b/src/main/java/ninja/bytecode/iris/generator/biome/IrisBiome.java @@ -32,6 +32,10 @@ public class IrisBiome .surface(MB.of(Material.SAND), MB.of(Material.SAND), MB.of(Material.SAND), MB.of(Material.CLAY), MB.of(Material.GRAVEL)) .simplexSurface() .height(-0.03); + public static final IrisBiome LAKE = new IrisBiome("Lake", Biome.OCEAN) + .surface(MB.of(Material.SAND), MB.of(Material.SAND), MB.of(Material.SAND), MB.of(Material.GRAVEL), MB.of(Material.CLAY), MB.of(Material.GRAVEL)) + .simplexSurface() + .height(-0.03); public static final IrisBiome DEEP_OCEAN = new IrisBiome("Deep Ocean", Biome.DEEP_OCEAN) .surface(MB.of(Material.SAND), MB.of(Material.CLAY), MB.of(Material.GRAVEL)) .simplexSurface() @@ -237,7 +241,7 @@ public class IrisBiome simplexScatter = true; return this; } - + public IrisBiome scatterSurface() { scatterSurface = true; @@ -312,9 +316,9 @@ public class IrisBiome }); } - return poly.getChoice(wx * 0.2D, wz * 0.2D); + return poly.getChoice(wx * 0.2D, wz * 0.2D); } - + if(scatterSurface) { if(poly == null) @@ -325,7 +329,7 @@ public class IrisBiome }); } - return poly.getChoice(wx * 0.2D, wz * 0.2D); + return poly.getChoice(wx * 0.2D, wz * 0.2D); } return getSurface().getRandom(); @@ -385,7 +389,12 @@ public class IrisBiome return null; } - public static IrisBiome find(Biome biome) + public static GList getBiomes() + { + return map.v().remove(IrisBiome.ROAD_GRASSY, IrisBiome.ROAD_GRAVEL, IrisBiome.BEACH, IrisBiome.LAKE, IrisBiome.RIVER); + } + + public static IrisBiome findByBiome(Biome biome) { if(map.containsKey(biome)) { @@ -394,4 +403,9 @@ public class IrisBiome return IrisBiome.PLAINS; } + + public GMap getSchematicGroups() + { + return schematicGroups; + } } diff --git a/src/main/java/ninja/bytecode/iris/generator/layer/GenLayerBiome.java b/src/main/java/ninja/bytecode/iris/generator/layer/GenLayerBiome.java index d0427e04d..b4b9ef3ca 100644 --- a/src/main/java/ninja/bytecode/iris/generator/layer/GenLayerBiome.java +++ b/src/main/java/ninja/bytecode/iris/generator/layer/GenLayerBiome.java @@ -33,38 +33,7 @@ public class GenLayerBiome extends GenLayer riverCheck = new CNG(rng.nextParallelRNG(30), 1D, 2).scale(0.00096); pathCheck = new CNG(rng.nextParallelRNG(31), 1D, 1).scale(0.00096); roads = new MaxingGenerator(rng.nextParallelRNG(32), 5, 0.00055, 8, factory); - biomeGenerator = new EnumMaxingGenerator(rng.nextParallelRNG(33), 0.00755 * Iris.settings.gen.biomeScale, 1, - new IrisBiome[] { - IrisBiome.HAUNTED_FOREST, - IrisBiome.FOREST_MOUNTAINS, - IrisBiome.DESERT, - IrisBiome.DESERT_HILLS, - IrisBiome.MESA, - IrisBiome.DESERT_COMBINED, - IrisBiome.SAVANNA, - IrisBiome.SAVANNA_HILLS, - IrisBiome.DESERT_RED, - IrisBiome.JUNGLE, - IrisBiome.JUNGLE_HILLS, - IrisBiome.SWAMP, - IrisBiome.OCEAN, - IrisBiome.PLAINS, - IrisBiome.DECAYING_PLAINS, - IrisBiome.FOREST, - IrisBiome.FOREST_HILLS, - IrisBiome.BIRCH_FOREST, - IrisBiome.BIRCH_FOREST_HILLS, - IrisBiome.ROOFED_FOREST, - IrisBiome.TAIGA, - IrisBiome.EXTREME_HILLS, - IrisBiome.EXTREME_HILLS_TREES, - IrisBiome.TAIGA_COLD, - IrisBiome.TAIGA_COLD_HILLS, - IrisBiome.ICE_FLATS, - IrisBiome.ICE_MOUNTAINS, - IrisBiome.REDWOOD_TAIGA, - IrisBiome.REDWOOD_TAIGA_HILLS, - }, factory); + biomeGenerator = new EnumMaxingGenerator(rng.nextParallelRNG(33), 0.00755 * Iris.settings.gen.biomeScale, 1, IrisBiome.getBiomes().toArray(new IrisBiome[IrisBiome.getBiomes().size()]), factory); //@done } @@ -72,7 +41,7 @@ public class GenLayerBiome extends GenLayer { double x = xx + (fracture.noise(zz, xx) * 1550D); double z = zz - (fracture.noise(xx, zz) * 1550D); - + if(riverCheck.noise(x, z) > 0.75) { if(biomeGenerator.hasBorder(3, 3 + Math.pow(riverCheck.noise(x, z), 1.25) * 16, x, z)) @@ -80,24 +49,24 @@ public class GenLayerBiome extends GenLayer return IrisBiome.RIVER; } } - + IrisBiome cbi = biomeGenerator.getChoice(x, z); - + if(pathCheck.noise(x, z) > 0.33) { IrisBiome road = IrisBiome.ROAD_GRAVEL; - + if(cbi.getSurface().get(0).material.equals(Material.GRASS)) { road = IrisBiome.ROAD_GRASSY; } - + if(Math.abs(road.getHeight() - cbi.getHeight()) < 0.0001 && roads.hasBorder(4, 3, xx, zz)) { return road; } } - + return cbi; } diff --git a/src/main/java/ninja/bytecode/iris/generator/layer/GenLayerCaves.java b/src/main/java/ninja/bytecode/iris/generator/layer/GenLayerCaves.java new file mode 100644 index 000000000..a23bd6286 --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/generator/layer/GenLayerCaves.java @@ -0,0 +1,78 @@ +package ninja.bytecode.iris.generator.layer; + +import java.util.Random; + +import org.bukkit.Material; +import org.bukkit.World; + +import ninja.bytecode.iris.generator.IrisGenerator; +import ninja.bytecode.iris.util.GenLayer; +import ninja.bytecode.iris.util.MaxingGenerator; +import ninja.bytecode.shuriken.math.CNG; +import ninja.bytecode.shuriken.math.RNG; + +public class GenLayerCaves extends GenLayer +{ + private CNG caveHeight; + private CNG caveGirth; + private CNG caveClamp; + private MaxingGenerator caveVeins; + + public GenLayerCaves(IrisGenerator iris, World world, Random random, RNG rng) + { + super(iris, world, random, rng); + caveHeight = new CNG(rng.nextParallelRNG(-100001), 1D, 7).scale(0.00222); + caveGirth = new CNG(rng.nextParallelRNG(-100002), 1D, 12).scale(0.03); + caveClamp = new CNG(rng.nextParallelRNG(-10000), 1D, 3).scale(0.1422); + caveVeins = new MaxingGenerator(rng.nextParallelRNG(-99999), 22, 0.002, 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) + { + for(double itr = 0; itr < 0.2; itr += 0.1) + { + double thickness = 0.25 + itr + (0.5 * caveClamp.noise(wxx, wzx)); + 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; + + if(caveVeins.hasBorder(3, width, wxx - (19949D * itr), wzx + (19949D * itr))) + { + double r = (((caveGirth.noise(wxx, wzx, width)) * variance) + height) / 2D; + + for(int i = (int) -r; i < r; i++) + { + if(i + height > s) + { + break; + } + + Material t = g.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); + } + } + + } + } + } + + @Override + public double generateLayer(double gnoise, double dx, double dz) + { + return gnoise; + } +} diff --git a/src/main/java/ninja/bytecode/iris/generator/populator/BiomeBiasSchematicPopulator.java b/src/main/java/ninja/bytecode/iris/generator/populator/BiomeBiasSchematicPopulator.java new file mode 100644 index 000000000..e8007e478 --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/generator/populator/BiomeBiasSchematicPopulator.java @@ -0,0 +1,33 @@ +package ninja.bytecode.iris.generator.populator; + +import java.util.Random; + +import org.bukkit.Chunk; +import org.bukkit.World; + +import ninja.bytecode.iris.generator.biome.IrisBiome; +import ninja.bytecode.iris.schematic.Schematic; +import ninja.bytecode.iris.util.MB; + +public class BiomeBiasSchematicPopulator extends SurfaceBiasSchematicPopulator +{ + protected IrisBiome biome; + public BiomeBiasSchematicPopulator(double chance, IrisBiome biome, Schematic... schematics) + { + super(chance, schematics); + this.biome = biome; + + for(MB i : biome.getSurface()) + { + surface(i.material); + } + } + + public void doPopulate(World world, Random random, Chunk source, int wx, int wz) + { + if(world.getBiome(wx, wz).equals(biome.getRealBiome())) + { + super.doPopulate(world, random, source, wx, wz); + } + } +} diff --git a/src/main/java/ninja/bytecode/iris/generator/populator/PopulatorTrees.java b/src/main/java/ninja/bytecode/iris/generator/populator/PopulatorTrees.java deleted file mode 100644 index a9c62f354..000000000 --- a/src/main/java/ninja/bytecode/iris/generator/populator/PopulatorTrees.java +++ /dev/null @@ -1,67 +0,0 @@ -package ninja.bytecode.iris.generator.populator; - -import java.util.Random; - -import org.bukkit.Chunk; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.TreeType; -import org.bukkit.World; -import org.bukkit.generator.BlockPopulator; - -import ninja.bytecode.iris.Iris; -import ninja.bytecode.iris.generator.biome.IrisBiome; -import ninja.bytecode.shuriken.bench.PrecisionStopwatch; -import ninja.bytecode.shuriken.math.RollingSequence; - -public class PopulatorTrees extends BlockPopulator -{ - public static RollingSequence timings = new RollingSequence(512); - - @Override - public void populate(World world, Random random, Chunk source) - { - if(!Iris.settings.gen.doTrees) - { - return; - } - - PrecisionStopwatch f = PrecisionStopwatch.start(); - int debuff = 0; - - for(int i = 0; i < 16; i++) - { - if(debuff > 0) - { - debuff--; - continue; - } - - int x = random.nextInt(15) + (source.getX() * 16); - int z = random.nextInt(15) + (source.getZ() * 16); - int y = world.getHighestBlockYAt(x, z); - Location l = new Location(world, x, y, z); - - if(!l.getBlock().getType().isSolid()) - { - l.getBlock().setType(Material.AIR, false); - } - - IrisBiome biome = IrisBiome.find(world.getBiome(x, z)); - TreeType tt = biome.getTreeChanceSingle(); - - if(tt != null) - { - world.generateTree(l, tt); - } - - else - { - debuff += 4; - } - } - - f.end(); - timings.put(f.getMilliseconds()); - } -} diff --git a/src/main/java/ninja/bytecode/iris/generator/populator/SchematicPopulator.java b/src/main/java/ninja/bytecode/iris/generator/populator/SchematicPopulator.java new file mode 100644 index 000000000..b253de7d4 --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/generator/populator/SchematicPopulator.java @@ -0,0 +1,35 @@ +package ninja.bytecode.iris.generator.populator; + +import java.util.Random; + +import org.bukkit.Chunk; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; + +import ninja.bytecode.iris.schematic.Schematic; +import ninja.bytecode.iris.util.ChancedPopulator; + +public class SchematicPopulator extends ChancedPopulator +{ + protected final Schematic[] schematics; + + public SchematicPopulator(double chance, Schematic... schematics) + { + super(chance); + this.schematics = schematics; + } + + @Override + public void doPopulate(World world, Random random, Chunk source, int wx, int wz) + { + Block b = world.getHighestBlockAt(wx, wz); + + if(!b.getRelative(BlockFace.DOWN).getType().isSolid()) + { + return; + } + + schematics[random.nextInt(schematics.length)].place(world, wx, b.getY() - 1, wz); + } +} diff --git a/src/main/java/ninja/bytecode/iris/generator/populator/SurfaceBiasSchematicPopulator.java b/src/main/java/ninja/bytecode/iris/generator/populator/SurfaceBiasSchematicPopulator.java new file mode 100644 index 000000000..8e5b5c2df --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/generator/populator/SurfaceBiasSchematicPopulator.java @@ -0,0 +1,44 @@ +package ninja.bytecode.iris.generator.populator; + +import java.util.Random; + +import org.bukkit.Chunk; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; + +import ninja.bytecode.iris.schematic.Schematic; +import ninja.bytecode.shuriken.collections.GList; + +public class SurfaceBiasSchematicPopulator extends SchematicPopulator +{ + private GList bias; + + public SurfaceBiasSchematicPopulator(double chance, Schematic... schematics) + { + super(chance, schematics); + this.bias = new GList<>(); + } + + public SurfaceBiasSchematicPopulator surface(Material mb) + { + bias.add(mb); + return this; + } + + @Override + public void doPopulate(World world, Random random, Chunk source, int wx, int wz) + { + Block b = world.getHighestBlockAt(wx, wz); + + for(Material i : bias) + { + if(b.getRelative(BlockFace.DOWN).getType().equals(i)) + { + schematics[random.nextInt(schematics.length)].place(world, wx, b.getY() - 1, wz); + break; + } + } + } +} diff --git a/src/main/java/ninja/bytecode/iris/schematic/Schematic.java b/src/main/java/ninja/bytecode/iris/schematic/Schematic.java new file mode 100644 index 000000000..b53886395 --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/schematic/Schematic.java @@ -0,0 +1,210 @@ +package ninja.bytecode.iris.schematic; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.zip.GZIPInputStream; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.util.BlockVector; + +import ninja.bytecode.iris.util.MB; +import ninja.bytecode.shuriken.collections.GMap; +import ninja.bytecode.shuriken.io.CustomOutputStream; +import ninja.bytecode.shuriken.logging.L; + +public class Schematic +{ + private int w; + private int h; + private int d; + private int x; + private int y; + private int z; + private final GMap s; + + public Schematic(int w, int h, int d, int x, int y, int z) + { + this.w = w; + this.h = h; + this.d = d; + this.x = x; + this.y = y; + this.z = z; + s = new GMap<>(); + } + + + + public int getW() + { + return w; + } + + + + public int getH() + { + return h; + } + + + + public int getD() + { + return d; + } + + + + public int getX() + { + return x; + } + + + + public int getY() + { + return y; + } + + + + public int getZ() + { + return z; + } + + + + public GMap getSchematic() + { + return s; + } + + + + @SuppressWarnings("deprecation") + public void read(InputStream in) throws IOException + { + GZIPInputStream gzi = new GZIPInputStream(in); + DataInputStream din = new DataInputStream(gzi); + w = din.readInt(); + h = din.readInt(); + d = din.readInt(); + x = din.readInt(); + y = din.readInt(); + z = din.readInt(); + int l = din.readInt(); + clear(); + + for(int i = 0; i < l; i++) + { + s.put(new BlockVector(din.readInt(), din.readInt(), din.readInt()), new MB(Material.getMaterial((int)din.readByte()), din.readByte())); + } + + din.close(); + } + + @SuppressWarnings("deprecation") + public void write(OutputStream out) throws IOException + { + CustomOutputStream cos = new CustomOutputStream(out, 9); + DataOutputStream dos = new DataOutputStream(cos); + dos.writeInt(w); + dos.writeInt(h); + dos.writeInt(d); + dos.writeInt(x); + dos.writeInt(y); + dos.writeInt(z); + dos.writeInt(s.size()); + + for(BlockVector i : s.keySet()) + { + dos.writeInt(i.getBlockX()); + dos.writeInt(i.getBlockY()); + dos.writeInt(i.getBlockZ()); + dos.writeByte(s.get(i).material.getId()); + dos.writeByte(s.get(i).data); + } + + dos.close(); + } + + public BlockVector getOffset() + { + return new BlockVector(x, y, z); + } + + public MB get(int x, int y, int z) + { + return s.get(new BlockVector(x, y, z)); + } + + public boolean has(int x, int y, int z) + { + return s.contains(new BlockVector(x, y, z)); + } + + public void put(int x, int y, int z, MB mb) + { + s.put(new BlockVector(x, y, z), mb); + } + + public Schematic copy() + { + Schematic s = new Schematic(w, h, d, x, y, z); + s.fill(this.s); + return s; + } + + public void clear() + { + s.clear(); + } + + public void fill(GMap b) + { + clear(); + s.put(b); + } + + @SuppressWarnings("deprecation") + public void place(World source, int wx, int wy, int wz) + { + Location start = new Location(source, wx, wy, wz).clone().subtract(getOffset()); + + for(BlockVector i : getSchematic().keySet()) + { + MB b = getSchematic().get(i); + Block blk = start.clone().add(i).getBlock(); + + if(!blk.isEmpty() && !b.material.isOccluding()) + { + continue; + } + + blk.setTypeIdAndData(b.material.getId(), b.data, false); + } + } + + + + public static Schematic load(File f) throws IOException + { + L.i("Loading Schematic: " + f.getPath()); + Schematic s = new Schematic(1, 1, 1, 1, 1, 1); + FileInputStream fin = new FileInputStream(f); + s.read(fin); + + return s; + } +} diff --git a/src/main/java/ninja/bytecode/iris/util/ChancedPopulator.java b/src/main/java/ninja/bytecode/iris/util/ChancedPopulator.java new file mode 100644 index 000000000..6e7e2563a --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/util/ChancedPopulator.java @@ -0,0 +1,48 @@ +package ninja.bytecode.iris.util; + +import java.util.Random; + +import org.bukkit.Chunk; +import org.bukkit.World; +import org.bukkit.generator.BlockPopulator; + +import ninja.bytecode.shuriken.math.M; + +public abstract class ChancedPopulator extends BlockPopulator +{ + private final double chance; + + public ChancedPopulator(double chance) + { + this.chance = chance; + } + + @Override + public void populate(World world, Random random, Chunk source) + { + if(chance == 0) + { + return; + } + + if(chance > 0 && chance < 1 && M.r(chance)) + { + doPopulate(world, random, source, (source.getX() << 4) + random.nextInt(16), (source.getZ() << 4) + random.nextInt(16)); + } + + if(chance > 1) + { + for(int i = 0; i < (int) chance; i++) + { + doPopulate(world, random, source, (source.getX() << 4) + random.nextInt(16), (source.getZ() << 4) + random.nextInt(16)); + } + + if(M.r(chance - ((int) chance))) + { + doPopulate(world, random, source, (source.getX() << 4) + random.nextInt(16), (source.getZ() << 4) + random.nextInt(16)); + } + } + } + + public abstract void doPopulate(World world, Random random, Chunk source, int x, int z); +} diff --git a/src/main/java/ninja/bytecode/iris/util/ChunkPlan.java b/src/main/java/ninja/bytecode/iris/util/ChunkPlan.java index a9ad82d39..e07f4c415 100644 --- a/src/main/java/ninja/bytecode/iris/util/ChunkPlan.java +++ b/src/main/java/ninja/bytecode/iris/util/ChunkPlan.java @@ -2,18 +2,28 @@ package ninja.bytecode.iris.util; import java.util.function.Supplier; +import org.bukkit.util.BlockVector; + import ninja.bytecode.iris.generator.biome.IrisBiome; +import ninja.bytecode.iris.schematic.Schematic; import ninja.bytecode.shuriken.collections.GMap; public class ChunkPlan { private final GMap heightCache; private final GMap biomeCache; + private final GMap schematics; public ChunkPlan() { - this.heightCache = new GMap(); - this.biomeCache = new GMap(); + this.schematics = new GMap<>(); + this.heightCache = new GMap<>(); + this.biomeCache = new GMap<>(); + } + + public void planSchematic(BlockVector b, Schematic s) + { + schematics.put(b, s); } public IrisBiome getBiome(int x, int z) diff --git a/src/main/java/ninja/bytecode/iris/util/Cuboid.java b/src/main/java/ninja/bytecode/iris/util/Cuboid.java new file mode 100644 index 000000000..d7d18ab5e --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/util/Cuboid.java @@ -0,0 +1,910 @@ +package ninja.bytecode.iris.util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +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.configuration.serialization.ConfigurationSerializable; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.material.MaterialData; + +import ninja.bytecode.shuriken.collections.GList; + +/** + * Cuboids + * + * @author cyberpwn + */ +public class Cuboid implements Iterable, Cloneable, ConfigurationSerializable +{ + protected final String worldName; + protected int x1, y1, z1; + protected int x2, y2, z2; + + /** + * Construct a Cuboid given two Location objects which represent any two corners + * of the Cuboid. + * + * @param l1 + * one of the corners + * @param l2 + * the other corner + */ + public Cuboid(Location l1, Location l2) + { + if(!l1.getWorld().equals(l2.getWorld())) + { + throw new IllegalArgumentException("locations must be on the same world"); + } + + worldName = l1.getWorld().getName(); + x1 = Math.min(l1.getBlockX(), l2.getBlockX()); + y1 = Math.min(l1.getBlockY(), l2.getBlockY()); + z1 = Math.min(l1.getBlockZ(), l2.getBlockZ()); + x2 = Math.max(l1.getBlockX(), l2.getBlockX()); + y2 = Math.max(l1.getBlockY(), l2.getBlockY()); + z2 = Math.max(l1.getBlockZ(), l2.getBlockZ()); + } + + public GList getLivingEntities() + { + return new GList(new GListAdapter() + { + @Override + public LivingEntity onAdapt(Entity from) + { + if(from instanceof LivingEntity) + { + return (LivingEntity) from; + } + + return null; + } + }.adapt(getEntities())); + } + + public GList getEntities() + { + GList en = new GList(); + + for(Chunk i : getChunks()) + { + for(Entity j : i.getEntities()) + { + if(contains(j.getLocation())) + { + en.add(j); + } + } + } + + return en; + } + + /** + * Set the locations + * + * @param l1 + * a + * @param l2 + * b + */ + public void set(Location l1, Location l2) + { + x1 = Math.min(l1.getBlockX(), l2.getBlockX()); + y1 = Math.min(l1.getBlockY(), l2.getBlockY()); + z1 = Math.min(l1.getBlockZ(), l2.getBlockZ()); + x2 = Math.max(l1.getBlockX(), l2.getBlockX()); + y2 = Math.max(l1.getBlockY(), l2.getBlockY()); + z2 = Math.max(l1.getBlockZ(), l2.getBlockZ()); + } + + /** + * Construct a one-block Cuboid at the given Location of the Cuboid. + * + * @param l1 + * location of the Cuboid + */ + public Cuboid(Location l1) + { + this(l1, l1); + } + + /** + * Copy constructor. + * + * @param other + * the Cuboid to copy + */ + public Cuboid(Cuboid other) + { + this(other.getWorld().getName(), other.x1, other.y1, other.z1, other.x2, other.y2, other.z2); + } + + /** + * Construct a Cuboid in the given World and xyz co-ordinates + * + * @param world + * the Cuboid's world + * @param x1 + * X co-ordinate of corner 1 + * @param y1 + * Y co-ordinate of corner 1 + * @param z1 + * Z co-ordinate of corner 1 + * @param x2 + * X co-ordinate of corner 2 + * @param y2 + * Y co-ordinate of corner 2 + * @param z2 + * Z co-ordinate of corner 2 + */ + public Cuboid(World world, int x1, int y1, int z1, int x2, int y2, int z2) + { + this.worldName = world.getName(); + this.x1 = Math.min(x1, x2); + this.x2 = Math.max(x1, x2); + this.y1 = Math.min(y1, y2); + this.y2 = Math.max(y1, y2); + this.z1 = Math.min(z1, z2); + this.z2 = Math.max(z1, z2); + } + + /** + * Construct a Cuboid in the given world name and xyz co-ordinates. + * + * @param worldName + * the Cuboid's world name + * @param x1 + * X co-ordinate of corner 1 + * @param y1 + * Y co-ordinate of corner 1 + * @param z1 + * Z co-ordinate of corner 1 + * @param x2 + * X co-ordinate of corner 2 + * @param y2 + * Y co-ordinate of corner 2 + * @param z2 + * Z co-ordinate of corner 2 + */ + private Cuboid(String worldName, int x1, int y1, int z1, int x2, int y2, int z2) + { + this.worldName = worldName; + this.x1 = Math.min(x1, x2); + this.x2 = Math.max(x1, x2); + this.y1 = Math.min(y1, y2); + this.y2 = Math.max(y1, y2); + this.z1 = Math.min(z1, z2); + this.z2 = Math.max(z1, z2); + } + + public Cuboid(Map map) + { + worldName = (String) map.get("worldName"); + x1 = (Integer) map.get("x1"); + x2 = (Integer) map.get("x2"); + y1 = (Integer) map.get("y1"); + y2 = (Integer) map.get("y2"); + z1 = (Integer) map.get("z1"); + z2 = (Integer) map.get("z2"); + } + + @Override + public Map serialize() + { + Map map = new HashMap(); + map.put("worldName", worldName); + map.put("x1", x1); + map.put("y1", y1); + map.put("z1", z1); + map.put("x2", x2); + map.put("y2", y2); + map.put("z2", z2); + return map; + } + + public Cuboid flatten(int level) + { + return new Cuboid(getWorld(), x1, level, z1, x2, level, z2); + } + + /** + * Get the Location of the lower northeast corner of the Cuboid (minimum XYZ + * co-ordinates). + * + * @return Location of the lower northeast corner + */ + public Location getLowerNE() + { + return new Location(getWorld(), x1, y1, z1); + } + + /** + * Get the Location of the upper southwest corner of the Cuboid (maximum XYZ + * co-ordinates). + * + * @return Location of the upper southwest corner + */ + public Location getUpperSW() + { + return new Location(getWorld(), x2, y2, z2); + } + + /** + * Get the the centre of the Cuboid + * + * @return Location at the centre of the Cuboid + */ + public Location getCenter() + { + int x1 = getUpperX() + 1; + int y1 = getUpperY() + 1; + int z1 = getUpperZ() + 1; + return new Location(getWorld(), getLowerX() + (x1 - getLowerX()) / 2.0, getLowerY() + (y1 - getLowerY()) / 2.0, getLowerZ() + (z1 - getLowerZ()) / 2.0); + } + + /** + * Get the Cuboid's world. + * + * @return the World object representing this Cuboid's world + * @throws IllegalStateException + * if the world is not loaded + */ + public World getWorld() + { + World world = Bukkit.getWorld(worldName); + if(world == null) + { + throw new IllegalStateException("world '" + worldName + "' is not loaded"); + } + return world; + } + + /** + * Get the size of this Cuboid along the X axis + * + * @return Size of Cuboid along the X axis + */ + public int getSizeX() + { + return (x2 - x1) + 1; + } + + /** + * Get the size of this Cuboid along the Y axis + * + * @return Size of Cuboid along the Y axis + */ + public int getSizeY() + { + return (y2 - y1) + 1; + } + + /** + * Get the size of this Cuboid along the Z axis + * + * @return Size of Cuboid along the Z axis + */ + public int getSizeZ() + { + return (z2 - z1) + 1; + } + + /** + * Get the cuboid dimensions + * + * @return the dimensions + */ + public Dimension getDimension() + { + return new Dimension(getSizeX(), getSizeY(), getSizeZ()); + } + + /** + * Get the minimum X co-ordinate of this Cuboid + * + * @return the minimum X co-ordinate + */ + public int getLowerX() + { + return x1; + } + + /** + * Get the minimum Y co-ordinate of this Cuboid + * + * @return the minimum Y co-ordinate + */ + public int getLowerY() + { + return y1; + } + + /** + * Get the minimum Z co-ordinate of this Cuboid + * + * @return the minimum Z co-ordinate + */ + public int getLowerZ() + { + return z1; + } + + /** + * Get the maximum X co-ordinate of this Cuboid + * + * @return the maximum X co-ordinate + */ + public int getUpperX() + { + return x2; + } + + /** + * Get the maximum Y co-ordinate of this Cuboid + * + * @return the maximum Y co-ordinate + */ + public int getUpperY() + { + return y2; + } + + /** + * Get the maximum Z co-ordinate of this Cuboid + * + * @return the maximum Z co-ordinate + */ + public int getUpperZ() + { + return z2; + } + + /** + * Get the Blocks at the eight corners of the Cuboid. + * + * @return array of Block objects representing the Cuboid corners + */ + public Block[] corners() + { + Block[] res = new Block[8]; + World w = getWorld(); + res[0] = w.getBlockAt(x1, y1, z1); + res[1] = w.getBlockAt(x1, y1, z2); + res[2] = w.getBlockAt(x1, y2, z1); + res[3] = w.getBlockAt(x1, y2, z2); + res[4] = w.getBlockAt(x2, y1, z1); + res[5] = w.getBlockAt(x2, y1, z2); + res[6] = w.getBlockAt(x2, y2, z1); + res[7] = w.getBlockAt(x2, y2, z2); + return res; + } + + /** + * Expand the Cuboid in the given direction by the given amount. Negative + * amounts will shrink the Cuboid in the given direction. Shrinking a cuboid's + * face past the opposite face is not an error and will return a valid Cuboid. + * + * @param dir + * the direction in which to expand + * @param amount + * the number of blocks by which to expand + * @return a new Cuboid expanded by the given direction and amount + */ + public Cuboid expand(CuboidDirection dir, int amount) + { + switch(dir) + { + case North: + return new Cuboid(worldName, x1 - amount, y1, z1, x2, y2, z2); + case South: + return new Cuboid(worldName, x1, y1, z1, x2 + amount, y2, z2); + case East: + return new Cuboid(worldName, x1, y1, z1 - amount, x2, y2, z2); + case West: + return new Cuboid(worldName, x1, y1, z1, x2, y2, z2 + amount); + case Down: + return new Cuboid(worldName, x1, y1 - amount, z1, x2, y2, z2); + case Up: + return new Cuboid(worldName, x1, y1, z1, x2, y2 + amount, z2); + default: + throw new IllegalArgumentException("invalid direction " + dir); + } + } + + /** + * Shift the Cuboid in the given direction by the given amount. + * + * @param dir + * the direction in which to shift + * @param amount + * the number of blocks by which to shift + * @return a new Cuboid shifted by the given direction and amount + */ + public Cuboid shift(CuboidDirection dir, int amount) + { + return expand(dir, amount).expand(dir.opposite(), -amount); + } + + /** + * Outset (grow) the Cuboid in the given direction by the given amount. + * + * @param dir + * the direction in which to outset (must be Horizontal, Vertical, or + * Both) + * @param amount + * the number of blocks by which to outset + * @return a new Cuboid outset by the given direction and amount + */ + public Cuboid outset(CuboidDirection dir, int amount) + { + Cuboid c; + switch(dir) + { + case Horizontal: + c = expand(CuboidDirection.North, amount).expand(CuboidDirection.South, amount).expand(CuboidDirection.East, amount).expand(CuboidDirection.West, amount); + break; + case Vertical: + c = expand(CuboidDirection.Down, amount).expand(CuboidDirection.Up, amount); + break; + case Both: + c = outset(CuboidDirection.Horizontal, amount).outset(CuboidDirection.Vertical, amount); + break; + default: + throw new IllegalArgumentException("invalid direction " + dir); + } + return c; + } + + /** + * Inset (shrink) the Cuboid in the given direction by the given amount. + * Equivalent to calling outset() with a negative amount. + * + * @param dir + * the direction in which to inset (must be Horizontal, Vertical, or + * Both) + * @param amount + * the number of blocks by which to inset + * @return a new Cuboid inset by the given direction and amount + */ + public Cuboid inset(CuboidDirection dir, int amount) + { + return outset(dir, -amount); + } + + /** + * Return true if the point at (x,y,z) is contained within this Cuboid. + * + * @param x + * the X co-ordinate + * @param y + * the Y co-ordinate + * @param z + * the Z co-ordinate + * @return true if the given point is within this Cuboid, false otherwise + */ + public boolean contains(int x, int y, int z) + { + return x >= x1 && x <= x2 && y >= y1 && y <= y2 && z >= z1 && z <= z2; + } + + /** + * Check if the given Block is contained within this Cuboid. + * + * @param b + * the Block to check for + * @return true if the Block is within this Cuboid, false otherwise + */ + public boolean contains(Block b) + { + return contains(b.getLocation()); + } + + /** + * Check if the given Location is contained within this Cuboid. + * + * @param l + * the Location to check for + * @return true if the Location is within this Cuboid, false otherwise + */ + public boolean contains(Location l) + { + return worldName.equals(l.getWorld().getName()) && contains(l.getBlockX(), l.getBlockY(), l.getBlockZ()); + } + + /** + * Get the volume of this Cuboid. + * + * @return the Cuboid volume, in blocks + */ + public int volume() + { + return getSizeX() * getSizeY() * getSizeZ(); + } + + /** + * Get the average light level of all empty (air) blocks in the Cuboid. Returns + * 0 if there are no empty blocks. + * + * @return the average light level of this Cuboid + */ + public byte averageLightLevel() + { + long total = 0; + int n = 0; + for(Block b : this) + { + if(b.isEmpty()) + { + total += b.getLightLevel(); + ++n; + } + } + return n > 0 ? (byte) (total / n) : 0; + } + + /** + * Contract the Cuboid, returning a Cuboid with any air around the edges + * removed, just large enough to include all non-air blocks. + * + * @return a new Cuboid with no external air blocks + */ + public Cuboid contract() + { + return this.contract(CuboidDirection.Down).contract(CuboidDirection.South).contract(CuboidDirection.East).contract(CuboidDirection.Up).contract(CuboidDirection.North).contract(CuboidDirection.West); + } + + /** + * Contract the Cuboid in the given direction, returning a new Cuboid which has + * no exterior empty space. E.g. a direction of Down will push the top face + * downwards as much as possible. + * + * @param dir + * the direction in which to contract + * @return a new Cuboid contracted in the given direction + */ + public Cuboid contract(CuboidDirection dir) + { + Cuboid face = getFace(dir.opposite()); + switch(dir) + { + case Down: + while(face.containsOnly(Material.AIR) && face.getLowerY() > this.getLowerY()) + { + face = face.shift(CuboidDirection.Down, 1); + } + return new Cuboid(worldName, x1, y1, z1, x2, face.getUpperY(), z2); + case Up: + while(face.containsOnly(Material.AIR) && face.getUpperY() < this.getUpperY()) + { + face = face.shift(CuboidDirection.Up, 1); + } + return new Cuboid(worldName, x1, face.getLowerY(), z1, x2, y2, z2); + case North: + while(face.containsOnly(Material.AIR) && face.getLowerX() > this.getLowerX()) + { + face = face.shift(CuboidDirection.North, 1); + } + return new Cuboid(worldName, x1, y1, z1, face.getUpperX(), y2, z2); + case South: + while(face.containsOnly(Material.AIR) && face.getUpperX() < this.getUpperX()) + { + face = face.shift(CuboidDirection.South, 1); + } + return new Cuboid(worldName, face.getLowerX(), y1, z1, x2, y2, z2); + case East: + while(face.containsOnly(Material.AIR) && face.getLowerZ() > this.getLowerZ()) + { + face = face.shift(CuboidDirection.East, 1); + } + return new Cuboid(worldName, x1, y1, z1, x2, y2, face.getUpperZ()); + case West: + while(face.containsOnly(Material.AIR) && face.getUpperZ() < this.getUpperZ()) + { + face = face.shift(CuboidDirection.West, 1); + } + return new Cuboid(worldName, x1, y1, face.getLowerZ(), x2, y2, z2); + default: + throw new IllegalArgumentException("Invalid direction " + dir); + } + } + + /** + * Get the Cuboid representing the face of this Cuboid. The resulting Cuboid + * will be one block thick in the axis perpendicular to the requested face. + * + * @param dir + * which face of the Cuboid to get + * @return the Cuboid representing this Cuboid's requested face + */ + public Cuboid getFace(CuboidDirection dir) + { + switch(dir) + { + case Down: + return new Cuboid(worldName, x1, y1, z1, x2, y1, z2); + case Up: + return new Cuboid(worldName, x1, y2, z1, x2, y2, z2); + case North: + return new Cuboid(worldName, x1, y1, z1, x1, y2, z2); + case South: + return new Cuboid(worldName, x2, y1, z1, x2, y2, z2); + case East: + return new Cuboid(worldName, x1, y1, z1, x2, y2, z1); + case West: + return new Cuboid(worldName, x1, y1, z2, x2, y2, z2); + default: + throw new IllegalArgumentException("Invalid direction " + dir); + } + } + + /** + * Check if the Cuboid contains only blocks of the given type + * + * @param material + * the material to check for + * @return true if this Cuboid contains only blocks of the given type + */ + public boolean containsOnly(Material material) + { + for(Block b : this) + { + if(b.getType() != material) + { + return false; + } + } + return true; + } + + /** + * Get the Cuboid big enough to hold both this Cuboid and the given one. + * + * @param other + * the other Cuboid to include + * @return a new Cuboid large enough to hold this Cuboid and the given Cuboid + */ + public Cuboid getBoundingCuboid(Cuboid other) + { + if(other == null) + { + return this; + } + + int xMin = Math.min(getLowerX(), other.getLowerX()); + int yMin = Math.min(getLowerY(), other.getLowerY()); + int zMin = Math.min(getLowerZ(), other.getLowerZ()); + int xMax = Math.max(getUpperX(), other.getUpperX()); + int yMax = Math.max(getUpperY(), other.getUpperY()); + int zMax = Math.max(getUpperZ(), other.getUpperZ()); + + return new Cuboid(worldName, xMin, yMin, zMin, xMax, yMax, zMax); + } + + /** + * Get a block relative to the lower NE point of the Cuboid. + * + * @param x + * the X co-ordinate + * @param y + * the Y co-ordinate + * @param z + * the Z co-ordinate + * @return the block at the given position + */ + public Block getRelativeBlock(int x, int y, int z) + { + return getWorld().getBlockAt(x1 + x, y1 + y, z1 + z); + } + + /** + * Get a block relative to the lower NE point of the Cuboid in the given World. + * This version of getRelativeBlock() should be used if being called many times, + * to avoid excessive calls to getWorld(). + * + * @param w + * the World + * @param x + * the X co-ordinate + * @param y + * the Y co-ordinate + * @param z + * the Z co-ordinate + * @return the block at the given position + */ + public Block getRelativeBlock(World w, int x, int y, int z) + { + return w.getBlockAt(x1 + x, y1 + y, z1 + z); + } + + /** + * Get a list of the chunks which are fully or partially contained in this + * cuboid. + * + * @return a list of Chunk objects + */ + public List getChunks() + { + List res = new ArrayList(); + + World w = getWorld(); + int x1 = getLowerX() & ~0xf; + int x2 = getUpperX() & ~0xf; + int z1 = getLowerZ() & ~0xf; + int z2 = getUpperZ() & ~0xf; + for(int x = x1; x <= x2; x += 16) + { + for(int z = z1; z <= z2; z += 16) + { + res.add(w.getChunkAt(x >> 4, z >> 4)); + } + } + return res; + } + + /** + * Set all the blocks within the Cuboid to the given block ID and data byte. + * + * @param blockId + * the block ID + * @param data + * the block data + * @deprecated use {@link #fill(MaterialData, MassBlockUpdate)} + */ + @Deprecated + public void fill(int blockId, byte data) + { + for(Block b : this) + { + b.setTypeIdAndData(blockId, data, false); + } + } + + /** + * Set all the blocks within the Cuboid to the given MaterialData, using a + * MassBlockUpdate object for fast updates. + * + * @param mat + * the MaterialData to set + * @param mbu + * the MassBlockUpdate object + */ + + /** + * Reset the light level of all blocks within this Cuboid. + */ + + /* + * (non-Javadoc) + * + * @see java.lang.Iterable#iterator() + */ + @Override + public Iterator iterator() + { + return new CuboidIterator(getWorld(), x1, y1, z1, x2, y2, z2); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#clone() + */ + @Override + public Cuboid clone() throws CloneNotSupportedException + { + return new Cuboid(this); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + return "Cuboid: " + worldName + "," + x1 + "," + y1 + "," + z1 + "=>" + x2 + "," + y2 + "," + z2; + } + + public class CuboidIterator implements Iterator + { + private World w; + private int baseX, baseY, baseZ; + private int x, y, z; + private int sizeX, sizeY, sizeZ; + + public CuboidIterator(World w, int x1, int y1, int z1, int x2, int y2, int z2) + { + this.w = w; + baseX = x1; + baseY = y1; + baseZ = z1; + sizeX = Math.abs(x2 - x1) + 1; + sizeY = Math.abs(y2 - y1) + 1; + sizeZ = Math.abs(z2 - z1) + 1; + x = y = z = 0; + } + + @Override + public boolean hasNext() + { + return x < sizeX && y < sizeY && z < sizeZ; + } + + @Override + public Block next() + { + Block b = w.getBlockAt(baseX + x, baseY + y, baseZ + z); + if(++x >= sizeX) + { + x = 0; + if(++y >= sizeY) + { + y = 0; + ++z; + } + } + return b; + } + + @Override + public void remove() + { + // nop + } + } + + public enum CuboidDirection + { + + North, + East, + South, + West, + Up, + Down, + Horizontal, + Vertical, + Both, + Unknown; + + public CuboidDirection opposite() + { + switch(this) + { + case North: + return South; + case East: + return West; + case South: + return North; + case West: + return East; + case Horizontal: + return Vertical; + case Vertical: + return Horizontal; + case Up: + return Down; + case Down: + return Up; + case Both: + return Both; + default: + return Unknown; + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/ninja/bytecode/iris/util/CuboidException.java b/src/main/java/ninja/bytecode/iris/util/CuboidException.java new file mode 100644 index 000000000..212f9f79c --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/util/CuboidException.java @@ -0,0 +1,16 @@ +package ninja.bytecode.iris.util; + +/** + * Represents a cuboid exception + * + * @author cyberpwn + */ +public class CuboidException extends Exception +{ + public CuboidException(String string) + { + super(string); + } + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/ninja/bytecode/iris/util/Dimension.java b/src/main/java/ninja/bytecode/iris/util/Dimension.java new file mode 100644 index 000000000..c9b8a0db6 --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/util/Dimension.java @@ -0,0 +1,86 @@ +package ninja.bytecode.iris.util; + +/** + * Dimensions + * + * @author cyberpwn + */ +public class Dimension +{ + private final int width; + private final int height; + private final int depth; + + /** + * Make a dimension + * + * @param width + * width of this (X) + * @param height + * the height (Y) + * @param depth + * the depth (Z) + */ + public Dimension(int width, int height, int depth) + { + this.width = width; + this.height = height; + this.depth = depth; + } + + /** + * Make a dimension + * + * @param width + * width of this (X) + * @param height + * the height (Y) + */ + public Dimension(int width, int height) + { + this.width = width; + this.height = height; + this.depth = 0; + } + + /** + * Get the direction of the flat part of this dimension (null if no thin + * face) + * + * @return the direction of the flat pane or null + */ + public DimensionFace getPane() + { + if(width == 1) + { + return DimensionFace.X; + } + + if(height == 1) + { + return DimensionFace.Y; + } + + if(depth == 1) + { + return DimensionFace.Z; + } + + return null; + } + + public int getWidth() + { + return width; + } + + public int getHeight() + { + return height; + } + + public int getDepth() + { + return depth; + } +} \ No newline at end of file diff --git a/src/main/java/ninja/bytecode/iris/util/DimensionFace.java b/src/main/java/ninja/bytecode/iris/util/DimensionFace.java new file mode 100644 index 000000000..0db5b6b07 --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/util/DimensionFace.java @@ -0,0 +1,24 @@ +package ninja.bytecode.iris.util; + +/** + * Represents a dimension (coordinates not worlds) + * + * @author cyberpwn + */ +public enum DimensionFace +{ + /** + * The X dimension (width) + */ + X, + + /** + * The Y dimension (height) + */ + Y, + + /** + * The Z dimension (depth) + */ + Z +} \ No newline at end of file diff --git a/src/main/java/ninja/bytecode/iris/util/Direction.java b/src/main/java/ninja/bytecode/iris/util/Direction.java new file mode 100644 index 000000000..a6fd200c4 --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/util/Direction.java @@ -0,0 +1,137 @@ +package ninja.bytecode.iris.util; + +import ninja.bytecode.iris.util.Cuboid.CuboidDirection; +import ninja.bytecode.shuriken.collections.GList; + +/** + * Directions + * + * @author cyberpwn + */ +public enum Direction +{ + U(0, 1, 0, CuboidDirection.Up), + D(0, -1, 0, CuboidDirection.Down), + N(0, 0, -1, CuboidDirection.North), + S(0, 0, 1, CuboidDirection.South), + E(1, 0, 0, CuboidDirection.East), + W(-1, 0, 0, CuboidDirection.West); + + private int x; + private int y; + private int z; + private CuboidDirection f; + + private Direction(int x, int y, int z, CuboidDirection f) + { + this.x = x; + this.y = y; + this.z = z; + this.f = f; + } + + public int x() + { + return x; + } + + public int y() + { + return y; + } + + public int z() + { + return z; + } + + public CuboidDirection f() + { + return f; + } + + public static GList news() + { + return new GList().add(N, E, W, S); + } + + public static GList udnews() + { + return new GList().add(U, D, N, E, W, S); + } + + /** + * Get the directional value from the given byte from common directional blocks + * (MUST BE BETWEEN 0 and 5 INCLUSIVE) + * + * @param b + * the byte + * @return the direction or null if the byte is outside of the inclusive range + * 0-5 + */ + public static Direction fromByte(byte b) + { + if(b > 5 || b < 0) + { + return null; + } + + if(b == 0) + { + return D; + } + + else if(b == 1) + { + return U; + } + + else if(b == 2) + { + return N; + } + + else if(b == 3) + { + return S; + } + + else if(b == 4) + { + return W; + } + + else + { + return E; + } + } + + /** + * Get the byte value represented in some directional blocks + * + * @return the byte value + */ + public byte byteValue() + { + switch(this) + { + case D: + return 0; + case E: + return 5; + case N: + return 2; + case S: + return 3; + case U: + return 1; + case W: + return 4; + default: + break; + } + + return -1; + } +} \ No newline at end of file diff --git a/src/main/java/ninja/bytecode/iris/util/GListAdapter.java b/src/main/java/ninja/bytecode/iris/util/GListAdapter.java new file mode 100644 index 000000000..6985abbfd --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/util/GListAdapter.java @@ -0,0 +1,51 @@ +package ninja.bytecode.iris.util; + + +import java.util.List; + +import ninja.bytecode.shuriken.collections.GList; + +/** + * Adapts a list of objects into a list of other objects + * + * @author cyberpwn + * @param + * the from object in lists (the item INSIDE the list) + * @param + * the to object in lists (the item INSIDE the list) + */ +public abstract class GListAdapter +{ + /** + * Adapts a list of FROM to a list of TO + * + * @param from + * the from list + * @return the to list + */ + public List adapt(List from) + { + List adapted = new GList(); + + for(FROM i : from) + { + TO t = onAdapt(i); + + if(t != null) + { + adapted.add(onAdapt(i)); + } + } + + return adapted; + } + + /** + * Adapts a list object FROM to TO for use with the adapt method + * + * @param from + * the from object + * @return the to object + */ + public abstract TO onAdapt(FROM from); +} diff --git a/src/main/java/ninja/bytecode/iris/util/ParallelChunkGenerator.java b/src/main/java/ninja/bytecode/iris/util/ParallelChunkGenerator.java index a2cecd4dc..e17fa4a5b 100644 --- a/src/main/java/ninja/bytecode/iris/util/ParallelChunkGenerator.java +++ b/src/main/java/ninja/bytecode/iris/util/ParallelChunkGenerator.java @@ -2,21 +2,16 @@ package ninja.bytecode.iris.util; import java.util.Random; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Biome; import org.bukkit.generator.ChunkGenerator; import ninja.bytecode.iris.Iris; -import ninja.bytecode.iris.generator.populator.PopulatorTrees; import ninja.bytecode.shuriken.Shuriken; import ninja.bytecode.shuriken.execution.ChronoLatch; -import ninja.bytecode.shuriken.execution.J; import ninja.bytecode.shuriken.execution.TaskExecutor.TaskGroup; import ninja.bytecode.shuriken.execution.TaskExecutor.TaskResult; -import ninja.bytecode.shuriken.format.F; -import ninja.bytecode.shuriken.logging.L; import ninja.bytecode.shuriken.math.RollingSequence; import ninja.bytecode.shuriken.reaction.O; @@ -31,42 +26,12 @@ public abstract class ParallelChunkGenerator extends ChunkGenerator private boolean ready = false; int cg = 0; private ChronoLatch cl = new ChronoLatch(1000); - private ChronoLatch cs = new ChronoLatch(1000); private RollingSequence rs = new RollingSequence(512); - private RollingSequence cps = new RollingSequence(3); private World world; - @SuppressWarnings("deprecation") - public ParallelChunkGenerator() + public World getWorld() { - Bukkit.getScheduler().scheduleAsyncRepeatingTask(Iris.instance, () -> - { - J.attempt(() -> - { - if(world.getPlayers().isEmpty()) - { - return; - } - - if(cs.flip()) - { - cps.put(cg); - cg = 0; - } - - double total = rs.getAverage() + PopulatorTrees.timings.getAverage(); - double rcs = (1000D / total); - double work = cps.getAverage() / (rcs + 1); - L.i("Terrain Gen for " + world.getName()); - L.i("- Terrain (MLTC): " + F.duration(rs.getAverage(), 2)); - L.i("- Trees (SGLC): " + F.duration(PopulatorTrees.timings.getAverage(), 2)); - L.i("Total: " + F.duration(total, 3) + " Work: " + F.f(cps.getAverage(), 0) + "/s of " + F.f(rcs, 0) + "/s (" + F.pc(work, 0) + " utilization)"); - L.flush(); - - System.out.println(""); - }); - - }, 20, 5); + return world; } public void generateFullColumn(int a, int b, int c, int d, BiomeGrid g, ChunkPlan p) @@ -144,33 +109,33 @@ public abstract class ParallelChunkGenerator extends ChunkGenerator public abstract void decorateColumn(int wx, int wz, int x, int z, ChunkPlan plan); @SuppressWarnings("deprecation") - protected void setBlock(int x, int y, int z, Material b) + public void setBlock(int x, int y, int z, Material b) { setBlock(x, y, z, b.getId(), (byte) 0); } @SuppressWarnings("deprecation") - protected void setBlock(int x, int y, int z, Material b, byte d) + public void setBlock(int x, int y, int z, Material b, byte d) { setBlock(x, y, z, b.getId(), d); } - protected void setBlock(int x, int y, int z, int b) + public void setBlock(int x, int y, int z, int b) { setBlock(x, y, z, b, (byte) 0); } - protected void setBlock(int x, int y, int z, int b, byte d) + public void setBlock(int x, int y, int z, int b, byte d) { data.setBlock(x, y, z, b, d); } - protected Material getType(int x, int y, int z) + public Material getType(int x, int y, int z) { return data.getType(x, y, z); } - protected byte getData(int x, int y, int z) + public byte getData(int x, int y, int z) { return data.getData(x, y, z); } diff --git a/src/main/java/ninja/bytecode/iris/util/WandUtil.java b/src/main/java/ninja/bytecode/iris/util/WandUtil.java new file mode 100644 index 000000000..3defb6f28 --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/util/WandUtil.java @@ -0,0 +1,157 @@ +package ninja.bytecode.iris.util; + +import java.util.Iterator; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.util.BlockVector; +import org.bukkit.util.Vector; + +import ninja.bytecode.iris.schematic.Schematic; +import ninja.bytecode.shuriken.collections.GList; + +public class WandUtil +{ + @SuppressWarnings("deprecation") + public static void pasteSchematic(Schematic s, Location at) + { + Location start = at.clone().add(s.getOffset()); + + for(BlockVector i : s.getSchematic().keySet()) + { + MB b = s.getSchematic().get(i); + System.out.println("Pasted " + b.material + " @ " + start.clone().add(i)); + start.clone().add(i).getBlock().setTypeIdAndData(b.material.getId(), b.data, false); + } + } + + @SuppressWarnings("deprecation") + public static Schematic createSchematic(ItemStack wand, Location at) + { + if(!isWand(wand)) + { + return null; + } + + try + { + Location[] f = getCuboid(wand); + Cuboid c = new Cuboid(f[0], f[1]); + Vector v = at.clone().subtract(c.getLowerNE()).toVector(); + Schematic s = new Schematic(c.getSizeX(), c.getSizeY(), c.getSizeZ(), v.getBlockX(), v.getBlockY(), v.getBlockZ()); + Iterator bb = c.iterator(); + while(bb.hasNext()) + { + Block b = bb.next(); + + if(b.getType().equals(Material.AIR)) + { + continue; + } + + BlockVector bv = b.getLocation().toVector().toBlockVector().subtract(c.getLowerNE().toVector().toBlockVector()).toBlockVector(); + System.out.println("Load " + bv + " " + b.getType()); + s.put(bv.getBlockX(), bv.getBlockY(), bv.getBlockZ(), new MB(b.getType(), b.getData())); + } + + return s; + } + + catch(Throwable e) + { + e.printStackTrace(); + } + + return null; + } + + public static Location stringToLocation(String s) + { + try + { + String[] f = s.split("\\Q in \\E"); + String[] g = f[0].split("\\Q,\\E"); + return new Location(Bukkit.getWorld(f[1]), Integer.valueOf(g[0]), Integer.valueOf(g[1]), Integer.valueOf(g[2])); + } + + catch(Throwable e) + { + return null; + } + } + + public static String locationToString(Location s) + { + if(s == null) + { + return "<#>"; + } + + return s.getBlockX() + "," + s.getBlockY() + "," + s.getBlockZ() + " in " + s.getWorld().getName(); + } + + public static ItemStack createWand() + { + return createWand(null, null); + } + + public static ItemStack update(boolean left, Location a, ItemStack item) + { + if(!isWand(item)) + { + return item; + } + + Location[] f = getCuboid(item); + Location other = left ? f[1] : f[0]; + + return createWand(left ? a : other, left ? other : a); + } + + public static ItemStack createWand(Location a, Location b) + { + ItemStack is = new ItemStack(Material.BLAZE_ROD); + is.addUnsafeEnchantment(Enchantment.ARROW_INFINITE, 1); + ItemMeta im = is.getItemMeta(); + im.setDisplayName(ChatColor.BOLD + "" + ChatColor.GOLD + "Wand of Iris"); + im.setUnbreakable(true); + im.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_PLACED_ON, ItemFlag.HIDE_POTION_EFFECTS, ItemFlag.HIDE_DESTROYS, ItemFlag.HIDE_ENCHANTS); + im.setLore(new GList().add(locationToString(a), locationToString(b))); + is.setItemMeta(im); + + return is; + } + + public static Location[] getCuboid(ItemStack is) + { + ItemMeta im = is.getItemMeta(); + return new Location[] {stringToLocation(im.getLore().get(0)), stringToLocation(im.getLore().get(1))}; + } + + public static boolean isWand(ItemStack item) + { + if(!item.getType().equals(createWand().getType())) + { + return false; + } + + if(!item.getItemMeta().getEnchants().equals(createWand().getItemMeta().getEnchants())) + { + return false; + } + + if(!item.getItemMeta().getDisplayName().equals(createWand().getItemMeta().getDisplayName())) + { + return false; + } + + return true; + } +}