diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 50b07f269..35eb1ddfb 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 61c8d8f05..9b385dde5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -33,7 +33,7 @@ dependencies { compileOnly("org.spigotmc:spigot-api:1.16.2-R0.1-SNAPSHOT") compileOnly("org.jetbrains:annotations:20.1.0") // more recent. implementation("commons-io:commons-io:2.4") - implementation(name = "Gaea-1.13.0", group = "") + compileOnly(name = "Gaea-1.13.0", group = "") implementation("org.apache.commons:commons-imaging:1.0-alpha2") compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.2.0-SNAPSHOT") implementation("org.bstats:bstats-bukkit:1.7") @@ -86,12 +86,6 @@ val setupServer = tasks.create("setupServer") { File("${testDir}/eula.txt").writeText("eula=true") - // Copy Terra into dir - copy { - from("${buildDir}/libs/Terra-${versionObj}.jar") - into("${testDir}/plugins/") - } - // clean up file("WorldGenTestServer").deleteRecursively() } @@ -99,6 +93,15 @@ val setupServer = tasks.create("setupServer") { val testWithPaper = task(name = "testWithPaper") { dependsOn(setupServer) + //dependsOn(tasks.shadowJar) + // Copy Terra into dir + doFirst { + copy { + from("${buildDir}/libs/Terra-${versionObj}.jar") + into("${testDir}/plugins/") + } + } + main = "io.papermc.paperclip.Paperclip" jvmArgs = listOf("-XX:+UseG1GC", "-XX:+ParallelRefProcEnabled", "-XX:MaxGCPauseMillis=200", "-XX:+UnlockExperimentalVMOptions", "-XX:+DisableExplicitGC", "-XX:+AlwaysPreTouch", diff --git a/src/main/java/com/dfsek/terra/EventListener.java b/src/main/java/com/dfsek/terra/EventListener.java index 7dabbc01b..df801638a 100644 --- a/src/main/java/com/dfsek/terra/EventListener.java +++ b/src/main/java/com/dfsek/terra/EventListener.java @@ -1,7 +1,7 @@ package com.dfsek.terra; import com.dfsek.terra.async.AsyncStructureFinder; -import com.dfsek.terra.config.genconfig.StructureConfig; +import com.dfsek.terra.config.genconfig.structure.StructureConfig; import com.dfsek.terra.util.StructureTypeEnum; import org.bukkit.entity.EnderSignal; import org.bukkit.entity.Entity; diff --git a/src/main/java/com/dfsek/terra/async/AsyncStructureFinder.java b/src/main/java/com/dfsek/terra/async/AsyncStructureFinder.java index 1421156cd..5ef704374 100644 --- a/src/main/java/com/dfsek/terra/async/AsyncStructureFinder.java +++ b/src/main/java/com/dfsek/terra/async/AsyncStructureFinder.java @@ -4,7 +4,7 @@ import com.dfsek.terra.Terra; import com.dfsek.terra.TerraWorld; import com.dfsek.terra.biome.TerraBiomeGrid; import com.dfsek.terra.biome.UserDefinedBiome; -import com.dfsek.terra.config.genconfig.StructureConfig; +import com.dfsek.terra.config.genconfig.structure.StructureConfig; import com.dfsek.terra.structure.Structure; import org.bukkit.Bukkit; import org.bukkit.Location; diff --git a/src/main/java/com/dfsek/terra/command/biome/BiomeInfoCommand.java b/src/main/java/com/dfsek/terra/command/biome/BiomeInfoCommand.java index c6416f825..685e6fd0c 100644 --- a/src/main/java/com/dfsek/terra/command/biome/BiomeInfoCommand.java +++ b/src/main/java/com/dfsek/terra/command/biome/BiomeInfoCommand.java @@ -3,7 +3,7 @@ package com.dfsek.terra.command.biome; import com.dfsek.terra.TerraWorld; import com.dfsek.terra.biome.UserDefinedBiome; import com.dfsek.terra.config.base.ConfigPack; -import com.dfsek.terra.config.genconfig.StructureConfig; +import com.dfsek.terra.config.genconfig.structure.StructureConfig; import com.dfsek.terra.config.genconfig.biome.BiomeConfig; import com.dfsek.terra.config.genconfig.biome.BiomeSnowConfig; import com.dfsek.terra.config.lang.LangUtil; diff --git a/src/main/java/com/dfsek/terra/command/structure/LocateCommand.java b/src/main/java/com/dfsek/terra/command/structure/LocateCommand.java index 2aefbad67..561abeef4 100644 --- a/src/main/java/com/dfsek/terra/command/structure/LocateCommand.java +++ b/src/main/java/com/dfsek/terra/command/structure/LocateCommand.java @@ -3,7 +3,7 @@ package com.dfsek.terra.command.structure; import com.dfsek.terra.Terra; import com.dfsek.terra.TerraWorld; import com.dfsek.terra.async.AsyncStructureFinder; -import com.dfsek.terra.config.genconfig.StructureConfig; +import com.dfsek.terra.config.genconfig.structure.StructureConfig; import com.dfsek.terra.config.lang.LangUtil; import com.dfsek.terra.generation.TerraChunkGenerator; import org.bukkit.Bukkit; @@ -12,7 +12,6 @@ import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; import org.polydev.gaea.command.WorldCommand; diff --git a/src/main/java/com/dfsek/terra/config/base/ConfigPack.java b/src/main/java/com/dfsek/terra/config/base/ConfigPack.java index 7e92cf092..64b6ae3a2 100644 --- a/src/main/java/com/dfsek/terra/config/base/ConfigPack.java +++ b/src/main/java/com/dfsek/terra/config/base/ConfigPack.java @@ -11,7 +11,7 @@ import com.dfsek.terra.config.genconfig.CarverConfig; import com.dfsek.terra.config.genconfig.FloraConfig; import com.dfsek.terra.config.genconfig.OreConfig; import com.dfsek.terra.config.genconfig.PaletteConfig; -import com.dfsek.terra.config.genconfig.StructureConfig; +import com.dfsek.terra.config.genconfig.structure.StructureConfig; import com.dfsek.terra.config.genconfig.TreeConfig; import com.dfsek.terra.config.genconfig.biome.AbstractBiomeConfig; import com.dfsek.terra.config.genconfig.biome.BiomeConfig; diff --git a/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeConfig.java index 662455443..c932628f0 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeConfig.java @@ -7,7 +7,7 @@ import com.dfsek.terra.config.TerraConfig; import com.dfsek.terra.config.base.ConfigPack; import com.dfsek.terra.config.exception.ConfigException; import com.dfsek.terra.config.exception.NotFoundException; -import com.dfsek.terra.config.genconfig.StructureConfig; +import com.dfsek.terra.config.genconfig.structure.StructureConfig; import com.dfsek.terra.generation.UserDefinedDecorator; import com.dfsek.terra.generation.UserDefinedGenerator; import org.bukkit.configuration.InvalidConfigurationException; diff --git a/src/main/java/com/dfsek/terra/config/genconfig/structure/EntityFeatureConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/structure/EntityFeatureConfig.java new file mode 100644 index 000000000..27078743e --- /dev/null +++ b/src/main/java/com/dfsek/terra/config/genconfig/structure/EntityFeatureConfig.java @@ -0,0 +1,59 @@ +package com.dfsek.terra.config.genconfig.structure; + +import com.dfsek.terra.Debug; +import com.dfsek.terra.config.base.ConfigUtil; +import com.dfsek.terra.config.exception.ConfigException; +import com.dfsek.terra.structure.features.EntityFeature; +import com.dfsek.terra.structure.features.Feature; +import org.bukkit.Material; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.entity.EntityType; +import org.polydev.gaea.math.Range; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class EntityFeatureConfig implements FeatureConfig { + private final EntityFeature feature; + + @SuppressWarnings("unchecked") + public EntityFeatureConfig(Map items) throws InvalidConfigurationException { + if(! items.containsKey("entity")) throw new ConfigException("No EntityType specified!", "EntityFeature"); + if(! items.containsKey("amount")) throw new ConfigException("No amount specified!", "EntityFeature"); + if(! items.containsKey("attempts")) throw new ConfigException("Attempts not specified!", "EntityFeature"); + if(! items.containsKey("in-height")) throw new ConfigException("Spawn Checking Height not specified!", "EntityFeature"); + if(! items.containsKey("spawnable-on")) throw new ConfigException("No Spawnable-on materials specified!", "EntityFeature"); + if(! items.containsKey("spawnable-in")) throw new ConfigException("No Spawnable-in materials specified!", "EntityFeature"); + + EntityType type; + try { + type = EntityType.valueOf((String) items.get("entity")); + } catch(IllegalArgumentException e) { + throw new InvalidConfigurationException("No such EntityType: " + items.get("entity")); + } catch(ClassCastException e) { + throw new InvalidConfigurationException("Error in Entity Configuration!"); + } + + int attempts = (Integer) items.get("attempts"); + int height = (Integer) items.get("in-height"); + + Range amount; + try { + Map amountMap = (Map) items.get("amount"); + amount = new Range(amountMap.get("min"), amountMap.get("max")); + } catch(ClassCastException e) { + throw new InvalidConfigurationException("Error in Amount Configuration!"); + } + + Set on = ConfigUtil.toBlockData((List) items.get("spawnable-on"), "SpawnableOn", ""); + Set in = ConfigUtil.toBlockData((List) items.get("spawnable-in"), "SpawnableIn", ""); + + this.feature = new EntityFeature(type, amount, attempts, on, in, height); + Debug.info("Loaded EntityFeature with type: " + type); + } + @Override + public Feature getFeature() { + return feature; + } +} diff --git a/src/main/java/com/dfsek/terra/config/genconfig/structure/FeatureConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/structure/FeatureConfig.java new file mode 100644 index 000000000..3bd839ebd --- /dev/null +++ b/src/main/java/com/dfsek/terra/config/genconfig/structure/FeatureConfig.java @@ -0,0 +1,8 @@ +package com.dfsek.terra.config.genconfig.structure; + +import com.dfsek.terra.config.TerraConfigSection; +import com.dfsek.terra.structure.features.Feature; + +public interface FeatureConfig { + Feature getFeature(); +} diff --git a/src/main/java/com/dfsek/terra/config/genconfig/StructureConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/structure/StructureConfig.java similarity index 87% rename from src/main/java/com/dfsek/terra/config/genconfig/StructureConfig.java rename to src/main/java/com/dfsek/terra/config/genconfig/structure/StructureConfig.java index 8591af58d..a44bf7197 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/StructureConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/structure/StructureConfig.java @@ -1,4 +1,4 @@ -package com.dfsek.terra.config.genconfig; +package com.dfsek.terra.config.genconfig.structure; import com.dfsek.terra.Debug; import com.dfsek.terra.config.TerraConfig; @@ -9,6 +9,7 @@ import com.dfsek.terra.config.exception.NotFoundException; import com.dfsek.terra.population.StructurePopulator; import com.dfsek.terra.procgen.GridSpawn; import com.dfsek.terra.structure.Structure; +import com.dfsek.terra.structure.features.Feature; import org.apache.commons.io.FileUtils; import org.bukkit.configuration.InvalidConfigurationException; import org.json.simple.parser.ParseException; @@ -19,7 +20,9 @@ import org.polydev.gaea.structures.loot.LootTable; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Random; @@ -31,8 +34,11 @@ public class StructureConfig extends TerraConfig { private final Range searchStart; private final Range bound; private final Map loot = new HashMap<>(); + private final List features; + StructurePopulator.SearchType type; + @SuppressWarnings("unchecked") public StructureConfig(File file, ConfigPack config) throws IOException, InvalidConfigurationException { super(file, config); if(! contains("id")) throw new ConfigException("No ID specified!", "null"); @@ -76,6 +82,16 @@ public class StructureConfig extends TerraConfig { } } + features = new ArrayList<>(); + if(contains("features")) { + for(Map map : getMapList("features")) { + for(Map.Entry entry : map.entrySet()) { + if(entry.getKey().equals("ENTITY_FEATURE")) + features.add(new EntityFeatureConfig((Map) entry.getValue()).getFeature()); + } + } + } + spawn = new GridSpawn(getInt("spawn.width", 500), getInt("spawn.padding", 100)); searchStart = new Range(getInt("spawn.start.min", 72), getInt("spawn.start.max", 72)); bound = new Range(getInt("spawn.bound.min", 48), getInt("spawn.bound.max", 72)); @@ -86,6 +102,10 @@ public class StructureConfig extends TerraConfig { } } + public List getFeatures() { + return features; + } + @Override public String getID() { return id; diff --git a/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java b/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java index 10bf04dbc..59bceea0a 100644 --- a/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java +++ b/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java @@ -132,11 +132,9 @@ public class TerraChunkGenerator extends GaeaChunkGenerator { private void load(World w) { try { popMan.loadBlocks(w); - } catch(IOException e) { - if(e instanceof FileNotFoundException) { - LangUtil.log("warning.no-population", Level.WARNING); - } else e.printStackTrace(); - } catch(ClassNotFoundException e) { + } catch(FileNotFoundException e) { + LangUtil.log("warning.no-population", Level.WARNING); + } catch(IOException | ClassNotFoundException e) { e.printStackTrace(); } popMap.put(w, popMan); diff --git a/src/main/java/com/dfsek/terra/population/StructurePopulator.java b/src/main/java/com/dfsek/terra/population/StructurePopulator.java index 887953403..b07aca188 100644 --- a/src/main/java/com/dfsek/terra/population/StructurePopulator.java +++ b/src/main/java/com/dfsek/terra/population/StructurePopulator.java @@ -6,10 +6,11 @@ import com.dfsek.terra.TerraWorld; import com.dfsek.terra.biome.TerraBiomeGrid; import com.dfsek.terra.biome.UserDefinedBiome; import com.dfsek.terra.config.base.ConfigPack; -import com.dfsek.terra.config.genconfig.StructureConfig; +import com.dfsek.terra.config.genconfig.structure.StructureConfig; import com.dfsek.terra.procgen.math.Vector2; import com.dfsek.terra.structure.Structure; import com.dfsek.terra.structure.StructureContainedInventory; +import com.dfsek.terra.structure.features.Feature; import com.dfsek.terra.util.structure.RotationUtil; import org.bukkit.Chunk; import org.bukkit.Location; @@ -67,6 +68,7 @@ public class StructurePopulator extends BlockPopulator { Debug.stack(e); } } + for(Feature f : conf.getFeatures()) f.apply(struc, spawn, chunk); // Apply features. break; } } diff --git a/src/main/java/com/dfsek/terra/structure/features/EntityFeature.java b/src/main/java/com/dfsek/terra/structure/features/EntityFeature.java new file mode 100644 index 000000000..0a57f1d2e --- /dev/null +++ b/src/main/java/com/dfsek/terra/structure/features/EntityFeature.java @@ -0,0 +1,118 @@ +package com.dfsek.terra.structure.features; + +import com.dfsek.terra.Debug; +import com.dfsek.terra.structure.Structure; +import com.dfsek.terra.structure.StructureInfo; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.EntityType; +import org.polydev.gaea.math.MathUtil; +import org.polydev.gaea.math.Range; + + +import java.util.List; +import java.util.Random; +import java.util.Set; + +public class EntityFeature implements Feature { + private final EntityType type; + private final Range amount; + private final int attempts; + private final Set in; + private final Set stand; + private final int inSize; + public EntityFeature(EntityType type, Range amount, int attempts, Set stand, Set in, int inSize) { + this.type = type; + this.amount = amount; + this.attempts = attempts; + this.in = in; + this.stand = stand; + this.inSize = inSize; + } + + @Override + public void apply(Structure structure, Location l, Chunk chunk) { + Random random = new Random(MathUtil.getCarverChunkSeed(chunk.getX(), chunk.getZ(), chunk.getWorld().getSeed())); + + int amountSpawn = amount.get(random); + + StructureInfo info = structure.getStructureInfo(); + Range x = new Range(0, info.getSizeZ()); + Range y = new Range(0, info.getSizeY()); + Range z = new Range(0, info.getSizeZ()); + + int cx = info.getCenterX(); + int cz = info.getCenterZ(); + + for(int i = 0; i < amountSpawn && i < attempts; i++) { + int yv = y.get(random); + Location attempt = l.clone().add(x.get(random)-cx, yv, z.get(random)-cz); + if(!isInChunk(chunk, attempt)) continue; // Don't attempt spawn if not in current chunk. + + boolean canSpawn = false; + while(yv >= 0 && attempt.getBlockY() >= l.getBlockY()) { // Go down, see if valid spawns exist. + canSpawn = true; + Block on = attempt.getBlock(); + attempt.subtract(0, 1, 0); + yv--; + + if(!stand.contains(on.getType())) continue; + + for(int j = 1; j < inSize + 1; j++) if(! in.contains(on.getRelative(BlockFace.UP, j).getType())) canSpawn = false; + + if(canSpawn) break; + } + if(canSpawn) { + Debug.info("Spawning entity at " + attempt); + chunk.getWorld().spawnEntity(attempt.add(0.5, 1, 0.5), type); // Add 0.5 to X & Z so entity spawns in center of block. + } + } + } + + private static boolean isInChunk(Chunk c, Location l) { + return Math.floorDiv(l.getBlockX(), 16) == c.getX() && Math.floorDiv(l.getBlockZ(), 16) == c.getZ(); + } + + @Override + public void apply(Structure structure, Location l, Random random) { + int amountSpawn = amount.get(random); + + StructureInfo info = structure.getStructureInfo(); + Range x = new Range(0, info.getSizeZ()); + Range y = new Range(0, info.getSizeY()); + Range z = new Range(0, info.getSizeZ()); + + int cx = info.getCenterX(); + int cz = info.getCenterZ(); + + for(int i = 0; i < amountSpawn && i < attempts; i++) { + int yv = y.get(random); + Location attempt = l.clone().add(x.get(random)-cx, yv, z.get(random)-cz); + + boolean canSpawn = false; + while(yv >= 0 && attempt.getBlockY() >= l.getBlockY()) { // Go down, see if valid spawns exist. + canSpawn = true; + Block on = attempt.getBlock(); + attempt.subtract(0, 1, 0); + yv--; + + if(!stand.contains(on.getType())) continue; + + for(int j = 1; j < inSize + 1; j++) if(! in.contains(on.getRelative(BlockFace.UP, j).getType())) canSpawn = false; + + if(canSpawn) break; + } + if(canSpawn) { + Debug.info("Spawning entity at " + attempt); + l.getWorld().spawnEntity(attempt.add(0.5, 1, 0.5), type); // Add 0.5 to X & Z so entity spawns in center of block. + } + } + } + + public static class SpawnRule { + + } +} diff --git a/src/main/java/com/dfsek/terra/structure/features/Feature.java b/src/main/java/com/dfsek/terra/structure/features/Feature.java new file mode 100644 index 000000000..33d51f2c1 --- /dev/null +++ b/src/main/java/com/dfsek/terra/structure/features/Feature.java @@ -0,0 +1,12 @@ +package com.dfsek.terra.structure.features; + +import com.dfsek.terra.structure.Structure; +import org.bukkit.Chunk; +import org.bukkit.Location; + +import java.util.Random; + +public interface Feature { + void apply(Structure structure, Location l, Chunk chunk); + void apply(Structure structure, Location l, Random random); +} diff --git a/src/main/resources/default-config/structures/single/stronghold.yml b/src/main/resources/default-config/structures/single/stronghold.yml index 17111a444..4df14f52e 100644 --- a/src/main/resources/default-config/structures/single/stronghold.yml +++ b/src/main/resources/default-config/structures/single/stronghold.yml @@ -13,4 +13,31 @@ spawn: loot: 1: wood_house 2: cobble_house - 3: cobble_house \ No newline at end of file + 3: cobble_house +features: + - ENTITY_FEATURE: + entity: SILVERFISH + attempts: 20 + in-height: 1 + amount: + min: 10 + max: 15 + spawnable-on: + - "minecraft:stone" + - "minecraft:stone_bricks" + - "minecraft:mossy_stone_bricks" + spawnable-in: + - "minecraft:air" + - ENTITY_FEATURE: + entity: ZOMBIE + attempts: 20 + in-height: 2 + amount: + min: 10 + max: 15 + spawnable-on: + - "minecraft:stone" + - "minecraft:stone_bricks" + - "minecraft:mossy_stone_bricks" + spawnable-in: + - "minecraft:air" \ No newline at end of file