diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..af9b7a546 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,40 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: Java CI with Gradle + +on: + push: + tags: + - "v*" # Push events to matching v*, i.e. v1.0, v20.15.10 + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + + - name: Build Terra + run: gradle shadowJar + + - name: Upload artifacts + uses: actions/upload-artifact@v2 + with: + name: Package + path: build/libs + + - name: Publish release + uses: marvinpinto/action-automatic-releases@latest + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + prerelease: false + files: | + build/libs/Terra-*.jar + LICENSE + \ No newline at end of file diff --git a/.gitignore b/.gitignore index 909ef81d5..6d85c9383 100644 --- a/.gitignore +++ b/.gitignore @@ -135,4 +135,5 @@ build .idea/**.xml .idea/modules/**.iml -!lib/*.jar \ No newline at end of file +!lib/*.jar +.idea/Terra.iml diff --git a/build.gradle.kts b/build.gradle.kts index abd4a14a1..973dd071c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,7 +15,6 @@ plugins { } repositories { - mavenCentral() flatDir { dirs("lib") } @@ -23,6 +22,7 @@ repositories { maven { url = uri("http://maven.enginehub.org/repo/") } maven { url = uri("https://repo.codemc.org/repository/maven-public") } maven { url = uri("https://papermc.io/repo/repository/maven-public/") } +// maven { url = uri("https://maven.pkg.github.com/solonovamax/Gaea") } } java { @@ -30,49 +30,36 @@ java { targetCompatibility = JavaVersion.VERSION_1_8 } -val versionObj = Version("1", "3", "1", true) +val versionObj = Version("1", "5", "0", true) version = versionObj dependencies { - - // Commons. Used for stuff. - implementation("commons-io:commons-io:2.4") - // Commons-imagine. Used for loading images from disk for the biome images. - implementation("org.apache.commons:commons-imaging:1.0-alpha2") - // Bstats. For tracking stats. - implementation("org.bstats:bstats-bukkit:1.7") - // Parsii. Does parsing of the equations. - implementation(name = "parsii-1.2", group = "") - // Papermc API. for Paper spigot - implementation("io.papermc:paperlib:1.0.5") - - // Jackson - implementation("com.fasterxml.jackson.core:jackson-core:2.11+") - implementation("com.fasterxml.jackson.core:jackson-annotations:2.11+") - implementation("com.fasterxml.jackson.core:jackson-databind:2.11+") - implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.11+") - - // Spigot mc API. Provided by spigot. - compileOnly("org.spigotmc:spigot-api:1.16.2-R0.1-SNAPSHOT") - // Annotations. Provided by mc. - compileOnly("org.jetbrains:annotations:20.1.0") - // Gaea. Provided as a plugin. - val gaeaVersion = "1.14.2" + val gaeaVersion = "1.14.3" compileOnly(name = "Gaea-${gaeaVersion}", group = "") testImplementation(name = "Gaea-${gaeaVersion}", group = "") - // Worldedit. For saving structures. Provided as a plugin. + + compileOnly("org.jetbrains:annotations:20.1.0") + + implementation("commons-io:commons-io:2.4") + implementation("org.apache.commons:commons-imaging:1.0-alpha2") + compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.2.0-SNAPSHOT") - // GSON. idk how it's used. + implementation("org.bstats:bstats-bukkit:1.7") + compileOnly("com.googlecode.json-simple:json-simple:1.1") - // JUnit. Used for testing. + implementation(name = "parsii-1.2.1", group = "") + + compileOnly("org.spigotmc:spigot-api:1.16.2-R0.1-SNAPSHOT") + implementation("io.papermc:paperlib:1.0.5") + + implementation("net.jafama:jafama:2.3.2") + + + // JUnit. testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.0") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.0") - - // Included here becaues tests rely on it. - testImplementation(name = "Gaea-1.14.2", group = "") - testImplementation("org.spigotmc:spigot-api:1.16.2-R0.1-SNAPSHOT") } val compileJava: JavaCompile by tasks @@ -81,6 +68,7 @@ val mainSourceSet: SourceSet = sourceSets["main"] val tokenizeJavaSources = task(name = "tokenizeJavaSources") { from(mainSourceSet.allSource) { include("**/plugin.yml") + println("version: $versionObj") val tokens = mapOf("VERSION" to versionObj.toString()) filter(org.apache.tools.ant.filters.ReplaceTokens::class, "tokens" to tokens) @@ -118,6 +106,8 @@ tasks.named("shadowJar") { relocate("org.bstats.bukkit", "com.dfsek.terra.lib.bstats") relocate("parsii", "com.dfsek.terra.lib.parsii") relocate("io.papermc.lib", "com.dfsek.terra.lib.paperlib") + relocate("net.jafama", "com.dfsek.terra.lib.jafama") + minimize() } tasks.build { @@ -204,31 +194,13 @@ val testWithPaper = task(name = "testWithPaper") { classpath = files("${testDir}/paper.jar") } -tasks.named("shadowJar") { - archiveClassifier.set("") - archiveBaseName.set("Terra") - setVersion(project.version) - relocate("org.apache.commons", "com.dfsek.terra.lib.commons") - relocate("org.bstats.bukkit", "com.dfsek.terra.lib.bstats") - relocate("parsii", "com.dfsek.terra.lib.parsii") - relocate("io.papermc.lib", "com.dfsek.terra.lib.paperlib") - relocate("com.fasterxml.jackson", "com.dfsek.terra.lib.jackson") -} - -tasks.build { - dependsOn(tasks.test) - dependsOn(tasks.shadowJar) -// dependsOn(testWithPaper) - tasks.shadowJar.get().mustRunAfter(tasks.test) -// testWithPaper.mustRunAfter(tasks.shadowJar) -} - /** * Version class that does version stuff. */ @Suppress("MemberVisibilityCanBePrivate") class Version(val major: String, val minor: String, val revision: String, val preRelease: Boolean = false) { + override fun toString(): String { return if (!preRelease) "$major.$minor.$revision" @@ -239,25 +211,19 @@ class Version(val major: String, val minor: String, val revision: String, val pr fun getGitHash(): String { val stdout = ByteArrayOutputStream() - val exitCode = exec { + exec { commandLine = mutableListOf("git", "rev-parse", "--short", "HEAD") standardOutput = stdout - }.exitValue - if (exitCode == 128) // https://canary.discord.com/channels/715448651786485780/765260067812540416/777236094768381954 - System.err.println("You can only use this in a git repo. Please run \"git clone https://github.com/PolyhedralDev/Terra.git\", then cd into there.") - if (exitCode == 127 || exitCode == 9009) // https://canary.discord.com/channels/715448651786485780/715448652411437099/777304618853335082 - System.err.println("You must install git for this to work. https://git-scm.com/downloads") + } return stdout.toString().trim() } fun gitClone(name: String) { val stdout = ByteArrayOutputStream() - val result = exec { + exec { commandLine = mutableListOf("git", "clone", name) standardOutput = stdout - }.exitValue - if (result == 127 || result == 9009) // https://canary.discord.com/channels/715448651786485780/715448652411437099/777304618853335082 - System.err.println("You must install git for this to work.") + } } fun downloadAndUnzipPack(packUrl: URL) { diff --git a/lib/Gaea-1.14.2.jar b/lib/Gaea-1.14.3.jar similarity index 53% rename from lib/Gaea-1.14.2.jar rename to lib/Gaea-1.14.3.jar index 966ebed95..42ac42aac 100644 Binary files a/lib/Gaea-1.14.2.jar and b/lib/Gaea-1.14.3.jar differ diff --git a/lib/parsii-1.2-javadoc.jar b/lib/parsii-1.2-javadoc.jar deleted file mode 100644 index 4e637a838..000000000 Binary files a/lib/parsii-1.2-javadoc.jar and /dev/null differ diff --git a/lib/parsii-1.2-sources.jar b/lib/parsii-1.2-sources.jar deleted file mode 100644 index c23d356b5..000000000 Binary files a/lib/parsii-1.2-sources.jar and /dev/null differ diff --git a/lib/parsii-1.2.jar b/lib/parsii-1.2.1.jar similarity index 53% rename from lib/parsii-1.2.jar rename to lib/parsii-1.2.1.jar index cdf63f92a..c68c7f1ad 100644 Binary files a/lib/parsii-1.2.jar and b/lib/parsii-1.2.1.jar differ diff --git a/src/main/java/com/dfsek/terra/EventListener.java b/src/main/java/com/dfsek/terra/EventListener.java index 1d9bf4f59..15551566c 100644 --- a/src/main/java/com/dfsek/terra/EventListener.java +++ b/src/main/java/com/dfsek/terra/EventListener.java @@ -2,6 +2,7 @@ package com.dfsek.terra; import com.dfsek.terra.async.AsyncStructureFinder; import com.dfsek.terra.config.base.ConfigPack; +import com.dfsek.terra.config.genconfig.TreeConfig; import com.dfsek.terra.config.genconfig.structure.StructureConfig; import com.dfsek.terra.registry.TreeRegistry; import com.dfsek.terra.util.StructureTypeEnum; @@ -22,8 +23,7 @@ import org.bukkit.event.world.StructureGrowEvent; import org.polydev.gaea.GaeaPlugin; import org.polydev.gaea.tree.Tree; import org.polydev.gaea.tree.TreeType; - -import java.util.Random; +import org.polydev.gaea.util.FastRandom; public class EventListener implements Listener { private final GaeaPlugin main; @@ -80,6 +80,10 @@ public class EventListener implements Listener { TreeRegistry registry = c.getTreeRegistry(); Tree tree = registry.get(TreeType.fromBukkit(e.getSpecies()).toString()); Debug.info("Overriding tree type: " + e.getSpecies()); - if(!tree.plant(e.getLocation(), new Random(), Terra.getInstance())) block.setBlockData(data); + if(tree instanceof TreeConfig) { + if(!((TreeConfig) tree).plantBlockCheck(e.getLocation(), new FastRandom())) { + block.setBlockData(data); + } + } else if(!tree.plant(e.getLocation(), new FastRandom(), Terra.getInstance())) block.setBlockData(data); } } diff --git a/src/main/java/com/dfsek/terra/TerraProfiler.java b/src/main/java/com/dfsek/terra/TerraProfiler.java index 428e318b8..02cd702cb 100644 --- a/src/main/java/com/dfsek/terra/TerraProfiler.java +++ b/src/main/java/com/dfsek/terra/TerraProfiler.java @@ -16,12 +16,12 @@ public class TerraProfiler extends WorldProfiler { super(w); this .addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "FloraTime") + .addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "TreeTime") .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), "ElevationTime") - .addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "SnowTime") - .addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "CaveBlockUpdate"); + .addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "SnowTime"); } public static TerraProfiler fromWorld(World w) { diff --git a/src/main/java/com/dfsek/terra/TerraWorld.java b/src/main/java/com/dfsek/terra/TerraWorld.java index dbc0bd9f4..ea25f2f8d 100644 --- a/src/main/java/com/dfsek/terra/TerraWorld.java +++ b/src/main/java/com/dfsek/terra/TerraWorld.java @@ -24,14 +24,6 @@ public class TerraWorld { private final WorldConfig worldConfig; private boolean safe; - public static void loadWorld(WorldConfig w) { - loaded.put(w.getWorldID(), w); - } - - public static synchronized TerraWorld getWorld(World w) { - return map.computeIfAbsent(w, TerraWorld::new); - } - private TerraWorld(World w) { safe = true; worldConfig = loaded.get(w.getName()); @@ -87,6 +79,14 @@ public class TerraWorld { grid = new TerraBiomeGrid(w, config.freq1, config.freq2, zone, config, erosion); } + public static void loadWorld(WorldConfig w) { + loaded.put(w.getWorldID(), w); + } + + public static synchronized TerraWorld getWorld(World w) { + return map.computeIfAbsent(w, TerraWorld::new); + } + public static synchronized void invalidate() { map.clear(); for(WorldConfig config : loaded.values()) { @@ -98,6 +98,10 @@ public class TerraWorld { return map.size(); } + public static boolean isTerraWorld(World w) { + return w.getGenerator() instanceof TerraChunkGenerator; + } + public TerraBiomeGrid getGrid() { return grid; } @@ -117,8 +121,4 @@ public class TerraWorld { public boolean isSafe() { return safe; } - - public static boolean isTerraWorld(World w) { - return w.getGenerator() instanceof TerraChunkGenerator; - } } diff --git a/src/main/java/com/dfsek/terra/async/AsyncBiomeFinder.java b/src/main/java/com/dfsek/terra/async/AsyncBiomeFinder.java index ed8d2e59c..df05abaee 100644 --- a/src/main/java/com/dfsek/terra/async/AsyncBiomeFinder.java +++ b/src/main/java/com/dfsek/terra/async/AsyncBiomeFinder.java @@ -1,6 +1,7 @@ package com.dfsek.terra.async; import com.dfsek.terra.biome.grid.TerraBiomeGrid; +import com.dfsek.terra.config.base.ConfigUtil; import org.bukkit.Location; import org.bukkit.util.Vector; import org.jetbrains.annotations.NotNull; @@ -27,11 +28,11 @@ public class AsyncBiomeFinder extends AsyncFeatureFinder { */ @Override public boolean isValid(int x, int z, Biome target) { - return getGrid().getBiome(x, z, GenerationPhase.POST_GEN).equals(target); + return getGrid().getBiome(x * ConfigUtil.biomeSearchRes, z * ConfigUtil.biomeSearchRes, GenerationPhase.POST_GEN).equals(target); } @Override public Vector finalizeVector(Vector orig) { - return orig; + return orig.multiply(ConfigUtil.biomeSearchRes); } } diff --git a/src/main/java/com/dfsek/terra/async/AsyncStructureFinder.java b/src/main/java/com/dfsek/terra/async/AsyncStructureFinder.java index c0f3c7bad..8a22f1158 100644 --- a/src/main/java/com/dfsek/terra/async/AsyncStructureFinder.java +++ b/src/main/java/com/dfsek/terra/async/AsyncStructureFinder.java @@ -1,6 +1,5 @@ package com.dfsek.terra.async; -import com.dfsek.terra.TerraWorld; import com.dfsek.terra.biome.UserDefinedBiome; import com.dfsek.terra.biome.grid.TerraBiomeGrid; import com.dfsek.terra.config.genconfig.structure.StructureConfig; @@ -10,6 +9,7 @@ import org.bukkit.Location; import org.bukkit.World; import org.bukkit.util.Vector; import org.jetbrains.annotations.NotNull; +import org.polydev.gaea.util.FastRandom; import java.util.Random; import java.util.function.Consumer; @@ -33,9 +33,9 @@ public class AsyncStructureFinder extends AsyncFeatureFinder { public boolean isValid(int x, int z, StructureConfig target) { World world = getWorld(); Location spawn = target.getSpawn().getChunkSpawn(x, z, world.getSeed()).toLocation(world); - if(!TerraWorld.getWorld(world).getConfig().getBiome((UserDefinedBiome) getGrid().getBiome(spawn)).getStructures().contains(target)) + if(!((UserDefinedBiome) getGrid().getBiome(spawn)).getConfig().getStructures().contains(target)) return false; - Random r2 = new Random(spawn.hashCode()); + Random r2 = new FastRandom(spawn.hashCode()); Structure struc = target.getStructure(r2); Rotation rotation = Rotation.fromDegrees(r2.nextInt(4) * 90); for(int y = target.getSearchStart().get(r2); y > 0; y--) { diff --git a/src/main/java/com/dfsek/terra/biome/UserDefinedBiome.java b/src/main/java/com/dfsek/terra/biome/UserDefinedBiome.java index fe319332f..687a2a1d9 100644 --- a/src/main/java/com/dfsek/terra/biome/UserDefinedBiome.java +++ b/src/main/java/com/dfsek/terra/biome/UserDefinedBiome.java @@ -1,7 +1,9 @@ package com.dfsek.terra.biome; +import com.dfsek.terra.config.genconfig.biome.BiomeConfig; +import com.dfsek.terra.config.genconfig.biome.GeneratorOptions; import com.dfsek.terra.generation.UserDefinedDecorator; -import com.dfsek.terra.generation.UserDefinedGenerator; +import org.bukkit.World; import org.polydev.gaea.biome.Biome; import org.polydev.gaea.biome.Decorator; import org.polydev.gaea.biome.Generator; @@ -13,19 +15,21 @@ import java.util.List; * Class representing a config-defined biome */ public class UserDefinedBiome implements Biome { - private final UserDefinedGenerator gen; + private final GeneratorOptions gen; private final UserDefinedDecorator decorator; private final org.bukkit.block.Biome vanilla; private final String id; + private final BiomeConfig config; private final boolean erode; - public UserDefinedBiome(org.bukkit.block.Biome vanilla, UserDefinedDecorator dec, UserDefinedGenerator gen, boolean erode, String id) { + public UserDefinedBiome(org.bukkit.block.Biome vanilla, UserDefinedDecorator dec, GeneratorOptions gen, boolean erode, BiomeConfig config) { this.vanilla = vanilla; this.decorator = dec; this.gen = gen; - this.id = id; + this.id = config.getID(); this.erode = erode; + this.config = config; } /** @@ -45,7 +49,7 @@ public class UserDefinedBiome implements Biome { */ @Override public Generator getGenerator() { - return gen; + return gen.getGenerator(0); } /** @@ -75,4 +79,13 @@ public class UserDefinedBiome implements Biome { public boolean isErodible() { return erode; } + + public BiomeConfig getConfig() { + return config; + } + + @Override + public Generator getGenerator(World w) { + return gen.getGenerator(w.getSeed()); + } } diff --git a/src/main/java/com/dfsek/terra/biome/failsafe/FailType.java b/src/main/java/com/dfsek/terra/biome/failsafe/FailType.java deleted file mode 100644 index bc5c7f959..000000000 --- a/src/main/java/com/dfsek/terra/biome/failsafe/FailType.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.dfsek.terra.biome.failsafe; - -import org.bukkit.Bukkit; -import org.polydev.gaea.biome.Biome; -import parsii.tokenizer.ParseException; - -/** - * What happens if terrain generation is attempted with an unrecoverable config error. - */ -@SuppressWarnings("unused") -public enum FailType { - /** - * Return failover biome, then shut down server to minimize damage. - * Generally the safest option. - */ - SHUTDOWN { - @Override - public Biome fail() { - Bukkit.getServer().shutdown(); - try { - return new FailoverBiome(); - } catch(ParseException e) { - e.printStackTrace(); - return null; - } - } - }, - /** - * Returns null, hard crashing the server, but not generating any corrupted terrain.
- * This option is NOT stable, but it has the least risk of blank chunks being generated. - * However, it has the highest risk of corruption! - */ - CRASH { - @Override - public Biome fail() { - return null; - } - }, - /** - * Returns a failover biome, which generates completely blank chunks. - * Recommended for debugging. - */ - FAILOVER { - @Override - public Biome fail() { - try { - return new FailoverBiome(); - } catch(ParseException e) { - e.printStackTrace(); - return null; - } - } - }; - - /** - * Performs the action specified by the enum type to occur on failure of terrain generation. - * - * @return Failover biome, if specified, null if not. - */ - public abstract Biome fail(); -} diff --git a/src/main/java/com/dfsek/terra/biome/failsafe/FailoverBiome.java b/src/main/java/com/dfsek/terra/biome/failsafe/FailoverBiome.java deleted file mode 100644 index e5e0f72b0..000000000 --- a/src/main/java/com/dfsek/terra/biome/failsafe/FailoverBiome.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.dfsek.terra.biome.failsafe; - -import com.dfsek.terra.biome.UserDefinedBiome; -import parsii.tokenizer.ParseException; - -/** - * Blank biome to generate in case of a severe config error - */ -public final class FailoverBiome extends UserDefinedBiome { - public FailoverBiome() throws ParseException { - super(org.bukkit.block.Biome.PLAINS, new FailoverDecorator(), new FailoverGenerator(), false, "FAILSAFE"); - } -} diff --git a/src/main/java/com/dfsek/terra/biome/failsafe/FailoverDecorator.java b/src/main/java/com/dfsek/terra/biome/failsafe/FailoverDecorator.java deleted file mode 100644 index 1cc01f82b..000000000 --- a/src/main/java/com/dfsek/terra/biome/failsafe/FailoverDecorator.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.dfsek.terra.biome.failsafe; - -import com.dfsek.terra.generation.UserDefinedDecorator; -import org.polydev.gaea.math.ProbabilityCollection; - -public final class FailoverDecorator extends UserDefinedDecorator { - public FailoverDecorator() { - super(new ProbabilityCollection<>(), new ProbabilityCollection<>(), 0, 0); - } -} diff --git a/src/main/java/com/dfsek/terra/biome/failsafe/FailoverGenerator.java b/src/main/java/com/dfsek/terra/biome/failsafe/FailoverGenerator.java deleted file mode 100644 index 6badad8e1..000000000 --- a/src/main/java/com/dfsek/terra/biome/failsafe/FailoverGenerator.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.dfsek.terra.biome.failsafe; - -import com.dfsek.terra.generation.UserDefinedGenerator; -import org.bukkit.Material; -import org.bukkit.block.data.BlockData; -import org.polydev.gaea.world.palette.Palette; -import org.polydev.gaea.world.palette.RandomPalette; -import parsii.tokenizer.ParseException; - -import java.util.HashMap; -import java.util.Random; -import java.util.TreeMap; - -public final class FailoverGenerator extends UserDefinedGenerator { - private static final TreeMap> palette = new TreeMap<>(); - - static { - palette.put(255, new RandomPalette(new Random(2403)).add(Material.STONE.createBlockData(), 1)); - } - - public FailoverGenerator() throws ParseException { - super("0", null, new HashMap<>(), palette, new TreeMap<>(), false); - } -} diff --git a/src/main/java/com/dfsek/terra/biome/grid/TerraBiomeGrid.java b/src/main/java/com/dfsek/terra/biome/grid/TerraBiomeGrid.java index 045d69f03..d57aedb4e 100644 --- a/src/main/java/com/dfsek/terra/biome/grid/TerraBiomeGrid.java +++ b/src/main/java/com/dfsek/terra/biome/grid/TerraBiomeGrid.java @@ -57,7 +57,7 @@ public class TerraBiomeGrid extends BiomeGrid { if(failNum % 256 == 0) LangUtil.log("error.severe-config", Level.SEVERE, String.valueOf(x), String.valueOf(z)); failNum++; - return ConfigUtil.failType.fail(); + return null; } if(erode != null && b.isErodible() && erode.isEroded(xp, zp)) { return erosionGrid.getBiome(xp, zp, phase); diff --git a/src/main/java/com/dfsek/terra/biome/postprocessing/ErosionNoise.java b/src/main/java/com/dfsek/terra/biome/postprocessing/ErosionNoise.java index 35e55ba96..3202dde91 100644 --- a/src/main/java/com/dfsek/terra/biome/postprocessing/ErosionNoise.java +++ b/src/main/java/com/dfsek/terra/biome/postprocessing/ErosionNoise.java @@ -1,5 +1,6 @@ package com.dfsek.terra.biome.postprocessing; +import net.jafama.FastMath; import org.polydev.gaea.math.FastNoiseLite; /** @@ -27,7 +28,7 @@ public class ErosionNoise { * @return Whether location is eroded */ public boolean isEroded(int x, int z) { - double abs = Math.pow(noise.getNoise(x, z), 2); + double abs = FastMath.pow(noise.getNoise(x, z), 2); return abs < thresh; } } diff --git a/src/main/java/com/dfsek/terra/carving/Cavern.java b/src/main/java/com/dfsek/terra/carving/Cavern.java index 65a842202..7ec405639 100644 --- a/src/main/java/com/dfsek/terra/carving/Cavern.java +++ b/src/main/java/com/dfsek/terra/carving/Cavern.java @@ -9,6 +9,7 @@ import org.bukkit.World; import org.bukkit.util.Vector; import org.polydev.gaea.math.FastNoiseLite; import org.polydev.gaea.math.MathUtil; +import org.polydev.gaea.util.FastRandom; import java.util.Random; @@ -23,7 +24,7 @@ public class Cavern { public VoxelGeometry carveChunk(int chunkX, int chunkZ) { long seedC = MathUtil.getCarverChunkSeed(chunkX, chunkZ, seed); - Random chunk = new Random(seedC); + Random chunk = new FastRandom(seedC); Vector org = node.getNodeLocation((chunkX << 4) + 8, (chunkZ << 4) + 8).clone().setY(chunk.nextInt(128)); VoxelGeometry carve = VoxelGeometry.getBlank(); @@ -33,7 +34,7 @@ public class Cavern { Bukkit.getLogger().info("Cavern: " + org.toString()); carve.merge(new DeformedSphere(org.clone(), chunk.nextInt(4) + 3, 0.75, smpl)); - Vector _00 = new Vector(org.getX() + 16, new Random(MathUtil.getCarverChunkSeed(chunkX + 1, chunkZ, seed)).nextInt(128), org.getZ()); + Vector _00 = new Vector(org.getX() + 16, new FastRandom(MathUtil.getCarverChunkSeed(chunkX + 1, chunkZ, seed)).nextInt(128), org.getZ()); carve.merge(new Tube(org, _00, 4)); return carve; diff --git a/src/main/java/com/dfsek/terra/carving/SimplexCarver.java b/src/main/java/com/dfsek/terra/carving/SimplexCarver.java index 42a951f80..8641dd229 100644 --- a/src/main/java/com/dfsek/terra/carving/SimplexCarver.java +++ b/src/main/java/com/dfsek/terra/carving/SimplexCarver.java @@ -1,5 +1,6 @@ package com.dfsek.terra.carving; +import net.jafama.FastMath; import org.bukkit.World; import org.bukkit.util.Vector; import org.polydev.gaea.math.FastNoiseLite; @@ -15,7 +16,7 @@ public class SimplexCarver extends Carver { private final FastNoiseLite height; private final FastNoiseLite column; private final FastNoiseLite hasCaves; - private final double root2inverse = 1D / Math.sqrt(2); + private final double root2inverse = 1D / FastMath.sqrt(2); public SimplexCarver(int minY, int maxY) { super(minY, maxY); @@ -40,6 +41,10 @@ public class SimplexCarver extends Carver { hasCaves.setFrequency(0.005f); } + private static double acot(double x) { + return FastMath.PI / 2 - FastMath.atan(x); + } + @Override public CarvingData carve(int chunkX, int chunkZ, World w) { CarvingData c = new CarvingData(chunkX, chunkZ); @@ -49,15 +54,15 @@ public class SimplexCarver extends Carver { for(int z = oz; z < oz + 16; z++) { double heightNoise = height.getNoise(x, z); double mainNoise = noise.getNoise(x, z) * 2; - double columnNoise = Math.pow(Math.max(column.getNoise(x, z), 0) * 2, 3); - double hc = (acot(16 * (hasCaves.getNoise(x, z) - 0.2)) / Math.PI) - 0.1; + double columnNoise = FastMath.pow(FastMath.max(column.getNoise(x, z), 0) * 2, 3); + double hc = (acot(16 * (hasCaves.getNoise(x, z) - 0.2)) / FastMath.PI) - 0.1; CarvingData.CarvingType type = CarvingData.CarvingType.BOTTOM; - double simplex = (Math.pow(mainNoise + root2inverse, 3) / 2 + columnNoise) * hc; + double simplex = (FastMath.pow(mainNoise + root2inverse, 3) / 2 + columnNoise) * hc; for(int y = 0; y < 64; y++) { - double finalNoise = (-0.05 * Math.abs(y - (heightNoise * 16 + 24)) + 1 - simplex) * hc; + double finalNoise = (-0.05 * FastMath.abs(y - (heightNoise * 16 + 24)) + 1 - simplex) * hc; if(finalNoise > 0.5) { c.carve(x - ox, y, z - oz, type); - double finalNoiseUp = (-0.05 * Math.abs((y + 1) - (heightNoise * 16 + 24)) + 1 - simplex) * hc; + double finalNoiseUp = (-0.05 * FastMath.abs((y + 1) - (heightNoise * 16 + 24)) + 1 - simplex) * hc; if(finalNoiseUp > 0.5) { type = CarvingData.CarvingType.CENTER; } else type = CarvingData.CarvingType.TOP; @@ -68,10 +73,6 @@ public class SimplexCarver extends Carver { return c; } - private static double acot(double x) { - return Math.PI / 2 - Math.atan(x); - } - @Override public Worm getWorm(long l, Vector vector) { return null; diff --git a/src/main/java/com/dfsek/terra/carving/UserDefinedCarver.java b/src/main/java/com/dfsek/terra/carving/UserDefinedCarver.java index ab50ba1a3..3ea0e040f 100644 --- a/src/main/java/com/dfsek/terra/carving/UserDefinedCarver.java +++ b/src/main/java/com/dfsek/terra/carving/UserDefinedCarver.java @@ -3,10 +3,12 @@ package com.dfsek.terra.carving; import com.dfsek.terra.TerraWorld; import com.dfsek.terra.biome.UserDefinedBiome; import com.dfsek.terra.config.base.ConfigPack; +import net.jafama.FastMath; import org.bukkit.World; import org.bukkit.util.Vector; import org.polydev.gaea.generation.GenerationPhase; import org.polydev.gaea.math.Range; +import org.polydev.gaea.util.FastRandom; import org.polydev.gaea.world.carving.Carver; import org.polydev.gaea.world.carving.Worm; @@ -39,7 +41,7 @@ public class UserDefinedCarver extends Carver { @Override public Worm getWorm(long l, Vector vector) { - Random r = new Random(l + hash); + Random r = new FastRandom(l + hash); return new UserDefinedWorm(length.get(r) / 2, r, vector, radius.getMax(), topCut, bottomCut); } @@ -58,7 +60,7 @@ public class UserDefinedCarver extends Carver { @Override public boolean isChunkCarved(World w, int chunkX, int chunkZ, Random random) { ConfigPack c = TerraWorld.getWorld(w).getConfig(); - return new Random(random.nextLong() + hash).nextInt(100) < c.getBiome((UserDefinedBiome) TerraWorld.getWorld(w).getGrid().getBiome(chunkX << 4, chunkZ << 4, GenerationPhase.POPULATE)).getCarverChance(this); + return new FastRandom(random.nextLong() + hash).nextInt(100) < ((UserDefinedBiome) TerraWorld.getWorld(w).getGrid().getBiome(chunkX << 4, chunkZ << 4, GenerationPhase.POPULATE)).getConfig().getCarverChance(this); } private class UserDefinedWorm extends Worm { @@ -81,9 +83,9 @@ public class UserDefinedCarver extends Carver { @Override public void step() { if(steps == nextDirection) { - direction.rotateAroundX(Math.toRadians((getRandom().nextGaussian()) * mutate[0] * recalcMagnitude)); - direction.rotateAroundY(Math.toRadians((getRandom().nextGaussian()) * mutate[1] * recalcMagnitude)); - direction.rotateAroundZ(Math.toRadians((getRandom().nextGaussian()) * mutate[2] * recalcMagnitude)); + direction.rotateAroundX(FastMath.toRadians((getRandom().nextGaussian()) * mutate[0] * recalcMagnitude)); + direction.rotateAroundY(FastMath.toRadians((getRandom().nextGaussian()) * mutate[1] * recalcMagnitude)); + direction.rotateAroundZ(FastMath.toRadians((getRandom().nextGaussian()) * mutate[2] * recalcMagnitude)); currentRotation = new double[] {(getRandom().nextGaussian()) * mutate[0], (getRandom().nextGaussian()) * mutate[1], (getRandom().nextGaussian()) * mutate[2]}; @@ -92,10 +94,10 @@ public class UserDefinedCarver extends Carver { steps++; setRadius(new int[] {(int) (runningRadius * radiusMultiplier[0]), (int) (runningRadius * radiusMultiplier[1]), (int) (runningRadius * radiusMultiplier[2])}); runningRadius += (getRandom().nextDouble() - 0.5) * mutate[3]; - runningRadius = Math.max(Math.min(runningRadius, maxRad), 1); - direction.rotateAroundX(Math.toRadians(currentRotation[0] * mutate[0])); - direction.rotateAroundY(Math.toRadians(currentRotation[1] * mutate[1])); - direction.rotateAroundZ(Math.toRadians(currentRotation[2] * mutate[2])); + runningRadius = FastMath.max(FastMath.min(runningRadius, maxRad), 1); + direction.rotateAroundX(FastMath.toRadians(currentRotation[0] * mutate[0])); + direction.rotateAroundY(FastMath.toRadians(currentRotation[1] * mutate[1])); + direction.rotateAroundZ(FastMath.toRadians(currentRotation[2] * mutate[2])); getRunning().add(direction); } } diff --git a/src/main/java/com/dfsek/terra/command/OreCommand.java b/src/main/java/com/dfsek/terra/command/OreCommand.java index 5ced28b1d..c5a13a29d 100644 --- a/src/main/java/com/dfsek/terra/command/OreCommand.java +++ b/src/main/java/com/dfsek/terra/command/OreCommand.java @@ -3,6 +3,7 @@ package com.dfsek.terra.command; import com.dfsek.terra.TerraWorld; import com.dfsek.terra.config.genconfig.OreConfig; import com.dfsek.terra.config.lang.LangUtil; +import net.jafama.FastMath; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.command.Command; @@ -11,10 +12,10 @@ import org.bukkit.entity.Player; import org.bukkit.util.Vector; import org.jetbrains.annotations.NotNull; import org.polydev.gaea.command.WorldCommand; +import org.polydev.gaea.util.FastRandom; import java.util.Collections; import java.util.List; -import java.util.Random; public class OreCommand extends WorldCommand { public OreCommand(org.polydev.gaea.command.Command parent) { @@ -34,8 +35,8 @@ public class OreCommand extends WorldCommand { LangUtil.send("command.ore.out-of-range", sender); return true; } - Vector source = new Vector(Math.floorMod(bl.getX(), 16), bl.getY(), Math.floorMod(bl.getZ(), 16)); - ore.doVein(source, bl.getChunk(), new Random()); + Vector source = new Vector(FastMath.floorMod(bl.getX(), 16), bl.getY(), FastMath.floorMod(bl.getZ(), 16)); + ore.doVein(source, bl.getChunk(), new FastRandom()); } else { LangUtil.send("command.ore.main-menu", sender); } diff --git a/src/main/java/com/dfsek/terra/command/biome/BiomeCommand.java b/src/main/java/com/dfsek/terra/command/biome/BiomeCommand.java index fbbd47429..8eb36ae61 100644 --- a/src/main/java/com/dfsek/terra/command/biome/BiomeCommand.java +++ b/src/main/java/com/dfsek/terra/command/biome/BiomeCommand.java @@ -25,7 +25,7 @@ public class BiomeCommand extends WorldCommand { public boolean execute(@NotNull Player sender, @NotNull Command command, @NotNull String label, @NotNull String[] args, World w) { TerraBiomeGrid grid = TerraWorld.getWorld(sender.getWorld()).getGrid(); UserDefinedBiome biome = (UserDefinedBiome) grid.getBiome(sender.getLocation(), GenerationPhase.POPULATE); - LangUtil.send("command.biome.in", sender, TerraWorld.getWorld(w).getConfig().getBiome(biome).getID()); + LangUtil.send("command.biome.in", sender, biome.getID()); return true; } 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 2a7e3b7d9..26b3f4b80 100644 --- a/src/main/java/com/dfsek/terra/command/biome/BiomeInfoCommand.java +++ b/src/main/java/com/dfsek/terra/command/biome/BiomeInfoCommand.java @@ -40,7 +40,7 @@ public class BiomeInfoCommand extends WorldCommand { sender.sendMessage("Erodible: " + b.isErodible()); - BiomeConfig bio = cfg.getBiome(b); + BiomeConfig bio = b.getConfig(); List structureConfigs = bio.getStructures(); if(structureConfigs.size() == 0) sender.sendMessage("No Structures"); diff --git a/src/main/java/com/dfsek/terra/command/structure/StructureCommand.java b/src/main/java/com/dfsek/terra/command/structure/StructureCommand.java index 99d7b0a15..af1a7ed74 100644 --- a/src/main/java/com/dfsek/terra/command/structure/StructureCommand.java +++ b/src/main/java/com/dfsek/terra/command/structure/StructureCommand.java @@ -1,5 +1,6 @@ package com.dfsek.terra.command.structure; +import com.dfsek.terra.command.structure.load.LoadCommand; import com.dfsek.terra.config.lang.LangUtil; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; diff --git a/src/main/java/com/dfsek/terra/command/structure/load/LoadCommand.java b/src/main/java/com/dfsek/terra/command/structure/load/LoadCommand.java new file mode 100644 index 000000000..b3a962887 --- /dev/null +++ b/src/main/java/com/dfsek/terra/command/structure/load/LoadCommand.java @@ -0,0 +1,62 @@ +package com.dfsek.terra.command.structure.load; + +import com.dfsek.terra.Terra; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.polydev.gaea.command.DebugCommand; +import org.polydev.gaea.command.PlayerCommand; + +import java.io.File; +import java.io.FilenameFilter; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class LoadCommand extends PlayerCommand implements DebugCommand { + public LoadCommand(org.polydev.gaea.command.Command parent) { + super(parent); + } + + public static List getStructureNames() { + List names = new ArrayList<>(); + File structureDir = new File(Terra.getInstance().getDataFolder() + File.separator + "export" + File.separator + "structures"); + if(!structureDir.exists()) return Collections.emptyList(); + Path structurePath = structureDir.toPath(); + + FilenameFilter filter = (dir, name) -> name.endsWith(".tstructure"); + for(File f : structureDir.listFiles(filter)) { + String path = structurePath.relativize(f.toPath()).toString(); + names.add(path.substring(0, path.length() - 11)); + } + return names; + } + + @Override + public boolean execute(@NotNull Player sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + return true; + } + + @Override + public String getName() { + return "load"; + } + + @Override + public List getSubCommands() { + return Arrays.asList(new LoadRawCommand(this), new LoadFullCommand(this, true), new LoadFullCommand(this, false)); + } + + @Override + public List getTabCompletions(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) { + return Collections.emptyList(); + } + + @Override + public int arguments() { + return 0; + } +} diff --git a/src/main/java/com/dfsek/terra/command/structure/LoadCommand.java b/src/main/java/com/dfsek/terra/command/structure/load/LoadFullCommand.java similarity index 61% rename from src/main/java/com/dfsek/terra/command/structure/LoadCommand.java rename to src/main/java/com/dfsek/terra/command/structure/load/LoadFullCommand.java index 51e327f6d..79b130325 100644 --- a/src/main/java/com/dfsek/terra/command/structure/LoadCommand.java +++ b/src/main/java/com/dfsek/terra/command/structure/load/LoadFullCommand.java @@ -1,4 +1,4 @@ -package com.dfsek.terra.command.structure; +package com.dfsek.terra.command.structure.load; import com.dfsek.terra.Terra; import com.dfsek.terra.config.lang.LangUtil; @@ -15,14 +15,19 @@ import java.io.File; import java.io.IOException; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; -public class LoadCommand extends PlayerCommand implements DebugCommand { - public LoadCommand(org.polydev.gaea.command.Command parent) { +public class LoadFullCommand extends PlayerCommand implements DebugCommand { + private final boolean chunk; + + public LoadFullCommand(org.polydev.gaea.command.Command parent, boolean chunk) { super(parent); + this.chunk = chunk; } @Override - public boolean execute(@NotNull Player sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + public boolean execute(@NotNull Player sender, @NotNull Command command, @NotNull String s, @NotNull String[] args) { try { Rotation r; try { @@ -32,8 +37,8 @@ public class LoadCommand extends PlayerCommand implements DebugCommand { return true; } Structure struc = Structure.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); + if(chunk) struc.paste(sender.getLocation(), sender.getLocation().getChunk(), r); + else struc.paste(sender.getLocation(), r); //sender.sendMessage(String.valueOf(struc.checkSpawns(sender.getLocation(), r))); } catch(IOException e) { e.printStackTrace(); @@ -44,7 +49,7 @@ public class LoadCommand extends PlayerCommand implements DebugCommand { @Override public String getName() { - return "load"; + return chunk ? "chunk" : "full"; } @Override @@ -54,11 +59,17 @@ public class LoadCommand extends PlayerCommand implements DebugCommand { @Override public int arguments() { - return 3; + return 2; } @Override - public List getTabCompletions(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) { + public List getTabCompletions(@NotNull CommandSender commandSender, @NotNull String s, @NotNull String[] args) { + switch(args.length) { + case 1: + return LoadCommand.getStructureNames().stream().filter(string -> string.toUpperCase().startsWith(args[0].toUpperCase())).collect(Collectors.toList()); + case 2: + return Stream.of("0", "90", "180", "270").filter(string -> string.toUpperCase().startsWith(args[1].toUpperCase())).collect(Collectors.toList()); + } return Collections.emptyList(); } } diff --git a/src/main/java/com/dfsek/terra/command/structure/load/LoadRawCommand.java b/src/main/java/com/dfsek/terra/command/structure/load/LoadRawCommand.java new file mode 100644 index 000000000..4ad7bd2a9 --- /dev/null +++ b/src/main/java/com/dfsek/terra/command/structure/load/LoadRawCommand.java @@ -0,0 +1,113 @@ +package com.dfsek.terra.command.structure.load; + +import com.dfsek.terra.Terra; +import com.dfsek.terra.config.lang.LangUtil; +import com.dfsek.terra.structure.Structure; +import com.dfsek.terra.structure.StructureContainedBlock; +import com.dfsek.terra.structure.StructureInfo; +import com.dfsek.terra.structure.StructureSpawnRequirement; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Sign; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.polydev.gaea.command.DebugCommand; +import org.polydev.gaea.command.PlayerCommand; + +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class LoadRawCommand extends PlayerCommand implements DebugCommand { + public LoadRawCommand(org.polydev.gaea.command.Command parent) { + super(parent); + } + + private static void setTerraSign(Sign sign, String data) { + sign.setLine(0, "[TERRA]"); + sign.setLine(2, data.substring(0, 16)); + sign.setLine(3, data.substring(16)); + } + + @Override + public boolean execute(@NotNull Player sender, @NotNull Command command, @NotNull String s, @NotNull String[] args) { + try { + Structure struc = Structure.load(new File(Terra.getInstance().getDataFolder() + File.separator + "export" + File.separator + "structures", args[0] + ".tstructure")); + StructureInfo info = struc.getStructureInfo(); + int centerX = info.getCenterX(); + int centerZ = info.getCenterZ(); + for(StructureContainedBlock[][] level0 : struc.getRawStructure()) { + for(StructureContainedBlock[] level1 : level0) { + for(StructureContainedBlock block : level1) { + Location bLocation = sender.getLocation().add(block.getX() - centerX, block.getY(), block.getZ() - centerZ); + if(!block.getPull().equals(StructureContainedBlock.Pull.NONE)) { + bLocation.getBlock().setBlockData(Material.OAK_SIGN.createBlockData(), false); + Sign sign = (Sign) bLocation.getBlock().getState(); + sign.setLine(1, "[PULL=" + block.getPull() + "_" + block.getPullOffset() + "]"); + String data = block.getBlockData().getAsString(true); + setTerraSign(sign, data); + sign.update(); + } else if(!block.getRequirement().equals(StructureSpawnRequirement.BLANK)) { + bLocation.getBlock().setBlockData(Material.OAK_SIGN.createBlockData(), false); + Sign sign = (Sign) bLocation.getBlock().getState(); + sign.setLine(1, "[SPAWN=" + block.getRequirement() + "]"); + String data = block.getBlockData().getAsString(true); + setTerraSign(sign, data); + sign.update(); + } else { + bLocation.getBlock().setBlockData(block.getBlockData(), false); + if(block.getState() != null) { + block.getState().getState(bLocation.getBlock().getState()).update(true, false); + } + } + } + } + } + + for(int y = 0; y < struc.getStructureInfo().getSizeY(); y++) { + StructureContainedBlock block = struc.getRawStructure()[centerX][centerZ][y]; + if(block.getRequirement().equals(StructureSpawnRequirement.BLANK) && block.getPull().equals(StructureContainedBlock.Pull.NONE)) { + Location bLocation = sender.getLocation().add(block.getX() - centerX, block.getY(), block.getZ() - centerZ); + bLocation.getBlock().setBlockData(Material.OAK_SIGN.createBlockData(), false); + Sign sign = (Sign) bLocation.getBlock().getState(); + sign.setLine(1, "[CENTER]"); + String data = block.getBlockData().getAsString(true); + setTerraSign(sign, data); + sign.update(); + break; + } + } + } catch(IOException e) { + e.printStackTrace(); + LangUtil.send("command.structure.invalid", sender, args[0]); + } + return true; + } + + @Override + public String getName() { + return "raw"; + } + + @Override + public List getSubCommands() { + return Collections.emptyList(); + } + + @Override + public int arguments() { + return 1; + } + + @Override + public List getTabCompletions(@NotNull CommandSender commandSender, @NotNull String s, @NotNull String[] args) { + if(args.length == 1) { + return LoadCommand.getStructureNames().stream().filter(string -> string.toUpperCase().startsWith(args[0].toUpperCase())).collect(Collectors.toList()); + } + return Collections.emptyList(); + } +} 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 c7437dc10..618bfebaf 100644 --- a/src/main/java/com/dfsek/terra/config/base/ConfigPack.java +++ b/src/main/java/com/dfsek/terra/config/base/ConfigPack.java @@ -2,7 +2,6 @@ package com.dfsek.terra.config.base; import com.dfsek.terra.Debug; import com.dfsek.terra.Terra; -import com.dfsek.terra.biome.UserDefinedBiome; import com.dfsek.terra.carving.UserDefinedCarver; import com.dfsek.terra.config.ConfigLoader; import com.dfsek.terra.config.exception.ConfigException; @@ -15,6 +14,7 @@ import com.dfsek.terra.config.genconfig.PaletteConfig; import com.dfsek.terra.config.genconfig.TreeConfig; import com.dfsek.terra.config.genconfig.biome.AbstractBiomeConfig; import com.dfsek.terra.config.genconfig.biome.BiomeConfig; +import com.dfsek.terra.config.genconfig.noise.NoiseConfig; import com.dfsek.terra.config.genconfig.structure.StructureConfig; import com.dfsek.terra.config.lang.LangUtil; import com.dfsek.terra.registry.FloraRegistry; @@ -24,6 +24,7 @@ import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.java.JavaPlugin; +import parsii.eval.Scope; import java.io.File; import java.io.IOException; @@ -56,8 +57,6 @@ public class ConfigPack extends YamlConfiguration { public final int blendAmp; public final boolean biomeBlend; public final double blendFreq; - public final int octaves; - public final double frequency; public final boolean vanillaCaves; public final boolean vanillaStructures; public final boolean vanillaDecoration; @@ -75,7 +74,8 @@ public class ConfigPack extends YamlConfiguration { private final TreeRegistry treeRegistry = new TreeRegistry(); private final FloraRegistry floraRegistry = new FloraRegistry(); private final Set allStructures = new HashSet<>(); - private final Map definedVariables = new HashMap<>(); + private final Map noiseBuilders = new HashMap<>(); + private final Scope vScope; private final File dataFolder; private final String id; @@ -87,6 +87,13 @@ public class ConfigPack extends YamlConfiguration { if(!contains("id")) throw new ConfigException("No ID specified!", "null"); this.id = getString("id"); + Map noise = Objects.requireNonNull(getConfigurationSection("noise")).getValues(false); + for(Map.Entry entry : noise.entrySet()) { + NoiseConfig noiseConfig = new NoiseConfig((ConfigurationSection) entry.getValue()); + noiseBuilders.put(entry.getKey(), noiseConfig); + Debug.info("Loaded noise function " + entry.getKey() + " with type " + noiseConfig.getBuilder().getType()); + } + ores = ConfigLoader.load(new File(file, "ores").toPath(), this, OreConfig.class); palettes = ConfigLoader.load(new File(file, "palettes").toPath(), this, PaletteConfig.class); @@ -108,11 +115,12 @@ public class ConfigPack extends YamlConfiguration { Debug.info("Overriding Vanilla tree: " + entry.getKey()); } + vScope = new Scope(); if(contains("variables")) { Map vars = Objects.requireNonNull(getConfigurationSection("variables")).getValues(false); for(Map.Entry entry : vars.entrySet()) { try { - definedVariables.put(entry.getKey(), Double.valueOf(entry.getValue().toString())); + vScope.getVariable(entry.getKey()).setValue(Double.parseDouble(entry.getValue().toString())); Debug.info("Registered variable " + entry.getKey() + " with value " + entry.getValue()); } catch(ClassCastException | NumberFormatException e) { Debug.stack(e); @@ -141,9 +149,6 @@ public class ConfigPack extends YamlConfiguration { erosionThresh = getDouble("erode.threshold", 0.04); erosionOctaves = getInt("erode.octaves", 3); - octaves = getInt("noise.octaves", 4); - frequency = getDouble("noise.frequency", 1f / 96); - erosionName = getString("erode.grid"); vanillaCaves = getBoolean("vanilla.caves", false); @@ -190,26 +195,6 @@ public class ConfigPack extends YamlConfiguration { LangUtil.log("config-pack.loaded", Level.INFO, getID(), String.valueOf((System.nanoTime() - l) / 1000000D)); } - public String getID() { - return id; - } - - public Map getDefinedVariables() { - return definedVariables; - } - - public Map getBiomes() { - return biomes; - } - - public StructureConfig getStructure(String id) { - return structures.get(id); - } - - public BiomeGridConfig getBiomeGrid(String id) { - return grids.get(id); - } - public static synchronized void loadAll(JavaPlugin main) { configs.clear(); File file = new File(main.getDataFolder(), "packs"); @@ -244,6 +229,30 @@ public class ConfigPack extends YamlConfiguration { return configs.get(id); } + public String getID() { + return id; + } + + public Scope getVariableScope() { + return vScope; + } + + public Map getBiomes() { + return biomes; + } + + public StructureConfig getStructure(String id) { + return structures.get(id); + } + + public BiomeGridConfig getBiomeGrid(String id) { + return grids.get(id); + } + + public Map getNoiseBuilders() { + return noiseBuilders; + } + public Map getLocatable() { return locatable; } @@ -264,13 +273,6 @@ public class ConfigPack extends YamlConfiguration { return dataFolder; } - public BiomeConfig getBiome(UserDefinedBiome b) { - for(BiomeConfig biome : biomes.values()) { - if(biome.getBiome().equals(b)) return biome; - } - throw new IllegalArgumentException("No BiomeConfig for provided biome."); - } - public BiomeConfig getBiome(String id) { return biomes.get(id); } diff --git a/src/main/java/com/dfsek/terra/config/base/ConfigUtil.java b/src/main/java/com/dfsek/terra/config/base/ConfigUtil.java index 98d502d6a..a08824f44 100644 --- a/src/main/java/com/dfsek/terra/config/base/ConfigUtil.java +++ b/src/main/java/com/dfsek/terra/config/base/ConfigUtil.java @@ -3,7 +3,6 @@ package com.dfsek.terra.config.base; import com.dfsek.terra.Debug; import com.dfsek.terra.Terra; import com.dfsek.terra.TerraWorld; -import com.dfsek.terra.biome.failsafe.FailType; import com.dfsek.terra.config.exception.ConfigException; import com.dfsek.terra.config.lang.LangUtil; import com.dfsek.terra.util.TagUtil; @@ -18,20 +17,19 @@ import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.time.Duration; -import java.util.HashSet; +import java.util.EnumSet; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.jar.JarFile; -import java.util.logging.Level; import java.util.logging.Logger; public final class ConfigUtil { public static boolean debug; public static long dataSave; // Period of population data saving, in ticks. public static boolean masterDisableCaves; + public static int biomeSearchRes; public static int cacheSize; - public static FailType failType; public static void loadConfig(JavaPlugin main) { main.saveDefaultConfig(); @@ -40,9 +38,10 @@ public final class ConfigUtil { LangUtil.load(config.getString("language", "en_us"), main); debug = config.getBoolean("debug", false); - cacheSize = config.getInt("cache-size", 3); dataSave = Duration.parse(Objects.requireNonNull(config.getString("data-save", "PT6M"))).toMillis() / 20L; masterDisableCaves = config.getBoolean("master-disable.caves", false); + biomeSearchRes = config.getInt("biome-search-resolution", 4); + cacheSize = config.getInt("cache-size", 384); if(config.getBoolean("dump-default", true)) { try(JarFile jar = new JarFile(new File(Terra.class.getProtectionDomain().getCodeSource().getLocation().toURI()))) { @@ -54,14 +53,6 @@ public final class ConfigUtil { } } - - String fail = config.getString("fail-type", "SHUTDOWN"); - try { - failType = FailType.valueOf(fail); - } catch(IllegalArgumentException e) { - LangUtil.log("config.invalid-failover", Level.SEVERE, fail); - } - Logger logger = main.getLogger(); logger.info("Loading config values"); @@ -70,7 +61,7 @@ public final class ConfigUtil { } public static Set toBlockData(List list, String phase, String id) throws InvalidConfigurationException { - Set bl = new HashSet<>(); + Set bl = EnumSet.noneOf(Material.class); for(String s : list) { try { if(s.startsWith("#")) { diff --git a/src/main/java/com/dfsek/terra/config/base/WorldConfig.java b/src/main/java/com/dfsek/terra/config/base/WorldConfig.java index ae8729502..ab292d9ae 100644 --- a/src/main/java/com/dfsek/terra/config/base/WorldConfig.java +++ b/src/main/java/com/dfsek/terra/config/base/WorldConfig.java @@ -42,7 +42,7 @@ public class WorldConfig { FileConfiguration config = new YamlConfiguration(); Debug.info("Loading config " + configID + " for world " + worldID); try { // Load/create world config file - if(configID == null || configID.equals("")) + if(configID == null || configID.isEmpty()) throw new ConfigException("Config pack unspecified in bukkit.yml!", worldID); File configFile = new File(main.getDataFolder() + File.separator + "worlds", worldID + ".yml"); if(!configFile.exists()) { diff --git a/src/main/java/com/dfsek/terra/config/genconfig/CarverConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/CarverConfig.java index df8ba61d1..2fde8d449 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/CarverConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/CarverConfig.java @@ -15,8 +15,8 @@ import org.polydev.gaea.math.Range; import java.io.File; import java.io.IOException; +import java.util.EnumSet; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -45,8 +45,9 @@ public class CarverConfig extends TerraConfig { @SuppressWarnings("unchecked") public CarverConfig(File file, ConfigPack config) throws IOException, InvalidConfigurationException { super(file, config); - if(!yaml.contains("id")) throw new ConfigException("No ID specified for Carver!", "null"); - id = Objects.requireNonNull(yaml.getString("id")); + load(file); + if(!contains("id")) throw new ConfigException("No ID specified for Carver!", "null"); + id = Objects.requireNonNull(getString("id")); inner = getBlocks("palette.inner.layers"); @@ -56,24 +57,24 @@ public class CarverConfig extends TerraConfig { bottom = getBlocks("palette.bottom.layers"); - replaceableInner = ConfigUtil.toBlockData(yaml.getStringList("palette.inner.replace"), "replaceable inner", getID()); + replaceableInner = ConfigUtil.toBlockData(getStringList("palette.inner.replace"), "replaceable inner", getID()); - replaceableOuter = ConfigUtil.toBlockData(yaml.getStringList("palette.outer.replace"), "replaceable outer", getID()); + replaceableOuter = ConfigUtil.toBlockData(getStringList("palette.outer.replace"), "replaceable outer", getID()); - replaceableTop = ConfigUtil.toBlockData(yaml.getStringList("palette.top.replace"), "replaceable top", getID()); + replaceableTop = ConfigUtil.toBlockData(getStringList("palette.top.replace"), "replaceable top", getID()); - replaceableBottom = ConfigUtil.toBlockData(yaml.getStringList("palette.bottom.replace"), "replaceable bottom", getID()); + replaceableBottom = ConfigUtil.toBlockData(getStringList("palette.bottom.replace"), "replaceable bottom", getID()); - update = ConfigUtil.toBlockData(yaml.getStringList("update"), "update", getID()); + update = ConfigUtil.toBlockData(getStringList("update"), "update", getID()); - updateOcean = yaml.getBoolean("update-liquids", false); + updateOcean = getBoolean("update-liquids", false); - double step = yaml.getDouble("step", 2); - Range recalc = new Range(yaml.getInt("recalculate-direction.min", 8), yaml.getInt("recalculate-direction.max", 12)); - double rm = yaml.getDouble("recalculate-magnitude", 4); + double step = getDouble("step", 2); + Range recalc = new Range(getInt("recalculate-direction.min", 8), getInt("recalculate-direction.max", 12)); + double rm = getDouble("recalculate-magnitude", 4); shift = new HashMap<>(); - for(Map.Entry e : Objects.requireNonNull(yaml.getConfigurationSection("shift")).getValues(false).entrySet()) { - Set l = new HashSet<>(); + for(Map.Entry e : Objects.requireNonNull(getConfigurationSection("shift")).getValues(false).entrySet()) { + Set l = EnumSet.noneOf(Material.class); for(String s : (List) e.getValue()) { l.add(Bukkit.createBlockData(s).getMaterial()); Debug.info("Added " + s + " to shift-able blocks"); @@ -82,19 +83,19 @@ public class CarverConfig extends TerraConfig { Debug.info("Added " + e.getKey() + " as master block"); } - replaceIsBlacklistInner = yaml.getBoolean("palette.inner.replace-blacklist", false); - replaceIsBlacklistOuter = yaml.getBoolean("palette.outer.replace-blacklist", false); - replaceIsBlacklistTop = yaml.getBoolean("palette.top.replace-blacklist", false); - replaceIsBlacklistBottom = yaml.getBoolean("palette.bottom.replace-blacklist", false); + replaceIsBlacklistInner = getBoolean("palette.inner.replace-blacklist", false); + replaceIsBlacklistOuter = getBoolean("palette.outer.replace-blacklist", false); + replaceIsBlacklistTop = getBoolean("palette.top.replace-blacklist", false); + replaceIsBlacklistBottom = getBoolean("palette.bottom.replace-blacklist", false); - double[] start = new double[] {yaml.getDouble("start.x"), yaml.getDouble("start.y"), yaml.getDouble("start.z")}; - double[] mutate = new double[] {yaml.getDouble("mutate.x"), yaml.getDouble("mutate.y"), yaml.getDouble("mutate.z"), yaml.getDouble("mutate.radius")}; - double[] radiusMultiplier = new double[] {yaml.getDouble("start.radius.multiply.x"), yaml.getDouble("start.radius.multiply.y"), yaml.getDouble("start.radius.multiply.z")}; - Range length = new Range(yaml.getInt("length.min"), yaml.getInt("length.max")); - Range radius = new Range(yaml.getInt("start.radius.min"), yaml.getInt("start.radius.max")); - Range height = new Range(yaml.getInt("start.height.min"), yaml.getInt("start.height.max")); + double[] start = new double[] {getDouble("start.x"), getDouble("start.y"), getDouble("start.z")}; + double[] mutate = new double[] {getDouble("mutate.x"), getDouble("mutate.y"), getDouble("mutate.z"), getDouble("mutate.radius")}; + double[] radiusMultiplier = new double[] {getDouble("start.radius.multiply.x"), getDouble("start.radius.multiply.y"), getDouble("start.radius.multiply.z")}; + Range length = new Range(getInt("length.min"), getInt("length.max")); + Range radius = new Range(getInt("start.radius.min"), getInt("start.radius.max")); + Range height = new Range(getInt("start.height.min"), getInt("start.height.max")); - carver = new UserDefinedCarver(height, radius, length, start, mutate, radiusMultiplier, id.hashCode(), yaml.getInt("cut.top", 0), yaml.getInt("cut.bottom", 0)); + carver = new UserDefinedCarver(height, radius, length, start, mutate, radiusMultiplier, id.hashCode(), getInt("cut.top", 0), getInt("cut.bottom", 0)); carver.setStep(step); carver.setRecalc(recalc); carver.setRecalcMagnitude(rm); @@ -102,9 +103,9 @@ public class CarverConfig extends TerraConfig { @SuppressWarnings("unchecked") private Map> getBlocks(String key) throws InvalidConfigurationException { - if(!yaml.contains(key)) throw new ConfigException("Missing Carver Palette!", getID()); + if(!contains(key)) throw new ConfigException("Missing Carver Palette!", getID()); Map> result = new TreeMap<>(); - for(Map m : yaml.getMapList(key)) { + for(Map m : getMapList(key)) { try { ProbabilityCollection layer = new ProbabilityCollection<>(); for(Map.Entry type : ((Map) m.get("materials")).entrySet()) { diff --git a/src/main/java/com/dfsek/terra/config/genconfig/FloraConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/FloraConfig.java index b9bbd36a3..858e02fb3 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/FloraConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/FloraConfig.java @@ -4,6 +4,7 @@ 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 net.jafama.FastMath; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; @@ -12,6 +13,7 @@ import org.bukkit.block.BlockFace; import org.bukkit.block.data.BlockData; import org.bukkit.configuration.InvalidConfigurationException; import org.polydev.gaea.math.Range; +import org.polydev.gaea.util.FastRandom; import org.polydev.gaea.world.Flora; import org.polydev.gaea.world.palette.Palette; import org.polydev.gaea.world.palette.RandomPalette; @@ -20,7 +22,6 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Random; import java.util.Set; public class FloraConfig extends TerraConfig implements Flora { @@ -36,24 +37,25 @@ public class FloraConfig extends TerraConfig implements Flora { public FloraConfig(File file, ConfigPack config) throws IOException, InvalidConfigurationException { super(file, config); - if(!yaml.contains("id")) throw new ConfigException("Flora ID unspecified!", "null"); - this.id = yaml.getString("id"); - if(!yaml.contains("layers")) throw new ConfigException("No blocks defined in custom flora!", getID()); - if(!yaml.contains("spawnable")) throw new ConfigException("Flora spawnable blocks unspecified!", getID()); + load(file); + if(!contains("id")) throw new ConfigException("Flora ID unspecified!", "null"); + this.id = getString("id"); + if(!contains("layers")) throw new ConfigException("No blocks defined in custom flora!", getID()); + if(!contains("spawnable")) throw new ConfigException("Flora spawnable blocks unspecified!", getID()); - spawnable = ConfigUtil.toBlockData(yaml.getStringList("spawnable"), "spawnable", getID()); - replaceable = ConfigUtil.toBlockData(yaml.getStringList("replaceable"), "replaceable", getID()); + spawnable = ConfigUtil.toBlockData(getStringList("spawnable"), "spawnable", getID()); + replaceable = ConfigUtil.toBlockData(getStringList("replaceable"), "replaceable", getID()); - if(yaml.contains("irrigable")) { - irrigable = ConfigUtil.toBlockData(yaml.getStringList("irrigable"), "irrigable", getID()); + if(contains("irrigable")) { + irrigable = ConfigUtil.toBlockData(getStringList("irrigable"), "irrigable", getID()); } else irrigable = null; - physics = yaml.getBoolean("physics", false); - ceiling = yaml.getBoolean("ceiling", false); + physics = getBoolean("physics", false); + ceiling = getBoolean("ceiling", false); - Palette p = new RandomPalette<>(new Random(yaml.getInt("seed", 4))); + Palette p = new RandomPalette<>(new FastRandom(getInt("seed", 4))); - floraPalette = PaletteConfig.getPalette(yaml.getMapList("layers"), p); + floraPalette = PaletteConfig.getPalette(getMapList("layers"), p); } public String getID() { @@ -62,26 +64,28 @@ public class FloraConfig extends TerraConfig implements Flora { @Override public List getValidSpawnsAt(Chunk chunk, int x, int z, Range range) { + int size = floraPalette.getSize(); + Block current = chunk.getBlock(x, range.getMin(), z); List blocks = new ArrayList<>(); - if(ceiling) for(int y : range) { - if(y > 255 || y < 1) continue; - Block check = chunk.getBlock(x, y, z); - Block other = check.getRelative(BlockFace.DOWN); - if(spawnable.contains(check.getType()) && replaceable.contains(other.getType())) { - blocks.add(check); - } - } - else for(int y : range) { - if(y > 254 || y < 0) continue; - Block check = chunk.getBlock(x, y, z); - Block other = check.getRelative(BlockFace.UP); - if(spawnable.contains(check.getType()) && replaceable.contains(other.getType()) && isIrrigated(check)) { - blocks.add(check); + for(int y : range) { + if(y > 255 || y < 0) continue; + current = current.getRelative(BlockFace.UP); + if(spawnable.contains(current.getType()) && isIrrigated(current) && valid(size, current)) { + blocks.add(current); } } return blocks; } + private boolean valid(int size, Block block) { + for(int i = 0; i < size; i++) { // Down if ceiling, up if floor + if(block.getY() + 1 > 255 || block.getY() < 0) return false; + block = block.getRelative(ceiling ? BlockFace.DOWN : BlockFace.UP); + if(!replaceable.contains(block.getType())) return false; + } + return true; + } + private boolean isIrrigated(Block b) { if(irrigable == null) return true; return irrigable.contains(b.getRelative(BlockFace.NORTH).getType()) @@ -94,12 +98,8 @@ public class FloraConfig extends TerraConfig implements Flora { public boolean plant(Location location) { int size = floraPalette.getSize(); int c = ceiling ? -1 : 1; - for(int i = 0; Math.abs(i) < size; i += c) { // Down if ceiling, up if floor - if(i + 1 > 255) return false; - if(!replaceable.contains(location.clone().add(0, i + c, 0).getBlock().getType())) return false; - } - for(int i = 0; Math.abs(i) < size; i += c) { // Down if ceiling, up if floor - int lvl = (Math.abs(i)); + for(int i = 0; FastMath.abs(i) < size; i += c) { // Down if ceiling, up if floor + int lvl = (FastMath.abs(i)); location.clone().add(0, i + c, 0).getBlock().setBlockData(floraPalette.get((ceiling ? lvl : size - lvl - 1), location.getBlockX(), location.getBlockZ()), physics); } return true; diff --git a/src/main/java/com/dfsek/terra/config/genconfig/OreConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/OreConfig.java index c2c75b036..10383b116 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/OreConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/OreConfig.java @@ -4,6 +4,7 @@ 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 net.jafama.FastMath; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.Material; @@ -30,34 +31,27 @@ public class OreConfig extends TerraConfig { private final double deformFrequency; private final String id; private final boolean update; - private final boolean crossChunks; - private final int chunkEdgeOffset; Set replaceable; public OreConfig(File file, ConfigPack config) throws IOException, InvalidConfigurationException { super(file, config); - if(!yaml.contains("id")) throw new ConfigException("Ore ID not found!", "null"); - this.id = yaml.getString("id"); - if(!yaml.contains("material")) throw new ConfigException("Ore material not found!", getID()); - if(!yaml.contains("deform")) throw new ConfigException("Ore vein deformation not found!", getID()); - if(!yaml.contains("replace")) throw new ConfigException("Ore replaceable materials not found!", getID()); - min = yaml.getInt("radius.min", 1); - max = yaml.getInt("radius.max", 1); - deform = yaml.getDouble("deform", 0.75); - deformFrequency = yaml.getDouble("deform-frequency", 0.1); - update = yaml.getBoolean("update", false); - crossChunks = yaml.getBoolean("cross-chunks", true); - chunkEdgeOffset = yaml.getInt("edge-offset", 1); + if(!contains("id")) throw new ConfigException("Ore ID not found!", "null"); + this.id = getString("id"); + if(!contains("material")) throw new ConfigException("Ore material not found!", getID()); + if(!contains("deform")) throw new ConfigException("Ore vein deformation not found!", getID()); + if(!contains("replace")) throw new ConfigException("Ore replaceable materials not found!", getID()); + min = getInt("radius.min", 1); + max = getInt("radius.max", 1); + deform = getDouble("deform", 0.75); + deformFrequency = getDouble("deform-frequency", 0.1); + update = getBoolean("update", false); - if(chunkEdgeOffset > 7 || chunkEdgeOffset < 0) - throw new ConfigException("Edge offset is too high/low!", getID()); - - replaceable = ConfigUtil.toBlockData(yaml.getStringList("replace"), "replaceable", getID()); + replaceable = ConfigUtil.toBlockData(getStringList("replace"), "replaceable", getID()); try { - oreData = Bukkit.createBlockData(Objects.requireNonNull(yaml.getString("material"))); + oreData = Bukkit.createBlockData(Objects.requireNonNull(getString("material"))); } catch(NullPointerException | IllegalArgumentException e) { - throw new ConfigException("Invalid ore material: " + yaml.getString("material"), getID()); + throw new ConfigException("Invalid ore material: " + getString("material"), getID()); } } @@ -80,9 +74,9 @@ public class OreConfig extends TerraConfig { Vector source = l.clone().add(new Vector(x, y, z)); if(oreLoc.getBlockY() > 255 || oreLoc.getBlockY() < 0) continue; if(source.distance(l) < (rad + 0.5) * ((ore.getNoise(x, y, z) + 1) * deform)) { - ChunkCoordinate coord = new ChunkCoordinate(Math.floorDiv(oreLoc.getBlockX(), 16), Math.floorDiv(oreLoc.getBlockZ(), 16), chunk.getWorld().getUID()); + ChunkCoordinate coord = new ChunkCoordinate(FastMath.floorDiv(oreLoc.getBlockX(), 16), FastMath.floorDiv(oreLoc.getBlockZ(), 16), chunk.getWorld().getUID()); Block b = chunks.computeIfAbsent(coord, k -> chunk.getWorld().getChunkAt(oreLoc.toLocation(chunk.getWorld()))) - .getBlock(Math.floorMod(source.getBlockX(), 16), source.getBlockY(), Math.floorMod(source.getBlockZ(), 16)); // Chunk caching conditional computation + .getBlock(FastMath.floorMod(source.getBlockX(), 16), source.getBlockY(), FastMath.floorMod(source.getBlockZ(), 16)); // Chunk caching conditional computation if(replaceable.contains(b.getType()) && b.getLocation().getY() >= 0) b.setBlockData(oreData, update); } @@ -120,12 +114,4 @@ public class OreConfig extends TerraConfig { public String toString() { return "Ore with ID " + getID(); } - - public boolean crossChunks() { - return crossChunks; - } - - public int getChunkEdgeOffset() { - return chunkEdgeOffset; - } } diff --git a/src/main/java/com/dfsek/terra/config/genconfig/PaletteConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/PaletteConfig.java index f85600c97..2d93a465c 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/PaletteConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/PaletteConfig.java @@ -9,6 +9,7 @@ import org.bukkit.block.data.BlockData; import org.bukkit.configuration.InvalidConfigurationException; import org.polydev.gaea.math.FastNoiseLite; import org.polydev.gaea.math.ProbabilityCollection; +import org.polydev.gaea.util.FastRandom; import org.polydev.gaea.world.palette.Palette; import org.polydev.gaea.world.palette.RandomPalette; import org.polydev.gaea.world.palette.SimplexPalette; @@ -17,7 +18,6 @@ import java.io.File; import java.io.IOException; import java.util.List; import java.util.Map; -import java.util.Random; public class PaletteConfig extends TerraConfig { private final Palette palette; @@ -26,18 +26,18 @@ public class PaletteConfig extends TerraConfig { public PaletteConfig(File file, ConfigPack config) throws IOException, InvalidConfigurationException { super(file, config); - if(!yaml.contains("id")) throw new ConfigException("Palette ID unspecified!", "null"); - this.paletteID = yaml.getString("id"); + if(!contains("id")) throw new ConfigException("Palette ID unspecified!", "null"); + this.paletteID = getString("id"); Palette pal; - if(yaml.getBoolean("simplex", false)) { + if(getBoolean("simplex", false)) { useNoise = true; - FastNoiseLite pNoise = new FastNoiseLite(yaml.getInt("seed", 2403)); + FastNoiseLite pNoise = new FastNoiseLite(getInt("seed", 2403)); pNoise.setNoiseType(FastNoiseLite.NoiseType.OpenSimplex2); pNoise.setFractalOctaves(4); - pNoise.setFrequency(yaml.getDouble("frequency", 0.02)); + pNoise.setFrequency(getDouble("frequency", 0.02)); pal = new SimplexPalette<>(pNoise); - } else pal = new RandomPalette<>(new Random(yaml.getInt("seed", 2403))); - palette = getPalette(yaml.getMapList("layers"), pal); + } else pal = new RandomPalette<>(new FastRandom(getInt("seed", 2403))); + palette = getPalette(getMapList("layers"), pal); } @SuppressWarnings("unchecked") diff --git a/src/main/java/com/dfsek/terra/config/genconfig/TreeConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/TreeConfig.java index a2ae5cb48..3f393f75d 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/TreeConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/TreeConfig.java @@ -6,8 +6,12 @@ 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.procgen.math.Vector2; import com.dfsek.terra.structure.Rotation; import com.dfsek.terra.structure.Structure; +import com.dfsek.terra.structure.StructureContainedBlock; +import com.dfsek.terra.structure.StructureInfo; +import com.dfsek.terra.util.structure.RotationUtil; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.configuration.InvalidConfigurationException; @@ -73,4 +77,28 @@ public class TreeConfig extends TerraConfig implements Tree { struc.paste(mut, rotation); return true; } + + public boolean plantBlockCheck(Location location, Random random) { + location.subtract(0, 1, 0); + Location mut = location.clone().subtract(0, yOffset, 0); + if(!spawnable.contains(location.getBlock().getType())) return false; + Structure struc = structure.get(random); + Rotation rotation = Rotation.fromDegrees(random.nextInt(4) * 90); + StructureInfo info = struc.getStructureInfo(); + for(StructureContainedBlock spawn : struc.getSpawns()) { + Vector2 rot = RotationUtil.getRotatedCoords(new Vector2(spawn.getX() - info.getCenterX(), spawn.getZ() - info.getCenterZ()), rotation); + int x = (int) rot.getX(); + int z = (int) rot.getZ(); + switch(spawn.getRequirement()) { + case AIR: + if(!mut.clone().add(x, spawn.getY() - 1, z).getBlock().isPassable()) return false; + break; + case LAND: + if(!mut.clone().add(x, spawn.getY() - 1, z).getBlock().getType().isSolid()) return false; + break; + } + } + struc.paste(mut, rotation); + return true; + } } 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 584b1b7b7..eebe63227 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 @@ -9,7 +9,6 @@ import com.dfsek.terra.config.exception.ConfigException; import com.dfsek.terra.config.exception.NotFoundException; import com.dfsek.terra.config.genconfig.structure.StructureConfig; import com.dfsek.terra.generation.UserDefinedDecorator; -import com.dfsek.terra.generation.UserDefinedGenerator; import org.bukkit.block.data.BlockData; import org.bukkit.configuration.InvalidConfigurationException; import org.polydev.gaea.math.Range; @@ -45,36 +44,36 @@ public class BiomeConfig extends TerraConfig { public BiomeConfig(File file, ConfigPack config) throws InvalidConfigurationException, IOException { super(file, config); + load(file); this.config = config; - if(!yaml.contains("id")) throw new ConfigException("Biome ID unspecified!", "null"); - this.biomeID = yaml.getString("id"); + if(!contains("id")) throw new ConfigException("Biome ID unspecified!", "null"); + this.biomeID = getString("id"); AbstractBiomeConfig abstractBiome = null; // Whether an abstract biome is to be extended. Default to false. boolean extending = false; // Check if biome extends an abstract biome, load abstract biome if so. - if(yaml.contains("extends")) { + if(contains("extends")) { try { - abstractBiome = config.getAbstractBiomes().get(yaml.getString("extends")); - if(abstractBiome == null) - throw new NotFoundException("Abstract Biome", yaml.getString("extends"), getID()); + abstractBiome = config.getAbstractBiomes().get(getString("extends")); + if(abstractBiome == null) throw new NotFoundException("Abstract Biome", getString("extends"), getID()); extending = true; - Debug.info("Extending biome " + yaml.getString("extends")); + Debug.info("Extending biome " + getString("extends")); } catch(NullPointerException e) { - throw new NotFoundException("Abstract Biome", yaml.getString("extends"), getID()); + throw new NotFoundException("Abstract Biome", getString("extends"), getID()); } } // Get various simple values using getOrDefault config methods. try { - eq = yaml.getString("noise-equation", Objects.requireNonNull(abstractBiome).getEquation()); + eq = getString("noise-equation", Objects.requireNonNull(abstractBiome).getEquation()); } catch(NullPointerException e) { - eq = yaml.getString("noise-equation", null); + eq = getString("noise-equation", null); } BiomePaletteConfig palette; // Check if biome is extending abstract biome, only use abstract biome's palette if palette is NOT defined for current biome. - if(extending && abstractBiome.getPaletteData() != null && !yaml.contains("palette")) { + if(extending && abstractBiome.getPaletteData() != null && !contains("palette")) { palette = abstractBiome.getPaletteData(); Debug.info("Using super palette"); } else palette = new BiomePaletteConfig(this, "palette"); @@ -84,31 +83,31 @@ public class BiomeConfig extends TerraConfig { throw new ConfigException("No Palette specified in biome or super biome.", getID()); // Check if carving should be handled by super biome. - if(extending && abstractBiome.getCarving() != null && !yaml.contains("carving")) { + if(extending && abstractBiome.getCarving() != null && !contains("carving")) { carver = abstractBiome.getCarving(); Debug.info("Using super carvers"); } else carver = new BiomeCarverConfig(this); // Check if flora should be handled by super biome. - if(extending && abstractBiome.getFlora() != null && !yaml.contains("flora")) { + if(extending && abstractBiome.getFlora() != null && !contains("flora")) { flora = abstractBiome.getFlora(); Debug.info("Using super flora (" + flora.getFlora().size() + " entries, " + flora.getFloraChance() + " % chance)"); } else flora = new BiomeFloraConfig(this); // Check if trees should be handled by super biome. - if(extending && abstractBiome.getTrees() != null && !yaml.contains("trees")) { + if(extending && abstractBiome.getTrees() != null && !contains("trees")) { tree = abstractBiome.getTrees(); Debug.info("Using super trees"); } else tree = new BiomeTreeConfig(this); // Check if ores should be handled by super biome. - if(extending && abstractBiome.getOres() != null && !yaml.contains("ores")) { + if(extending && abstractBiome.getOres() != null && !contains("ores")) { ore = abstractBiome.getOres(); Debug.info("Using super ores"); } else ore = new BiomeOreConfig(this); // Get slab stuff - if(extending && abstractBiome.getSlabs() != null && !yaml.contains("slabs")) { + if(extending && abstractBiome.getSlabs() != null && !contains("slabs")) { slab = abstractBiome.getSlabs(); Debug.info("Using super slabs"); } else slab = new BiomeSlabConfig(this); @@ -127,17 +126,17 @@ public class BiomeConfig extends TerraConfig { // Get slant stuff TreeMap> slant = new TreeMap<>(); - if(yaml.contains("slant")) { - String slantS = yaml.getString("slant.palette"); + if(contains("slant")) { + String slantS = getString("slant.palette"); slant = new BiomePaletteConfig(this, "slant.palette").getPaletteMap(); Debug.info("Using slant palette: " + slantS); if(slant == null) throw new NotFoundException("Slant Palette", slantS, getID()); } - ySlantOffsetTop = yaml.getDouble("slant.y-offset.top", 0.25); - ySlantOffsetBottom = yaml.getDouble("slant.y-offset.bottom", 0.25); + ySlantOffsetTop = getDouble("slant.y-offset.top", 0.25); + ySlantOffsetBottom = getDouble("slant.y-offset.bottom", 0.25); //Make sure equation is non-null - if(eq == null || eq.equals("")) + if(eq == null || eq.isEmpty()) throw new ConfigException("Could not find noise equation! Biomes must include a noise equation, or extend an abstract biome with one.", getID()); // Create decorator for this config. @@ -146,10 +145,10 @@ public class BiomeConfig extends TerraConfig { // Get Vanilla biome, throw exception if it is invalid/unspecified. org.bukkit.block.Biome vanillaBiome; try { - if(!yaml.contains("vanilla")) throw new ConfigException("Vanilla Biome unspecified!", getID()); - vanillaBiome = org.bukkit.block.Biome.valueOf(yaml.getString("vanilla")); + if(!contains("vanilla")) throw new ConfigException("Vanilla Biome unspecified!", getID()); + vanillaBiome = org.bukkit.block.Biome.valueOf(getString("vanilla")); } catch(IllegalArgumentException e) { - throw new ConfigException("Invalid Vanilla biome: \"" + yaml.getString("vanilla") + "\"", getID()); + throw new ConfigException("Invalid Vanilla biome: \"" + getString("vanilla") + "\"", getID()); } // Structure stuff @@ -157,7 +156,7 @@ public class BiomeConfig extends TerraConfig { List st = new ArrayList<>(); if(abstractBiome != null && abstractBiome.getStructureConfigs() != null) st = abstractBiome.getStructureConfigs(); - if(yaml.contains("structures")) st = yaml.getStringList("structures"); + if(contains("structures")) st = getStringList("structures"); for(String s : st) { try { structures.add(Objects.requireNonNull(config.getStructure(s))); @@ -166,14 +165,13 @@ public class BiomeConfig extends TerraConfig { } } - String elevation = yaml.getString("elevation.equation", null); - boolean doElevationInterpolation = yaml.getBoolean("elevation.interpolation", true); + String elevation = getString("elevation.equation", null); + boolean doElevationInterpolation = getBoolean("elevation.interpolation", true); try { // Get UserDefinedBiome instance representing this config. - UserDefinedGenerator gen = new UserDefinedGenerator(eq, elevation, config.getDefinedVariables(), palette.getPaletteMap(), slant, yaml.getBoolean("prevent-smooth", false)); - gen.setElevationInterpolation(doElevationInterpolation); - this.biome = new UserDefinedBiome(vanillaBiome, dec, gen, yaml.getBoolean("erodible", false), biomeID); + GeneratorOptions gen = new GeneratorOptions(eq, elevation, config.getVariableScope(), palette.getPaletteMap(), slant, config.getNoiseBuilders(), getBoolean("prevent-smooth", false), doElevationInterpolation); + this.biome = new UserDefinedBiome(vanillaBiome, dec, gen, getBoolean("erodible", false), this); } catch(ParseException e) { e.printStackTrace(); throw new ConfigException("Unable to parse noise equation!", getID()); diff --git a/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeOceanConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeOceanConfig.java index 67f7c10f5..4e616016c 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeOceanConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeOceanConfig.java @@ -10,13 +10,12 @@ import org.bukkit.block.data.BlockData; import org.bukkit.configuration.InvalidConfigurationException; import org.jetbrains.annotations.NotNull; import org.polydev.gaea.math.ProbabilityCollection; +import org.polydev.gaea.util.FastRandom; import org.polydev.gaea.world.palette.Palette; import org.polydev.gaea.world.palette.RandomPalette; -import java.util.Random; - public class BiomeOceanConfig extends TerraConfigSection { - private static final Palette oceanDefault = new RandomPalette(new Random(0)).add(Material.WATER.createBlockData(), 1); + private static final Palette oceanDefault = new RandomPalette(new FastRandom(0)).add(Material.WATER.createBlockData(), 1); private final Palette ocean; private final int seaLevel; @@ -27,7 +26,7 @@ public class BiomeOceanConfig extends TerraConfigSection { if(oceanN != null) { if(oceanN.startsWith("BLOCK:")) { try { - ocean = new RandomPalette(new Random(0)).add(new ProbabilityCollection().add(Bukkit.createBlockData(oceanN.substring(6)), 1), 1); + ocean = new RandomPalette(new FastRandom(0)).add(new ProbabilityCollection().add(Bukkit.createBlockData(oceanN.substring(6)), 1), 1); } catch(IllegalArgumentException ex) { throw new ConfigException("BlockData \"" + oceanN + "\" is invalid! (Ocean Palette)", parent.getID()); } diff --git a/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomePaletteConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomePaletteConfig.java index bb321bf0e..0738a6d0f 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomePaletteConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomePaletteConfig.java @@ -8,12 +8,12 @@ import org.bukkit.Bukkit; import org.bukkit.block.data.BlockData; import org.bukkit.configuration.InvalidConfigurationException; import org.polydev.gaea.math.ProbabilityCollection; +import org.polydev.gaea.util.FastRandom; import org.polydev.gaea.world.palette.Palette; import org.polydev.gaea.world.palette.RandomPalette; import java.util.List; import java.util.Map; -import java.util.Random; import java.util.TreeMap; public class BiomePaletteConfig extends TerraConfigSection { @@ -29,7 +29,7 @@ public class BiomePaletteConfig extends TerraConfigSection { try { if(((String) entry.getKey()).startsWith("BLOCK:")) { try { - paletteMap.put((Integer) entry.getValue(), new RandomPalette(new Random(0)).add(new ProbabilityCollection().add(Bukkit.createBlockData(((String) entry.getKey()).substring(6)), 1), 1)); + paletteMap.put((Integer) entry.getValue(), new RandomPalette(new FastRandom(0)).add(new ProbabilityCollection().add(Bukkit.createBlockData(((String) entry.getKey()).substring(6)), 1), 1)); } catch(IllegalArgumentException ex) { throw new ConfigException("BlockData " + entry.getKey() + " is invalid! (Palettes)", parent.getID()); } diff --git a/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeSlabConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeSlabConfig.java index a031f070d..2e12797d6 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeSlabConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeSlabConfig.java @@ -11,13 +11,14 @@ import org.bukkit.block.data.BlockData; import org.bukkit.configuration.InvalidConfigurationException; import org.jetbrains.annotations.NotNull; import org.polydev.gaea.math.ProbabilityCollection; +import org.polydev.gaea.util.FastRandom; import org.polydev.gaea.world.palette.Palette; import org.polydev.gaea.world.palette.RandomPalette; +import java.util.EnumMap; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Random; public class BiomeSlabConfig extends TerraConfigSection { private final Map> slabs; @@ -34,7 +35,7 @@ public class BiomeSlabConfig extends TerraConfigSection { } protected Map> getSlabPalettes(List> paletteConfigSection) throws InvalidConfigurationException { - Map> paletteMap = new HashMap<>(); + Map> paletteMap = new EnumMap<>(Material.class); for(Map e : paletteConfigSection) { for(Map.Entry entry : e.entrySet()) { @@ -42,7 +43,7 @@ public class BiomeSlabConfig extends TerraConfigSection { if(((String) entry.getValue()).startsWith("BLOCK:")) { try { Debug.info("Adding slab palette with single material " + entry.getKey()); - paletteMap.put(Bukkit.createBlockData((String) entry.getKey()).getMaterial(), new RandomPalette(new Random(0)).add(new ProbabilityCollection().add(Bukkit.createBlockData(((String) entry.getValue()).substring(6)), 1), 1)); + paletteMap.put(Bukkit.createBlockData((String) entry.getKey()).getMaterial(), new RandomPalette(new FastRandom(0)).add(new ProbabilityCollection().add(Bukkit.createBlockData(((String) entry.getValue()).substring(6)), 1), 1)); } catch(IllegalArgumentException ex) { throw new ConfigException("Invalid BlockData in slab configuration: " + ex.getMessage(), getParent().getConfig().getID()); } diff --git a/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeSnowConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeSnowConfig.java index 933f04323..e1a49dcc3 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeSnowConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/biome/BiomeSnowConfig.java @@ -12,12 +12,14 @@ import java.util.Map; public class BiomeSnowConfig extends TerraConfigSection { private final int[] snowHeights; + private final boolean physics; private boolean doSnow = false; public BiomeSnowConfig(TerraConfig parent) throws InvalidConfigurationException { super(parent); snowHeights = new int[256]; - List> maps = parent.getYaml().getMapList("snow"); + List> maps = parent.getMapList("snow"); + this.physics = getParent().getBoolean("snow-physics", false); if(maps.size() == 0) return; try { for(Map e : maps) { @@ -41,4 +43,8 @@ public class BiomeSnowConfig extends TerraConfigSection { public boolean doSnow() { return doSnow; } + + public boolean doPhysics() { + return physics; + } } diff --git a/src/main/java/com/dfsek/terra/config/genconfig/biome/GeneratorOptions.java b/src/main/java/com/dfsek/terra/config/genconfig/biome/GeneratorOptions.java new file mode 100644 index 000000000..a5705ebce --- /dev/null +++ b/src/main/java/com/dfsek/terra/config/genconfig/biome/GeneratorOptions.java @@ -0,0 +1,78 @@ +package com.dfsek.terra.config.genconfig.biome; + +import com.dfsek.terra.config.genconfig.noise.NoiseConfig; +import com.dfsek.terra.generation.config.WorldGenerator; +import com.dfsek.terra.math.BlankFunction; +import com.dfsek.terra.util.DataUtil; +import org.bukkit.block.data.BlockData; +import org.jetbrains.annotations.Nullable; +import org.polydev.gaea.world.palette.Palette; +import parsii.eval.Parser; +import parsii.eval.Scope; +import parsii.tokenizer.ParseException; + +import java.util.HashMap; +import java.util.Map; + +public class GeneratorOptions { + + private final Map generators = new HashMap<>(); + + private final boolean preventSmooth; + @SuppressWarnings({"unchecked", "rawtypes", "RedundantSuppression"}) + private final Palette[] palettes = new Palette[256]; + @SuppressWarnings({"unchecked", "rawtypes", "RedundantSuppression"}) + private final Palette[] slantPalettes = new Palette[256]; + + + private final String equation; + private final String elevationEquation; + private final Scope userVariables; + private final Map noiseBuilders; + + private final boolean elevationInterpolation; + + public GeneratorOptions(String equation, @Nullable String elevateEquation, Scope userVariables, Map> paletteMap, Map> slantPaletteMap, Map noiseBuilders, boolean preventSmooth, boolean elevationInterpolation) + throws ParseException { + this.equation = equation; + this.elevationEquation = elevateEquation; + this.userVariables = userVariables; + this.noiseBuilders = noiseBuilders; + this.preventSmooth = preventSmooth; + + Scope s = new Scope().withParent(userVariables); + Parser p = new Parser(); + for(Map.Entry e : noiseBuilders.entrySet()) { + int dimensions = e.getValue().getDimensions(); + if(dimensions == 2 || dimensions == 3) p.registerFunction(e.getKey(), new BlankFunction(dimensions)); + } + p.parse(equation, s); // Validate equation at config load time to prevent error during world load. + if(elevateEquation != null) p.parse(elevateEquation, s); + + + for(int y = 0; y < 256; y++) { + Palette d = DataUtil.BLANK_PALETTE; + for(Map.Entry> e : paletteMap.entrySet()) { + if(e.getKey() >= y) { + d = e.getValue(); + break; + } + } + palettes[y] = d; + Palette slantPalette = null; + for(Map.Entry> e : slantPaletteMap.entrySet()) { + if(e.getKey() >= y) { + slantPalette = e.getValue(); + break; + } + } + slantPalettes[y] = slantPalette; + } + this.elevationInterpolation = elevationInterpolation; + } + + public WorldGenerator getGenerator(long seed) { + return generators.computeIfAbsent(seed, s -> new WorldGenerator(seed, equation, elevationEquation, userVariables, noiseBuilders, palettes, slantPalettes, preventSmooth) + .setElevationInterpolation(elevationInterpolation)); + } +} diff --git a/src/main/java/com/dfsek/terra/config/genconfig/noise/NoiseConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/noise/NoiseConfig.java new file mode 100644 index 000000000..ff27b8439 --- /dev/null +++ b/src/main/java/com/dfsek/terra/config/genconfig/noise/NoiseConfig.java @@ -0,0 +1,56 @@ +package com.dfsek.terra.config.genconfig.noise; + +import com.dfsek.terra.config.exception.ConfigException; +import com.dfsek.terra.generation.config.NoiseBuilder; +import org.bukkit.configuration.ConfigurationSection; +import org.polydev.gaea.math.FastNoiseLite; + +public class NoiseConfig { + private final NoiseBuilder builder; + private final int dimensions; + + public NoiseConfig(ConfigurationSection section) throws ConfigException { + NoiseBuilder builder = new NoiseBuilder(); + try { + builder.setType(FastNoiseLite.NoiseType.valueOf(section.getString("type", "OpenSimplex2"))) + .setFrequency(section.getDouble("frequency", 0.02D)) + .setRotationType3D(FastNoiseLite.RotationType3D.valueOf(section.getString("rotation", "None"))) + .setSeedOffset(section.getInt("offset", 0)); + + dimensions = section.getInt("dimensions", 3); + if(dimensions != 2 && dimensions != 3) + throw new ConfigException("Invalid number of dimensions: " + dimensions, "Noise"); + + + if(section.contains("fractal")) { + builder.setFractalType(FastNoiseLite.FractalType.valueOf(section.getString("fractal.type", "FBm"))) + .setOctaves(section.getInt("fractal.octaves", 1)) + .setFractalGain(section.getDouble("fractal.gain", 0.5D)) + .setFractalLacunarity(section.getDouble("fractal.lacunarity", 2.0D)) + .setPingPong(section.getDouble("fractal.ping-pong", 2.0D)) + .setWeightedStrength(section.getDouble("fractal.weighted-strength", 0.0D)); + } + + if(section.contains("cellular")) { + builder.setCellularDistanceFunction(FastNoiseLite.CellularDistanceFunction.valueOf(section.getString("cellular.distance", "EuclideanSq"))) + .setCellularReturnType(FastNoiseLite.CellularReturnType.valueOf(section.getString("cellular.return", "Distance"))); + } + + if(section.contains("warp")) { + builder.setDomainWarpType(FastNoiseLite.DomainWarpType.valueOf(section.getString("warp.type", "OpenSimplex2"))) + .setDomainWarpAmp(section.getDouble("warp.amplitude", 1.0D)); + } + this.builder = builder; + } catch(IllegalArgumentException | ClassCastException e) { + throw new ConfigException(e.getMessage(), "Noise"); + } + } + + public NoiseBuilder getBuilder() { + return builder; + } + + public int getDimensions() { + return dimensions; + } +} 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 index bd8cbfe7f..264759446 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/structure/EntityFeatureConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/structure/EntityFeatureConfig.java @@ -38,7 +38,6 @@ public class EntityFeatureConfig implements FeatureConfig { throw new InvalidConfigurationException("Error in Entity Configuration!"); } - int attempts = (Integer) items.get("attempts"); int height = (Integer) items.get("in-height"); Range amount; @@ -52,7 +51,7 @@ public class EntityFeatureConfig implements FeatureConfig { 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); + this.feature = new EntityFeature(type, amount, on, in, height); Debug.info("Loaded EntityFeature with type: " + type); } diff --git a/src/main/java/com/dfsek/terra/debug/gui/DebugFrame.java b/src/main/java/com/dfsek/terra/debug/gui/DebugFrame.java index fdad86db2..0ca112209 100644 --- a/src/main/java/com/dfsek/terra/debug/gui/DebugFrame.java +++ b/src/main/java/com/dfsek/terra/debug/gui/DebugFrame.java @@ -4,6 +4,7 @@ import com.dfsek.terra.TerraWorld; import com.dfsek.terra.biome.UserDefinedBiome; import com.dfsek.terra.generation.TerraChunkGenerator; import com.dfsek.terra.image.ImageLoader; +import net.jafama.FastMath; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.polydev.gaea.generation.GenerationPhase; @@ -33,14 +34,14 @@ public class DebugFrame extends JFrame implements ActionListener { super.paintComponents(g); for(Player p : Bukkit.getOnlinePlayers()) { if(!(p.getWorld().getGenerator() instanceof TerraChunkGenerator)) break; - int xp = (int) (((double) Math.floorMod(p.getLocation().getBlockX() - (img.getWidth() / 2), x) / x) * getWidth()); - int zp = (int) (((double) Math.floorMod(p.getLocation().getBlockZ() - (img.getHeight() / 2), z) / z) * getHeight()); + int xp = (int) (((double) FastMath.floorMod(p.getLocation().getBlockX() - (img.getWidth() / 2), x) / x) * getWidth()); + int zp = (int) (((double) FastMath.floorMod(p.getLocation().getBlockZ() - (img.getHeight() / 2), z) / z) * getHeight()); ImageLoader loader = TerraWorld.getWorld(p.getWorld()).getWorldConfig().imageLoader; if(loader != null && loader.getAlign().equals(ImageLoader.Align.NONE)) { - xp = (int) (((double) Math.floorMod(p.getLocation().getBlockX(), x) / x) * getWidth()); - zp = (int) (((double) Math.floorMod(p.getLocation().getBlockZ(), z) / z) * getHeight()); + xp = (int) (((double) FastMath.floorMod(p.getLocation().getBlockX(), x) / x) * getWidth()); + zp = (int) (((double) FastMath.floorMod(p.getLocation().getBlockZ(), z) / z) * getHeight()); } - String str = TerraWorld.getWorld(p.getWorld()).getConfig().getBiome((UserDefinedBiome) TerraWorld.getWorld(p.getWorld()).getGrid().getBiome(p.getLocation(), GenerationPhase.POPULATE)).getID(); + String str = ((UserDefinedBiome) TerraWorld.getWorld(p.getWorld()).getGrid().getBiome(p.getLocation(), GenerationPhase.POPULATE)).getID(); g.setColor(new Color(255, 255, 255, 128)); g.fillRect(xp + 13, zp - 13, (int) (8 + 8.25 * str.length()), 33); g.setColor(Color.BLACK); diff --git a/src/main/java/com/dfsek/terra/generation/ElevationEquation.java b/src/main/java/com/dfsek/terra/generation/ElevationEquation.java deleted file mode 100644 index 8cff5e1fb..000000000 --- a/src/main/java/com/dfsek/terra/generation/ElevationEquation.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.dfsek.terra.generation; - -import com.dfsek.terra.math.NoiseFunction2; -import org.polydev.gaea.math.FastNoiseLite; -import parsii.eval.Expression; -import parsii.eval.Parser; -import parsii.eval.Scope; -import parsii.eval.Variable; -import parsii.tokenizer.ParseException; - -public class ElevationEquation { - private static final Object noiseLock = new Object(); - private final Expression delegate; - private final Scope s = new Scope(); - private final NoiseFunction2 n2 = new NoiseFunction2(); - - private final Variable xVar = s.getVariable("x"); - private final Variable zVar = s.getVariable("z"); - - public ElevationEquation(String equation) throws ParseException { - Parser p = new Parser(); - p.registerFunction("noise2", n2); - delegate = p.parse(equation, s); - } - - public double getNoise(double x, double z, FastNoiseLite noiseLite) { - synchronized(noiseLock) { - xVar.setValue(x); - zVar.setValue(z); - - n2.setNoise(noiseLite); - return delegate.evaluate(); - } - } -} diff --git a/src/main/java/com/dfsek/terra/generation/ElevationInterpolator.java b/src/main/java/com/dfsek/terra/generation/ElevationInterpolator.java index e6d2c896d..c446775d0 100644 --- a/src/main/java/com/dfsek/terra/generation/ElevationInterpolator.java +++ b/src/main/java/com/dfsek/terra/generation/ElevationInterpolator.java @@ -1,34 +1,31 @@ package com.dfsek.terra.generation; import com.dfsek.terra.biome.grid.TerraBiomeGrid; -import org.bukkit.World; +import com.dfsek.terra.generation.config.WorldGenerator; import org.polydev.gaea.generation.GenerationPhase; -import org.polydev.gaea.math.FastNoiseLite; import org.polydev.gaea.math.Interpolator; public class ElevationInterpolator { - private final UserDefinedGenerator[][] gens = new UserDefinedGenerator[10][10]; + private final WorldGenerator[][] gens = new WorldGenerator[10][10]; private final double[][] values = new double[18][18]; - private final FastNoiseLite noise; private final int xOrigin; private final int zOrigin; private final TerraBiomeGrid grid; - public ElevationInterpolator(World w, int chunkX, int chunkZ, TerraBiomeGrid grid, FastNoiseLite noise) { + public ElevationInterpolator(int chunkX, int chunkZ, TerraBiomeGrid grid) { this.xOrigin = chunkX << 4; this.zOrigin = chunkZ << 4; - this.noise = noise; this.grid = grid; for(int x = -2; x < 8; x++) { for(int z = -2; z < 8; z++) { - gens[x + 2][z + 2] = (UserDefinedGenerator) grid.getBiome(xOrigin + x * 4, zOrigin + z * 4, GenerationPhase.BASE).getGenerator(); + gens[x + 2][z + 2] = (WorldGenerator) grid.getBiome(xOrigin + (x << 2), zOrigin + (z << 2), GenerationPhase.BASE).getGenerator(); } } for(byte x = -1; x <= 16; x++) { for(byte z = -1; z <= 16; z++) { - UserDefinedGenerator generator = getGenerator(x, z); + WorldGenerator generator = getGenerator(x, z); if(compareGens((x / 4), (z / 4)) && generator.interpolateElevation()) { Interpolator interpolator = new Interpolator(biomeAvg(x / 4, z / 4), biomeAvg((x / 4) + 1, z / 4), @@ -41,16 +38,16 @@ public class ElevationInterpolator { } } - private UserDefinedGenerator getGenerator(int x, int z) { - return (UserDefinedGenerator) grid.getBiome(xOrigin + x, zOrigin + z, GenerationPhase.BASE).getGenerator(); + private WorldGenerator getGenerator(int x, int z) { + return (WorldGenerator) grid.getBiome(xOrigin + x, zOrigin + z, GenerationPhase.BASE).getGenerator(); } - private UserDefinedGenerator getStoredGen(int x, int z) { + private WorldGenerator getStoredGen(int x, int z) { return gens[x + 2][z + 2]; } private boolean compareGens(int x, int z) { - UserDefinedGenerator comp = getStoredGen(x, z); + WorldGenerator comp = getStoredGen(x, z); for(int xi = x - 2; xi <= x + 2; xi++) { for(int zi = z - 2; zi <= z + 2; zi++) { @@ -61,20 +58,19 @@ public class ElevationInterpolator { } private double biomeAvg(int x, int z) { - return (elevate(getStoredGen(x + 1, z), x * 4 + 4 + xOrigin, z * 4 + zOrigin) - + elevate(getStoredGen(x - 1, z), x * 4 - 4 + xOrigin, z * 4 + zOrigin) - + elevate(getStoredGen(x, z + 1), x * 4 + xOrigin, z * 4 + 4 + zOrigin) - + elevate(getStoredGen(x, z - 1), x * 4 + xOrigin, z * 4 - 4 + zOrigin) - + elevate(getStoredGen(x, z), x * 4 + xOrigin, z * 4 + zOrigin) - + elevate(getStoredGen(x - 1, z - 1), x * 4 + xOrigin, z * 4 + zOrigin) - + elevate(getStoredGen(x - 1, z + 1), x * 4 + xOrigin, z * 4 + zOrigin) - + elevate(getStoredGen(x + 1, z - 1), x * 4 + xOrigin, z * 4 + zOrigin) - + elevate(getStoredGen(x + 1, z + 1), x * 4 + xOrigin, z * 4 + zOrigin)) / 9D; + return (elevate(getStoredGen(x + 1, z), (x << 2) + 4 + xOrigin, (z << 2) + zOrigin) + + elevate(getStoredGen(x - 1, z), (x << 2) - 4 + xOrigin, (z << 2) + zOrigin) + + elevate(getStoredGen(x, z + 1), (x << 2) + xOrigin, (z << 2) + 4 + zOrigin) + + elevate(getStoredGen(x, z - 1), (x << 2) + xOrigin, (z << 2) - 4 + zOrigin) + + elevate(getStoredGen(x, z), (x << 2) + xOrigin, (z << 2) + zOrigin) + + elevate(getStoredGen(x - 1, z - 1), (x << 2) + xOrigin, (z << 2) + zOrigin) + + elevate(getStoredGen(x - 1, z + 1), (x << 2) + xOrigin, (z << 2) + zOrigin) + + elevate(getStoredGen(x + 1, z - 1), (x << 2) + xOrigin, (z << 2) + zOrigin) + + elevate(getStoredGen(x + 1, z + 1), (x << 2) + xOrigin, (z << 2) + zOrigin)) / 9D; } - private double elevate(UserDefinedGenerator g, int x, int z) { - if(g.getElevationEquation() != null) return g.getElevationEquation().getNoise(x, z, noise); - return 0; + private double elevate(WorldGenerator g, int x, int z) { + return g.getElevation(x, z); } public double getElevation(int x, int z) { diff --git a/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java b/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java index 551cf67bd..dc1fd894f 100644 --- a/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java +++ b/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java @@ -9,11 +9,13 @@ import com.dfsek.terra.config.base.ConfigPack; import com.dfsek.terra.config.genconfig.biome.BiomeConfig; import com.dfsek.terra.config.genconfig.biome.BiomeSlabConfig; import com.dfsek.terra.config.lang.LangUtil; +import com.dfsek.terra.generation.config.WorldGenerator; import com.dfsek.terra.population.CavePopulator; import com.dfsek.terra.population.FloraPopulator; import com.dfsek.terra.population.OrePopulator; import com.dfsek.terra.population.SnowPopulator; import com.dfsek.terra.population.StructurePopulator; +import com.dfsek.terra.population.TreePopulator; import com.dfsek.terra.util.DataUtil; import org.bukkit.Chunk; import org.bukkit.Material; @@ -55,8 +57,9 @@ public class TerraChunkGenerator extends GaeaChunkGenerator { public TerraChunkGenerator(ConfigPack c) { super(ChunkInterpolator.InterpolationType.TRILINEAR); this.configPack = c; - popMan.attach(new FloraPopulator()); popMan.attach(new OrePopulator()); + popMan.attach(new TreePopulator()); + popMan.attach(new FloraPopulator()); popMan.attach(new SnowPopulator()); } @@ -76,26 +79,22 @@ public class TerraChunkGenerator extends GaeaChunkGenerator { popMap.get(c.getWorld()).checkNeighbors(c.getX(), c.getZ(), c.getWorld()); } - @Override - public void attachProfiler(WorldProfiler p) { - super.attachProfiler(p); - popMan.attachProfiler(p); - } - private static Palette getPalette(int x, int y, int z, BiomeConfig c, ChunkInterpolator interpolator, ElevationInterpolator elevationInterpolator) { - Palette slant = ((UserDefinedGenerator) c.getBiome().getGenerator()).getSlantPalette(y); + Palette slant = ((WorldGenerator) c.getBiome().getGenerator()).getSlantPalette(y); if(slant != null) { - boolean north = interpolator.getNoise(x, y - elevationInterpolator.getElevation(x, z + 1), z + 1) > 0; - boolean south = interpolator.getNoise(x, y - elevationInterpolator.getElevation(x, z - 1), z - 1) > 0; - boolean east = interpolator.getNoise(x + 1, y - elevationInterpolator.getElevation(x + 1, z), z) > 0; - boolean west = interpolator.getNoise(x - 1, y - elevationInterpolator.getElevation(x - 1, z), z) > 0; - double ySlantOffsetTop = c.getYSlantOffsetTop(); double ySlantOffsetBottom = c.getYSlantOffsetBottom(); boolean top = interpolator.getNoise(x, y + ySlantOffsetTop - elevationInterpolator.getElevation(x, z), z) > 0; boolean bottom = interpolator.getNoise(x, y - ySlantOffsetBottom - elevationInterpolator.getElevation(x, z), z) > 0; - if((top && bottom) && (north || south || east || west) && (!(north && south && east && west))) return slant; + if(top && bottom) { + boolean north = interpolator.getNoise(x, y - elevationInterpolator.getElevation(x, z + 1), z + 1) > 0; + boolean south = interpolator.getNoise(x, y - elevationInterpolator.getElevation(x, z - 1), z - 1) > 0; + boolean east = interpolator.getNoise(x + 1, y - elevationInterpolator.getElevation(x + 1, z), z) > 0; + boolean west = interpolator.getNoise(x - 1, y - elevationInterpolator.getElevation(x - 1, z), z) > 0; + + if((north || south || east || west) && (!(north && south && east && west))) return slant; + } } return c.getBiome().getGenerator().getPalette(y); } @@ -109,10 +108,10 @@ public class TerraChunkGenerator extends GaeaChunkGenerator { if(stairPalette != null) { BlockData stair = stairPalette.get(0, block.getBlockX(), block.getBlockZ()); Stairs stairNew = (Stairs) stair.clone(); - double elevationN = elevationInterpolator.getElevation(block.getBlockX(), block.getBlockZ() - 1); // Northern elevation - double elevationS = elevationInterpolator.getElevation(block.getBlockX(), block.getBlockZ() + 1); // Southern elevation - double elevationE = elevationInterpolator.getElevation(block.getBlockX() + 1, block.getBlockZ()); // Eastern elevation - double elevationW = elevationInterpolator.getElevation(block.getBlockX() - 1, block.getBlockZ()); // Western elevation + int elevationN = (int) elevationInterpolator.getElevation(block.getBlockX(), block.getBlockZ() - 1); // Northern elevation + int elevationS = (int) elevationInterpolator.getElevation(block.getBlockX(), block.getBlockZ() + 1); // Southern elevation + int elevationE = (int) elevationInterpolator.getElevation(block.getBlockX() + 1, block.getBlockZ()); // Eastern elevation + int elevationW = (int) elevationInterpolator.getElevation(block.getBlockX() - 1, block.getBlockZ()); // Western elevation if(interpolator.getNoise(block.getBlockX() - 0.5, block.getBlockY() - elevationW, block.getBlockZ()) > thresh) { stairNew.setFacing(BlockFace.WEST); } else if(interpolator.getNoise(block.getBlockX(), block.getBlockY() - elevationN, block.getBlockZ() - 0.5) > thresh) { @@ -137,6 +136,12 @@ public class TerraChunkGenerator extends GaeaChunkGenerator { } } + @Override + public void attachProfiler(WorldProfiler p) { + super.attachProfiler(p); + popMan.attachProfiler(p); + } + @Override @SuppressWarnings("try") public ChunkData generateBase(@NotNull World world, @NotNull Random random, int chunkX, int chunkZ, ChunkInterpolator interpolator) { @@ -144,14 +149,13 @@ public class TerraChunkGenerator extends GaeaChunkGenerator { ChunkData chunk = createChunkData(world); TerraWorld tw = TerraWorld.getWorld(world); if(!tw.isSafe()) return chunk; - ConfigPack config = tw.getConfig(); int xOrig = (chunkX << 4); int zOrig = (chunkZ << 4); org.polydev.gaea.biome.BiomeGrid grid = getBiomeGrid(world); ElevationInterpolator elevationInterpolator; try(ProfileFuture ignore = TerraProfiler.fromWorld(world).measure("ElevationTime")) { - elevationInterpolator = new ElevationInterpolator(world, chunkX, chunkZ, tw.getGrid(), getNoiseGenerator()); + elevationInterpolator = new ElevationInterpolator(chunkX, chunkZ, tw.getGrid()); } for(byte x = 0; x < 16; x++) { @@ -162,7 +166,7 @@ public class TerraChunkGenerator extends GaeaChunkGenerator { int cz = zOrig + z; Biome b = grid.getBiome(xOrig + x, zOrig + z, GenerationPhase.PALETTE_APPLY); - BiomeConfig c = config.getBiome((UserDefinedBiome) b); + BiomeConfig c = ((UserDefinedBiome) b).getConfig(); double elevate = elevationInterpolator.getElevation(x, z); @@ -203,12 +207,12 @@ public class TerraChunkGenerator extends GaeaChunkGenerator { @Override public int getNoiseOctaves(World world) { - return configPack.octaves; + return 1; } @Override public double getNoiseFrequency(World world) { - return configPack.frequency; + return 0.02; } @Override diff --git a/src/main/java/com/dfsek/terra/generation/UserDefinedGenerator.java b/src/main/java/com/dfsek/terra/generation/UserDefinedGenerator.java deleted file mode 100644 index e44770a4e..000000000 --- a/src/main/java/com/dfsek/terra/generation/UserDefinedGenerator.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.dfsek.terra.generation; - -import com.dfsek.terra.Debug; -import com.dfsek.terra.math.NoiseFunction2; -import com.dfsek.terra.math.NoiseFunction3; -import com.dfsek.terra.util.DataUtil; -import org.bukkit.World; -import org.bukkit.block.data.BlockData; -import org.jetbrains.annotations.Nullable; -import org.polydev.gaea.biome.Generator; -import org.polydev.gaea.math.FastNoiseLite; -import org.polydev.gaea.math.Interpolator; -import org.polydev.gaea.world.palette.Palette; -import parsii.eval.Expression; -import parsii.eval.Parser; -import parsii.eval.Scope; -import parsii.eval.Variable; -import parsii.tokenizer.ParseException; - -import java.util.Map; - - -public class UserDefinedGenerator extends Generator { - private static final Object noiseLock = new Object(); - private final Expression noiseExp; - private final Scope s = new Scope(); - private final Variable xVar = s.getVariable("x"); - private final Variable yVar = s.getVariable("y"); - private final Variable zVar = s.getVariable("z"); - @SuppressWarnings({"unchecked", "rawtypes", "RedundantSuppression"}) - private final Palette[] palettes = new Palette[256]; - @SuppressWarnings({"unchecked", "rawtypes", "RedundantSuppression"}) - private final Palette[] slantPalettes = new Palette[256]; - private final NoiseFunction2 n2 = new NoiseFunction2(); - private final NoiseFunction3 n3 = new NoiseFunction3(); - private final ElevationEquation elevationEquation; - private final boolean preventSmooth; - private boolean elevationInterpolation; - - - public UserDefinedGenerator(String equation, @Nullable String elevateEquation, Map userVariables, Map> paletteMap, Map> slantPaletteMap, boolean preventSmooth) - throws ParseException { - for(Map.Entry entry : userVariables.entrySet()) { - s.getVariable(entry.getKey()).setValue(entry.getValue()); // Define all user variables. - } - Parser p = new Parser(); - p.registerFunction("noise2", n2); - p.registerFunction("noise3", n3); - for(int y = 0; y < 256; y++) { - Palette d = DataUtil.BLANK_PALETTE; - for(Map.Entry> e : paletteMap.entrySet()) { - if(e.getKey() >= y) { - d = e.getValue(); - break; - } - } - palettes[y] = d; - Palette slantPalette = null; - for(Map.Entry> e : slantPaletteMap.entrySet()) { - if(e.getKey() >= y) { - slantPalette = e.getValue(); - break; - } - } - slantPalettes[y] = slantPalette; - } - if(elevateEquation != null) { - Debug.info("Using elevation equation"); - this.elevationEquation = new ElevationEquation(elevateEquation); - } else this.elevationEquation = null; - this.noiseExp = p.parse(equation, s); - this.preventSmooth = preventSmooth; - } - - /** - * Gets the 2D noise at a pair of coordinates using the provided FastNoiseLite instance. - * - * @param gen - The FastNoiseLite instance to use. - * @param x - The x coordinate. - * @param z - The z coordinate. - * @return double - Noise value at the specified coordinates. - */ - @Override - public double getNoise(FastNoiseLite gen, World w, int x, int z) { - synchronized(noiseLock) { - xVar.setValue(x); - yVar.setValue(0); - zVar.setValue(z); - n2.setNoise(gen); - n3.setNoise(gen); - return noiseExp.evaluate(); - } - } - - /** - * Gets the 3D noise at a pair of coordinates using the provided FastNoiseLite instance. - * - * @param gen - The FastNoiseLite instance to use. - * @param x - The x coordinate. - * @param y - The y coordinate. - * @param z - The z coordinate. - * @return double - Noise value at the specified coordinates. - */ - @Override - public double getNoise(FastNoiseLite gen, World w, int x, int y, int z) { - synchronized(noiseLock) { - xVar.setValue(x); - yVar.setValue(y); - zVar.setValue(z); - n2.setNoise(gen); - n3.setNoise(gen); - return noiseExp.evaluate(); - } - } - - /** - * Gets the BlocPalette to generate the biome with. - * - * @return BlocPalette - The biome's palette. - */ - @Override - public Palette getPalette(int y) { - return palettes[y]; - } - - public Palette getSlantPalette(int y) { - return slantPalettes[y]; - } - - - @Override - public boolean useMinimalInterpolation() { - return preventSmooth; - } - - @Override - public Interpolator.Type getInterpolationType() { - return Interpolator.Type.LINEAR; - } - - public ElevationEquation getElevationEquation() { - return elevationEquation; - } - - public boolean interpolateElevation() { - return elevationInterpolation; - } - - public void setElevationInterpolation(boolean elevationInterpolation) { - this.elevationInterpolation = elevationInterpolation; - } -} diff --git a/src/main/java/com/dfsek/terra/generation/config/NoiseBuilder.java b/src/main/java/com/dfsek/terra/generation/config/NoiseBuilder.java new file mode 100644 index 000000000..051dc3403 --- /dev/null +++ b/src/main/java/com/dfsek/terra/generation/config/NoiseBuilder.java @@ -0,0 +1,186 @@ +package com.dfsek.terra.generation.config; + +import org.polydev.gaea.math.FastNoiseLite; + +public class NoiseBuilder { + private FastNoiseLite.NoiseType type = FastNoiseLite.NoiseType.OpenSimplex2; + private int octaves = 1; + private FastNoiseLite.FractalType fractalType = FastNoiseLite.FractalType.None; + private double frequency = 0.02D; + private double fractalGain = 0.5D; + private double fractalLacunarity = 2.0D; + private double pingPong = 2.0D; + private double weightedStrength = 0.0D; + private int seedOffset = 0; + + private FastNoiseLite.CellularDistanceFunction cellularDistanceFunction = FastNoiseLite.CellularDistanceFunction.EuclideanSq; + private FastNoiseLite.CellularReturnType cellularReturnType = FastNoiseLite.CellularReturnType.Distance; + private double cellularJitter = 1.0D; + + private FastNoiseLite.DomainWarpType domainWarpType = FastNoiseLite.DomainWarpType.OpenSimplex2; + private double domainWarpAmp = 1.0D; + + private FastNoiseLite.RotationType3D rotationType3D = FastNoiseLite.RotationType3D.None; + + public FastNoiseLite build(int seed) { + FastNoiseLite noise = new FastNoiseLite(seed + seedOffset); + if(!fractalType.equals(FastNoiseLite.FractalType.None)) { + noise.setFractalType(fractalType); + noise.setFractalOctaves(octaves); + noise.setFractalGain(fractalGain); + noise.setFractalLacunarity(fractalLacunarity); + if(fractalType.equals(FastNoiseLite.FractalType.PingPong)) noise.setFractalPingPongStrength(pingPong); + noise.setFractalWeightedStrength(weightedStrength); + } + if(type.equals(FastNoiseLite.NoiseType.Cellular)) { + noise.setCellularDistanceFunction(cellularDistanceFunction); + noise.setCellularReturnType(cellularReturnType); + noise.setCellularJitter(cellularJitter); + } + + noise.setNoiseType(type); + + noise.setDomainWarpType(domainWarpType); + noise.setDomainWarpAmp(domainWarpAmp); + + noise.setRotationType3D(rotationType3D); + + noise.setFrequency(frequency); + return noise; + } + + public FastNoiseLite.NoiseType getType() { + return type; + } + + public NoiseBuilder setType(FastNoiseLite.NoiseType type) { + this.type = type; + return this; + } + + public int getSeedOffset() { + return seedOffset; + } + + public void setSeedOffset(int seedOffset) { + this.seedOffset = seedOffset; + } + + public FastNoiseLite.CellularDistanceFunction getCellularDistanceFunction() { + return cellularDistanceFunction; + } + + public NoiseBuilder setCellularDistanceFunction(FastNoiseLite.CellularDistanceFunction cellularDistanceFunction) { + this.cellularDistanceFunction = cellularDistanceFunction; + return this; + } + + public FastNoiseLite.CellularReturnType getCellularReturnType() { + return cellularReturnType; + } + + public NoiseBuilder setCellularReturnType(FastNoiseLite.CellularReturnType cellularReturnType) { + this.cellularReturnType = cellularReturnType; + return this; + } + + public FastNoiseLite.DomainWarpType getDomainWarpType() { + return domainWarpType; + } + + public NoiseBuilder setDomainWarpType(FastNoiseLite.DomainWarpType domainWarpType) { + this.domainWarpType = domainWarpType; + return this; + } + + public double getCellularJitter() { + return cellularJitter; + } + + public NoiseBuilder setCellularJitter(double cellularJitter) { + this.cellularJitter = cellularJitter; + return this; + } + + public double getDomainWarpAmp() { + return domainWarpAmp; + } + + public NoiseBuilder setDomainWarpAmp(double domainWarpAmp) { + this.domainWarpAmp = domainWarpAmp; + return this; + } + + public double getFractalGain() { + return fractalGain; + } + + public NoiseBuilder setFractalGain(double fractalGain) { + this.fractalGain = fractalGain; + return this; + } + + public double getFractalLacunarity() { + return fractalLacunarity; + } + + public NoiseBuilder setFractalLacunarity(double fractalLacunarity) { + this.fractalLacunarity = fractalLacunarity; + return this; + } + + public double getFrequency() { + return frequency; + } + + public NoiseBuilder setFrequency(double frequency) { + this.frequency = frequency; + return this; + } + + public double getPingPong() { + return pingPong; + } + + public NoiseBuilder setPingPong(double pingPong) { + this.pingPong = pingPong; + return this; + } + + public double getWeightedStrength() { + return weightedStrength; + } + + public NoiseBuilder setWeightedStrength(double weightedStrength) { + this.weightedStrength = weightedStrength; + return this; + } + + public int getOctaves() { + return octaves; + } + + public NoiseBuilder setOctaves(int octaves) { + this.octaves = octaves; + return this; + } + + public FastNoiseLite.FractalType getFractalType() { + return fractalType; + } + + public NoiseBuilder setFractalType(FastNoiseLite.FractalType fractalType) { + this.fractalType = fractalType; + return this; + } + + public FastNoiseLite.RotationType3D getRotationType3D() { + return rotationType3D; + } + + public NoiseBuilder setRotationType3D(FastNoiseLite.RotationType3D rotationType3D) { + this.rotationType3D = rotationType3D; + return this; + } +} + diff --git a/src/main/java/com/dfsek/terra/generation/config/WorldGenerator.java b/src/main/java/com/dfsek/terra/generation/config/WorldGenerator.java new file mode 100644 index 000000000..eacb1cb4a --- /dev/null +++ b/src/main/java/com/dfsek/terra/generation/config/WorldGenerator.java @@ -0,0 +1,140 @@ +package com.dfsek.terra.generation.config; + +import com.dfsek.terra.config.genconfig.noise.NoiseConfig; +import com.dfsek.terra.math.NoiseFunction2; +import com.dfsek.terra.math.NoiseFunction3; +import com.dfsek.terra.math.RandomFunction; +import org.bukkit.World; +import org.bukkit.block.data.BlockData; +import org.polydev.gaea.biome.Generator; +import org.polydev.gaea.math.FastNoiseLite; +import org.polydev.gaea.math.Interpolator; +import org.polydev.gaea.world.palette.Palette; +import parsii.eval.Expression; +import parsii.eval.Parser; +import parsii.eval.Scope; +import parsii.eval.Variable; +import parsii.tokenizer.ParseException; + +import java.util.Map; + +public class WorldGenerator extends Generator { + @SuppressWarnings({"unchecked", "rawtypes", "RedundantSuppression"}) + private final Palette[] palettes; + @SuppressWarnings({"unchecked", "rawtypes", "RedundantSuppression"}) + private final Palette[] slantPalettes; + + private final boolean preventSmooth; + private final Expression noiseExp; + private final Expression elevationExp; + private final Variable xVar; + private final Variable yVar; + private final Variable zVar; + private final Variable elevationXVar; + private final Variable elevationZVar; + private boolean elevationInterpolation = true; + + @SuppressWarnings({"rawtypes", "unchecked"}) + public WorldGenerator(long seed, String equation, String elevateEquation, Scope vScope, Map noiseBuilders, Palette[] palettes, Palette[] slantPalettes, boolean preventSmooth) { + Parser p = new Parser(); + p.registerFunction("rand", new RandomFunction()); + Parser ep = new Parser(); + ep.registerFunction("rand", new RandomFunction()); + + Scope s = new Scope().withParent(vScope); + xVar = s.create("x"); + yVar = s.create("y"); + zVar = s.create("z"); + s.create("seed").setValue(seed); + + this.preventSmooth = preventSmooth; + + this.palettes = palettes; + this.slantPalettes = slantPalettes; + + for(Map.Entry e : noiseBuilders.entrySet()) { + switch(e.getValue().getDimensions()) { + case 2: + p.registerFunction(e.getKey(), new NoiseFunction2(seed, e.getValue().getBuilder())); + ep.registerFunction(e.getKey(), new NoiseFunction2(seed, e.getValue().getBuilder())); + break; + case 3: + p.registerFunction(e.getKey(), new NoiseFunction3(seed, e.getValue().getBuilder())); + break; + } + } + try { + this.noiseExp = p.parse(equation, s).simplify(); + if(elevateEquation != null) { + Scope es = new Scope().withParent(vScope); + es.create("seed").setValue(seed); + this.elevationXVar = es.create("x"); + this.elevationZVar = es.create("z"); + this.elevationExp = ep.parse(elevateEquation, es).simplify(); + } else { + this.elevationExp = null; + this.elevationXVar = null; + this.elevationZVar = null; + } + } catch(ParseException e) { + throw new IllegalArgumentException(); + } + } + + public synchronized double getElevation(int x, int z) { + if(elevationExp == null) return 0; + elevationXVar.setValue(x); + elevationZVar.setValue(z); + return elevationExp.evaluate(); + } + + @Override + public synchronized double getNoise(FastNoiseLite fastNoiseLite, World world, int x, int z) { + xVar.setValue(x); + yVar.setValue(0); + zVar.setValue(z); + return noiseExp.evaluate(); + } + + @Override + public synchronized double getNoise(FastNoiseLite fastNoiseLite, World world, int x, int y, int z) { + xVar.setValue(x); + yVar.setValue(y); + zVar.setValue(z); + return noiseExp.evaluate(); + } + + /** + * Gets the BlockPalette to generate the biome with. + * + * @return BlockPalette - The biome's palette. + */ + @Override + public Palette getPalette(int y) { + return palettes[y]; + } + + public Palette getSlantPalette(int y) { + return slantPalettes[y]; + } + + + @Override + public boolean useMinimalInterpolation() { + return preventSmooth; + } + + @Override + public Interpolator.Type getInterpolationType() { + return Interpolator.Type.LINEAR; + } + + public boolean interpolateElevation() { + return elevationInterpolation; + } + + public WorldGenerator setElevationInterpolation(boolean elevationInterpolation) { + this.elevationInterpolation = elevationInterpolation; + return this; + } +} diff --git a/src/main/java/com/dfsek/terra/image/ImageLoader.java b/src/main/java/com/dfsek/terra/image/ImageLoader.java index e0865a5bb..99167bde8 100644 --- a/src/main/java/com/dfsek/terra/image/ImageLoader.java +++ b/src/main/java/com/dfsek/terra/image/ImageLoader.java @@ -5,6 +5,7 @@ import com.dfsek.terra.biome.BiomeZone; import com.dfsek.terra.biome.grid.TerraBiomeGrid; import com.dfsek.terra.config.base.ConfigUtil; import com.dfsek.terra.debug.gui.DebugGUI; +import net.jafama.FastMath; import org.bukkit.World; import org.polydev.gaea.biome.NormalizationUtil; @@ -108,12 +109,12 @@ public class ImageLoader { NONE { @Override public int getRGB(BufferedImage image, int x, int y) { - return image.getRGB(Math.floorMod(x, image.getWidth()), Math.floorMod(y, image.getHeight())); + return image.getRGB(FastMath.floorMod(x, image.getWidth()), FastMath.floorMod(y, image.getHeight())); } }; private static int getRGBNoAlign(BufferedImage image, int x, int y) { - return image.getRGB(Math.floorMod(x, image.getWidth()), Math.floorMod(y, image.getHeight())); + return image.getRGB(FastMath.floorMod(x, image.getWidth()), FastMath.floorMod(y, image.getHeight())); } public abstract int getRGB(BufferedImage image, int x, int y); diff --git a/src/main/java/com/dfsek/terra/math/BlankFunction.java b/src/main/java/com/dfsek/terra/math/BlankFunction.java new file mode 100644 index 000000000..def469f97 --- /dev/null +++ b/src/main/java/com/dfsek/terra/math/BlankFunction.java @@ -0,0 +1,29 @@ +package com.dfsek.terra.math; + +import parsii.eval.Expression; +import parsii.eval.Function; + +import java.util.List; + +public class BlankFunction implements Function { + private final int args; + + public BlankFunction(int args) { + this.args = args; + } + + @Override + public int getNumberOfArguments() { + return args; + } + + @Override + public double eval(List list) { + return 0; + } + + @Override + public boolean isNaturalFunction() { + return true; + } +} diff --git a/src/main/java/com/dfsek/terra/math/NoiseFunction.java b/src/main/java/com/dfsek/terra/math/NoiseFunction.java new file mode 100644 index 000000000..ccc604c9e --- /dev/null +++ b/src/main/java/com/dfsek/terra/math/NoiseFunction.java @@ -0,0 +1,6 @@ +package com.dfsek.terra.math; + +import parsii.eval.Function; + +public interface NoiseFunction extends Function { +} diff --git a/src/main/java/com/dfsek/terra/math/NoiseFunction2.java b/src/main/java/com/dfsek/terra/math/NoiseFunction2.java index 3a011be24..f2f17cbc4 100644 --- a/src/main/java/com/dfsek/terra/math/NoiseFunction2.java +++ b/src/main/java/com/dfsek/terra/math/NoiseFunction2.java @@ -1,15 +1,20 @@ package com.dfsek.terra.math; import com.dfsek.terra.config.base.ConfigUtil; +import com.dfsek.terra.generation.config.NoiseBuilder; +import com.dfsek.terra.util.hash.HashMapDoubleDouble; import org.polydev.gaea.math.FastNoiseLite; import parsii.eval.Expression; -import parsii.eval.Function; import java.util.List; -public class NoiseFunction2 implements Function { +public class NoiseFunction2 implements NoiseFunction { + private final FastNoiseLite gen; private final Cache cache = new Cache(); - private FastNoiseLite gen; + + public NoiseFunction2(long seed, NoiseBuilder builder) { + this.gen = builder.build((int) seed); + } @Override public int getNumberOfArguments() { @@ -18,7 +23,17 @@ public class NoiseFunction2 implements Function { @Override public double eval(List list) { - return cache.get(list.get(0).evaluate(), list.get(1).evaluate()); + return cache.get(gen, list.get(0).evaluate(), list.get(1).evaluate()); + } + + /** + * Evaluate without cache. For testing. + * + * @param list Parameters. + * @return Result. + */ + public double evalNoCache(List list) { + return gen.getNoise(list.get(0).evaluate(), list.get(1).evaluate()); } @Override @@ -26,28 +41,24 @@ public class NoiseFunction2 implements Function { return true; } - public void setNoise(FastNoiseLite gen) { - this.gen = gen; - } + private static class Cache extends HashMapDoubleDouble { + private static final long serialVersionUID = 8915092734723467010L; + private static final int cacheSize = ConfigUtil.cacheSize; - private final class Cache { - private final double[] cacheX = new double[ConfigUtil.cacheSize]; - private final double[] cacheZ = new double[ConfigUtil.cacheSize]; - private final double[] cacheValues = new double[ConfigUtil.cacheSize]; + public double get(FastNoiseLite noise, double x, double z) { + double xx = x >= 0 ? x * 2 : x * -2 - 1; + double zz = z >= 0 ? z * 2 : z * -2 - 1; + double key = (xx >= zz) ? (xx * xx + xx + zz) : (zz * zz + xx); + double value = this.get(key); + if(this.size() > cacheSize) { + this.clear(); + } + return (value == 4.9E-324D ? addAndReturn(noise.getNoise(x, z), key) : value); + } - public double get(double x, double z) { - for(int i = 0; i < cacheX.length; i++) { - if(cacheX[i] == x && cacheZ[i] == z) return cacheValues[i]; - } - cacheX[0] = x; - cacheZ[0] = z; - cacheValues[0] = gen.getNoise(x, z); - for(int i = 0; i < cacheX.length - 1; i++) { - cacheX[i + 1] = cacheX[i]; - cacheZ[i + 1] = cacheZ[i]; - cacheValues[i + 1] = cacheValues[i]; - } - return cacheValues[0]; + private double addAndReturn(double value, double key) { + this.put(key, value); + return value; } } } diff --git a/src/main/java/com/dfsek/terra/math/NoiseFunction3.java b/src/main/java/com/dfsek/terra/math/NoiseFunction3.java index 6f6a10c82..f257afff5 100644 --- a/src/main/java/com/dfsek/terra/math/NoiseFunction3.java +++ b/src/main/java/com/dfsek/terra/math/NoiseFunction3.java @@ -1,13 +1,17 @@ package com.dfsek.terra.math; +import com.dfsek.terra.generation.config.NoiseBuilder; import org.polydev.gaea.math.FastNoiseLite; import parsii.eval.Expression; -import parsii.eval.Function; import java.util.List; -public class NoiseFunction3 implements Function { - private FastNoiseLite gen; +public class NoiseFunction3 implements NoiseFunction { + private final FastNoiseLite gen; + + public NoiseFunction3(long seed, NoiseBuilder builder) { + this.gen = builder.build((int) seed); + } @Override public int getNumberOfArguments() { @@ -23,8 +27,4 @@ public class NoiseFunction3 implements Function { public boolean isNaturalFunction() { return true; } - - public void setNoise(FastNoiseLite gen) { - this.gen = gen; - } } diff --git a/src/main/java/com/dfsek/terra/math/RandomFunction.java b/src/main/java/com/dfsek/terra/math/RandomFunction.java new file mode 100644 index 000000000..bad8d3d6e --- /dev/null +++ b/src/main/java/com/dfsek/terra/math/RandomFunction.java @@ -0,0 +1,30 @@ +package com.dfsek.terra.math; + +import org.polydev.gaea.util.FastRandom; +import parsii.eval.Expression; +import parsii.eval.Function; + +import java.util.List; + +/** + * Provides access to a PRNG ({@link org.polydev.gaea.util.FastRandom}) + *

+ * Takes 1 argument, which sets the seed + */ +public class RandomFunction implements Function { + @Override + public int getNumberOfArguments() { + return 1; + } + + @Override + public double eval(List list) { + long seed = (long) list.get(0).evaluate(); + return new FastRandom(seed).nextDouble(); + } + + @Override + public boolean isNaturalFunction() { + return true; + } +} diff --git a/src/main/java/com/dfsek/terra/population/CavePopulator.java b/src/main/java/com/dfsek/terra/population/CavePopulator.java index a8630ec3b..2399692a1 100644 --- a/src/main/java/com/dfsek/terra/population/CavePopulator.java +++ b/src/main/java/com/dfsek/terra/population/CavePopulator.java @@ -5,6 +5,7 @@ import com.dfsek.terra.TerraWorld; import com.dfsek.terra.config.base.ConfigPack; import com.dfsek.terra.config.base.ConfigUtil; import com.dfsek.terra.config.genconfig.CarverConfig; +import com.dfsek.terra.util.PopulationUtil; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; @@ -30,9 +31,10 @@ public class CavePopulator extends BlockPopulator { @SuppressWarnings("try") @Override - public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk chunk) { + public void populate(@NotNull World world, @NotNull Random r, @NotNull Chunk chunk) { if(ConfigUtil.masterDisableCaves) return; try(ProfileFuture ignored = TerraProfiler.fromWorld(world).measure("CaveTime")) { + Random random = PopulationUtil.getRandom(chunk); TerraWorld tw = TerraWorld.getWorld(world); if(!tw.isSafe()) return; ConfigPack config = tw.getConfig(); @@ -70,24 +72,23 @@ public class CavePopulator extends BlockPopulator { updateNeeded.add(b); } } - for(Location l : shiftCandidate.keySet()) { + for(Map.Entry entry : shiftCandidate.entrySet()) { + Location l = entry.getKey(); 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); + if(c.getShiftedBlocks().get(entry.getValue()).contains(mut.getBlock().getType())) { + mut.getBlock().setBlockData(shiftStorage.computeIfAbsent(entry.getValue(), 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(AIR, false); + b.setBlockData(orig, true); } /*for(Map.Entry e : new SimplexCarver(chunk.getX(), chunk.getZ()).carve(chunk.getX(), chunk.getZ(), world).getCarvedBlocks().entrySet()) { Vector v = e.getKey(); diff --git a/src/main/java/com/dfsek/terra/population/FloraPopulator.java b/src/main/java/com/dfsek/terra/population/FloraPopulator.java index 23b7827bf..a8716f75e 100644 --- a/src/main/java/com/dfsek/terra/population/FloraPopulator.java +++ b/src/main/java/com/dfsek/terra/population/FloraPopulator.java @@ -1,29 +1,20 @@ package com.dfsek.terra.population; -import com.dfsek.terra.Terra; import com.dfsek.terra.TerraProfiler; import com.dfsek.terra.TerraWorld; import com.dfsek.terra.biome.UserDefinedBiome; import com.dfsek.terra.biome.grid.TerraBiomeGrid; -import com.dfsek.terra.config.base.ConfigPack; import com.dfsek.terra.config.genconfig.biome.BiomeConfig; import com.dfsek.terra.config.genconfig.biome.BiomeFloraConfig; -import com.dfsek.terra.event.TreeGenerateEvent; -import org.bukkit.Bukkit; import org.bukkit.Chunk; -import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.Block; import org.jetbrains.annotations.NotNull; import org.polydev.gaea.generation.GenerationPhase; -import org.polydev.gaea.math.Range; import org.polydev.gaea.population.GaeaBlockPopulator; import org.polydev.gaea.profiler.ProfileFuture; -import org.polydev.gaea.tree.Tree; import org.polydev.gaea.world.Flora; -import java.util.ArrayList; -import java.util.List; import java.util.Random; /** @@ -39,21 +30,12 @@ public class FloraPopulator extends GaeaBlockPopulator { int originX = chunk.getX() << 4; int originZ = chunk.getZ() << 4; TerraBiomeGrid grid = tw.getGrid(); - ConfigPack config = tw.getConfig(); for(int x = 0; x < 16; x++) { for(int z = 0; z < 16; z++) { UserDefinedBiome biome = (UserDefinedBiome) grid.getBiome((chunk.getX() << 4) + x, (chunk.getZ() << 4) + z, GenerationPhase.POPULATE); - if((x & 1) == 0 && (z & 1) == 0) { - int treeChance = biome.getDecorator().getTreeDensity(); - if(random.nextInt(1000) < treeChance) { - int xt = offset(random, x); - int zt = offset(random, z); - if(doTrees(biome, tw, random, chunk, xt, zt)) continue; - } - } if(biome.getDecorator().getFloraChance() <= 0) continue; try { - BiomeConfig c = config.getBiome(biome); + BiomeConfig c = biome.getConfig(); BiomeFloraConfig f = c.getFlora(); for(int i = 0; i < f.getFloraAttempts(); i++) { Flora item; @@ -71,34 +53,4 @@ public class FloraPopulator extends GaeaBlockPopulator { } } } - - private static boolean doTrees(@NotNull UserDefinedBiome biome, TerraWorld world, @NotNull Random random, @NotNull Chunk chunk, int x, int z) { - for(Block block : getValidTreeSpawnsAt(chunk, x, z, new Range(0, 254))) { - Tree tree = biome.getDecorator().getTrees().get(random); - Range range = world.getConfig().getBiome(biome).getTreeRange(tree); - if(!range.isInRange(block.getY())) continue; - try { - Location l = block.getLocation(); - TreeGenerateEvent event = new TreeGenerateEvent(world, l, tree); - Bukkit.getPluginManager().callEvent(event); - if(!event.isCancelled()) tree.plant(l, random, Terra.getInstance()); - } catch(NullPointerException ignore) { - } - } - return false; - } - - public static List getValidTreeSpawnsAt(Chunk chunk, int x, int z, Range check) { - List blocks = new ArrayList<>(); - for(int y : check) { - if(chunk.getBlock(x, y, z).getType().isSolid() && chunk.getBlock(x, y + 1, z).getType().isAir()) { - blocks.add(chunk.getBlock(x, y + 1, z)); - } - } - return blocks; - } - - private static int offset(Random r, int i) { - return Math.min(Math.max(i + r.nextInt(3) - 1, 0), 15); - } } diff --git a/src/main/java/com/dfsek/terra/population/OrePopulator.java b/src/main/java/com/dfsek/terra/population/OrePopulator.java index 4a6097b1b..3aa1d4262 100644 --- a/src/main/java/com/dfsek/terra/population/OrePopulator.java +++ b/src/main/java/com/dfsek/terra/population/OrePopulator.java @@ -3,7 +3,6 @@ package com.dfsek.terra.population; import com.dfsek.terra.TerraProfiler; import com.dfsek.terra.TerraWorld; import com.dfsek.terra.biome.UserDefinedBiome; -import com.dfsek.terra.config.base.ConfigPack; import com.dfsek.terra.config.genconfig.OreConfig; import com.dfsek.terra.config.genconfig.biome.BiomeOreConfig; import com.dfsek.terra.event.OreVeinGenerateEvent; @@ -24,28 +23,29 @@ import java.util.Random; public class OrePopulator extends GaeaBlockPopulator { @SuppressWarnings("try") @Override - public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk chunk) { + public void populate(@NotNull World world, @NotNull Random r, @NotNull Chunk chunk) { try(ProfileFuture ignored = TerraProfiler.fromWorld(world).measure("OreTime")) { TerraWorld tw = TerraWorld.getWorld(world); if(!tw.isSafe()) return; - ConfigPack config = tw.getConfig(); - Biome b = TerraWorld.getWorld(world).getGrid().getBiome((chunk.getX() << 4) + 8, (chunk.getZ() << 4) + 8, GenerationPhase.POPULATE); - BiomeOreConfig ores = config.getBiome((UserDefinedBiome) b).getOres(); - for(Map.Entry e : ores.getOres().entrySet()) { - int num = e.getValue().get(random); - OreConfig ore = e.getKey(); - int edgeOffset = ore.getChunkEdgeOffset(); - for(int i = 0; i < num; i++) { - int x = random.nextInt(16 - edgeOffset * 2) + edgeOffset; - int z = random.nextInt(16 - edgeOffset * 2) + edgeOffset; - int y = ores.getOreHeights().get(ore).get(random); + for(int cx = -1; cx <= 1; cx++) { + for(int cz = -1; cz <= 1; cz++) { + Biome b = TerraWorld.getWorld(world).getGrid().getBiome(((chunk.getX() + cx) << 4) + 8, ((chunk.getZ() + cz) << 4) + 8, GenerationPhase.POPULATE); + BiomeOreConfig ores = ((UserDefinedBiome) b).getConfig().getOres(); + for(Map.Entry e : ores.getOres().entrySet()) { + int num = e.getValue().get(r); + OreConfig ore = e.getKey(); + for(int i = 0; i < num; i++) { + int x = r.nextInt(16) + cx * 16; + int z = r.nextInt(16) + cz * 16; + int y = ores.getOreHeights().get(ore).get(r); - Vector v = new Vector(x, y, z); - OreVeinGenerateEvent event = new OreVeinGenerateEvent(tw, v.toLocation(world), ore); - Bukkit.getPluginManager().callEvent(event); - if(!event.isCancelled()) { - if(ore.crossChunks()) ore.doVein(v, chunk, random); - else ore.doVeinSingle(new Vector(x, y, z), chunk, random); + Vector v = new Vector(x, y, z); + OreVeinGenerateEvent event = new OreVeinGenerateEvent(tw, v.toLocation(world), ore); + Bukkit.getPluginManager().callEvent(event); + if(!event.isCancelled()) { + ore.doVeinSingle(new Vector(x, y, z), chunk, r); + } + } } } } diff --git a/src/main/java/com/dfsek/terra/population/SnowPopulator.java b/src/main/java/com/dfsek/terra/population/SnowPopulator.java index 4abba7978..9802fe5e1 100644 --- a/src/main/java/com/dfsek/terra/population/SnowPopulator.java +++ b/src/main/java/com/dfsek/terra/population/SnowPopulator.java @@ -12,6 +12,9 @@ import org.bukkit.Chunk; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Snowable; import org.jetbrains.annotations.NotNull; import org.polydev.gaea.generation.GenerationPhase; import org.polydev.gaea.population.GaeaBlockPopulator; @@ -55,7 +58,7 @@ public class SnowPopulator extends GaeaBlockPopulator { TerraBiomeGrid g = w.getGrid(); for(int x = 0; x < 16; x++) { for(int z = 0; z < 16; z++) { - BiomeConfig biome = w.getConfig().getBiome((UserDefinedBiome) g.getBiome(origX + x, origZ + z, GenerationPhase.PALETTE_APPLY)); + BiomeConfig biome = ((UserDefinedBiome) g.getBiome(origX + x, origZ + z, GenerationPhase.PALETTE_APPLY)).getConfig(); if(!biome.getSnow().doSnow()) continue; int y; Block b = null; @@ -66,7 +69,13 @@ public class SnowPopulator extends GaeaBlockPopulator { if(random.nextInt(100) >= biome.getSnow().getSnowChance(y)) continue; if(blacklistSpawn.contains(b.getType()) || b.isPassable()) continue; - chunk.getBlock(x, ++y, z).setBlockData(DataUtil.SNOW); + boolean phys = biome.getSnow().doPhysics(); + if(!phys) { + BlockData data = b.getBlockData(); + if(data instanceof Snowable) phys = true; + } + b.getRelative(BlockFace.UP).setBlockData(DataUtil.SNOW, phys); + } } } diff --git a/src/main/java/com/dfsek/terra/population/StructurePopulator.java b/src/main/java/com/dfsek/terra/population/StructurePopulator.java index c37ac42d4..10da463a7 100644 --- a/src/main/java/com/dfsek/terra/population/StructurePopulator.java +++ b/src/main/java/com/dfsek/terra/population/StructurePopulator.java @@ -12,7 +12,9 @@ import com.dfsek.terra.structure.Rotation; import com.dfsek.terra.structure.Structure; import com.dfsek.terra.structure.StructureContainedInventory; import com.dfsek.terra.structure.features.Feature; +import com.dfsek.terra.util.PopulationUtil; import com.dfsek.terra.util.structure.RotationUtil; +import net.jafama.FastMath; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.World; @@ -21,6 +23,7 @@ import org.bukkit.inventory.BlockInventoryHolder; import org.jetbrains.annotations.NotNull; import org.polydev.gaea.profiler.ProfileFuture; import org.polydev.gaea.structures.loot.LootTable; +import org.polydev.gaea.util.FastRandom; import java.util.Random; @@ -28,8 +31,9 @@ public class StructurePopulator extends BlockPopulator { @SuppressWarnings("try") @Override - public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk chunk) { + public void populate(@NotNull World world, @NotNull Random r, @NotNull Chunk chunk) { try(ProfileFuture ignored = TerraProfiler.fromWorld(world).measure("StructureTime")) { + Random random = PopulationUtil.getRandom(chunk); int cx = (chunk.getX() << 4); int cz = (chunk.getZ() << 4); TerraWorld tw = TerraWorld.getWorld(world); @@ -39,8 +43,8 @@ public class StructurePopulator extends BlockPopulator { structure: for(StructureConfig conf : config.getAllStructures()) { Location spawn = conf.getSpawn().getNearestSpawn(cx + 8, cz + 8, world.getSeed()).toLocation(world); - if(!config.getBiome((UserDefinedBiome) grid.getBiome(spawn)).getStructures().contains(conf)) continue; - Random r2 = new Random(spawn.hashCode()); + if(!((UserDefinedBiome) grid.getBiome(spawn)).getConfig().getStructures().contains(conf)) continue; + Random r2 = new FastRandom(spawn.hashCode()); Structure struc = conf.getStructure(r2); Rotation rotation = Rotation.fromDegrees(r2.nextInt(4) * 90); for(int y = conf.getSearchStart().get(r2); y > 0; y--) { @@ -48,15 +52,15 @@ public class StructurePopulator extends BlockPopulator { spawn.setY(y); 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) { + if(FastMath.abs((cx + 8) - spawn.getBlockX()) <= horizontal && FastMath.abs((cz + 8) - spawn.getBlockZ()) <= horizontal) { struc.paste(spawn, chunk, rotation); for(StructureContainedInventory i : struc.getInventories()) { try { Debug.info("Attempting to populate loot: " + i.getUid()); Vector2 lootCoords = RotationUtil.getRotatedCoords(new Vector2(i.getX() - struc.getStructureInfo().getCenterX(), i.getZ() - struc.getStructureInfo().getCenterZ()), rotation.inverse()); Location inv = spawn.clone().add(lootCoords.getX(), i.getY(), lootCoords.getZ()); - Debug.info(Math.floorDiv(inv.getBlockX(), 16) + ":" + chunk.getX() + ", " + Math.floorDiv(inv.getBlockZ(), 16) + ":" + chunk.getZ()); - if(Math.floorDiv(inv.getBlockX(), 16) != chunk.getX() || Math.floorDiv(inv.getBlockZ(), 16) != chunk.getZ()) + Debug.info(FastMath.floorDiv(inv.getBlockX(), 16) + ":" + chunk.getX() + ", " + FastMath.floorDiv(inv.getBlockZ(), 16) + ":" + chunk.getZ()); + if(FastMath.floorDiv(inv.getBlockX(), 16) != chunk.getX() || FastMath.floorDiv(inv.getBlockZ(), 16) != chunk.getZ()) continue; Debug.info("Target is in chunk."); Debug.info(spawn.toString() + " became: " + inv.toString() + " (" + rotation + ", " + inv.getBlock().getType() + ")"); @@ -69,7 +73,7 @@ public class StructurePopulator extends BlockPopulator { Debug.stack(e); } } - for(Feature f : conf.getFeatures()) f.apply(struc, spawn, chunk); // Apply features. + for(Feature f : conf.getFeatures()) f.apply(struc, rotation, spawn, chunk); // Apply features. break; } } diff --git a/src/main/java/com/dfsek/terra/population/TreePopulator.java b/src/main/java/com/dfsek/terra/population/TreePopulator.java new file mode 100644 index 000000000..8b77f275d --- /dev/null +++ b/src/main/java/com/dfsek/terra/population/TreePopulator.java @@ -0,0 +1,77 @@ +package com.dfsek.terra.population; + +import com.dfsek.terra.Terra; +import com.dfsek.terra.TerraProfiler; +import com.dfsek.terra.TerraWorld; +import com.dfsek.terra.biome.UserDefinedBiome; +import com.dfsek.terra.biome.grid.TerraBiomeGrid; +import com.dfsek.terra.event.TreeGenerateEvent; +import net.jafama.FastMath; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.jetbrains.annotations.NotNull; +import org.polydev.gaea.generation.GenerationPhase; +import org.polydev.gaea.math.Range; +import org.polydev.gaea.population.GaeaBlockPopulator; +import org.polydev.gaea.profiler.ProfileFuture; +import org.polydev.gaea.tree.Tree; +import org.polydev.gaea.util.GlueList; + +import java.util.List; +import java.util.Random; + + +public class TreePopulator extends GaeaBlockPopulator { + private static void doTrees(@NotNull UserDefinedBiome biome, TerraWorld world, @NotNull Random random, @NotNull Chunk chunk, int x, int z) { + for(Block block : getValidTreeSpawnsAt(chunk, x, z, new Range(0, 254))) { + Tree tree = biome.getDecorator().getTrees().get(random); + Range range = biome.getConfig().getTreeRange(tree); + if(!range.isInRange(block.getY())) continue; + try { + Location l = block.getLocation(); + TreeGenerateEvent event = new TreeGenerateEvent(world, l, tree); + Bukkit.getPluginManager().callEvent(event); + if(!event.isCancelled()) tree.plant(l, random, Terra.getInstance()); + } catch(NullPointerException ignore) { + } + } + } + + public static List getValidTreeSpawnsAt(Chunk chunk, int x, int z, Range check) { + List blocks = new GlueList<>(); + for(int y : check) { + if(chunk.getBlock(x, y, z).getType().isSolid() && chunk.getBlock(x, y + 1, z).isPassable()) { + blocks.add(chunk.getBlock(x, y + 1, z)); + } + } + return blocks; + } + + private static int offset(Random r, int i) { + return FastMath.min(FastMath.max(i + r.nextInt(3) - 1, 0), 15); + } + + @Override + @SuppressWarnings("try") + public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk chunk) { + try(ProfileFuture ignored = TerraProfiler.fromWorld(world).measure("TreeTime")) { + TerraWorld tw = TerraWorld.getWorld(world); + if(!tw.isSafe()) return; + TerraBiomeGrid grid = tw.getGrid(); + for(int x = 0; x < 16; x += 2) { + for(int z = 0; z < 16; z += 2) { + UserDefinedBiome biome = (UserDefinedBiome) grid.getBiome((chunk.getX() << 4) + x, (chunk.getZ() << 4) + z, GenerationPhase.POPULATE); + int treeChance = biome.getDecorator().getTreeDensity(); + if(random.nextInt(1000) < treeChance) { + int xt = offset(random, x); + int zt = offset(random, z); + doTrees(biome, tw, random, chunk, xt, zt); + } + } + } + } + } +} diff --git a/src/main/java/com/dfsek/terra/procgen/GridSpawn.java b/src/main/java/com/dfsek/terra/procgen/GridSpawn.java index 50d267139..6a53a9209 100644 --- a/src/main/java/com/dfsek/terra/procgen/GridSpawn.java +++ b/src/main/java/com/dfsek/terra/procgen/GridSpawn.java @@ -2,8 +2,9 @@ package com.dfsek.terra.procgen; import org.bukkit.util.Vector; import org.polydev.gaea.math.MathUtil; +import org.polydev.gaea.util.FastRandom; +import org.polydev.gaea.util.GlueList; -import java.util.ArrayList; import java.util.List; import java.util.Random; @@ -30,7 +31,7 @@ public class GridSpawn { public Vector getNearestSpawn(int x, int z, long seed) { int structureChunkX = x / (width + 2 * separation); int structureChunkZ = z / (width + 2 * separation); - List zones = new ArrayList<>(); + List zones = new GlueList<>(); for(int xi = structureChunkX - 1; xi <= structureChunkX + 1; xi++) { for(int zi = structureChunkZ - 1; zi <= structureChunkZ + 1; zi++) { zones.add(getChunkSpawn(xi, zi, seed)); @@ -53,7 +54,7 @@ public class GridSpawn { * @return Vector representing spawnpoint */ public Vector getChunkSpawn(int structureChunkX, int structureChunkZ, long seed) { - Random r = new Random(MathUtil.getCarverChunkSeed(structureChunkX, structureChunkZ, seed)); + Random r = new FastRandom(MathUtil.getCarverChunkSeed(structureChunkX, structureChunkZ, seed)); int offsetX = r.nextInt(width); int offsetZ = r.nextInt(width); int sx = structureChunkX * (width + 2 * separation) + offsetX; diff --git a/src/main/java/com/dfsek/terra/procgen/math/Vector2.java b/src/main/java/com/dfsek/terra/procgen/math/Vector2.java index d7d296455..6ec295b17 100644 --- a/src/main/java/com/dfsek/terra/procgen/math/Vector2.java +++ b/src/main/java/com/dfsek/terra/procgen/math/Vector2.java @@ -1,5 +1,7 @@ package com.dfsek.terra.procgen.math; +import net.jafama.FastMath; + /** * oh yeah */ @@ -111,7 +113,7 @@ public class Vector2 implements Cloneable { * @return length */ public double length() { - return Math.sqrt(lengthSquared()); + return FastMath.sqrt(lengthSquared()); } /** @@ -130,7 +132,7 @@ public class Vector2 implements Cloneable { * @return Distance between vectors */ public double distance(Vector2 other) { - return Math.sqrt(distanceSquared(other)); + return FastMath.sqrt(distanceSquared(other)); } /** diff --git a/src/main/java/com/dfsek/terra/procgen/pixel/Rectangle.java b/src/main/java/com/dfsek/terra/procgen/pixel/Rectangle.java index e2769aa6b..fb2df4824 100644 --- a/src/main/java/com/dfsek/terra/procgen/pixel/Rectangle.java +++ b/src/main/java/com/dfsek/terra/procgen/pixel/Rectangle.java @@ -1,6 +1,7 @@ package com.dfsek.terra.procgen.pixel; import com.dfsek.terra.procgen.math.Vector2; +import net.jafama.FastMath; import java.util.HashSet; import java.util.Set; @@ -11,8 +12,8 @@ public class Rectangle extends Polygon { private final Vector2 max; public Rectangle(Vector2 min, Vector2 max) { - this.max = new Vector2(Math.min(min.getX(), max.getX()), Math.min(min.getZ(), max.getZ())); - this.min = new Vector2(Math.max(min.getX(), max.getX()), Math.max(min.getZ(), max.getZ())); + this.max = new Vector2(FastMath.min(min.getX(), max.getX()), FastMath.min(min.getZ(), max.getZ())); + this.min = new Vector2(FastMath.max(min.getX(), max.getX()), FastMath.max(min.getZ(), max.getZ())); } public Rectangle(Vector2 center, double xRadius, double zRadius) { diff --git a/src/main/java/com/dfsek/terra/procgen/voxel/VoxelGeometry.java b/src/main/java/com/dfsek/terra/procgen/voxel/VoxelGeometry.java index a1bdc6343..3d0405722 100644 --- a/src/main/java/com/dfsek/terra/procgen/voxel/VoxelGeometry.java +++ b/src/main/java/com/dfsek/terra/procgen/voxel/VoxelGeometry.java @@ -1,12 +1,12 @@ package com.dfsek.terra.procgen.voxel; import org.bukkit.util.Vector; +import org.polydev.gaea.util.GlueList; -import java.util.ArrayList; import java.util.List; public abstract class VoxelGeometry { - private final List geometry = new ArrayList<>(); + private final List geometry = new GlueList<>(); public static VoxelGeometry getBlank() { return new VoxelGeometry() { diff --git a/src/main/java/com/dfsek/terra/structure/Rotation.java b/src/main/java/com/dfsek/terra/structure/Rotation.java index f1a91d212..e9aac096e 100644 --- a/src/main/java/com/dfsek/terra/structure/Rotation.java +++ b/src/main/java/com/dfsek/terra/structure/Rotation.java @@ -1,5 +1,7 @@ package com.dfsek.terra.structure; +import net.jafama.FastMath; + public enum Rotation { CW_90(90), CW_180(180), CCW_90(270), NONE(0); private final int degrees; @@ -9,7 +11,7 @@ public enum Rotation { } public static Rotation fromDegrees(int deg) { - switch(Math.floorMod(deg, 360)) { + switch(FastMath.floorMod(deg, 360)) { case 0: return Rotation.NONE; case 90: diff --git a/src/main/java/com/dfsek/terra/structure/Structure.java b/src/main/java/com/dfsek/terra/structure/Structure.java index bb5e0f0d0..d68394b2f 100644 --- a/src/main/java/com/dfsek/terra/structure/Structure.java +++ b/src/main/java/com/dfsek/terra/structure/Structure.java @@ -3,6 +3,7 @@ package com.dfsek.terra.structure; import com.dfsek.terra.Debug; import com.dfsek.terra.procgen.math.Vector2; import com.dfsek.terra.util.structure.RotationUtil; +import net.jafama.FastMath; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.Location; @@ -144,6 +145,16 @@ public class Structure implements Serializable { return (Structure) o; } + private static void toFile(@NotNull Serializable o, @NotNull File f) throws IOException { + ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f)); + oos.writeObject(o); + oos.close(); + } + + public StructureContainedBlock[][][] getRawStructure() { + return structure; + } + /** * Paste the structure at a Location, ignoring chunk boundaries. * @@ -188,7 +199,7 @@ public class Structure implements Serializable { } int offset = block.getPullOffset(); if(offset != 0) - worldBlock = worldBlock.getRelative((offset > 0) ? BlockFace.UP : BlockFace.DOWN, Math.abs(offset)); + worldBlock = worldBlock.getRelative((offset > 0) ? BlockFace.UP : BlockFace.DOWN, FastMath.abs(offset)); RotationUtil.rotateBlockData(data, r); @@ -255,9 +266,9 @@ public class Structure implements Serializable { Vector2 max = getRotatedCoords(new Vector2(x.getMax(), z.getMax()).subtract(center), r.inverse()).add(center); if(a.equals(Rotation.Axis.X)) - return new Range((int) Math.floor(Math.min(min.getX(), max.getX())), (int) Math.ceil(Math.max(min.getX(), max.getX())) + 1); + return new Range((int) FastMath.floor(FastMath.min(min.getX(), max.getX())), (int) FastMath.ceil(FastMath.max(min.getX(), max.getX())) + 1); else - return new Range((int) Math.floor(Math.min(min.getZ(), max.getZ())), (int) Math.ceil(Math.max(min.getZ(), max.getZ())) + 1); + return new Range((int) FastMath.floor(FastMath.min(min.getZ(), max.getZ())), (int) FastMath.ceil(FastMath.max(min.getZ(), max.getZ())) + 1); } @NotNull @@ -283,6 +294,10 @@ public class Structure implements Serializable { return true; } + public HashSet getSpawns() { + return spawns; + } + public HashSet getInventories() { return inventories; } @@ -324,12 +339,6 @@ public class Structure implements Serializable { toFile(this, f); } - 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; diff --git a/src/main/java/com/dfsek/terra/structure/StructureInfo.java b/src/main/java/com/dfsek/terra/structure/StructureInfo.java index eceab039f..e08fddcbf 100644 --- a/src/main/java/com/dfsek/terra/structure/StructureInfo.java +++ b/src/main/java/com/dfsek/terra/structure/StructureInfo.java @@ -1,6 +1,7 @@ package com.dfsek.terra.structure; import com.dfsek.terra.procgen.math.Vector2; +import net.jafama.FastMath; import java.io.Serializable; @@ -41,6 +42,6 @@ public class StructureInfo implements Serializable { } public double getMaxHorizontal() { - return Math.sqrt(Math.pow(sizeX, 2) + Math.pow(sizeZ, 2)); + return FastMath.sqrt(FastMath.pow(sizeX, 2) + FastMath.pow(sizeZ, 2)); } } diff --git a/src/main/java/com/dfsek/terra/structure/features/EntityFeature.java b/src/main/java/com/dfsek/terra/structure/features/EntityFeature.java index 78859e6da..c44bf2d85 100644 --- a/src/main/java/com/dfsek/terra/structure/features/EntityFeature.java +++ b/src/main/java/com/dfsek/terra/structure/features/EntityFeature.java @@ -1,8 +1,10 @@ package com.dfsek.terra.structure.features; import com.dfsek.terra.Debug; +import com.dfsek.terra.structure.Rotation; import com.dfsek.terra.structure.Structure; import com.dfsek.terra.structure.StructureInfo; +import net.jafama.FastMath; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; @@ -11,105 +13,83 @@ import org.bukkit.block.BlockFace; import org.bukkit.entity.EntityType; import org.polydev.gaea.math.MathUtil; import org.polydev.gaea.math.Range; +import org.polydev.gaea.util.FastRandom; +import org.polydev.gaea.util.GlueList; +import java.util.List; +import java.util.Objects; 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) { + public EntityFeature(EntityType type, Range amount, 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); + private static boolean isInChunk(Chunk c, Location l) { + return FastMath.floorDiv(l.getBlockX(), 16) == c.getX() && FastMath.floorDiv(l.getBlockZ(), 16) == c.getZ(); + } + private static List getLocations(Structure structure, Rotation r, Location origin, Random random, int number) { 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()); + Range x = structure.getRange(Rotation.Axis.X, r); + Range y = structure.getRange(Rotation.Axis.Y, r); + Range z = structure.getRange(Rotation.Axis.Z, r); int cx = info.getCenterX(); int cz = info.getCenterZ(); + List locations = new GlueList<>(); + for(int i = 0; i < number; i++) + locations.add(origin.clone().add(x.get(random) - cx, y.get(random), z.get(random) - cz)); + return locations; + } - 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); + @Override + public void apply(Structure structure, Rotation r, Location l, Chunk chunk) { + Random random = new FastRandom(MathUtil.getCarverChunkSeed(chunk.getX(), chunk.getZ(), chunk.getWorld().getSeed())); + for(Location attempt : getLocations(structure, r, l, random, amount.get(random))) { 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, 2, 0.5), type); // Add 0.5 to X & Z so entity spawns in center of block. - } + attemptSpawn(attempt, l); } } - private static boolean isInChunk(Chunk c, Location l) { - return Math.floorDiv(l.getBlockX(), 16) == c.getX() && Math.floorDiv(l.getBlockZ(), 16) == c.getZ(); + private void attemptSpawn(Location attempt, Location origin) { + boolean canSpawn = false; + while(attempt.getBlockY() >= origin.getBlockY()) { // Go down, see if valid spawns exist. + canSpawn = true; + Block on = attempt.getBlock(); + attempt.subtract(0, 1, 0); + + if(!stand.contains(on.getType())) { + canSpawn = false; + 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); + Objects.requireNonNull(attempt.getWorld()).spawnEntity(attempt.add(0.5, 2, 0.5), type); // Add 0.5 to X & Z so entity spawns in center of block. + } } @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 void apply(Structure structure, Rotation r, Location l, Random random) { + for(Location attempt : getLocations(structure, r, l, random, amount.get(random))) { + attemptSpawn(attempt, l); } } } diff --git a/src/main/java/com/dfsek/terra/structure/features/Feature.java b/src/main/java/com/dfsek/terra/structure/features/Feature.java index 5590443dc..8046dac67 100644 --- a/src/main/java/com/dfsek/terra/structure/features/Feature.java +++ b/src/main/java/com/dfsek/terra/structure/features/Feature.java @@ -1,5 +1,6 @@ package com.dfsek.terra.structure.features; +import com.dfsek.terra.structure.Rotation; import com.dfsek.terra.structure.Structure; import org.bukkit.Chunk; import org.bukkit.Location; @@ -7,7 +8,7 @@ import org.bukkit.Location; import java.util.Random; public interface Feature { - void apply(Structure structure, Location l, Chunk chunk); + void apply(Structure structure, Rotation r, Location l, Chunk chunk); - void apply(Structure structure, Location l, Random random); + void apply(Structure structure, Rotation r, Location l, Random random); } diff --git a/src/main/java/com/dfsek/terra/structure/spawn/AirSpawn.java b/src/main/java/com/dfsek/terra/structure/spawn/AirSpawn.java index a90b29a9d..126b121bc 100644 --- a/src/main/java/com/dfsek/terra/structure/spawn/AirSpawn.java +++ b/src/main/java/com/dfsek/terra/structure/spawn/AirSpawn.java @@ -2,8 +2,8 @@ package com.dfsek.terra.structure.spawn; import com.dfsek.terra.TerraWorld; import com.dfsek.terra.biome.UserDefinedBiome; -import com.dfsek.terra.config.base.ConfigPack; import com.dfsek.terra.config.genconfig.biome.BiomeConfig; +import com.dfsek.terra.generation.config.WorldGenerator; import org.bukkit.World; import org.polydev.gaea.generation.GenerationPhase; @@ -15,10 +15,10 @@ public class AirSpawn extends Requirement { @Override public boolean matches(int x, int y, int z) { TerraWorld tw = TerraWorld.getWorld(getWorld()); - ConfigPack wc = tw.getConfig(); UserDefinedBiome b = (UserDefinedBiome) tw.getGrid().getBiome(x, z, GenerationPhase.POPULATE); - BiomeConfig c = wc.getBiome(b); + BiomeConfig c = b.getConfig(); if(y <= c.getOcean().getSeaLevel()) return false; - return b.getGenerator().getNoise(getNoise(), getWorld(), x, y, z) <= 0; + int yf = (int) (y - ((WorldGenerator) b.getGenerator()).getElevation(x, z)); + return b.getGenerator().getNoise(getNoise(), getWorld(), x, yf, z) <= 0; } } diff --git a/src/main/java/com/dfsek/terra/structure/spawn/LandSpawn.java b/src/main/java/com/dfsek/terra/structure/spawn/LandSpawn.java index 361c17ab4..50f210215 100644 --- a/src/main/java/com/dfsek/terra/structure/spawn/LandSpawn.java +++ b/src/main/java/com/dfsek/terra/structure/spawn/LandSpawn.java @@ -2,6 +2,7 @@ package com.dfsek.terra.structure.spawn; import com.dfsek.terra.TerraWorld; import com.dfsek.terra.biome.UserDefinedBiome; +import com.dfsek.terra.generation.config.WorldGenerator; import org.bukkit.World; import org.polydev.gaea.generation.GenerationPhase; @@ -14,6 +15,7 @@ public class LandSpawn extends Requirement { public boolean matches(int x, int y, int z) { TerraWorld tw = TerraWorld.getWorld(getWorld()); UserDefinedBiome b = (UserDefinedBiome) tw.getGrid().getBiome(x, z, GenerationPhase.POPULATE); - return b.getGenerator().getNoise(getNoise(), getWorld(), x, y, z) > 0; + int yf = (int) (y - ((WorldGenerator) b.getGenerator()).getElevation(x, z)); + return b.getGenerator().getNoise(getNoise(), getWorld(), x, yf, z) > 0; } } diff --git a/src/main/java/com/dfsek/terra/structure/spawn/OceanSpawn.java b/src/main/java/com/dfsek/terra/structure/spawn/OceanSpawn.java index d417d3b5c..52a39b3b4 100644 --- a/src/main/java/com/dfsek/terra/structure/spawn/OceanSpawn.java +++ b/src/main/java/com/dfsek/terra/structure/spawn/OceanSpawn.java @@ -3,6 +3,7 @@ package com.dfsek.terra.structure.spawn; import com.dfsek.terra.TerraWorld; import com.dfsek.terra.biome.UserDefinedBiome; import com.dfsek.terra.config.genconfig.biome.BiomeConfig; +import com.dfsek.terra.generation.config.WorldGenerator; import org.bukkit.World; import org.polydev.gaea.generation.GenerationPhase; @@ -15,8 +16,9 @@ public class OceanSpawn extends Requirement { public boolean matches(int x, int y, int z) { TerraWorld tw = TerraWorld.getWorld(getWorld()); UserDefinedBiome b = (UserDefinedBiome) tw.getGrid().getBiome(x, z, GenerationPhase.POPULATE); - BiomeConfig c = tw.getConfig().getBiome(b); + BiomeConfig c = b.getConfig(); if(y > c.getOcean().getSeaLevel()) return false; - return b.getGenerator().getNoise(getNoise(), getWorld(), x, y, z) <= 0; + int yf = (int) (y - ((WorldGenerator) b.getGenerator()).getElevation(x, z)); + return b.getGenerator().getNoise(getNoise(), getWorld(), x, yf, z) <= 0; } } diff --git a/src/main/java/com/dfsek/terra/util/DataUtil.java b/src/main/java/com/dfsek/terra/util/DataUtil.java index 546acbad3..902d1accf 100644 --- a/src/main/java/com/dfsek/terra/util/DataUtil.java +++ b/src/main/java/com/dfsek/terra/util/DataUtil.java @@ -2,15 +2,14 @@ package com.dfsek.terra.util; import org.bukkit.Material; import org.bukkit.block.data.BlockData; +import org.polydev.gaea.util.FastRandom; import org.polydev.gaea.world.palette.Palette; import org.polydev.gaea.world.palette.RandomPalette; -import java.util.Random; - public final class DataUtil { public static final BlockData STONE = Material.STONE.createBlockData(); public static final BlockData SNOW = Material.SNOW.createBlockData(); public static final BlockData WATER = Material.WATER.createBlockData(); public static final BlockData AIR = Material.AIR.createBlockData(); - public static final Palette BLANK_PALETTE = new RandomPalette(new Random(2403)).add(AIR, 1); + public static final Palette BLANK_PALETTE = new RandomPalette(new FastRandom(2403)).add(AIR, 1); } diff --git a/src/main/java/com/dfsek/terra/util/PaperUtil.java b/src/main/java/com/dfsek/terra/util/PaperUtil.java index 55fafc07f..08f1fc2bc 100644 --- a/src/main/java/com/dfsek/terra/util/PaperUtil.java +++ b/src/main/java/com/dfsek/terra/util/PaperUtil.java @@ -1,17 +1,16 @@ package com.dfsek.terra.util; -import com.dfsek.terra.config.lang.LangUtil; import io.papermc.lib.PaperLib; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; -import java.util.logging.Level; +import static io.papermc.lib.PaperLib.suggestPaper; public final class PaperUtil { public static void checkPaper(JavaPlugin main) { Bukkit.getScheduler().scheduleSyncDelayedTask(main, () -> { if(!PaperLib.isPaper()) { - LangUtil.log("use-paper", Level.WARNING); + suggestPaper(main); } }, 100L); } diff --git a/src/main/java/com/dfsek/terra/util/PopulationUtil.java b/src/main/java/com/dfsek/terra/util/PopulationUtil.java new file mode 100644 index 000000000..eb0638175 --- /dev/null +++ b/src/main/java/com/dfsek/terra/util/PopulationUtil.java @@ -0,0 +1,11 @@ +package com.dfsek.terra.util; + +import org.bukkit.Chunk; +import org.polydev.gaea.math.MathUtil; +import org.polydev.gaea.util.FastRandom; + +public final class PopulationUtil { + public static FastRandom getRandom(Chunk c) { + return new FastRandom(MathUtil.getCarverChunkSeed(c.getX(), c.getZ(), c.getWorld().getSeed())); + } +} diff --git a/src/main/java/com/dfsek/terra/util/TagUtil.java b/src/main/java/com/dfsek/terra/util/TagUtil.java index 41298ec88..dc2334db1 100644 --- a/src/main/java/com/dfsek/terra/util/TagUtil.java +++ b/src/main/java/com/dfsek/terra/util/TagUtil.java @@ -15,7 +15,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; @SuppressWarnings("unchecked") -public class TagUtil { +public final class TagUtil { private static final Map> tagMap; static { diff --git a/src/main/java/com/dfsek/terra/util/hash/HashIntrinsic.java b/src/main/java/com/dfsek/terra/util/hash/HashIntrinsic.java new file mode 100644 index 000000000..74166a7a8 --- /dev/null +++ b/src/main/java/com/dfsek/terra/util/hash/HashIntrinsic.java @@ -0,0 +1,118 @@ +/* +Copyright 2009 Sandia Corporation. Under the terms of Contract +DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government +retains certain rights in this software. + +BSD Open Source License. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Sandia National Laboratories nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + */ + +package com.dfsek.terra.util.hash; + +import java.io.Serializable; + +public abstract class HashIntrinsic implements Serializable { + public static final int FLOAT_EXP_BIT_MASK = 2139095040; + public static final int FLOAT_SIGNIF_BIT_MASK = 8388607; + public static final long DOUBLE_EXP_BIT_MASK = 9218868437227405312L; + public static final long DOUBLE_SIGNIF_BIT_MASK = 4503599627370495L; + protected static final int DEFAULT_INITIAL_CAPACITY = 16; + protected static final int MAXIMUM_CAPACITY = 1073741824; + protected static final float DEFAULT_LOAD_FACTOR = 0.75F; + private static final long serialVersionUID = 8058099372006904458L; + protected int size; + protected int threshold; + protected float loadFactor; + protected int capMinus1; + + protected HashIntrinsic(int initialCapacity, float loadFactor) { + if(initialCapacity <= 0) { + throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); + } else if(!(loadFactor <= 0.0F) && !Float.isNaN(loadFactor)) { + if(initialCapacity > 1073741824) { + initialCapacity = 1073741824; + } + + int capacity; + for(capacity = 1; capacity < initialCapacity; capacity <<= 1) { + } + + this.capMinus1 = capacity - 1; + this.loadFactor = loadFactor; + this.threshold = (int) ((float) capacity * loadFactor); + } else { + throw new IllegalArgumentException("Illegal load factor: " + loadFactor); + } + } + + protected static int hashCodeLong(long value) { + return (int) (value ^ value >>> 32); + } + + protected static int hashCodeFloat(float value) { + return floatToIntBits(value); + } + + protected static int hashCodeDouble(double value) { + long bits = doubleToLongBits(value); + return (int) (bits ^ bits >>> 32); + } + + public static int floatToIntBits(float value) { + int result = Float.floatToRawIntBits(value); + if((result & 2139095040) == 2139095040 && (result & 8388607) != 0) { + result = 2143289344; + } + + return result; + } + + public static long doubleToLongBits(double value) { + long result = Double.doubleToRawLongBits(value); + if((result & 9218868437227405312L) == 9218868437227405312L && (result & 4503599627370495L) != 0L) { + result = 9221120237041090560L; + } + + return result; + } + + protected static int tableIndex(int hc, int lm1) { + hc ^= hc >>> 20 ^ hc >>> 12; + hc ^= hc >>> 7 ^ hc >>> 4; + return hc & lm1; + } + + public int size() { + return this.size; + } + + public boolean isEmpty() { + return this.size == 0; + } + + public abstract void clear(); +} diff --git a/src/main/java/com/dfsek/terra/util/hash/HashMapDoubleDouble.java b/src/main/java/com/dfsek/terra/util/hash/HashMapDoubleDouble.java new file mode 100644 index 000000000..7ea6f735a --- /dev/null +++ b/src/main/java/com/dfsek/terra/util/hash/HashMapDoubleDouble.java @@ -0,0 +1,291 @@ +/* +Copyright 2009 Sandia Corporation. Under the terms of Contract +DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government +retains certain rights in this software. + +BSD Open Source License. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Sandia National Laboratories nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + */ + +package com.dfsek.terra.util.hash; + +import java.io.Serializable; +import java.util.NoSuchElementException; + +public class HashMapDoubleDouble extends HashIntrinsic { + private static final long serialVersionUID = 2109458761298324234L; + private HashMapDoubleDouble.Entry[] table; + + public HashMapDoubleDouble(int initialCapacity, float loadFactor) { + super(initialCapacity, loadFactor); + this.table = this.createTable(this.capMinus1 + 1); + } + + public HashMapDoubleDouble(int initialCapacity) { + this(initialCapacity, 0.75F); + } + + public HashMapDoubleDouble() { + this(16, 0.75F); + } + + public final boolean contains(double key) { + int i = tableIndex(hashCodeDouble(key), this.capMinus1); + + for(HashMapDoubleDouble.Entry e = this.table[i]; e != null; e = e.next) { + if(e.key == key) { + return true; + } + } + + return false; + } + + public boolean containsValue(double value) { + for(int i = 0; i < this.table.length; ++i) { + for(HashMapDoubleDouble.Entry e = this.table[i]; e != null; e = e.next) { + if(value == e.value) { + return true; + } + } + } + + return false; + } + + public double get(double key) { + int i = tableIndex(hashCodeDouble(key), this.capMinus1); + + for(HashMapDoubleDouble.Entry e = this.table[i]; e != null; e = e.next) { + if(key == e.key) { + return e.value; + } + } + + return 4.9E-324D; + } + + public HashMapDoubleDouble.Entry getEntry(double key) { + int i = tableIndex(hashCodeDouble(key), this.capMinus1); + + for(HashMapDoubleDouble.Entry e = this.table[i]; e != null; e = e.next) { + if(key == e.key) { + return e; + } + } + + return null; + } + + public double put(double key, double value) { + int i = tableIndex(hashCodeDouble(key), this.capMinus1); + + for(HashMapDoubleDouble.Entry e = this.table[i]; e != null; e = e.next) { + if(key == e.key) { + double oldValue = e.value; + e.value = value; + return oldValue; + } + } + + this.addEntry(key, value, i); + return 4.9E-324D; + } + + private void addEntry(double key, double value, int index) { + HashMapDoubleDouble.Entry e = this.table[index]; + this.table[index] = new HashMapDoubleDouble.Entry(key, value, e); + if(this.size++ >= this.threshold) { + this.resize(2 * this.table.length); + } + + } + + public void resize(int newCapacity) { + int oldCapacity = this.table.length; + if(oldCapacity == 1073741824) { + this.threshold = 2147483647; + } else { + HashMapDoubleDouble.Entry[] newTable = this.createTable(newCapacity); + this.capMinus1 = newCapacity - 1; + this.transfer(newTable); + this.table = newTable; + this.threshold = (int) ((float) newCapacity * this.loadFactor); + } + } + + private void transfer(HashMapDoubleDouble.Entry[] newTable) { + for(int j = 0; j < this.table.length; ++j) { + HashMapDoubleDouble.Entry e = this.table[j]; + if(e != null) { + this.table[j] = null; + + HashMapDoubleDouble.Entry next; + do { + next = e.next; + int i = tableIndex(hashCodeDouble(e.key), this.capMinus1); + e.next = newTable[i]; + newTable[i] = e; + e = next; + } while(next != null); + } + } + + } + + public final HashMapDoubleDouble.Entry remove(double key) { + int i = tableIndex(hashCodeDouble(key), this.capMinus1); + HashMapDoubleDouble.Entry prev = this.table[i]; + + HashMapDoubleDouble.Entry e; + HashMapDoubleDouble.Entry next; + for(e = prev; e != null; e = next) { + next = e.next; + if(key == e.key) { + --this.size; + if(prev == e) { + this.table[i] = next; + } else { + prev.next = next; + } + + return e; + } + + prev = e; + } + + return e; + } + + public void clear() { + for(int i = 0; i < this.table.length; ++i) { + this.table[i] = null; + } + + this.size = 0; + } + + private HashMapDoubleDouble.Entry[] createTable(int capacity) { + return new HashMapDoubleDouble.Entry[capacity]; + } + + public long memoryEstimate(int ptrsize) { + return (long) ptrsize * (long) (this.capMinus1 + this.size + 1) + (long) (this.size * 64 / 4); + } + + public HashMapDoubleDouble.Iterator iterator() { + return new HashMapDoubleDouble.Iterator(); + } + + public static class Entry implements Serializable { + private static final long serialVersionUID = 7972173983741231238L; + private final double key; + private double value; + private HashMapDoubleDouble.Entry next; + + public Entry(double key, double val, HashMapDoubleDouble.Entry n) { + this.key = key; + this.value = val; + this.next = n; + } + + public final double getKey() { + return this.key; + } + + public final double getValue() { + return this.value; + } + + public final double setValue(double newValue) { + double oldValue = this.value; + this.value = newValue; + return oldValue; + } + + public final boolean equals(Object o) { + HashMapDoubleDouble.Entry e = (HashMapDoubleDouble.Entry) o; + return this.key == e.key && this.value == e.value; + } + + public final String toString() { + return this.key + " = " + this.value; + } + + public final int hashCode() { + return hashCodeDouble(key) + hashCodeDouble(value); + } + } + + public class Iterator { + HashMapDoubleDouble.Entry next; + int index; + HashMapDoubleDouble.Entry current; + + Iterator() { + if(HashMapDoubleDouble.this.size > 0) { + while(this.index < HashMapDoubleDouble.this.table.length && (this.next = HashMapDoubleDouble.this.table[this.index++]) == null) { + } + } + + } + + public final boolean hasNext() { + return this.next != null; + } + + public HashMapDoubleDouble.Entry nextEntry() { + HashMapDoubleDouble.Entry e = this.next; + if(e == null) { + throw new NoSuchElementException(); + } else { + if((this.next = e.next) == null) { + while(this.index < HashMapDoubleDouble.this.table.length && (this.next = HashMapDoubleDouble.this.table[this.index++]) == null) { + } + } + + this.current = e; + return e; + } + } + + public double next() { + return this.nextEntry().value; + } + + public void remove() { + if(this.current == null) { + throw new IllegalStateException(); + } else { + double k = this.current.key; + this.current = null; + HashMapDoubleDouble.this.remove(k); + } + } + } +} diff --git a/src/main/java/com/dfsek/terra/util/structure/RotationUtil.java b/src/main/java/com/dfsek/terra/util/structure/RotationUtil.java index 9a97c0aa0..8e75a0fed 100644 --- a/src/main/java/com/dfsek/terra/util/structure/RotationUtil.java +++ b/src/main/java/com/dfsek/terra/util/structure/RotationUtil.java @@ -2,6 +2,7 @@ package com.dfsek.terra.util.structure; import com.dfsek.terra.procgen.math.Vector2; import com.dfsek.terra.structure.Rotation; +import net.jafama.FastMath; import org.bukkit.block.BlockFace; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Directional; @@ -11,7 +12,7 @@ import org.bukkit.block.data.Rail; import org.bukkit.block.data.Rotatable; import org.bukkit.block.data.type.RedstoneWire; -import java.util.HashMap; +import java.util.EnumMap; import java.util.Map; public final class RotationUtil { @@ -107,7 +108,7 @@ public final class RotationUtil { * @return BlockFace represented by integer. */ public static BlockFace fromRotation(int r) { - switch(Math.floorMod(r, 16)) { + switch(FastMath.floorMod(r, 16)) { case 0: return BlockFace.NORTH; case 1: @@ -251,7 +252,7 @@ public final class RotationUtil { ((Directional) data).setFacing(rt); } else if(data instanceof MultipleFacing) { MultipleFacing mfData = (MultipleFacing) data; - Map faces = new HashMap<>(); + Map faces = new EnumMap<>(BlockFace.class); for(BlockFace f : mfData.getAllowedFaces()) { faces.put(f, mfData.hasFace(f)); } @@ -265,7 +266,7 @@ public final class RotationUtil { org.bukkit.Axis newAxis = getRotatedAxis(((Orientable) data).getAxis(), r); ((Orientable) data).setAxis(newAxis); } else if(data instanceof RedstoneWire) { - Map connections = new HashMap<>(); + Map connections = new EnumMap<>(BlockFace.class); RedstoneWire rData = (RedstoneWire) data; for(BlockFace f : rData.getAllowedFaces()) { connections.put(f, rData.getFace(f)); diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index d87580b25..b13793a3f 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -3,6 +3,7 @@ data-save: PT6M language: "en_us" fail-type: SHUTDOWN dump-default: true -cache-size: 8 +biome-search-resolution: 4 +cache-size: 384 master-disable: caves: false \ No newline at end of file diff --git a/src/test/java/DistributionTest.java b/src/test/java/DistributionTest.java index 0c25823e8..27f2d556b 100644 --- a/src/test/java/DistributionTest.java +++ b/src/test/java/DistributionTest.java @@ -1,3 +1,4 @@ +import net.jafama.FastMath; import org.junit.jupiter.api.Test; import org.polydev.gaea.math.FastNoiseLite; @@ -26,8 +27,8 @@ class DistributionTest { long l = System.nanoTime(); for(int i = 0; i < 1000000; i++) { double n = noise.getNoise(0, i); - max = Math.max(max, n); - min = Math.min(min, n); + max = FastMath.max(max, n); + min = FastMath.min(min, n); numbers[normalize(n, attempts)]++; } long l2 = System.nanoTime() - l; @@ -36,8 +37,8 @@ class DistributionTest { l = System.nanoTime(); for(int i = 0; i < 1000000; i++) { double n = noise.getNoise(0, i); - max = Math.max(max, n); - min = Math.min(min, n); + max = FastMath.max(max, n); + min = FastMath.min(min, n); } l2 = System.nanoTime() - l; System.out.println("Took " + (double) l2 / 1000000 + "ms (" + ((double) l2 / 1000000) + "ns per."); @@ -61,22 +62,22 @@ class DistributionTest { int end = normalMap.length - 1; while(start + 1 < end) { int mid = start + (end - start) / 2; - if (normalMap[mid] <= d) { + if(normalMap[mid] <= d) { start = mid; } else { end = mid; } } - double left = Math.abs(normalMap[start] - d); - double right = Math.abs(normalMap[end] - d); - if (left <= right) { + double left = FastMath.abs(normalMap[start] - d); + double right = FastMath.abs(normalMap[end] - d); + if(left <= right) { return start * (num) / (normalMap.length); } return end * (num) / (normalMap.length); } public static int normal(double d, int max) { - double ranged = Math.max(0, Math.min((d + 1) / 2D, 1)); + double ranged = FastMath.max(0, FastMath.min((d + 1) / 2D, 1)); return (int) (ranged * max); } } diff --git a/src/test/java/LookupGeneratorTest.java b/src/test/java/LookupGeneratorTest.java index 1422d2e8b..eea731910 100644 --- a/src/test/java/LookupGeneratorTest.java +++ b/src/test/java/LookupGeneratorTest.java @@ -1,5 +1,7 @@ +import net.jafama.FastMath; import org.junit.jupiter.api.Test; import org.polydev.gaea.math.FastNoiseLite; +import org.polydev.gaea.util.GlueList; import java.util.ArrayList; import java.util.Collections; @@ -12,7 +14,7 @@ class LookupGenerator { static void main(String[] args) throws InterruptedException { int dist = 4096; - List vals = new ArrayList<>(); + List vals = new GlueList<>(); FastNoiseLite noise = new FastNoiseLite(); noise.setNoiseType(FastNoiseLite.NoiseType.OpenSimplex2); noise.setFrequency(0.02f); @@ -25,10 +27,10 @@ class LookupGenerator { int workerAmount = 16; - List workers = new ArrayList<>(); + List workers = new GlueList<>(); for(int i = 0; i < workerAmount; i++) { - workers.add(new Worker(new ArrayList<>(), 5000000, noise)); + workers.add(new Worker(new GlueList<>(), 5000000, noise)); } for(Worker w : workers) { @@ -98,6 +100,12 @@ class LookupGenerator { return lookup.length - 1; } + public static int normalize(double i, int n) { + i *= 1.42; // Magic simplex value (sqrt(2) plus a little) + i = FastMath.min(FastMath.max(i, -1), 1); + return FastMath.min((int) FastMath.floor((i + 1) * ((double) n / 2)), n - 1); + } + private static class Worker extends Thread { private final List l; private final int searches; @@ -120,5 +128,9 @@ class LookupGenerator { public List getResult() { return l; } + + public String getStatus() { + return "Generating values. " + l.size() + "/" + searches + " (" + ((long) l.size() * 100L) / searches + "%)"; + } } } diff --git a/src/test/java/NoiseTest.java b/src/test/java/NoiseTest.java new file mode 100644 index 000000000..346b4e5a0 --- /dev/null +++ b/src/test/java/NoiseTest.java @@ -0,0 +1,52 @@ +import com.dfsek.terra.generation.config.NoiseBuilder; +import com.dfsek.terra.math.NoiseFunction2; +import org.junit.jupiter.api.Test; +import parsii.eval.Expression; + +import java.util.Arrays; + +public class NoiseTest { + @Test + public void noise() { + NoiseFunction2 noiseFunction = new NoiseFunction2(12345, new NoiseBuilder()); + System.out.println("Cache:"); + int a = 0; + for(int i = 0; i < 200; i++) { + long l = System.nanoTime(); + for(int j = 0; j < 1000; j++) { + for(int x = 0; x < 4; x++) { + for(int y = 0; y < 64; y++) { + for(int z = 0; z < 4; z++) { + noiseFunction.eval(Arrays.asList(get(j * 16 + (x * 4)), get(i * 16 + (z * 4)))); + } + } + } + } + double n = System.nanoTime() - l; + System.out.print((long) n / 1000000 + "ms" + ((a % 10 == 0) ? "\n" : " ")); + a++; + } + System.out.println(); + System.out.println(); + System.out.println("No Cache:"); + for(int i = 0; i < 200; i++) { + long l = System.nanoTime(); + for(int j = 0; j < 1000; j++) { + for(int x = 0; x < 4; x++) { + for(int y = 0; y < 64; y++) { + for(int z = 0; z < 4; z++) { + noiseFunction.evalNoCache(Arrays.asList(get(j * 16 + (x * 4)), get(i * 16 + (z * 4)))); + } + } + } + } + double n = System.nanoTime() - l; + System.out.print((long) n / 1000000 + "ms" + ((a % 10 == 0) ? "\n" : " ")); + a++; + } + } + + private Expression get(double val) { + return () -> val; + } +}