diff --git a/src/main/java/com/dfsek/terra/async/AsyncStructureFinder.java b/src/main/java/com/dfsek/terra/async/AsyncStructureFinder.java index f61c04e9e..a1b87bdcd 100644 --- a/src/main/java/com/dfsek/terra/async/AsyncStructureFinder.java +++ b/src/main/java/com/dfsek/terra/async/AsyncStructureFinder.java @@ -90,10 +90,6 @@ public class AsyncStructureFinder implements Runnable { main: for(int y = target.getSearchStart().get(r2); y > 0; y--) { spawn.setY(y); if(y > target.getBound().getMax() || y < target.getBound().getMin()) return false; - for(StructureSpawnRequirement s : struc.getSpawns()) { - if(! s.isValidSpawn(spawn)) continue main; // Probably(tm) async safe - if(!b.equals(grid.getBiome(spawn.clone().add(s.getX(), s.getY(), s.getZ()), GenerationPhase.POPULATE))) return false; - } } return true; } diff --git a/src/main/java/com/dfsek/terra/command/structure/LoadCommand.java b/src/main/java/com/dfsek/terra/command/structure/LoadCommand.java index acabb7eab..1a62cc59f 100644 --- a/src/main/java/com/dfsek/terra/command/structure/LoadCommand.java +++ b/src/main/java/com/dfsek/terra/command/structure/LoadCommand.java @@ -22,6 +22,7 @@ public class LoadCommand extends PlayerCommand { GaeaStructure struc = GaeaStructure.load(new File(Terra.getInstance().getDataFolder() + File.separator + "export" + File.separator + "structures", args[0] + ".tstructure")); if("true".equals(args[2])) struc.paste(sender.getLocation(), r); else struc.paste(sender.getLocation(), sender.getLocation().getChunk(), r); + //sender.sendMessage(String.valueOf(struc.checkSpawns(sender.getLocation(), r))); } catch(IOException e) { e.printStackTrace(); LangUtil.send("command.structure.invalid", sender, args[0]); diff --git a/src/main/java/com/dfsek/terra/config/genconfig/TreeConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/TreeConfig.java new file mode 100644 index 000000000..a710fb876 --- /dev/null +++ b/src/main/java/com/dfsek/terra/config/genconfig/TreeConfig.java @@ -0,0 +1,66 @@ +package com.dfsek.terra.config.genconfig; + +import com.dfsek.terra.Debug; +import com.dfsek.terra.config.TerraConfig; +import com.dfsek.terra.config.base.ConfigPack; +import com.dfsek.terra.config.base.ConfigUtil; +import com.dfsek.terra.config.exception.ConfigException; +import com.dfsek.terra.config.exception.NotFoundException; +import com.dfsek.terra.structure.GaeaStructure; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.plugin.java.JavaPlugin; +import org.polydev.gaea.math.ProbabilityCollection; +import org.polydev.gaea.tree.Tree; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Random; +import java.util.Set; + +public class TreeConfig extends TerraConfig implements Tree { + private final Set spawnable; + private final String id; + private final ProbabilityCollection structure = new ProbabilityCollection<>(); + public TreeConfig(File file, ConfigPack config) throws IOException, InvalidConfigurationException { + super(file, config); + spawnable = ConfigUtil.toBlockData(getStringList("spawnable"), "spawnable", getID()); + if(!contains("id")) throw new ConfigException("No ID specified!", "null"); + id = getString("id"); + if(!contains("files")) throw new ConfigException("No files specified!", getID()); + try { + for(Map.Entry e : Objects.requireNonNull(getConfigurationSection("files")).getValues(false).entrySet()) { + try { + File structureFile = new File(config.getDataFolder() + File.separator + "trees" + File.separator + "data", e.getKey() + ".tstructure"); + structure.add(GaeaStructure.load(structureFile), (Integer) e.getValue()); + } catch(FileNotFoundException ex) { + Debug.stack(ex); + throw new NotFoundException("Tree Structure File", e.getKey(), getID()); + } catch(ClassCastException ex) { + Debug.stack(ex); + throw new ConfigException("Unable to parse Tree configuration! Check YAML syntax.", getID()); + } + } + } catch(IOException | NullPointerException e) { + if(ConfigUtil.debug) { + e.printStackTrace(); + } + throw new NotFoundException("Tree Structure", getString("file"), getID()); + } + } + + @Override + public String getID() { + return id; + } + + @Override + public boolean plant(Location location, Random random, boolean b, JavaPlugin javaPlugin) { + return false; + } +} diff --git a/src/main/java/com/dfsek/terra/population/StructurePopulator.java b/src/main/java/com/dfsek/terra/population/StructurePopulator.java index 2d7e71097..76c131264 100644 --- a/src/main/java/com/dfsek/terra/population/StructurePopulator.java +++ b/src/main/java/com/dfsek/terra/population/StructurePopulator.java @@ -34,17 +34,15 @@ public class StructurePopulator extends BlockPopulator { Location spawn = conf.getSpawn().getNearestSpawn(cx + 8, cz + 8, world.getSeed()).toLocation(world); Random r2 = new Random(spawn.hashCode()); GaeaStructure struc = conf.getStructure(r2); + GaeaStructure.Rotation rotation = GaeaStructure.Rotation.fromDegrees(r2.nextInt(4) * 90); main: for(int y = conf.getSearchStart().get(r2); y > 0; y--) { if(y > conf.getBound().getMax() || y < conf.getBound().getMin()) continue structure; spawn.setY(y); - for(StructureSpawnRequirement s : struc.getSpawns()) { - if(! s.isValidSpawn(spawn)) continue main; - if(!b.equals(grid.getBiome(spawn.clone().add(s.getX(), s.getY(), s.getZ()), GenerationPhase.POPULATE))) continue structure; - } + if(!struc.checkSpawns(spawn, rotation)) continue; double horizontal = struc.getStructureInfo().getMaxHorizontal(); if(Math.abs((cx + 8) - spawn.getBlockX()) <= horizontal && Math.abs((cz + 8) - spawn.getBlockZ()) <= horizontal) { try(ProfileFuture ignore = TerraProfiler.fromWorld(world).measure("StructurePasteTime")) { - struc.paste(spawn, chunk, GaeaStructure.Rotation.fromDegrees(r2.nextInt(4) * 90)); + struc.paste(spawn, chunk, rotation); break; } } diff --git a/src/main/java/com/dfsek/terra/structure/GaeaStructure.java b/src/main/java/com/dfsek/terra/structure/GaeaStructure.java index c99c61fd6..886de1169 100644 --- a/src/main/java/com/dfsek/terra/structure/GaeaStructure.java +++ b/src/main/java/com/dfsek/terra/structure/GaeaStructure.java @@ -31,8 +31,10 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.UUID; import java.util.function.Consumer; @@ -41,10 +43,10 @@ import static com.dfsek.terra.util.structure.RotationUtil.*; public class GaeaStructure implements Serializable { public static final long serialVersionUID = -6664585217063842035L; private final StructureContainedBlock[][][] structure; - private final ArrayList spawns = new ArrayList<>(); private final GaeaStructureInfo structureInfo; private final String id; private final UUID uuid; + private final HashSet spawns; @NotNull public static GaeaStructure load(@NotNull File f) throws IOException { @@ -56,9 +58,10 @@ public class GaeaStructure implements Serializable { } public GaeaStructure(@NotNull Location l1, @NotNull Location l2, @NotNull String id) throws InitializationException { - int centerX = -1, centerY = -1, centerZ = -1; + int centerX = -1, centerZ = -1; this.id = id; this.uuid = UUID.randomUUID(); + this.spawns = new HashSet<>(); if(l1.getX() > l2.getX() || l1.getY() > l2.getY() || l1.getZ() > l2.getZ()) throw new IllegalArgumentException("Invalid locations provided!"); structure = new StructureContainedBlock[l2.getBlockX()-l1.getBlockX()+1][l2.getBlockZ()-l1.getBlockZ()+1][l2.getBlockY()-l1.getBlockY()+1]; for(int x = 0; x <= l2.getBlockX()-l1.getBlockX(); x++) { @@ -68,6 +71,7 @@ public class GaeaStructure implements Serializable { BlockState state = b.getState(); BlockData d = b.getBlockData(); boolean useState = true; + StructureSpawnRequirement requirement = StructureSpawnRequirement.BLANK; if(state instanceof Sign) { Sign s = (Sign) b.getState(); if(s.getLine(0).equals("[TERRA]")) { @@ -76,22 +80,14 @@ public class GaeaStructure implements Serializable { useState = false; if(s.getLine(1).equals("[CENTER]")) { centerX = x; - centerY = y; centerZ = z; } else if(s.getLine(1).startsWith("[SPAWN=") && s.getLine(1).endsWith("]")) { String og = s.getLine(1); String spawn = og.substring(og.indexOf("=")+1, og.length()-1); - switch(spawn.toUpperCase()) { - case "LAND": - spawns.add(new StructureSpawnRequirement(StructureSpawnRequirement.RequirementType.LAND, x, y, z)); - break; - case "OCEAN": - spawns.add(new StructureSpawnRequirement(StructureSpawnRequirement.RequirementType.OCEAN, x, y, z)); - break; - case "AIR": - spawns.add(new StructureSpawnRequirement(StructureSpawnRequirement.RequirementType.AIR, x, y, z)); - break; - default: throw new InitializationException("Invalid spawn type, " + spawn); + try { + requirement = StructureSpawnRequirement.valueOf(spawn.toUpperCase()); + } catch(IllegalArgumentException e) { + throw new InitializationException("Invalid spawn type: " + spawn); } } } catch(IllegalArgumentException e) { @@ -99,16 +95,14 @@ public class GaeaStructure implements Serializable { } } } - structure[x][z][y] = new StructureContainedBlock(x, y, z, useState ? state : null, d); + StructureContainedBlock block = new StructureContainedBlock(x, y, z, useState ? state : null, d, requirement); + if(!requirement.equals(StructureSpawnRequirement.BLANK)) spawns.add(block); + structure[x][z][y] = block; } } } - if(centerX < 0 || centerY < 0 || centerZ < 0) throw new InitializationException("No structure center specified."); - structureInfo = new GaeaStructureInfo(l2.getBlockX()-l1.getBlockX()+1, l2.getBlockY()-l1.getBlockY()+1, l2.getBlockZ()-l1.getBlockZ()+1, new Vector(centerX, centerY, centerZ)); - } - - public ArrayList getSpawns() { - return spawns; + if(centerX < 0 || centerZ < 0) throw new InitializationException("No structure center specified."); + structureInfo = new GaeaStructureInfo(l2.getBlockX()-l1.getBlockX()+1, l2.getBlockY()-l1.getBlockY()+1, l2.getBlockZ()-l1.getBlockZ()+1, new Vector2(centerX, centerZ)); } /** @@ -129,6 +123,13 @@ public class GaeaStructure implements Serializable { this.executeForBlocksInRange(getRange(Axis.X), getRange(Axis.Y), getRange(Axis.Z), block -> pasteBlock(block, origin, r), r); } + public boolean checkSpawns(Location origin, Rotation r) { + for(StructureContainedBlock b : spawns) { + Vector2 rot = getRotatedCoords(new Vector2(b.getX()-structureInfo.getCenterX(), b.getZ()-structureInfo.getCenterZ()), r); + if(!b.getRequirement().matches(origin.getWorld(), (int) rot.getX()+origin.getBlockX(), origin.getBlockY(), (int) rot.getZ()+origin.getBlockZ())) return false; + } + return true; + } @@ -158,7 +159,8 @@ public class GaeaStructure implements Serializable { */ private void pasteBlock(StructureContainedBlock block, Location origin, Rotation r) { BlockData data = block.getBlockData().clone(); - Block worldBlock = origin.clone().add(block.getX(), block.getY(), block.getZ()).getBlock(); + Location loc = origin.clone().add(block.getX(), block.getY(), block.getZ()); + Block worldBlock = loc.getBlock(); if(!data.getMaterial().equals(Material.STRUCTURE_VOID)) { if(data instanceof Rotatable) { BlockFace rt = getRotatedFace(((Rotatable) data).getRotation(), r); @@ -203,7 +205,7 @@ public class GaeaStructure implements Serializable { c.add(new Vector2(structureInfo.getCenterX(), structureInfo.getCenterZ())); if(isInStructure((int) c.getX(), y, (int) c.getZ())) { StructureContainedBlock b = structure[(int) c.getX()][(int) c.getZ()][y]; - exec.accept(new StructureContainedBlock(x - getStructureInfo().getCenterX(), y, z - getStructureInfo().getCenterZ(), b.getState(), b.getBlockData())); + exec.accept(new StructureContainedBlock(x - getStructureInfo().getCenterX(), y, z - getStructureInfo().getCenterZ(), b.getState(), b.getBlockData(), b.getRequirement())); } } } diff --git a/src/main/java/com/dfsek/terra/structure/GaeaStructureInfo.java b/src/main/java/com/dfsek/terra/structure/GaeaStructureInfo.java index 22bb742f7..781ff7f02 100644 --- a/src/main/java/com/dfsek/terra/structure/GaeaStructureInfo.java +++ b/src/main/java/com/dfsek/terra/structure/GaeaStructureInfo.java @@ -1,5 +1,6 @@ package com.dfsek.terra.structure; +import com.dfsek.terra.procgen.math.Vector2; import org.bukkit.util.Vector; import java.io.Serializable; @@ -11,14 +12,12 @@ public class GaeaStructureInfo implements Serializable { private final int sizeZ; private final int centerX; private final int centerZ; - private final int centerY; - public GaeaStructureInfo(int sizeX, int sizeY, int sizeZ, Vector center) { + public GaeaStructureInfo(int sizeX, int sizeY, int sizeZ, Vector2 center) { this.sizeX = sizeX; this.sizeY = sizeY; this.sizeZ = sizeZ; - this.centerX = center.getBlockX(); - this.centerZ = center.getBlockZ(); - this.centerY = center.getBlockY(); + this.centerX = (int) center.getX(); + this.centerZ = (int) center.getZ(); } public int getSizeX() { @@ -37,10 +36,6 @@ public class GaeaStructureInfo implements Serializable { return centerX; } - public int getCenterY() { - return centerY; - } - public int getCenterZ() { return centerZ; } diff --git a/src/main/java/com/dfsek/terra/structure/StructureContainedBlock.java b/src/main/java/com/dfsek/terra/structure/StructureContainedBlock.java index 35d31e69c..9e0d947bf 100644 --- a/src/main/java/com/dfsek/terra/structure/StructureContainedBlock.java +++ b/src/main/java/com/dfsek/terra/structure/StructureContainedBlock.java @@ -18,7 +18,8 @@ public class StructureContainedBlock implements Serializable { private final int y; private final int z; private final SerializableBlockState state; - public StructureContainedBlock(int x, int y, int z, BlockState state, BlockData d) { + private final StructureSpawnRequirement requirement; + public StructureContainedBlock(int x, int y, int z, BlockState state, BlockData d, StructureSpawnRequirement spawn) { if(state instanceof Sign) { this.state = new SerializableSign((org.bukkit.block.Sign) state); } else this.state = null; @@ -26,8 +27,9 @@ public class StructureContainedBlock implements Serializable { this.y = y; this.z = z; this.bl = new SerializableBlockData(d); + this.requirement = spawn; } - public StructureContainedBlock(int x, int y, int z, SerializableBlockState state, BlockData d) { + public StructureContainedBlock(int x, int y, int z, SerializableBlockState state, BlockData d, StructureSpawnRequirement spawn) { if(state instanceof SerializableSign) { this.state = state; } else this.state = null; @@ -35,6 +37,11 @@ public class StructureContainedBlock implements Serializable { this.y = y; this.z = z; this.bl = new SerializableBlockData(d); + this.requirement = spawn; + } + + public StructureSpawnRequirement getRequirement() { + return requirement; } public int getX() { diff --git a/src/main/java/com/dfsek/terra/structure/StructureSpawnRequirement.java b/src/main/java/com/dfsek/terra/structure/StructureSpawnRequirement.java index 81390a768..037496b2d 100644 --- a/src/main/java/com/dfsek/terra/structure/StructureSpawnRequirement.java +++ b/src/main/java/com/dfsek/terra/structure/StructureSpawnRequirement.java @@ -12,65 +12,38 @@ import java.io.Serializable; import java.util.HashMap; import java.util.Map; -public class StructureSpawnRequirement implements Serializable { +public enum StructureSpawnRequirement implements Serializable { + AIR { + @Override + public boolean matches(World w, int x, int y, int z) { + UserDefinedBiome b = (UserDefinedBiome) TerraWorld.getWorld(w).getGrid().getBiome(x, z, GenerationPhase.POPULATE); + BiomeConfig c = TerraWorld.getWorld(w).getConfig().getBiome(b); + if(y <= c.getOcean().getSeaLevel()) return false; + return b.getGenerator().getNoise(getNoise(w), w, x, y, z) <= 0; + } + }, OCEAN { + @Override + public boolean matches(World w, int x, int y, int z) { + UserDefinedBiome b = (UserDefinedBiome) TerraWorld.getWorld(w).getGrid().getBiome(x, z, GenerationPhase.POPULATE); + BiomeConfig c = TerraWorld.getWorld(w).getConfig().getBiome(b); + if(y > c.getOcean().getSeaLevel()) return false; + return b.getGenerator().getNoise(getNoise(w), w, x, y, z) <= 0; + } + }, LAND { + @Override + public boolean matches(World w, int x, int y, int z) { + UserDefinedBiome b = (UserDefinedBiome) TerraWorld.getWorld(w).getGrid().getBiome(x, z, GenerationPhase.POPULATE); + return b.getGenerator().getNoise(getNoise(w), w, x, y, z) > 0; + } + }, BLANK { + @Override + public boolean matches(World w, int x, int y, int z) { + return true; + } + }; private static final long serialVersionUID = -175639605885943679L; private static final transient Map noiseMap = new HashMap<>(); - private final RequirementType type; - private final int x, y, z; - - public StructureSpawnRequirement(RequirementType type, int x, int y, int z) { - this.type = type; - this.x = x; - this.y = y; - this.z = z; - } - - public RequirementType getType() { - return type; - } - - public int getX() { - return x; - } - - public int getY() { - return y; - } - - public int getZ() { - return z; - } - - public boolean isValidSpawn(Location origin) { - return type.matches(origin.getWorld(), origin.getBlockX()+x, origin.getBlockY()+y, origin.getBlockZ()+z); - } - - public enum RequirementType { - AIR { - @Override - public boolean matches(World w, int x, int y, int z) { - UserDefinedBiome b = (UserDefinedBiome) TerraWorld.getWorld(w).getGrid().getBiome(x, z, GenerationPhase.POPULATE); - BiomeConfig c = TerraWorld.getWorld(w).getConfig().getBiome(b); - if(y <= c.getOcean().getSeaLevel()) return false; - return b.getGenerator().getNoise(getNoise(w), w, x, y, z) <= 0; - } - }, OCEAN { - @Override - public boolean matches(World w, int x, int y, int z) { - UserDefinedBiome b = (UserDefinedBiome) TerraWorld.getWorld(w).getGrid().getBiome(x, z, GenerationPhase.POPULATE); - BiomeConfig c = TerraWorld.getWorld(w).getConfig().getBiome(b); - if(y > c.getOcean().getSeaLevel()) return false; - return b.getGenerator().getNoise(getNoise(w), w, x, y, z) <= 0; - } - }, LAND { - @Override - public boolean matches(World w, int x, int y, int z) { - UserDefinedBiome b = (UserDefinedBiome) TerraWorld.getWorld(w).getGrid().getBiome(x, z, GenerationPhase.POPULATE); - return b.getGenerator().getNoise(getNoise(w), w, x, y, z) > 0; - } - }; - public abstract boolean matches(World w, int x, int y, int z); - } + public abstract boolean matches(World w, int x, int y, int z); public static void putNoise(World w, FastNoise noise) { noiseMap.putIfAbsent(w, noise); }