diff --git a/pom.xml b/pom.xml index 3c6e4739b..e6d172e23 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ org.polydev gaea - 1.10.42 + 1.10.45 me.lucko @@ -114,6 +114,12 @@ 7.2.0-SNAPSHOT provided + + org.junit.jupiter + junit-jupiter + RELEASE + test + \ No newline at end of file diff --git a/src/main/java/com/dfsek/terra/MaxMin.java b/src/main/java/com/dfsek/terra/MaxMin.java index a2c54a644..9cdc0a353 100644 --- a/src/main/java/com/dfsek/terra/MaxMin.java +++ b/src/main/java/com/dfsek/terra/MaxMin.java @@ -1,8 +1,12 @@ package com.dfsek.terra; +import org.bukkit.Chunk; +import org.jetbrains.annotations.NotNull; + +import java.util.Iterator; import java.util.Random; -public class MaxMin { +public class MaxMin implements Iterable { private final int min; private final int max; public MaxMin(int min, int max) { @@ -21,7 +25,39 @@ public class MaxMin { public int getMin() { return min; } + public int get(Random r) { return r.nextInt((max-min)+1)+min; } + + @Override + public String toString() { + return "Min: " + getMin() + " Max:" + getMax(); + } + + @NotNull + @Override + public Iterator iterator() { + return new MaxMinIterator(this); + } + private static class MaxMinIterator implements Iterator { + private Integer current; + private final MaxMin m; + + public MaxMinIterator(MaxMin m) { + this.m = m; + current = m.getMin(); + } + + @Override + public boolean hasNext() { + return current < m.getMax(); + } + + @Override + public Integer next() { + current++; + return current - 1; + } + } } diff --git a/src/main/java/com/dfsek/terra/Terra.java b/src/main/java/com/dfsek/terra/Terra.java index ceb03ce06..9765ea7a6 100644 --- a/src/main/java/com/dfsek/terra/Terra.java +++ b/src/main/java/com/dfsek/terra/Terra.java @@ -2,13 +2,11 @@ package com.dfsek.terra; import com.dfsek.terra.config.ConfigUtil; import com.dfsek.terra.generation.TerraChunkGenerator; -import com.dfsek.terra.structure.StructureManager; import com.mojang.brigadier.tree.LiteralCommandNode; import me.lucko.commodore.Commodore; import me.lucko.commodore.CommodoreProvider; import me.lucko.commodore.file.CommodoreFileFormat; import org.bukkit.Bukkit; -import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.PluginCommand; import org.bukkit.configuration.file.FileConfiguration; @@ -18,13 +16,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.InputStream; -import java.util.Map; -import java.util.logging.Logger; public class Terra extends JavaPlugin { private static FileConfiguration config; private static Terra instance; - private static StructureManager manager; public static Terra getInstance() { return instance; @@ -38,7 +33,6 @@ public class Terra extends JavaPlugin { @Override public void onEnable() { ConfigUtil.loadConfig(this); - manager = new StructureManager(this); PluginCommand command = getCommand("terra"); command.setExecutor(new TerraCommand()); @@ -67,10 +61,6 @@ public class Terra extends JavaPlugin { } } - public static StructureManager getStructureManager() { - return manager; - } - @Override public @Nullable ChunkGenerator getDefaultWorldGenerator(@NotNull String worldName, @Nullable String id) { return new TerraChunkGenerator(); diff --git a/src/main/java/com/dfsek/terra/TerraCommand.java b/src/main/java/com/dfsek/terra/TerraCommand.java index 2c51771ab..b4a3a6fb7 100644 --- a/src/main/java/com/dfsek/terra/TerraCommand.java +++ b/src/main/java/com/dfsek/terra/TerraCommand.java @@ -9,6 +9,7 @@ import com.dfsek.terra.config.genconfig.OreConfig; import com.dfsek.terra.generation.TerraChunkGenerator; import com.dfsek.terra.image.WorldImageGenerator; import com.dfsek.terra.structure.GaeaStructure; +import com.dfsek.terra.structure.InitializationException; import com.dfsek.terra.structure.StructureSpawn; import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.bukkit.BukkitAdapter; @@ -158,7 +159,13 @@ public class TerraCommand implements CommandExecutor, TabExecutor { BlockVector3 max = selection.getMaximumPoint(); Location l1 = new Location(pl.getWorld(), min.getBlockX(), min.getBlockY(), min.getBlockZ()); Location l2 = new Location(pl.getWorld(), max.getBlockX(), max.getBlockY(), max.getBlockZ()); - GaeaStructure structure = new GaeaStructure(l1, l2, args[2]); + GaeaStructure structure = null; + try { + structure = new GaeaStructure(l1, l2, args[2]); + } catch(InitializationException e) { + sender.sendMessage(e.getMessage()); + return true; + } try { File file = new File(Terra.getInstance().getDataFolder() + File.separator + "export" + File.separator + "structures", args[2] + ".tstructure"); file.getParentFile().mkdirs(); @@ -173,14 +180,14 @@ public class TerraCommand implements CommandExecutor, TabExecutor { try { GaeaStructure struc = GaeaStructure.load(new File(Terra.getInstance().getDataFolder() + File.separator + "export" + File.separator + "structures", args[2] + ".tstructure")); if("true".equals(args[3])) struc.paste(pl.getLocation()); - else struc.paste(pl.getLocation(), pl.getLocation().getChunk()); + //else struc.paste(pl.getLocation(), pl.getLocation().getChunk()); } catch(IOException e) { e.printStackTrace(); sender.sendMessage("Structure not found."); } return true; } else if("getspawn".equals(args[1])) { - Vector v = new StructureSpawn(500, 100).getNearestSpawn(pl.getLocation().getBlockX(), pl.getLocation().getBlockZ(), pl.getWorld().getSeed()); + Vector v = new StructureSpawn(250, 250).getNearestSpawn(pl.getLocation().getBlockX(), pl.getLocation().getBlockZ(), pl.getWorld().getSeed()); sender.sendMessage(v.getBlockX() + ":" + v.getBlockZ()); } } diff --git a/src/main/java/com/dfsek/terra/TerraProfiler.java b/src/main/java/com/dfsek/terra/TerraProfiler.java index ddf534453..2c5ad2d69 100644 --- a/src/main/java/com/dfsek/terra/TerraProfiler.java +++ b/src/main/java/com/dfsek/terra/TerraProfiler.java @@ -13,12 +13,12 @@ public class TerraProfiler extends WorldProfiler { private static final Map profilerMap = new HashMap<>(); public TerraProfiler(World w) { super(w); - this.addMeasurement(new Measurement(2500000, DataType.PERIOD_MILLISECONDS), "TotalChunkGenTime") - .addMeasurement(new Measurement(2500000, DataType.PERIOD_MILLISECONDS), "ChunkBaseGenTime") - .addMeasurement(new Measurement(2000000, DataType.PERIOD_MILLISECONDS), "BiomeSetTime") + this .addMeasurement(new Measurement(25000000, DataType.PERIOD_MILLISECONDS), "TreeGenTime") .addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "FloraTime") + .addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "OreTime") .addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "CaveTime") + .addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "StructureTime") .addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "CaveBlockUpdate"); profilerMap.put(w, this); } diff --git a/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java b/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java index da16583e4..93ce0a436 100644 --- a/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java +++ b/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java @@ -37,7 +37,6 @@ public class TerraChunkGenerator extends GaeaChunkGenerator { public TerraChunkGenerator() { super(ChunkInterpolator.InterpolationType.TRILINEAR); - popMan.attach(new StructurePopulator()); popMan.attach(new TreePopulator()); popMan.attach(new FloraPopulator()); popMan.attach(new OrePopulator()); diff --git a/src/main/java/com/dfsek/terra/population/CavePopulator.java b/src/main/java/com/dfsek/terra/population/CavePopulator.java index 8b7d1e417..ca45946bf 100644 --- a/src/main/java/com/dfsek/terra/population/CavePopulator.java +++ b/src/main/java/com/dfsek/terra/population/CavePopulator.java @@ -22,56 +22,59 @@ import java.util.Set; public class CavePopulator extends BlockPopulator { private static final Map shiftStorage = new HashMap<>(); // Persist BlockData created for shifts, to avoid re-calculating each time. + private static final BlockData AIR = Material.AIR.createBlockData(); @Override public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk chunk) { - ProfileFuture cave = TerraProfiler.fromWorld(world).measure("CaveTime"); - for(CarverConfig c : CarverConfig.getCarvers()) { - Map shiftCandidate = new HashMap<>(); - Set updateNeeded = new HashSet<>(); - Map blocks = c.getCarver().carve(chunk.getX(), chunk.getZ(), world).getCarvedBlocks(); - for(Map.Entry e : blocks.entrySet()) { - Vector v = e.getKey(); - Block b = chunk.getBlock(v.getBlockX(), v.getBlockY(), v.getBlockZ()); - Material m = b.getType(); - if(e.getValue().equals(CarvingData.CarvingType.CENTER) && c.isReplaceableInner(m)) { - if(c.getShiftedBlocks().containsKey(b.getType())) shiftCandidate.put(b.getLocation(), b.getType()); - b.setBlockData(c.getPaletteInner(v.getBlockY()).get(random), c.getUpdateBlocks().contains(m)); - } else if(e.getValue().equals(CarvingData.CarvingType.WALL) && c.isReplaceableOuter(m)){ - if(c.getShiftedBlocks().containsKey(b.getType())) shiftCandidate.put(b.getLocation(), b.getType()); - b.setBlockData(c.getPaletteOuter(v.getBlockY()).get(random), c.getUpdateBlocks().contains(m)); - } else if(e.getValue().equals(CarvingData.CarvingType.TOP) && c.isReplaceableTop(m)){ - if(c.getShiftedBlocks().containsKey(b.getType())) shiftCandidate.put(b.getLocation(), b.getType()); - b.setBlockData(c.getPaletteTop(v.getBlockY()).get(random), c.getUpdateBlocks().contains(m)); - } else if(e.getValue().equals(CarvingData.CarvingType.BOTTOM) && c.isReplaceableBottom(m)){ - if(c.getShiftedBlocks().containsKey(b.getType())) shiftCandidate.put(b.getLocation(), b.getType()); - b.setBlockData(c.getPaletteBottom(v.getBlockY()).get(random), c.getUpdateBlocks().contains(m)); - } - if(c.getUpdateBlocks().contains(m)) { - updateNeeded.add(b); - } - } - int i = 0; - int j = 0; - for(Location l : shiftCandidate.keySet()) { - Location mut = l.clone(); - Material orig = l.getBlock().getType(); - do mut.subtract(0, 1, 0); - while(mut.getBlock().getType().equals(orig)); - try { - if(c.getShiftedBlocks().get(shiftCandidate.get(l)).contains(mut.getBlock().getType())) { - mut.getBlock().setBlockData(shiftStorage.computeIfAbsent(shiftCandidate.get(l), Material::createBlockData), false); - j++; + try(ProfileFuture ignored = TerraProfiler.fromWorld(world).measure("CaveTime")) { + for(CarverConfig c : CarverConfig.getCarvers()) { + Map shiftCandidate = new HashMap<>(); + Set updateNeeded = new HashSet<>(); + Map blocks = c.getCarver().carve(chunk.getX(), chunk.getZ(), world).getCarvedBlocks(); + for(Map.Entry e : blocks.entrySet()) { + Vector v = e.getKey(); + Block b = chunk.getBlock(v.getBlockX(), v.getBlockY(), v.getBlockZ()); + Material m = b.getType(); + if(e.getValue().equals(CarvingData.CarvingType.CENTER) && c.isReplaceableInner(m)) { + if(c.getShiftedBlocks().containsKey(b.getType())) + shiftCandidate.put(b.getLocation(), b.getType()); + b.setBlockData(c.getPaletteInner(v.getBlockY()).get(random), c.getUpdateBlocks().contains(m)); + } else if(e.getValue().equals(CarvingData.CarvingType.WALL) && c.isReplaceableOuter(m)) { + if(c.getShiftedBlocks().containsKey(b.getType())) + shiftCandidate.put(b.getLocation(), b.getType()); + b.setBlockData(c.getPaletteOuter(v.getBlockY()).get(random), c.getUpdateBlocks().contains(m)); + } else if(e.getValue().equals(CarvingData.CarvingType.TOP) && c.isReplaceableTop(m)) { + if(c.getShiftedBlocks().containsKey(b.getType())) + shiftCandidate.put(b.getLocation(), b.getType()); + b.setBlockData(c.getPaletteTop(v.getBlockY()).get(random), c.getUpdateBlocks().contains(m)); + } else if(e.getValue().equals(CarvingData.CarvingType.BOTTOM) && c.isReplaceableBottom(m)) { + if(c.getShiftedBlocks().containsKey(b.getType())) + shiftCandidate.put(b.getLocation(), b.getType()); + b.setBlockData(c.getPaletteBottom(v.getBlockY()).get(random), c.getUpdateBlocks().contains(m)); } - } catch(NullPointerException ignored) {} - i++; + if(c.getUpdateBlocks().contains(m)) { + updateNeeded.add(b); + } + } + for(Location l : shiftCandidate.keySet()) { + Location mut = l.clone(); + Material orig = l.getBlock().getType(); + do mut.subtract(0, 1, 0); + while(mut.getBlock().getType().equals(orig)); + try { + if(c.getShiftedBlocks().get(shiftCandidate.get(l)).contains(mut.getBlock().getType())) { + mut.getBlock().setBlockData(shiftStorage.computeIfAbsent(shiftCandidate.get(l), Material::createBlockData), false); + } + } catch(NullPointerException ignore) {} + } + try(ProfileFuture ignore = TerraProfiler.fromWorld(world).measure("CaveBlockUpdate")) { + for(Block b : updateNeeded) { + BlockData orig = b.getBlockData(); + b.setBlockData(AIR, false); + b.setBlockData(orig, true); + } + } } - for(Block b : updateNeeded) { - BlockData orig = b.getBlockData(); - b.setBlockData(Material.AIR.createBlockData(), true); - b.setBlockData(orig, true); - } - } - if(cave != null) cave.complete(); + } } } diff --git a/src/main/java/com/dfsek/terra/population/FloraPopulator.java b/src/main/java/com/dfsek/terra/population/FloraPopulator.java index 12b126de7..b5d56ed8d 100644 --- a/src/main/java/com/dfsek/terra/population/FloraPopulator.java +++ b/src/main/java/com/dfsek/terra/population/FloraPopulator.java @@ -22,24 +22,25 @@ public class FloraPopulator extends GaeaBlockPopulator { Set pop = new HashSet<>(); @Override public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk chunk) { - if(pop.contains(chunk)) Bukkit.getLogger().warning("Already populated flora in chunk: " + chunk); - pop.add(chunk); - ProfileFuture flora = TerraProfiler.fromWorld(world).measure("FloraTime"); - for(int x = 0; x < 16; x++) { - for(int z = 0; z < 16; z++) { - UserDefinedBiome biome = (UserDefinedBiome) TerraBiomeGrid.fromWorld(world).getBiome((chunk.getX() << 4) + x, (chunk.getZ() << 4) + z); - if(biome.getDecorator().getFloraChance() <= 0 || random.nextInt(100) > biome.getDecorator().getFloraChance()) - continue; - try { - BiomeConfig c = BiomeConfig.fromBiome(biome); - for(int i = 0; i < c.getFloraAttempts(); i++) { - Flora item = biome.getDecorator().getFlora().get(random); - Block highest = item.getHighestValidSpawnAt(chunk, x, z); - if(highest != null && c.getFloraHeights(item).isInRange(highest.getY())) item.plant(highest.getLocation()); - } - } catch(NullPointerException ignored) {} + try (ProfileFuture ignored = TerraProfiler.fromWorld(world).measure("FloraTime")) { + if(pop.contains(chunk)) Bukkit.getLogger().warning("Already populated flora in chunk: " + chunk); + pop.add(chunk); + for(int x = 0; x < 16; x++) { + for(int z = 0; z < 16; z++) { + UserDefinedBiome biome = (UserDefinedBiome) TerraBiomeGrid.fromWorld(world).getBiome((chunk.getX() << 4) + x, (chunk.getZ() << 4) + z); + if(biome.getDecorator().getFloraChance() <= 0 || random.nextInt(100) > biome.getDecorator().getFloraChance()) + continue; + try { + BiomeConfig c = BiomeConfig.fromBiome(biome); + for(int i = 0; i < c.getFloraAttempts(); i++) { + Flora item = biome.getDecorator().getFlora().get(random); + Block highest = item.getHighestValidSpawnAt(chunk, x, z); + if(highest != null && c.getFloraHeights(item).isInRange(highest.getY())) + item.plant(highest.getLocation()); + } + } catch(NullPointerException ignore) {} + } } } - if(flora!=null) flora.complete(); } } diff --git a/src/main/java/com/dfsek/terra/population/OrePopulator.java b/src/main/java/com/dfsek/terra/population/OrePopulator.java index 3cf47f71b..65fe93b9e 100644 --- a/src/main/java/com/dfsek/terra/population/OrePopulator.java +++ b/src/main/java/com/dfsek/terra/population/OrePopulator.java @@ -1,6 +1,7 @@ package com.dfsek.terra.population; import com.dfsek.terra.MaxMin; +import com.dfsek.terra.TerraProfiler; import com.dfsek.terra.biome.TerraBiomeGrid; import com.dfsek.terra.biome.UserDefinedBiome; import com.dfsek.terra.config.genconfig.BiomeConfig; @@ -11,6 +12,7 @@ import org.bukkit.World; import org.jetbrains.annotations.NotNull; import org.polydev.gaea.biome.Biome; import org.polydev.gaea.population.GaeaBlockPopulator; +import org.polydev.gaea.profiler.ProfileFuture; import java.util.Map; import java.util.Random; @@ -18,15 +20,17 @@ import java.util.Random; public class OrePopulator extends GaeaBlockPopulator { @Override public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk chunk) { - Location l = chunk.getBlock(8, 0, 0).getLocation(); - Biome b = TerraBiomeGrid.fromWorld(world).getBiome(l.getBlockX(), l.getBlockZ()); - for(Map.Entry e : BiomeConfig.fromBiome((UserDefinedBiome) b).getOres().entrySet()) { - int num = e.getValue().get(random); - for(int i = 0; i < num; i++) { - int x = random.nextInt(16); - int z = random.nextInt(16); - int y = BiomeConfig.fromBiome((UserDefinedBiome) b).getOreHeight(e.getKey()).get(random); - e.getKey().doVein(chunk.getBlock(x, y, z).getLocation(), random); + try (ProfileFuture ignored = TerraProfiler.fromWorld(world).measure("OreTime")) { + Location l = chunk.getBlock(8, 0, 0).getLocation(); + Biome b = TerraBiomeGrid.fromWorld(world).getBiome(l.getBlockX(), l.getBlockZ()); + for(Map.Entry e : BiomeConfig.fromBiome((UserDefinedBiome) b).getOres().entrySet()) { + int num = e.getValue().get(random); + for(int i = 0; i < num; i++) { + int x = random.nextInt(16); + int z = random.nextInt(16); + int y = BiomeConfig.fromBiome((UserDefinedBiome) b).getOreHeight(e.getKey()).get(random); + e.getKey().doVein(chunk.getBlock(x, y, z).getLocation(), random); + } } } } diff --git a/src/main/java/com/dfsek/terra/population/StructurePopulator.java b/src/main/java/com/dfsek/terra/population/StructurePopulator.java index e424b1daf..e737696c0 100644 --- a/src/main/java/com/dfsek/terra/population/StructurePopulator.java +++ b/src/main/java/com/dfsek/terra/population/StructurePopulator.java @@ -1,14 +1,17 @@ package com.dfsek.terra.population; import com.dfsek.terra.Terra; +import com.dfsek.terra.TerraProfiler; import com.dfsek.terra.structure.GaeaStructure; import com.dfsek.terra.structure.StructureSpawn; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.World; +import org.bukkit.generator.BlockPopulator; import org.jetbrains.annotations.NotNull; import org.polydev.gaea.population.GaeaBlockPopulator; +import org.polydev.gaea.profiler.ProfileFuture; import java.io.File; import java.io.IOException; @@ -16,23 +19,18 @@ import java.util.HashSet; import java.util.Random; import java.util.Set; -public class StructurePopulator extends GaeaBlockPopulator { - StructureSpawn spawnTest = new StructureSpawn(100, 5); - Set pop = new HashSet<>(); +public class StructurePopulator extends BlockPopulator { + StructureSpawn spawnTest = new StructureSpawn(250, 250); + GaeaStructure struc = GaeaStructure.load(new File(Terra.getInstance().getDataFolder() + File.separator + "export" + File.separator + "structures", "demo2.tstructure")); + double horizontal = struc.getStructureInfo().getMaxHorizontal()/16D + 1D; + + public StructurePopulator() throws IOException { + } + @Override public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk chunk) { - if(pop.contains(chunk)) Bukkit.getLogger().warning("Already populated structures in chunk: " + chunk); - pop.add(chunk); - Location near = spawnTest.getNearestSpawn((chunk.getX() << 4) + 8, (chunk.getZ() << 4), world.getSeed()).toLocation(world); - if(near.getChunk().equals(chunk)) { - Terra.getInstance().getLogger().info("Spawning structure at " + near.toString() + " in chunk " + chunk); - try { - GaeaStructure struc = GaeaStructure.load(new File(Terra.getInstance().getDataFolder() + File.separator + "export" + File.separator + "structures", "demo.tstructure")); - near.setY(world.getHighestBlockYAt(near)); - struc.paste(near); - } catch(IOException e) { - e.printStackTrace(); - } + try(ProfileFuture ignored = TerraProfiler.fromWorld(world).measure("StructureTime")) { + } } } diff --git a/src/main/java/com/dfsek/terra/population/TreePopulator.java b/src/main/java/com/dfsek/terra/population/TreePopulator.java index 6d0a9e5fe..6634ae6bd 100644 --- a/src/main/java/com/dfsek/terra/population/TreePopulator.java +++ b/src/main/java/com/dfsek/terra/population/TreePopulator.java @@ -18,26 +18,26 @@ import java.util.Random; public class TreePopulator extends GaeaBlockPopulator { @Override public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk chunk) { - ProfileFuture tree = TerraProfiler.fromWorld(world).measure("TreeGenTime"); - int x = random.nextInt(16); // Decrease chances of chunk-crossing trees - int z = random.nextInt(16); - Location origin = chunk.getBlock(x, 0, z).getLocation(); - Biome b = TerraBiomeGrid.fromWorld(world).getBiome(origin); - if(((UserDefinedDecorator) b.getDecorator()).getTreeChance() < random.nextInt(100)) return; - int numTrees = 0; - for(int i = 0; i < 48; i++) { - int y = WorldUtil.getHighestValidSpawnAt(chunk, x, z); - if(y <= 0) continue; - origin = chunk.getBlock(x, y, z).getLocation().add(0, 1, 0); - b = TerraBiomeGrid.fromWorld(world).getBiome(origin); - numTrees++; - try { - b.getDecorator().getTrees().get(random).plant(origin, random, false, Terra.getInstance()); - } catch(NullPointerException ignored) {} - if(numTrees >= b.getDecorator().getTreeDensity()) break; - x = random.nextInt(16); // Decrease chances of chunk-crossing trees - z = random.nextInt(16); + try(ProfileFuture ignored = TerraProfiler.fromWorld(world).measure("TreeGenTime")) { + int x = random.nextInt(16); // Decrease chances of chunk-crossing trees + int z = random.nextInt(16); + Location origin = chunk.getBlock(x, 0, z).getLocation(); + Biome b = TerraBiomeGrid.fromWorld(world).getBiome(origin); + if(((UserDefinedDecorator) b.getDecorator()).getTreeChance() < random.nextInt(100)) return; + int numTrees = 0; + for(int i = 0; i < 48; i++) { + int y = WorldUtil.getHighestValidSpawnAt(chunk, x, z); + if(y <= 0) continue; + origin = chunk.getBlock(x, y, z).getLocation().add(0, 1, 0); + b = TerraBiomeGrid.fromWorld(world).getBiome(origin); + numTrees++; + try { + b.getDecorator().getTrees().get(random).plant(origin, random, false, Terra.getInstance()); + } catch(NullPointerException ignore) {} + if(numTrees >= b.getDecorator().getTreeDensity()) break; + x = random.nextInt(16); // Decrease chances of chunk-crossing trees + z = random.nextInt(16); + } } - if(tree!=null) tree.complete(); } } diff --git a/src/main/java/com/dfsek/terra/structure/GaeaStructure.java b/src/main/java/com/dfsek/terra/structure/GaeaStructure.java index 486bd8f8e..af8d548f3 100644 --- a/src/main/java/com/dfsek/terra/structure/GaeaStructure.java +++ b/src/main/java/com/dfsek/terra/structure/GaeaStructure.java @@ -1,10 +1,14 @@ package com.dfsek.terra.structure; +import com.dfsek.terra.MaxMin; +import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; +import org.bukkit.block.Sign; import org.bukkit.block.data.BlockData; +import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.FileInputStream; @@ -16,14 +20,18 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import java.util.UUID; +import java.util.function.Consumer; +import java.util.function.Function; public class GaeaStructure implements Serializable { public static final long serialVersionUID = -6664585217063842035L; private final StructureContainedBlock[][][] structure; + private final GaeaStructureInfo structureInfo; private final String id; private final UUID uuid; - public static GaeaStructure load(File f) throws IOException { + @NotNull + public static GaeaStructure load(@NotNull File f) throws IOException { try { return fromFile(f); } catch(ClassNotFoundException e) { @@ -31,7 +39,8 @@ public class GaeaStructure implements Serializable { } } - public GaeaStructure(Location l1, Location l2, String id) { + public GaeaStructure(@NotNull Location l1, @NotNull Location l2, @NotNull String id) throws InitializationException { + int centerX = -1, centerZ = -1; this.id = id; this.uuid = UUID.randomUUID(); if(l1.getX() > l2.getX() || l1.getY() > l2.getY() || l1.getZ() > l2.getZ()) throw new IllegalArgumentException("Invalid locations provided!"); @@ -39,13 +48,32 @@ public class GaeaStructure implements Serializable { for(int x = 0; x <= l2.getBlockX()-l1.getBlockX(); x++) { for(int y = 0; y <= l2.getBlockY()-l1.getBlockY(); y++) { for(int z = 0; z <= l2.getBlockZ()-l1.getBlockZ(); z++) { - structure[x][y][z] = new StructureContainedBlock(x, y, z, Objects.requireNonNull(l1.getWorld()).getBlockAt(l1.clone().add(x, y, z))); + Block b = Objects.requireNonNull(l1.getWorld()).getBlockAt(l1.clone().add(x, y, z)); + BlockData d = b.getBlockData(); + if(d instanceof Sign) { + Sign s = (Sign) b.getState(); + if(s.getLine(0).equals("[TERRA]")) { + d = Bukkit.createBlockData(s.getLine(2)+s.getLine(3)); + if(s.getLine(1).equals("[CENTER]")) { + centerX = x; + centerZ = z; + } + } + } + structure[x][y][z] = new StructureContainedBlock(x, y, z, d); } } } + if(centerX == -1 || centerZ == -1) throw new InitializationException("No structure center specified."); + structureInfo = new GaeaStructureInfo(l2.getBlockX()-l1.getBlockX(), l2.getBlockY()-l1.getBlockY(), l2.getBlockZ()-l1.getBlockZ(), centerX, centerZ); } - public void paste(Location origin) { + @NotNull + public GaeaStructureInfo getStructureInfo() { + return structureInfo; + } + + public void paste(@NotNull Location origin) { for(StructureContainedBlock[][] bList2 : structure) { for(StructureContainedBlock[] bList1 : bList2) { for(StructureContainedBlock block : bList1) { @@ -57,46 +85,54 @@ public class GaeaStructure implements Serializable { } } - public void paste(Location origin, Chunk c) { - for(StructureContainedBlock[][] bList2 : structure) { - for(StructureContainedBlock[] bList1 : bList2) { - for(StructureContainedBlock block : bList1) { - Location newLoc = origin.clone().add(block.getX(), block.getY(), block.getZ()); - BlockData data = block.getBlockData(); - if(newLoc.getChunk().equals(c) && !data.getMaterial().equals(Material.STRUCTURE_VOID)) newLoc.getBlock().setBlockData(block.getBlockData()); + private StructureContainedBlock[][][] executeForBlocksInRange(MaxMin xM, MaxMin yM, MaxMin zM, Consumer exec) { + StructureContainedBlock[][][] temp = new StructureContainedBlock[xM.getMax()-xM.getMin()+1][yM.getMax()-yM.getMin()+1][zM.getMax()-zM.getMin()+1]; + for(int x : xM) { + for(int y : yM) { + for(int z : zM) { + if(isInStructure(x, y, z)) exec.accept(structure[x][y][z]); } } } + return temp; } - public void save(File f) throws IOException { + private boolean isInStructure(int x, int y, int z) { + return x < structure.length && y < structure[0].length && z < structure[0][0].length; + } + + public void save(@NotNull File f) throws IOException { toFile(this, f); } - private static GaeaStructure fromFile(File f) throws IOException, ClassNotFoundException { + @NotNull + private static GaeaStructure fromFile(@NotNull File f) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f)); Object o = ois.readObject(); ois.close(); return (GaeaStructure) o; } - public static GaeaStructure fromStream(InputStream f) throws IOException, ClassNotFoundException { + @NotNull + public static GaeaStructure fromStream(@NotNull InputStream f) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(f); Object o = ois.readObject(); ois.close(); return (GaeaStructure) o; } - private static void toFile(Serializable o, File f) throws IOException { + private static void toFile(@NotNull Serializable o, @NotNull File f) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f)); oos.writeObject(o); oos.close(); } + @NotNull public String getId() { return id; } + @NotNull public UUID getUuid() { return uuid; } diff --git a/src/main/java/com/dfsek/terra/structure/GaeaStructureInfo.java b/src/main/java/com/dfsek/terra/structure/GaeaStructureInfo.java new file mode 100644 index 000000000..1168339fb --- /dev/null +++ b/src/main/java/com/dfsek/terra/structure/GaeaStructureInfo.java @@ -0,0 +1,43 @@ +package com.dfsek.terra.structure; + +import java.io.Serializable; + +public class GaeaStructureInfo implements Serializable { + public static final long serialVersionUID = -175639605885943678L; + private final int sizeX; + private final int sizeY; + private final int sizeZ; + private final int centerX; + private final int centerZ; + public GaeaStructureInfo(int sizeX, int sizeY, int sizeZ, int centerX, int centerZ) { + this.sizeX = sizeX; + this.sizeY = sizeY; + this.sizeZ = sizeZ; + this.centerX = centerX; + this.centerZ = centerZ; + } + + public int getSizeX() { + return sizeX; + } + + public int getSizeZ() { + return sizeZ; + } + + public int getSizeY() { + return sizeY; + } + + public int getCenterX() { + return centerX; + } + + public int getCenterZ() { + return centerZ; + } + + public double getMaxHorizontal() { + return Math.sqrt(Math.pow(sizeX, 2) + Math.pow(sizeZ, 2)); + } +} diff --git a/src/main/java/com/dfsek/terra/structure/InitializationException.java b/src/main/java/com/dfsek/terra/structure/InitializationException.java new file mode 100644 index 000000000..8289a3a83 --- /dev/null +++ b/src/main/java/com/dfsek/terra/structure/InitializationException.java @@ -0,0 +1,7 @@ +package com.dfsek.terra.structure; + +public class InitializationException extends Exception { + public InitializationException(String message) { + super(message); + } +} diff --git a/src/main/java/com/dfsek/terra/structure/StructureContainedBlock.java b/src/main/java/com/dfsek/terra/structure/StructureContainedBlock.java index 0b91683ba..5f84a0d2e 100644 --- a/src/main/java/com/dfsek/terra/structure/StructureContainedBlock.java +++ b/src/main/java/com/dfsek/terra/structure/StructureContainedBlock.java @@ -4,6 +4,7 @@ import org.bukkit.Bukkit; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; import org.bukkit.entity.Entity; +import org.bukkit.event.block.BlockDamageEvent; import java.io.Serializable; @@ -14,11 +15,11 @@ public class StructureContainedBlock implements Serializable { private final int x; private final int y; private final int z; - public StructureContainedBlock(int x, int y, int z, Block block) { + public StructureContainedBlock(int x, int y, int z, BlockData block) { this.x = x; this.y = y; this.z = z; - this.bl = block.getBlockData(); + this.bl = block; dataString = bl.getAsString(false); } diff --git a/src/main/java/com/dfsek/terra/structure/StructureManager.java b/src/main/java/com/dfsek/terra/structure/StructureManager.java deleted file mode 100644 index a5256dced..000000000 --- a/src/main/java/com/dfsek/terra/structure/StructureManager.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.dfsek.terra.structure; - -import org.bukkit.plugin.java.JavaPlugin; - -import java.io.File; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import java.util.logging.Logger; - -public class StructureManager { - private static StructureManager singleton; - private Logger logger; - private final Map structures = new HashMap<>(); - public StructureManager(JavaPlugin main) { - if(singleton!= null) throw new IllegalStateException("Only one instance of StructureManager may exist at a given time."); - this.logger = main.getLogger(); - logger.info("Initializing StructureManager..."); - singleton = this; - } - public GaeaStructure get(UUID uuid) { - return structures.get(uuid); - } - public void load(File file) throws IOException { - GaeaStructure s = GaeaStructure.load(file); - structures.put(s.getUuid(), s); - } -} diff --git a/src/test/java/MaxMinTest.java b/src/test/java/MaxMinTest.java new file mode 100644 index 000000000..5479c9e51 --- /dev/null +++ b/src/test/java/MaxMinTest.java @@ -0,0 +1,17 @@ +import com.dfsek.terra.MaxMin; +import org.jetbrains.annotations.TestOnly; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class MaxMinTest { + @Test + public void iterator() { + MaxMin m = new MaxMin(0, 100); + int i = 0; + for(int mint : m) { + assertEquals(i, mint); + i++; + } + assertEquals(100, i); + } +}