Compare commits

..

87 Commits

Author SHA1 Message Date
Zoë Gidiere b10fd84e00 Merge remote-tracking branch 'origin/ver/6.5.0' into dev/metapacks 2024-01-05 09:11:42 -07:00
Astrash 271e7f3c69 Fix empty slant holders attempting to calculate slant
This occurs when the NoiseChunkGenerator3D#getSlant method is invoked at
a position where the biome has an empty slant holder, currently possible
when using the slant locator addon.

This is fixed by making slant calculation independent of SlantHolders.
Some minor refactoring has also been done as a result of this change.
2024-01-05 18:42:35 +11:00
Zoë 3e04bae828 Merge branch 'ver/6.5.0' into dev/metapacks 2023-12-24 22:39:15 -06:00
Zoë 6681bdfbf2 Merge remote-tracking branch 'origin/master' into ver/6.5.0 2023-12-24 22:39:06 -06:00
dfsek ab60f14ff1 Merge pull request #444 from PolyhedralDev/dev/fix-lerp
6.4.3 - Fix lerp (urgent)
2023-12-24 02:15:54 -07:00
dfsek d8ba9e1016 bump version 2023-12-24 02:06:11 -07:00
dfsek 3622003a39 fix other lerp issues 2023-12-24 02:02:46 -07:00
dfsek 84cd96ecf7 fix BrownianMotionSampler lerp usage 2023-12-24 01:53:05 -07:00
Zoë 82334cfe9e Merge branch 'ver/6.5.0' into dev/metapacks 2023-12-22 15:03:04 -06:00
Zoë 8afda7424c Merge branch 'master' into ver/6.5.0 2023-12-22 15:02:53 -06:00
Zoë Gidiere 50ba1c6eab push changes 2023-12-21 08:47:52 -07:00
Zoë da4ab8b71c Merge pull request #441 from PolyhedralDev/ver/6.4.2
Ver/6.4.2
2023-12-18 13:01:36 -07:00
Zoë Gidiere 3745c3e947 fix typos 2023-12-17 15:32:35 -07:00
Zoë Gidiere fd20837b55 fix error on startup 2023-12-17 15:29:18 -07:00
Zoë Gidiere 27a967f3a6 fix build 2023-12-12 20:17:15 -07:00
Zoë Gidiere 4c71355535 Reformat 2023-12-12 19:07:57 -07:00
Zoë Gidiere e83b70b5ae generation settings 2023-12-12 18:52:31 -07:00
Zoë Gidiere 56b428d501 refactor 2023-12-12 17:19:32 -07:00
Zoë Gidiere 7ca24faad3 fix loading 2023-12-12 17:15:54 -07:00
Zoë Gidiere 9d200565d7 more fixes 2023-12-12 16:54:27 -07:00
Zoë Gidiere f6c2795eaf remove debug loging 2023-12-12 16:12:36 -07:00
Zoë Gidiere 6f03746e41 another fix 2023-12-12 16:09:08 -07:00
Zoë Gidiere 47c8cb3168 Fix up random changes 2023-12-12 16:00:56 -07:00
Zoë Gidiere a9f973cae9 WIP Random Changes 2023-12-12 15:57:04 -07:00
Zoë Gidiere 033181d7c8 more wip changes 2023-12-12 15:48:31 -07:00
Zoë Gidiere e11a235386 WIP Dim opts 2023-12-12 13:55:03 -07:00
Zoë Gidiere db1e924246 more metapack work 2023-12-12 13:35:52 -07:00
Zoë Gidiere c86faa44ec Remove netherfossiloptimization because it's no longer applicable
also it's not needed mc seems to have done this themselves
2023-12-12 13:35:24 -07:00
Zoë Gidiere de91a6facb metapack linking instead of shading 2023-12-11 23:36:05 -07:00
Zoë Gidiere d4a328eb38 who let the datadrive (the dimensions) 2023-12-11 21:14:43 -07:00
Zoë Gidiere b039629b2d WIP meta pack system 2023-12-11 16:25:13 -07:00
Zoë Gidiere d48fa96ec7 opt import 2023-12-11 14:56:07 -07:00
Zoë Gidiere 8dd1f49b88 Merge remote-tracking branch 'origin/dev/remove-loader' into dev/metapacks 2023-12-11 14:54:59 -07:00
Zoë Gidiere 81528915a8 Merge branch 'ver/6.5.0' into dev/metapacks 2023-12-11 14:54:44 -07:00
Zoë Gidiere 5979254808 wip 2023-12-11 14:54:27 -07:00
Zoë Gidiere 0091e5b785 spell check 2023-12-10 18:50:40 -07:00
Zoë Gidiere 41045ae8aa literally forgor the most important part 2023-12-10 18:47:12 -07:00
Zoë Gidiere 4a83f01c1f fix incase mods are used 2023-12-10 18:44:26 -07:00
Zoë Gidiere fc764a0fb3 fix issues with spawners and entity parsing 2023-12-10 18:40:43 -07:00
Zoë Gidiere 22c46f2f80 Pin OWConfig Version 2023-12-10 17:45:52 -07:00
Zoë Gidiere f408faaa80 reformat 2023-12-10 17:07:18 -07:00
Zoë Gidiere 0ad7ee4f9f fix CME 2023-12-10 16:57:27 -07:00
Zoë Gidiere 1e55074cfa Enforce NMS bindings 2023-12-10 15:46:00 -07:00
Zoë 2a92d76276 Merge pull request #437 from PolyhedralDev/dev/1.20.3
Dev/1.20.3
2023-12-09 15:13:11 -07:00
Zoë Gidiere 687ad5db59 Final dep update
confirmed working on paper and fabric
2023-12-09 15:00:46 -07:00
Zoë Gidiere f9c9789016 remove unused maven repo 2023-12-09 01:16:38 -07:00
Zoë Gidiere 3142c2a4f0 update paper to 1.20.4 2023-12-08 22:57:01 -07:00
Zoë Gidiere b5532b7679 Merge branch 'ver/6.4.2' into dev/1.20.3 2023-12-08 22:31:12 -07:00
Zoë Gidiere 8af299d09c fix path of vale ignore 2023-12-08 22:31:04 -07:00
Zoë Gidiere 5cf8a5061d Merge branch 'ver/6.4.2' into dev/1.20.3 2023-12-08 22:11:57 -07:00
Zoë Gidiere 017876475e Add Vale config to gitignore 2023-12-08 22:11:48 -07:00
Zoë Gidiere d88e4f1f9b fix stupid error
thank you caffeine mc peeps
2023-12-08 19:57:05 -07:00
Zoë Gidiere 17eac505a0 Merge branch 'ver/6.4.2' into dev/1.20.3 2023-12-07 11:29:31 -07:00
Zoë Gidiere 8043814138 pull up version range of fabric 2023-12-07 11:29:19 -07:00
Zoë Gidiere 554887ab54 build fabric against 1.20.4 2023-12-07 11:27:06 -07:00
Zoë Gidiere 2f46a01adc Merge remote-tracking branch 'origin/master' into ver/6.4.2 2023-12-07 00:34:00 -07:00
Zoë Gidiere a1db0574e9 Merge branch 'ver/6.4.2' into dev/1.20.3 2023-12-07 00:33:45 -07:00
Zoë fd6decc706 Merge pull request #439 from PolyhedralDev/dev/github-actions
Create gradle-build.yml
2023-12-06 23:11:33 -07:00
Zoë Gidiere 0ba0d472e6 seems arch loom auto uses vineflower now 2023-12-06 22:47:35 -07:00
Zoë Gidiere 11b03eb93d actually bump bukkit version 2023-12-06 22:27:26 -07:00
Zoë Gidiere 46ca45cb1d fix bukkit build 2023-12-06 22:25:53 -07:00
Zoë Gidiere 4e5b066b91 add bukkit grass workaround 2023-12-06 22:24:55 -07:00
Zoë Gidiere db8df9741f bukkit 1.20.3 2023-12-06 22:22:18 -07:00
Zoë Gidiere 80c52870f5 add additional spigot warning 2023-12-06 12:00:03 -07:00
Zoë Gidiere 5048bc8ede Remove folialib 2023-12-06 10:52:31 -07:00
Zoë Gidiere fc0d7374e4 remove old bukkit versions 2023-12-06 10:14:43 -07:00
Zoë Gidiere e4c3affb9f pull up version range 2023-12-05 20:11:00 -07:00
Zoë Gidiere e160cae3d8 reformat 2023-12-05 20:04:08 -07:00
Zoë Gidiere cb9aa4f167 fix permissions 2023-12-05 19:53:05 -07:00
Zoë Gidiere b612d1c7aa fix name 2023-12-05 19:52:50 -07:00
Zoë Gidiere e4df8dce1d proper gradle cache 2023-12-05 19:52:12 -07:00
Zoë Gidiere 601d174720 pin versions and remove dead lines 2023-12-05 19:44:00 -07:00
Zoë 3339d08e11 Create gradle-build.yml 2023-12-05 19:35:23 -07:00
Zoë Gidiere bc65015af0 update gradle 2023-12-05 18:53:19 -07:00
Zoë Gidiere 913637b0a5 update deps 2023-12-05 18:29:43 -07:00
Zoë Gidiere 86f2356cc8 Merge branch 'ver/6.4.2' into ver/6.5.0 2023-11-27 17:55:40 -07:00
Zoë 55d09818f7 Merge pull request #436 from PolyhedralDev/dev/flatten-biome-array
Dev/flatten biome array
2023-11-27 17:54:38 -07:00
Astrash c0aaf6c6e8 Add messages to exceptions 2023-11-28 10:36:30 +11:00
Zoë Gidiere aecdcd578c initial 1.20.3
also disable forge. we're not publishing it anyway
2023-11-27 13:35:48 -07:00
Zoë Gidiere 382069b094 Bump Version to 6.4.2 2023-11-27 11:35:06 -07:00
Astrash 1ab3233cba Reformat code 2023-11-25 15:14:16 +11:00
Astrash 59ea5a69d8 Refactor pack loading
- Combine initial load and reload logic together between each platform implementation
- Should prevent pack load errors from blocking other packs from loading.
2023-11-25 15:10:43 +11:00
Astrash 4ba71e9c27 packDirectory -> rootPath 2023-11-25 15:07:45 +11:00
Astrash 5c7441241c Replace Loader with java.nio.files 2023-11-25 13:31:42 +11:00
Astrash ffb1198da2 Merge branch 'master' into ver/6.5.0 2023-11-25 12:33:51 +11:00
Astrash 2c211f0aa6 Merge branch 'master' into ver/6.5.0 2023-11-25 12:29:54 +11:00
Zoë Gidiere 866d527d35 bump version 2023-11-17 13:11:28 -07:00
187 changed files with 1750 additions and 4116 deletions
+47
View File
@@ -0,0 +1,47 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created
# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle
name: Gradle Build
on: [ pull_request ]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- name: Set up JDK 17
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93
with:
java-version: '17'
distribution: 'temurin'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
- uses: burrunan/gradle-cache-action@03c71a8ba93d670980695505f48f49daf43704a6
name: Build Terra
with:
# Specifies arguments for Gradle execution
# If arguments is missing or empty, then Gradle is not executed
arguments: build
# arguments can be multi-line for better readability
# arguments: |
# --no-paralell
# build
# -x test
# Gradle version to use for execution:
# wrapper (default), current, rc, nightly, release-nightly, or
# versions like 6.6 (see https://services.gradle.org/versions/all)
gradle-version: wrapper
# Properties are passed as -Pname=value
properties: |
kotlin.js.compiler=ir
kotlin.parallel.tasks.in.project=true
+2
View File
@@ -249,3 +249,5 @@ nbdist/
platforms/**/run/**
#Vale Config File
**/.vale.ini
+3 -3
View File
@@ -1,8 +1,8 @@
preRelease(true)
versionProjects(":common:api", version("6.4.1"))
versionProjects(":common:implementation", version("6.4.1"))
versionProjects(":platforms", version("6.4.1"))
versionProjects(":common:api", version("6.5.0"))
versionProjects(":common:implementation", version("6.5.0"))
versionProjects(":platforms", version("6.5.0"))
allprojects {
+4 -4
View File
@@ -17,10 +17,10 @@ repositories {
dependencies {
//TODO Allow pulling from Versions.kt
implementation("com.github.johnrengelman", "shadow", "8.1.1")
implementation("io.papermc.paperweight.userdev", "io.papermc.paperweight.userdev.gradle.plugin", "1.5.6")
implementation("io.papermc.paperweight.userdev", "io.papermc.paperweight.userdev.gradle.plugin", "1.5.11")
implementation("org.ow2.asm", "asm", "9.5")
implementation("org.ow2.asm", "asm-tree", "9.5")
implementation("com.dfsek.tectonic", "common", "4.2.0")
implementation("org.ow2.asm", "asm", "9.6")
implementation("org.ow2.asm", "asm-tree", "9.6")
implementation("com.dfsek.tectonic", "common", "4.2.1")
implementation("org.yaml", "snakeyaml", "2.2")
}
+5 -8
View File
@@ -48,17 +48,14 @@ fun Project.configureDependencies() {
maven("https://jitpack.io") {
name = "JitPack"
}
maven("https://nexuslite.gcnt.net/repos/other/") {
name = "GCNT"
}
}
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.0")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.7.0")
compileOnly("org.jetbrains:annotations:23.0.0")
testImplementation("org.junit.jupiter", "junit-jupiter-api", Versions.Libraries.Internal.junit)
testImplementation("org.junit.jupiter", "junit-jupiter-engine", Versions.Libraries.Internal.junit)
compileOnly("org.jetbrains", "annotations", Versions.Libraries.Internal.jetBrainsAnnotations)
compileOnly("com.google.guava:guava:30.0-jre")
testImplementation("com.google.guava:guava:30.0-jre")
compileOnly("com.google.guava", "guava", Versions.Libraries.Internal.guava)
testImplementation("com.google.guava", "guava", Versions.Libraries.Internal.guava)
}
}
@@ -27,7 +27,8 @@ fun Project.configureDistribution() {
group = "terra"
doFirst {
file("${buildDir}/resources/main/packs/").deleteRecursively()
val defaultPackUrl = URL("https://github.com/PolyhedralDev/TerraOverworldConfig/releases/download/latest/default.zip")
val defaultPackUrl =
URL("https://github.com/PolyhedralDev/TerraOverworldConfig/releases/download/" + Versions.Terra.overworldConfig + "/default.zip")
downloadPack(defaultPackUrl, project)
}
}
+31 -25
View File
@@ -1,4 +1,8 @@
object Versions {
object Terra {
const val overworldConfig = "v1.3.4"
}
object Libraries {
const val tectonic = "4.2.1"
const val paralithic = "0.7.1"
@@ -6,21 +10,25 @@ object Versions {
const val cloud = "1.8.4"
const val caffeine = "3.1.8"
const val slf4j = "2.0.9"
const val log4j_slf4j_impl = "2.20.0"
object Internal {
const val shadow = "8.1.1"
const val apacheText = "1.10.0"
const val apacheIO = "2.14.0"
const val apacheText = "1.11.0"
const val apacheIO = "2.15.1"
const val guava = "32.1.3-jre"
const val asm = "9.5"
const val asm = "9.6"
const val snakeYml = "2.2"
const val jetBrainsAnnotations = "24.1.0"
const val junit = "5.10.1"
}
}
object Fabric {
const val fabricAPI = "0.90.0+${Mod.minecraft}"
const val fabricAPI = "0.91.2+${Mod.minecraft}"
}
//
// object Quilt {
@@ -31,30 +39,28 @@ object Versions {
object Mod {
const val mixin = "0.12.5+mixin.0.8.5"
const val minecraft = "1.20.2"
const val yarn = "$minecraft+build.4"
const val fabricLoader = "0.14.23"
const val minecraft = "1.20.4"
const val yarn = "$minecraft+build.1"
const val fabricLoader = "0.15.1"
const val architecuryLoom = "1.3.357"
const val architecturyPlugin = "3.4.146"
const val loomVineflower = "1.11.0"
}
object Forge {
const val forge = "${Mod.minecraft}-48.0.13"
const val burningwave = "12.63.0"
const val architecuryLoom = "1.4.369"
const val architecturyPlugin = "3.4.151"
}
//
// object Forge {
// const val forge = "${Mod.minecraft}-48.0.13"
// const val burningwave = "12.63.0"
// }
object Bukkit {
const val paper = "1.18.2-R0.1-SNAPSHOT"
const val paperLib = "1.0.5"
const val foliaLib = "0.2.5"
const val minecraft = "1.20.2"
const val reflectionRemapper = "0.1.0-SNAPSHOT"
const val paperDevBundle = "1.20.2-R0.1-SNAPSHOT"
const val runPaper = "2.2.0"
const val paperWeight = "1.5.6"
const val minecraft = "1.20.4"
const val paperBuild = "$minecraft-R0.1-20231209.173338-2"
const val paper = paperBuild
const val paperLib = "1.0.8"
const val reflectionRemapper = "0.1.0"
const val paperDevBundle = paperBuild
const val runPaper = "2.2.2"
const val paperWeight = "1.5.11"
}
//
@@ -66,6 +72,6 @@ object Versions {
//
object CLI {
const val nbt = "6.1"
const val logback = "1.4.11"
const val logback = "1.4.14"
}
}
@@ -1,9 +1,9 @@
package com.dfsek.terra.addons.biome.pipeline.v2.api.biome;
import java.util.Set;
import com.dfsek.terra.api.world.biome.Biome;
import java.util.Set;
public final class DelegatedPipelineBiome implements PipelineBiome {
private final Biome biome;
@@ -7,17 +7,17 @@
package com.dfsek.terra.addons.biome.pipeline.v2.stage.mutators;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.v2.pipeline.BiomeChunkImpl;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
public class ReplaceListStage implements Stage {
private final Map<PipelineBiome, ProbabilityCollection<PipelineBiome>> replace;
@@ -13,6 +13,7 @@ import com.dfsek.terra.addons.chunkgenerator.config.noise.BiomeNoiseProperties;
import com.dfsek.terra.addons.chunkgenerator.config.palette.BiomePaletteTemplate;
import com.dfsek.terra.addons.chunkgenerator.config.palette.slant.SlantLayerTemplate;
import com.dfsek.terra.addons.chunkgenerator.generation.NoiseChunkGenerator3D;
import com.dfsek.terra.addons.chunkgenerator.generation.math.SlantCalculationMethod;
import com.dfsek.terra.addons.chunkgenerator.palette.BiomePaletteInfo;
import com.dfsek.terra.addons.chunkgenerator.palette.slant.SlantHolder;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
@@ -45,8 +46,8 @@ public class NoiseChunkGenerator3DAddon implements AddonInitializer {
.priority(1000)
.then(event -> {
event.getPack().applyLoader(SlantHolder.CalculationMethod.class,
(type, o, loader, depthTracker) -> SlantHolder.CalculationMethod.valueOf((String) o));
event.getPack().applyLoader(SlantCalculationMethod.class,
(type, o, loader, depthTracker) -> SlantCalculationMethod.valueOf((String) o));
NoiseChunkGeneratorPackConfigTemplate config = event.loadTemplate(new NoiseChunkGeneratorPackConfigTemplate());
event.getPack().getContext().put(config);
@@ -57,7 +58,7 @@ public class NoiseChunkGenerator3DAddon implements AddonInitializer {
pack -> new NoiseChunkGenerator3D(pack, platform, config.getElevationBlend(),
config.getHorizontalRes(),
config.getVerticalRes(), noisePropertiesPropertyKey,
paletteInfoPropertyKey));
paletteInfoPropertyKey, config.getSlantCalculationMethod()));
event.getPack()
.applyLoader(SlantHolder.Layer.class, SlantLayerTemplate::new);
})
@@ -4,7 +4,7 @@ import com.dfsek.tectonic.api.config.template.ConfigTemplate;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.chunkgenerator.palette.slant.SlantHolder;
import com.dfsek.terra.addons.chunkgenerator.generation.math.SlantCalculationMethod;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.properties.Properties;
@@ -24,7 +24,7 @@ public class NoiseChunkGeneratorPackConfigTemplate implements ConfigTemplate, Pr
@Value("slant.calculation-method")
@Default
private SlantHolder.@Meta CalculationMethod slantCalculationMethod = SlantHolder.CalculationMethod.Derivative;
private @Meta SlantCalculationMethod slantCalculationMethod = SlantCalculationMethod.Derivative;
public int getElevationBlend() {
return elevationBlend;
@@ -38,7 +38,7 @@ public class NoiseChunkGeneratorPackConfigTemplate implements ConfigTemplate, Pr
return verticalRes;
}
public SlantHolder.CalculationMethod getSlantCalculationMethod() {
public SlantCalculationMethod getSlantCalculationMethod() {
return slantCalculationMethod;
}
}
@@ -16,6 +16,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.dfsek.terra.addons.chunkgenerator.generation.math.SlantCalculationMethod;
import com.dfsek.terra.addons.chunkgenerator.palette.BiomePaletteInfo;
import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolder;
import com.dfsek.terra.addons.chunkgenerator.palette.slant.SlantHolder;
@@ -27,7 +28,7 @@ import com.dfsek.terra.api.world.chunk.generation.util.Palette;
public class BiomePaletteTemplate implements ObjectTemplate<BiomePaletteInfo> {
private final Platform platform;
private final SlantHolder.CalculationMethod slantCalculationMethod;
private final SlantCalculationMethod slantCalculationMethod;
@Value("slant")
@Default
@Description("The slant palettes to use in this biome.")
@@ -56,7 +57,7 @@ public class BiomePaletteTemplate implements ObjectTemplate<BiomePaletteInfo> {
@Default
private @Meta boolean updatePalette = false;
public BiomePaletteTemplate(Platform platform, SlantHolder.CalculationMethod slantCalculationMethod) {
public BiomePaletteTemplate(Platform platform, SlantCalculationMethod slantCalculationMethod) {
this.platform = platform;
this.slantCalculationMethod = slantCalculationMethod;
}
@@ -8,10 +8,13 @@
package com.dfsek.terra.addons.chunkgenerator.generation;
import com.dfsek.terra.addons.chunkgenerator.palette.slant.SlantHolder;
import com.dfsek.terra.addons.chunkgenerator.generation.math.SlantCalculationMethod;
import org.jetbrains.annotations.NotNull;
import com.dfsek.terra.addons.chunkgenerator.config.noise.BiomeNoiseProperties;
import com.dfsek.terra.addons.chunkgenerator.generation.math.PaletteUtil;
import com.dfsek.terra.addons.chunkgenerator.generation.math.interpolation.LazilyEvaluatedInterpolator;
import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.Sampler3D;
import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.SamplerProvider;
@@ -42,16 +45,19 @@ public class NoiseChunkGenerator3D implements ChunkGenerator {
private final PropertyKey<BiomePaletteInfo> paletteInfoPropertyKey;
private final PropertyKey<BiomeNoiseProperties> noisePropertiesKey;
private final SlantCalculationMethod slantCalculationMethod;
public NoiseChunkGenerator3D(ConfigPack pack, Platform platform, int elevationBlend, int carverHorizontalResolution,
int carverVerticalResolution,
PropertyKey<BiomeNoiseProperties> noisePropertiesKey,
PropertyKey<BiomePaletteInfo> paletteInfoPropertyKey) {
PropertyKey<BiomePaletteInfo> paletteInfoPropertyKey, SlantCalculationMethod slantCalculationMethod) {
this.platform = platform;
this.air = platform.getWorldHandle().air();
this.carverHorizontalResolution = carverHorizontalResolution;
this.carverVerticalResolution = carverVerticalResolution;
this.paletteInfoPropertyKey = paletteInfoPropertyKey;
this.noisePropertiesKey = noisePropertiesKey;
this.slantCalculationMethod = slantCalculationMethod;
int maxBlend = pack
.getBiomeProvider()
.stream()
@@ -63,6 +69,17 @@ public class NoiseChunkGenerator3D implements ChunkGenerator {
this.samplerCache = new SamplerProvider(platform, elevationBlend, noisePropertiesKey, maxBlend);
}
private Palette paletteAt(int x, int y, int z, Sampler3D sampler, BiomePaletteInfo paletteInfo, int depth) {
SlantHolder slantHolder = paletteInfo.slantHolder();
if(slantHolder.isAboveDepth(depth)) {
double slant = slantCalculationMethod.slant(sampler, x, y, z);
if(slantHolder.isInSlantThreshold(slant)) {
return slantHolder.getPalette(slant).getPalette(y);
}
}
return paletteInfo.paletteHolder().getPalette(y);
}
@Override
@SuppressWarnings("try")
public void generateChunkData(@NotNull ProtoChunk chunk, @NotNull WorldProperties world,
@@ -103,8 +120,7 @@ public class NoiseChunkGenerator3D implements ChunkGenerator {
if(sampler.sample(x, y, z) > 0) {
if(carver.sample(x, y, z) <= 0) {
data = PaletteUtil
.getPalette(x, y, z, sampler, paletteInfo, paletteLevel)
data = paletteAt(x, y, z, sampler, paletteInfo, paletteLevel)
.get(paletteLevel, cx, y, cz, seed);
chunk.setBlock(x, y, z, data);
paletteLevel++;
@@ -135,7 +151,7 @@ public class NoiseChunkGenerator3D implements ChunkGenerator {
int fdX = Math.floorMod(x, 16);
int fdZ = Math.floorMod(z, 16);
Palette palette = PaletteUtil.getPalette(fdX, y, fdZ, sampler, paletteInfo, 0);
Palette palette = paletteAt(fdX, y, fdZ, sampler, paletteInfo, 0);
double noise = sampler.sample(fdX, y, fdZ);
if(noise > 0) {
int level = 0;
@@ -157,11 +173,8 @@ public class NoiseChunkGenerator3D implements ChunkGenerator {
public double getSlant(int x, int y, int z, WorldProperties world, BiomeProvider biomeProvider) {
int fdX = Math.floorMod(x, 16);
int fdZ = Math.floorMod(z, 16);
return biomeProvider.getBiome(x, y, z, world.getSeed())
.getContext()
.get(paletteInfoPropertyKey)
.slantHolder()
.calculateSlant(samplerCache.get(x, z, world, biomeProvider), fdX, y, fdZ);
Sampler3D sampler = samplerCache.get(x, z, world, biomeProvider);
return slantCalculationMethod.slant(sampler, fdX, y, fdZ);
}
public SamplerProvider samplerProvider() {
@@ -1,29 +0,0 @@
/*
* Copyright (c) 2020-2023 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.chunkgenerator.generation.math;
import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.Sampler3D;
import com.dfsek.terra.addons.chunkgenerator.palette.BiomePaletteInfo;
import com.dfsek.terra.addons.chunkgenerator.palette.slant.SlantHolder;
import com.dfsek.terra.api.world.chunk.generation.util.Palette;
public final class PaletteUtil {
public static Palette getPalette(int x, int y, int z, Sampler3D sampler, BiomePaletteInfo paletteInfo, int depth) {
SlantHolder slantHolder = paletteInfo.slantHolder();
if(slantHolder.isAboveDepth(depth)) {
double slant = slantHolder.calculateSlant(sampler, x, y, z);
if(slantHolder.isInSlantThreshold(slant)) {
return slantHolder.getPalette(slant).getPalette(y);
}
}
return paletteInfo.paletteHolder().getPalette(y);
}
}
@@ -0,0 +1,69 @@
package com.dfsek.terra.addons.chunkgenerator.generation.math;
import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.Sampler3D;
import com.dfsek.terra.api.util.vector.Vector3;
public enum SlantCalculationMethod {
DotProduct {
private static final Vector3 DOT_PRODUCT_DIRECTION = Vector3.of(0, 1, 0);
private static final Vector3[] DOT_PRODUCT_SAMPLE_POINTS = {
Vector3.of(0, 0, -DERIVATIVE_DIST),
Vector3.of(0, 0, DERIVATIVE_DIST),
Vector3.of(0, -DERIVATIVE_DIST, 0),
Vector3.of(0, DERIVATIVE_DIST, 0),
Vector3.of(-DERIVATIVE_DIST, 0, 0),
Vector3.of(DERIVATIVE_DIST, 0, 0)
};
@Override
public double slant(Sampler3D sampler, double x, double y, double z) {
Vector3.Mutable normalApproximation = Vector3.Mutable.of(0, 0, 0);
for(Vector3 point : DOT_PRODUCT_SAMPLE_POINTS) {
var scalar = -sampler.sample(x + point.getX(), y + point.getY(), z + point.getZ());
normalApproximation.add(point.mutable().multiply(scalar));
}
return DOT_PRODUCT_DIRECTION.dot(normalApproximation.normalize());
}
@Override
public boolean floorToThreshold() {
return false;
}
},
Derivative {
@Override
public double slant(Sampler3D sampler, double x, double y, double z) {
double baseSample = sampler.sample(x, y, z);
double xVal1 = (sampler.sample(x + DERIVATIVE_DIST, y, z) - baseSample) / DERIVATIVE_DIST;
double xVal2 = (sampler.sample(x - DERIVATIVE_DIST, y, z) - baseSample) / DERIVATIVE_DIST;
double zVal1 = (sampler.sample(x, y, z + DERIVATIVE_DIST) - baseSample) / DERIVATIVE_DIST;
double zVal2 = (sampler.sample(x, y, z - DERIVATIVE_DIST) - baseSample) / DERIVATIVE_DIST;
double yVal1 = (sampler.sample(x, y + DERIVATIVE_DIST, z) - baseSample) / DERIVATIVE_DIST;
double yVal2 = (sampler.sample(x, y - DERIVATIVE_DIST, z) - baseSample) / DERIVATIVE_DIST;
return Math.sqrt(
((xVal2 - xVal1) * (xVal2 - xVal1)) + ((zVal2 - zVal1) * (zVal2 - zVal1)) + ((yVal2 - yVal1) * (yVal2 - yVal1)));
}
@Override
public boolean floorToThreshold() {
return true;
}
};
private static final double DERIVATIVE_DIST = 0.55;
public abstract double slant(Sampler3D sampler, double x, double y, double z);
/*
* Controls whether palettes should be applied before or after their respective thresholds.
*
* If true, slant values will map to the palette of the next floor threshold, otherwise they
* will map to the ceiling.
*/
public abstract boolean floorToThreshold();
}
@@ -14,6 +14,7 @@ import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.dfsek.terra.addons.chunkgenerator.generation.math.SlantCalculationMethod;
import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolder;
@@ -21,7 +22,7 @@ public class MultipleSlantHolder extends SlantHolderImpl {
private final NavigableMap<Double, PaletteHolder> layers;
private final double slantThreshold;
MultipleSlantHolder(List<SlantHolder.Layer> slant, int slantDepth, CalculationMethod calculationMethod) {
MultipleSlantHolder(List<SlantHolder.Layer> slant, int slantDepth, SlantCalculationMethod calculationMethod) {
super(slantDepth, calculationMethod);
NavigableMap<Double, PaletteHolder> layers = new TreeMap<>(
slant.stream().collect(Collectors.toMap(SlantHolder.Layer::threshold, SlantHolder.Layer::palette)));
@@ -1,5 +1,6 @@
package com.dfsek.terra.addons.chunkgenerator.palette.slant;
import com.dfsek.terra.addons.chunkgenerator.generation.math.SlantCalculationMethod;
import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolder;
@@ -7,7 +8,7 @@ final class SingleSlantHolder extends SlantHolderImpl {
private final SlantHolder.Layer layer;
public SingleSlantHolder(SlantHolder.Layer layer, int slantDepth, CalculationMethod calculationMethod) {
public SingleSlantHolder(SlantHolder.Layer layer, int slantDepth, SlantCalculationMethod calculationMethod) {
super(slantDepth, calculationMethod);
this.layer = layer;
}
@@ -2,19 +2,13 @@ package com.dfsek.terra.addons.chunkgenerator.palette.slant;
import java.util.List;
import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.Sampler3D;
import com.dfsek.terra.addons.chunkgenerator.generation.math.SlantCalculationMethod;
import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolder;
import com.dfsek.terra.api.util.vector.Vector3;
public interface SlantHolder {
SlantHolder EMPTY = new SlantHolder() {
@Override
public double calculateSlant(Sampler3D sampler, double x, double y, double z) {
throw new UnsupportedOperationException("Empty holder should not calculate slant");
}
@Override
public boolean isAboveDepth(int depth) {
return false;
@@ -31,7 +25,7 @@ public interface SlantHolder {
}
};
static SlantHolder of(List<SlantHolder.Layer> layers, int slantDepth, CalculationMethod calculationMethod) {
static SlantHolder of(List<SlantHolder.Layer> layers, int slantDepth, SlantCalculationMethod calculationMethod) {
if(layers.isEmpty()) {
return EMPTY;
} else if(layers.size() == 1) {
@@ -40,8 +34,6 @@ public interface SlantHolder {
return new MultipleSlantHolder(layers, slantDepth, calculationMethod);
}
double calculateSlant(Sampler3D sampler, double x, double y, double z);
boolean isAboveDepth(int depth);
boolean isInSlantThreshold(double slant);
@@ -49,71 +41,6 @@ public interface SlantHolder {
PaletteHolder getPalette(double slant);
enum CalculationMethod {
DotProduct {
private static final Vector3 DOT_PRODUCT_DIRECTION = Vector3.of(0, 1, 0);
private static final Vector3[] DOT_PRODUCT_SAMPLE_POINTS = {
Vector3.of(0, 0, -DERIVATIVE_DIST),
Vector3.of(0, 0, DERIVATIVE_DIST),
Vector3.of(0, -DERIVATIVE_DIST, 0),
Vector3.of(0, DERIVATIVE_DIST, 0),
Vector3.of(-DERIVATIVE_DIST, 0, 0),
Vector3.of(DERIVATIVE_DIST, 0, 0)
};
@Override
public double slant(Sampler3D sampler, double x, double y, double z) {
Vector3.Mutable normalApproximation = Vector3.Mutable.of(0, 0, 0);
for(Vector3 point : DOT_PRODUCT_SAMPLE_POINTS) {
var scalar = -sampler.sample(x + point.getX(), y + point.getY(), z + point.getZ());
normalApproximation.add(point.mutable().multiply(scalar));
}
return DOT_PRODUCT_DIRECTION.dot(normalApproximation.normalize());
}
@Override
public boolean floorToThreshold() {
return false;
}
},
Derivative {
@Override
public double slant(Sampler3D sampler, double x, double y, double z) {
double baseSample = sampler.sample(x, y, z);
double xVal1 = (sampler.sample(x + DERIVATIVE_DIST, y, z) - baseSample) / DERIVATIVE_DIST;
double xVal2 = (sampler.sample(x - DERIVATIVE_DIST, y, z) - baseSample) / DERIVATIVE_DIST;
double zVal1 = (sampler.sample(x, y, z + DERIVATIVE_DIST) - baseSample) / DERIVATIVE_DIST;
double zVal2 = (sampler.sample(x, y, z - DERIVATIVE_DIST) - baseSample) / DERIVATIVE_DIST;
double yVal1 = (sampler.sample(x, y + DERIVATIVE_DIST, z) - baseSample) / DERIVATIVE_DIST;
double yVal2 = (sampler.sample(x, y - DERIVATIVE_DIST, z) - baseSample) / DERIVATIVE_DIST;
return Math.sqrt(
((xVal2 - xVal1) * (xVal2 - xVal1)) + ((zVal2 - zVal1) * (zVal2 - zVal1)) + ((yVal2 - yVal1) * (yVal2 - yVal1)));
}
@Override
public boolean floorToThreshold() {
return true;
}
};
private static final double DERIVATIVE_DIST = 0.55;
public abstract double slant(Sampler3D sampler, double x, double y, double z);
/*
* Controls whether palettes should be applied before or after their respective thresholds.
*
* If true, slant values will map to the palette of the next floor threshold, otherwise they
* will map to the ceiling.
*/
public abstract boolean floorToThreshold();
}
record Layer(PaletteHolder palette, double threshold) {
}
}
@@ -1,26 +1,19 @@
package com.dfsek.terra.addons.chunkgenerator.palette.slant;
import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.Sampler3D;
import com.dfsek.terra.addons.chunkgenerator.generation.math.SlantCalculationMethod;
public abstract class SlantHolderImpl implements SlantHolder {
protected final boolean floorToThreshold;
private final SlantHolder.CalculationMethod calculationMethod;
private final int slantDepth;
protected SlantHolderImpl(int slantDepth, CalculationMethod calculationMethod) {
protected SlantHolderImpl(int slantDepth, SlantCalculationMethod calculationMethod) {
this.floorToThreshold = calculationMethod.floorToThreshold();
this.calculationMethod = calculationMethod;
this.slantDepth = slantDepth;
}
protected abstract double getSlantThreshold();
@Override
public final double calculateSlant(Sampler3D sampler, double x, double y, double z) {
return calculationMethod.slant(sampler, x, y, z);
}
@Override
public final boolean isAboveDepth(int depth) {
return depth <= slantDepth;
@@ -6,7 +6,8 @@ import cloud.commandframework.arguments.standard.EnumArgument;
import cloud.commandframework.arguments.standard.LongArgument;
import cloud.commandframework.context.CommandContext;
import java.util.Random;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.api.Platform;
@@ -56,7 +57,11 @@ public class StructureCommandAddon implements AddonInitializer {
structure.generate(
sender.position().toInt(),
sender.world(),
((Long) context.get("seed") == 0) ? new Random() : new Random(context.get("seed")),
((Long) context.get("seed") == 0)
? RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of("Xoroshiro128PlusPlus")
.create()
: RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of("Xoroshiro128PlusPlus")
.create(context.get("seed")),
context.get("rotation")
);
})
@@ -1,6 +1,7 @@
package com.dfsek.terra.addons.feature.distributor.distributors;
import java.util.Random;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.api.structure.feature.Distributor;
import com.dfsek.terra.api.util.MathUtil;
@@ -24,7 +25,8 @@ public class PaddedGridDistributor implements Distributor {
int cellX = Math.floorDiv(x, cellWidth);
int cellZ = Math.floorDiv(z, cellWidth);
Random random = new Random((MathUtil.murmur64(MathUtil.squash(cellX, cellZ)) ^ seed) + salt);
RandomGenerator random = RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of("Xoroshiro128PlusPlus").create(
(MathUtil.murmur64(MathUtil.squash(cellX, cellZ)) ^ seed) + salt);
int pointX = random.nextInt(width) + cellX * cellWidth;
int pointZ = random.nextInt(width) + cellZ * cellWidth;
@@ -10,7 +10,8 @@ package com.dfsek.terra.addons.flora.flora.gen;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Random;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.block.state.properties.enums.Direction;
@@ -71,7 +72,7 @@ public class TerraFlora implements Structure {
}
@Override
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) {
public boolean generate(Vector3Int location, WritableWorld world, RandomGenerator random, Rotation rotation) {
boolean doRotation = testRotation.size() > 0;
int size = layers.size();
int c = ceiling ? -1 : 1;
@@ -86,7 +87,9 @@ public class TerraFlora implements Structure {
location.getZ(), world.getSeed());
if(doRotation) {
Direction oneFace = new ArrayList<>(faces).get(
new Random(location.getX() ^ location.getZ()).nextInt(faces.size())); // Get random face.
RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of("Xoroshiro128PlusPlus")
.create(location.getX() ^ location.getZ())
.nextInt(faces.size())); // Get RandomGenerator face.
}
world.setBlockState(location.mutable().add(0, i + c, 0).immutable(), data, physics);
}
@@ -7,7 +7,8 @@
package com.dfsek.terra.addons.feature.locator.locators;
import java.util.Random;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.api.structure.feature.BinaryColumn;
import com.dfsek.terra.api.structure.feature.Locator;
@@ -40,7 +41,7 @@ public class GaussianRandomLocator implements Locator {
seed = 31 * seed + column.getZ();
seed += salt;
Random r = new Random(seed);
RandomGenerator r = RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of("Xoroshiro128PlusPlus").create(seed);
int size = points.get(r);
@@ -7,7 +7,8 @@
package com.dfsek.terra.addons.feature.locator.locators;
import java.util.Random;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.api.structure.feature.BinaryColumn;
import com.dfsek.terra.api.structure.feature.Locator;
@@ -36,7 +37,7 @@ public class RandomLocator implements Locator {
seed = 31 * seed + column.getZ();
seed += salt;
Random r = new Random(seed);
RandomGenerator r = RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of("Xoroshiro128PlusPlus").create(seed);
int size = points.get(r);
@@ -24,7 +24,7 @@ public class BrownianMotionSampler extends FractalNoiseFunction {
for(int i = 0; i < octaves; i++) {
double noise = input.noise(seed++, x, y);
sum += noise * amp;
amp *= MathUtil.lerp(1.0, Math.min(noise + 1, 2) * 0.5, weightedStrength);
amp *= MathUtil.lerp(weightedStrength, 1.0, Math.min(noise + 1, 2) * 0.5);
x *= lacunarity;
y *= lacunarity;
@@ -42,7 +42,7 @@ public class BrownianMotionSampler extends FractalNoiseFunction {
for(int i = 0; i < octaves; i++) {
double noise = input.noise(seed++, x, y, z);
sum += noise * amp;
amp *= MathUtil.lerp(1.0, (noise + 1) * 0.5, weightedStrength);
amp *= MathUtil.lerp(weightedStrength, 1.0, (noise + 1) * 0.5);
x *= lacunarity;
y *= lacunarity;
@@ -36,7 +36,7 @@ public class PingPongSampler extends FractalNoiseFunction {
for(int i = 0; i < octaves; i++) {
double noise = pingPong((input.noise(seed++, x, y) + 1) * pingPongStrength);
sum += (noise - 0.5) * 2 * amp;
amp *= MathUtil.lerp(1.0, noise, weightedStrength);
amp *= MathUtil.lerp(weightedStrength, 1.0, noise);
x *= lacunarity;
y *= lacunarity;
@@ -54,7 +54,7 @@ public class PingPongSampler extends FractalNoiseFunction {
for(int i = 0; i < octaves; i++) {
double noise = pingPong((input.noise(seed++, x, y, z) + 1) * pingPongStrength);
sum += (noise - 0.5) * 2 * amp;
amp *= MathUtil.lerp(1.0, noise, weightedStrength);
amp *= MathUtil.lerp(weightedStrength, 1.0, noise);
x *= lacunarity;
y *= lacunarity;
@@ -25,7 +25,7 @@ public class RidgedFractalSampler extends FractalNoiseFunction {
for(int i = 0; i < octaves; i++) {
double noise = Math.abs(input.noise(seed++, x, y));
sum += (noise * -2 + 1) * amp;
amp *= MathUtil.lerp(1.0, 1 - noise, weightedStrength);
amp *= MathUtil.lerp(weightedStrength, 1.0, 1 - noise);
x *= lacunarity;
y *= lacunarity;
@@ -43,7 +43,7 @@ public class RidgedFractalSampler extends FractalNoiseFunction {
for(int i = 0; i < octaves; i++) {
double noise = Math.abs(input.noise(seed++, x, y, z));
sum += (noise * -2 + 1) * amp;
amp *= MathUtil.lerp(1.0, 1 - noise, weightedStrength);
amp *= MathUtil.lerp(weightedStrength, 1.0, 1 - noise);
x *= lacunarity;
y *= lacunarity;
@@ -33,10 +33,10 @@ public class PerlinSampler extends SimplexStyleSampler {
int x1 = x0 + PRIME_X;
int y1 = y0 + PRIME_Y;
double xf0 = MathUtil.lerp(gradCoord(seed, x0, y0, xd0, yd0), gradCoord(seed, x1, y0, xd1, yd0), xs);
double xf1 = MathUtil.lerp(gradCoord(seed, x0, y1, xd0, yd1), gradCoord(seed, x1, y1, xd1, yd1), xs);
double xf0 = MathUtil.lerp(xs, gradCoord(seed, x0, y0, xd0, yd0), gradCoord(seed, x1, y0, xd1, yd0));
double xf1 = MathUtil.lerp(xs, gradCoord(seed, x0, y1, xd0, yd1), gradCoord(seed, x1, y1, xd1, yd1));
return MathUtil.lerp(xf0, xf1, ys) * 1.4247691104677813;
return MathUtil.lerp(ys, xf0, xf1) * 1.4247691104677813;
}
@Override
@@ -64,14 +64,14 @@ public class PerlinSampler extends SimplexStyleSampler {
int y1 = y0 + PRIME_Y;
int z1 = z0 + PRIME_Z;
double xf00 = MathUtil.lerp(gradCoord(seed, x0, y0, z0, xd0, yd0, zd0), gradCoord(seed, x1, y0, z0, xd1, yd0, zd0), xs);
double xf10 = MathUtil.lerp(gradCoord(seed, x0, y1, z0, xd0, yd1, zd0), gradCoord(seed, x1, y1, z0, xd1, yd1, zd0), xs);
double xf01 = MathUtil.lerp(gradCoord(seed, x0, y0, z1, xd0, yd0, zd1), gradCoord(seed, x1, y0, z1, xd1, yd0, zd1), xs);
double xf11 = MathUtil.lerp(gradCoord(seed, x0, y1, z1, xd0, yd1, zd1), gradCoord(seed, x1, y1, z1, xd1, yd1, zd1), xs);
double xf00 = MathUtil.lerp(xs, gradCoord(seed, x0, y0, z0, xd0, yd0, zd0), gradCoord(seed, x1, y0, z0, xd1, yd0, zd0));
double xf10 = MathUtil.lerp(xs, gradCoord(seed, x0, y1, z0, xd0, yd1, zd0), gradCoord(seed, x1, y1, z0, xd1, yd1, zd0));
double xf01 = MathUtil.lerp(xs, gradCoord(seed, x0, y0, z1, xd0, yd0, zd1), gradCoord(seed, x1, y0, z1, xd1, yd0, zd1));
double xf11 = MathUtil.lerp(xs, gradCoord(seed, x0, y1, z1, xd0, yd1, zd1), gradCoord(seed, x1, y1, z1, xd1, yd1, zd1));
double yf0 = MathUtil.lerp(xf00, xf10, ys);
double yf1 = MathUtil.lerp(xf01, xf11, ys);
double yf0 = MathUtil.lerp(ys, xf00, xf10);
double yf1 = MathUtil.lerp(ys, xf01, xf11);
return MathUtil.lerp(yf0, yf1, zs) * 0.964921414852142333984375;
return MathUtil.lerp(zs, yf0, yf1) * 0.964921414852142333984375;
}
}
@@ -25,10 +25,10 @@ public class ValueSampler extends ValueStyleNoise {
int x1 = x0 + PRIME_X;
int y1 = y0 + PRIME_Y;
double xf0 = MathUtil.lerp(valCoord(seed, x0, y0), valCoord(seed, x1, y0), xs);
double xf1 = MathUtil.lerp(valCoord(seed, x0, y1), valCoord(seed, x1, y1), xs);
double xf0 = MathUtil.lerp(xs, valCoord(seed, x0, y0), valCoord(seed, x1, y0));
double xf1 = MathUtil.lerp(xs, valCoord(seed, x0, y1), valCoord(seed, x1, y1));
return MathUtil.lerp(xf0, xf1, ys);
return MathUtil.lerp(ys, xf0, xf1);
}
@Override
@@ -49,14 +49,14 @@ public class ValueSampler extends ValueStyleNoise {
int y1 = y0 + PRIME_Y;
int z1 = z0 + PRIME_Z;
double xf00 = MathUtil.lerp(valCoord(seed, x0, y0, z0), valCoord(seed, x1, y0, z0), xs);
double xf10 = MathUtil.lerp(valCoord(seed, x0, y1, z0), valCoord(seed, x1, y1, z0), xs);
double xf01 = MathUtil.lerp(valCoord(seed, x0, y0, z1), valCoord(seed, x1, y0, z1), xs);
double xf11 = MathUtil.lerp(valCoord(seed, x0, y1, z1), valCoord(seed, x1, y1, z1), xs);
double xf00 = MathUtil.lerp(xs, valCoord(seed, x0, y0, z0), valCoord(seed, x1, y0, z0));
double xf10 = MathUtil.lerp(xs, valCoord(seed, x0, y1, z0), valCoord(seed, x1, y1, z0));
double xf01 = MathUtil.lerp(xs, valCoord(seed, x0, y0, z1), valCoord(seed, x1, y0, z1));
double xf11 = MathUtil.lerp(xs, valCoord(seed, x0, y1, z1), valCoord(seed, x1, y1, z1));
double yf0 = MathUtil.lerp(xf00, xf10, ys);
double yf1 = MathUtil.lerp(xf01, xf11, ys);
double yf0 = MathUtil.lerp(ys, xf00, xf10);
double yf1 = MathUtil.lerp(ys, xf01, xf11);
return MathUtil.lerp(yf0, yf1, zs);
return MathUtil.lerp(zs, yf0, yf1);
}
}
@@ -9,7 +9,7 @@ package com.dfsek.terra.addons.ore.ores;
import java.util.BitSet;
import java.util.Map;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.block.BlockType;
import com.dfsek.terra.api.block.state.BlockState;
@@ -44,7 +44,7 @@ public class VanillaOre implements Structure {
}
@Override
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) {
public boolean generate(Vector3Int location, WritableWorld world, RandomGenerator random, Rotation rotation) {
float randomRadian = random.nextFloat() * (float) Math.PI;
double eighthSize = size / 8.0F;
@@ -1,7 +1,7 @@
package com.dfsek.terra.addons.ore.ores;
import java.util.Map;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.block.BlockType;
import com.dfsek.terra.api.block.state.BlockState;
@@ -24,7 +24,7 @@ public class VanillaScatteredOre extends VanillaOre {
}
@Override
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) {
public boolean generate(Vector3Int location, WritableWorld world, RandomGenerator random, Rotation rotation) {
int i = random.nextInt((int) (size + 1));
Vector3Int.Mutable mutable = Vector3Int.zero().mutable();
@@ -39,7 +39,7 @@ public class VanillaScatteredOre extends VanillaOre {
return true;
}
private void setPos(Vector3Int.Mutable mutable, Random random, Vector3Int location, int spread) {
private void setPos(Vector3Int.Mutable mutable, RandomGenerator random, Vector3Int location, int spread) {
int x = this.getSpread(random, spread);
int y = this.getSpread(random, spread);
int z = this.getSpread(random, spread);
@@ -48,7 +48,7 @@ public class VanillaScatteredOre extends VanillaOre {
mutable.setZ(location.getZ() + z);
}
private int getSpread(Random random, int spread) {
private int getSpread(RandomGenerator random, int spread) {
return Math.round((random.nextFloat() - random.nextFloat()) * (float) spread);
}
}
@@ -1,6 +1,6 @@
package com.dfsek.terra.addons.ore.utils;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.block.BlockType;
import com.dfsek.terra.api.util.collection.MaterialSet;
@@ -8,13 +8,14 @@ import com.dfsek.terra.api.world.WritableWorld;
public class VanillaOreUtils {
private static boolean shouldExpose(Random random, double exposedChance) {
private static boolean shouldExpose(RandomGenerator random, double exposedChance) {
if(exposedChance >= 1.0F) return true;
if(exposedChance <= 0.0F) return false;
return random.nextFloat() < exposedChance;
}
public static boolean shouldPlace(MaterialSet replaceable, BlockType type, Double exposedChance, Random random, WritableWorld world,
public static boolean shouldPlace(MaterialSet replaceable, BlockType type, Double exposedChance, RandomGenerator random,
WritableWorld world,
int x,
int y, int z) {
if(!replaceable.contains(type)) return false;
@@ -12,7 +12,7 @@ import org.json.simple.JSONObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.addons.structure.structures.loot.functions.AmountFunction;
import com.dfsek.terra.addons.structure.structures.loot.functions.DamageFunction;
@@ -85,11 +85,11 @@ public class Entry {
/**
* Fetches a single ItemStack from the Entry, applying all functions to it.
*
* @param r The Random instance to apply functions with
* @param r The RandomGenerator instance to apply functions with
*
* @return ItemStack - The ItemStack with all functions applied.
*/
public ItemStack getItem(Random r) {
public ItemStack getItem(RandomGenerator r) {
ItemStack item = this.item.newItemStack(1);
for(LootFunction f : functions) {
item = f.apply(item, r);
@@ -14,7 +14,7 @@ import org.json.simple.parser.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.inventory.Inventory;
@@ -44,7 +44,7 @@ public class LootTableImpl implements com.dfsek.terra.api.structure.LootTable {
}
@Override
public void fillInventory(Inventory i, Random r) {
public void fillInventory(Inventory i, RandomGenerator r) {
List<ItemStack> loot = getLoot(r);
for(ItemStack stack : loot) {
int attempts = 0;
@@ -70,7 +70,7 @@ public class LootTableImpl implements com.dfsek.terra.api.structure.LootTable {
}
@Override
public List<ItemStack> getLoot(Random r) {
public List<ItemStack> getLoot(RandomGenerator r) {
List<ItemStack> itemList = new ArrayList<>();
for(Pool pool : pools) {
itemList.addAll(pool.getItems(r));
@@ -12,7 +12,7 @@ import org.json.simple.JSONObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.inventory.ItemStack;
@@ -50,13 +50,13 @@ public class Pool {
}
/**
* Fetches a list of items from the pool using the provided Random instance.
* Fetches a list of items from the pool using the provided RandomGenerator instance.
*
* @param r The Random instance to use.
* @param r The RandomGenerator instance to use.
*
* @return List&lt;ItemStack&gt; - The list of items fetched.
*/
public List<ItemStack> getItems(Random r) {
public List<ItemStack> getItems(RandomGenerator r) {
int rolls = r.nextInt(max - min + 1) + min;
List<ItemStack> items = new ArrayList<>();
@@ -8,7 +8,7 @@
package com.dfsek.terra.addons.structure.structures.loot.functions;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.inventory.ItemStack;
@@ -35,12 +35,12 @@ public class AmountFunction implements LootFunction {
* Applies the function to an ItemStack.
*
* @param original The ItemStack on which to apply the function.
* @param r The Random instance to use.
* @param r The RandomGenerator instance to use.
*
* @return - ItemStack - The mutated ItemStack.
*/
@Override
public ItemStack apply(ItemStack original, Random r) {
public ItemStack apply(ItemStack original, RandomGenerator r) {
original.setAmount(r.nextInt(max - min + 1) + min);
return original;
}
@@ -7,7 +7,7 @@
package com.dfsek.terra.addons.structure.structures.loot.functions;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.inventory.ItemStack;
import com.dfsek.terra.api.inventory.item.Damageable;
@@ -36,12 +36,12 @@ public class DamageFunction implements LootFunction {
* Applies the function to an ItemStack.
*
* @param original The ItemStack on which to apply the function.
* @param r The Random instance to use.
* @param r The RandomGenerator instance to use.
*
* @return - ItemStack - The mutated ItemStack.
*/
@Override
public ItemStack apply(ItemStack original, Random r) {
public ItemStack apply(ItemStack original, RandomGenerator r) {
if(original == null) return null;
if(!original.isDamageable()) return original;
ItemMeta meta = original.getItemMeta();
@@ -14,7 +14,7 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.inventory.ItemStack;
@@ -41,12 +41,12 @@ public class EnchantFunction implements LootFunction {
* Applies the function to an ItemStack.
*
* @param original The ItemStack on which to apply the function.
* @param r The Random instance to use.
* @param r The RandomGenerator instance to use.
*
* @return - ItemStack - The mutated ItemStack.
*/
@Override
public ItemStack apply(ItemStack original, Random r) {
public ItemStack apply(ItemStack original, RandomGenerator r) {
if(original.getItemMeta() == null) return original;
double enchant = (r.nextDouble() * (max - min)) + min;
@@ -8,7 +8,7 @@
package com.dfsek.terra.addons.structure.structures.loot.functions;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.inventory.ItemStack;
@@ -21,9 +21,9 @@ public interface LootFunction {
* Applies the function to an ItemStack.
*
* @param original The ItemStack on which to apply the function.
* @param r The Random instance to use.
* @param r The RandomGenerator instance to use.
*
* @return - ItemStack - The mutated ItemStack.
*/
ItemStack apply(ItemStack original, Random r);
ItemStack apply(ItemStack original, RandomGenerator r);
}
@@ -8,7 +8,8 @@
package com.dfsek.terra.addons.generation.feature;
import java.util.Collections;
import java.util.Random;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.addons.generation.feature.config.BiomeFeatures;
import com.dfsek.terra.api.Platform;
@@ -72,7 +73,9 @@ public class FeatureGenerationStage implements GenerationStage, StringIdentifiab
.forEach(y -> feature.getStructure(world, x, y, z)
.generate(Vector3Int.of(x, y, z),
world,
new Random(coordinateSeed * 31 + y),
RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of(
"Xoroshiro128PlusPlus")
.create(coordinateSeed * 31 + y),
Rotation.NONE)
);
}
@@ -11,12 +11,16 @@ import com.dfsek.tectonic.yaml.YamlConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.Files;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.event.events.config.ConfigurationDiscoveryEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.inject.annotations.Inject;
import com.dfsek.terra.api.util.FileUtil;
public class YamlAddon implements AddonInitializer {
@@ -33,10 +37,21 @@ public class YamlAddon implements AddonInitializer {
platform.getEventManager()
.getHandler(FunctionalEventHandler.class)
.register(addon, ConfigurationDiscoveryEvent.class)
.then(event -> event.getLoader().open("", ".yml").thenEntries(entries -> entries.forEach(entry -> {
LOGGER.debug("Discovered config {}", entry.getKey());
event.register(entry.getKey(), new YamlConfiguration(entry.getValue(), entry.getKey()));
})).close())
.then(event -> {
try {
FileUtil.filesWithExtension(event.getPack().getRootPath(), ".yml")
.forEach((key, value) -> {
LOGGER.debug("Discovered config {}", key);
try {
event.register(key, new YamlConfiguration(Files.newInputStream(value), key));
} catch(IOException e) {
throw new RuntimeException("Failed to load config " + key, e);
}
});
} catch(IOException e) {
throw new RuntimeException("Error occurred while reading config pack files", e);
}
})
.failThrough();
}
}
@@ -60,8 +60,8 @@ public class ImageLibraryAddon implements AddonInitializer {
.then(event -> {
ConfigPack pack = event.getPack();
CheckedRegistry<Supplier<ObjectTemplate<Image>>> imageRegistry = pack.getOrCreateRegistry(IMAGE_REGISTRY_KEY);
imageRegistry.register(addon.key("BITMAP"), () -> new ImageTemplate(pack.getLoader(), pack));
imageRegistry.register(addon.key("STITCHED_BITMAP"), () -> new StitchedImageTemplate(pack.getLoader(), pack));
imageRegistry.register(addon.key("BITMAP"), () -> new ImageTemplate(pack));
imageRegistry.register(addon.key("STITCHED_BITMAP"), () -> new StitchedImageTemplate(pack));
})
.then(event -> {
event.getPack()
@@ -4,8 +4,10 @@ import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import javax.imageio.ImageIO;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
import com.dfsek.terra.addons.image.config.ImageLibraryPackConfigTemplate;
@@ -13,7 +15,6 @@ import com.dfsek.terra.addons.image.image.BufferedImageWrapper;
import com.dfsek.terra.addons.image.image.Image;
import com.dfsek.terra.addons.image.image.SuppliedImage;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.Loader;
import com.dfsek.terra.api.properties.Properties;
import com.dfsek.terra.api.util.generic.Lazy;
@@ -22,13 +23,13 @@ import com.dfsek.terra.api.util.generic.Lazy;
* Cache prevents configs from loading the same image multiple times into memory
*/
record ImageCache(LoadingCache<String, Image> cache) implements Properties {
public static Image load(String path, ConfigPack pack, Loader files) throws IOException {
public static Image load(String path, ConfigPack pack) throws IOException {
ImageLibraryPackConfigTemplate config = pack.getContext().get(ImageLibraryPackConfigTemplate.class);
ImageCache images;
if(!pack.getContext().has(ImageCache.class)) {
var cacheBuilder = Caffeine.newBuilder();
if(config.unloadOnTimeout()) cacheBuilder.expireAfterAccess(config.getCacheTimeout(), TimeUnit.SECONDS);
images = new ImageCache(cacheBuilder.build(s -> loadImage(s, files)));
images = new ImageCache(cacheBuilder.build(s -> loadImage(s, pack.getRootPath())));
pack.getContext().put(images);
} else images = pack.getContext().get(ImageCache.class);
@@ -45,17 +46,8 @@ record ImageCache(LoadingCache<String, Image> cache) implements Properties {
return images.cache.get(path);
}
private static Image loadImage(String path, Loader files) throws IOException {
try {
return new BufferedImageWrapper(ImageIO.read(files.get(path)));
} catch(IllegalArgumentException e) {
throw new IllegalArgumentException("Unable to load image (image might be too large?)", e);
} catch(IOException e) {
if(e instanceof FileNotFoundException) {
// Rethrow using nicer message
throw new IOException("Unable to load image: No such file or directory: " + path, e);
}
throw new IOException("Unable to load image", e);
}
private static Image loadImage(String path, Path directory) throws IOException {
InputStream is = Files.newInputStream(directory.resolve(path));
return new BufferedImageWrapper(ImageIO.read(is));
}
}
@@ -7,25 +7,22 @@ import java.io.IOException;
import com.dfsek.terra.addons.image.image.Image;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.Loader;
public class ImageTemplate implements ObjectTemplate<Image> {
private final Loader files;
private final ConfigPack pack;
@Value("path")
private String path;
public ImageTemplate(Loader files, ConfigPack pack) {
this.files = files;
public ImageTemplate(ConfigPack pack) {
this.pack = pack;
}
@Override
public Image get() {
try {
return ImageCache.load(path, pack, files);
return ImageCache.load(path, pack);
} catch(IOException e) {
throw new RuntimeException(e);
}
@@ -11,12 +11,10 @@ import java.io.IOException;
import com.dfsek.terra.addons.image.image.Image;
import com.dfsek.terra.addons.image.image.StitchedImage;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.Loader;
public class StitchedImageTemplate implements ObjectTemplate<Image>, ValidatedConfigTemplate {
private final Loader files;
private final ConfigPack pack;
@Value("path-format")
private String path;
@@ -28,8 +26,7 @@ public class StitchedImageTemplate implements ObjectTemplate<Image>, ValidatedCo
@Default
private boolean zeroIndexed = false;
public StitchedImageTemplate(Loader files, ConfigPack pack) {
this.files = files;
public StitchedImageTemplate(ConfigPack pack) {
this.pack = pack;
}
@@ -39,7 +36,7 @@ public class StitchedImageTemplate implements ObjectTemplate<Image>, ValidatedCo
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
try {
grid[i][j] = ImageCache.load(getFormattedPath(i, j), pack, files);
grid[i][j] = ImageCache.load(getFormattedPath(i, j), pack);
} catch(IOException e) {
throw new RuntimeException(e);
}
@@ -1,6 +1,6 @@
package com.dfsek.terra.addons.palette.shortcut.block;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.structure.Structure;
@@ -17,7 +17,7 @@ public class SingletonStructure implements Structure {
}
@Override
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) {
public boolean generate(Vector3Int location, WritableWorld world, RandomGenerator random, Rotation rotation) {
world.setBlockState(location, blockState);
return true;
}
@@ -1,7 +1,5 @@
package com.dfsek.terra.addons.structure.mutator;
import java.util.Random;
import com.dfsek.terra.api.registry.key.Keyed;
import com.dfsek.terra.api.registry.key.RegistryKey;
import com.dfsek.terra.api.structure.Structure;
@@ -11,6 +9,8 @@ import com.dfsek.terra.api.world.WritableWorld;
import com.dfsek.terra.api.world.util.ReadInterceptor;
import com.dfsek.terra.api.world.util.WriteInterceptor;
import java.util.random.RandomGenerator;
public class MutatedStructure implements Structure, Keyed<MutatedStructure> {
private final RegistryKey key;
@@ -32,7 +32,7 @@ public class MutatedStructure implements Structure, Keyed<MutatedStructure> {
}
@Override
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) {
public boolean generate(Vector3Int location, WritableWorld world, RandomGenerator random, Rotation rotation) {
return base.generate(location,
world
.buffer()
@@ -16,6 +16,7 @@ import net.querz.nbt.tag.Tag;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.GZIPInputStream;
@@ -29,7 +30,7 @@ import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.inject.annotations.Inject;
import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.structure.Structure;
import com.dfsek.terra.api.util.StringUtil;
import com.dfsek.terra.api.util.FileUtil;
import com.dfsek.terra.api.util.vector.Vector3Int;
@@ -58,13 +59,21 @@ public class SpongeSchematicAddon implements AddonInitializer {
.register(addon, ConfigPackPreLoadEvent.class)
.then(event -> {
CheckedRegistry<Structure> structureRegistry = event.getPack().getOrCreateRegistry(Structure.class);
event.getPack()
.getLoader()
.open("", ".schem")
.thenEntries(entries -> entries
try {
FileUtil.filesWithExtension(event.getPack().getRootPath(), ".schem")
.entrySet()
.stream()
.map(entry -> convert(entry.getValue(), StringUtil.fileName(entry.getKey())))
.forEach(structureRegistry::register)).close();
.map(entry -> {
try {
return convert(Files.newInputStream(entry.getValue()), FileUtil.fileName(entry.getKey()));
} catch(IOException e) {
throw new RuntimeException("Failed to load config " + entry.getKey(), e);
}
})
.forEach(structureRegistry::register);
} catch(IOException e) {
throw new RuntimeException("Error occurred while reading config pack files", e);
}
})
.failThrough();
}
@@ -7,7 +7,7 @@
package com.dfsek.terra.addons.sponge;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.registry.key.Keyed;
@@ -36,7 +36,7 @@ public class SpongeStructure implements Structure, Keyed<SpongeStructure> {
}
@Override
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) {
public boolean generate(Vector3Int location, WritableWorld world, RandomGenerator random, Rotation rotation) {
int bX = location.getX();
int bY = location.getY();
int bZ = location.getZ();
@@ -7,6 +7,9 @@
package com.dfsek.terra.addons.terrascript;
import java.io.IOException;
import java.nio.file.Files;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder;
@@ -19,7 +22,7 @@ import com.dfsek.terra.api.inject.annotations.Inject;
import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.structure.LootTable;
import com.dfsek.terra.api.structure.Structure;
import com.dfsek.terra.api.util.StringUtil;
import com.dfsek.terra.api.util.FileUtil;
public class TerraScriptAddon implements AddonInitializer {
@@ -37,26 +40,28 @@ public class TerraScriptAddon implements AddonInitializer {
.then(event -> {
CheckedRegistry<Structure> structureRegistry = event.getPack().getOrCreateRegistry(Structure.class);
CheckedRegistry<LootTable> lootRegistry = event.getPack().getOrCreateRegistry(LootTable.class);
event.getPack().getLoader().open("", ".tesf").thenEntries(
entries ->
entries.stream()
.parallel()
.map(entry -> {
try {
String id = StringUtil.fileName(entry.getKey());
return new StructureScript(entry.getValue(),
addon.key(id),
platform,
structureRegistry,
lootRegistry,
event.getPack().getOrCreateRegistry(FunctionBuilder.class));
} catch(ParseException e) {
throw new RuntimeException("Failed to load script \"" + entry.getKey() + "\"", e);
}
})
.toList()
.forEach(structureRegistry::register))
.close();
try {
FileUtil.filesWithExtension(event.getPack().getRootPath(), ".tesf")
.entrySet()
.stream()
.parallel()
.map(entry -> {
try {
String id = FileUtil.fileName(entry.getKey());
return new StructureScript(Files.newInputStream(entry.getValue()),
addon.key(id),
platform,
structureRegistry,
lootRegistry,
event.getPack().getOrCreateRegistry(FunctionBuilder.class));
} catch(ParseException | IOException e) {
throw new RuntimeException("Failed to load script \"" + entry.getKey() + "\"", e);
}
})
.forEach(structureRegistry::register);
} catch(IOException e) {
throw new RuntimeException("Error occurred while reading config pack files", e);
}
})
.priority(100)
.failThrough();
@@ -14,7 +14,7 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.addons.terrascript.parser.Parser;
import com.dfsek.terra.addons.terrascript.parser.lang.Executable;
@@ -131,14 +131,14 @@ public class StructureScript implements Structure, Keyed<StructureScript> {
@Override
@SuppressWarnings("try")
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) {
public boolean generate(Vector3Int location, WritableWorld world, RandomGenerator random, Rotation rotation) {
platform.getProfiler().push(profile);
boolean result = applyBlock(new TerraImplementationArguments(location, rotation, random, world, 0));
platform.getProfiler().pop(profile);
return result;
}
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation, int recursions) {
public boolean generate(Vector3Int location, WritableWorld world, RandomGenerator random, Rotation rotation, int recursions) {
platform.getProfiler().push(profile);
boolean result = applyBlock(new TerraImplementationArguments(location, rotation, random, world, recursions));
platform.getProfiler().pop(profile);
@@ -9,7 +9,7 @@ package com.dfsek.terra.addons.terrascript.script;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments;
import com.dfsek.terra.api.util.Rotation;
@@ -20,14 +20,14 @@ import com.dfsek.terra.api.world.WritableWorld;
public class TerraImplementationArguments implements ImplementationArguments {
private final Rotation rotation;
private final Random random;
private final RandomGenerator random;
private final WritableWorld world;
private final Map<Vector3, String> marks = new HashMap<>();
private final int recursions;
private final Vector3Int origin;
private boolean waterlog = false;
public TerraImplementationArguments(Vector3Int origin, Rotation rotation, Random random, WritableWorld world, int recursions) {
public TerraImplementationArguments(Vector3Int origin, Rotation rotation, RandomGenerator random, WritableWorld world, int recursions) {
this.rotation = rotation;
this.random = random;
this.world = world;
@@ -39,7 +39,7 @@ public class TerraImplementationArguments implements ImplementationArguments {
return recursions;
}
public Random getRandom() {
public RandomGenerator getRandom() {
return random;
}
@@ -10,8 +10,8 @@ package com.dfsek.terra.addons.terrascript.script.functions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
@@ -32,7 +32,7 @@ public class BlockFunction implements Function<Void> {
protected final Returnable<Number> x, y, z;
protected final Returnable<String> blockData;
protected final Platform platform;
private final Map<String, BlockState> data = new HashMap<>();
private final Map<String, BlockState> data = new ConcurrentHashMap<>();
private final Returnable<Boolean> overwrite;
private final Returnable<Boolean> physics;
private final Position position;
@@ -10,7 +10,8 @@ package com.dfsek.terra.addons.terrascript.script.functions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Random;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
@@ -84,7 +85,8 @@ public class LootFunction implements Function<Void> {
if(event.isCancelled()) return;
event.getTable().fillInventory(container.getInventory(),
new Random(apply.hashCode()));
RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of(
"Xoroshiro128PlusPlus").create(apply.hashCode()));
data.update(false);
} catch(Exception e) {
LOGGER.error("Could not apply loot at {}", apply, e);
+1 -1
View File
@@ -6,6 +6,6 @@ dependencies {
api("com.dfsek.tectonic", "common", Versions.Libraries.tectonic)
api("com.github.ben-manes.caffeine:caffeine:3.1.0")
api("com.github.ben-manes.caffeine", "caffeine", Versions.Libraries.caffeine)
}
@@ -14,6 +14,7 @@ import java.io.File;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.MetaPack;
import com.dfsek.terra.api.config.PluginConfig;
import com.dfsek.terra.api.event.EventManager;
import com.dfsek.terra.api.handle.ItemHandle;
@@ -61,6 +62,10 @@ public interface Platform extends LoaderRegistrar {
@Contract(pure = true)
CheckedRegistry<ConfigPack> getConfigRegistry();
@NotNull
@Contract(pure = true)
CheckedRegistry<MetaPack> getMetaConfigRegistry();
@NotNull
@Contract(pure = true)
Registry<BaseAddon> getAddons();
@@ -10,6 +10,7 @@ package com.dfsek.terra.api.config;
import ca.solostudios.strata.version.Version;
import ca.solostudios.strata.version.VersionRange;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
@@ -43,7 +44,7 @@ public interface ConfigPack extends LoaderRegistrar,
List<GenerationStage> getStages();
Loader getLoader();
Path getRootPath();
String getAuthor();
@@ -1,47 +0,0 @@
/*
* Copyright (c) 2020-2023 Polyhedral Development
*
* The Terra API is licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in the common/api directory.
*/
package com.dfsek.terra.api.config;
import com.dfsek.tectonic.api.exception.ConfigException;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
public interface Loader {
Loader thenNames(Consumer<List<String>> consumer) throws ConfigException;
Loader thenEntries(Consumer<Set<Map.Entry<String, InputStream>>> consumer) throws ConfigException;
/**
* Get a single file from this Loader.
*
* @param singleFile File to get
*
* @return InputStream from file.
*/
InputStream get(String singleFile) throws IOException;
/**
* Open a subdirectory.
*
* @param directory Directory to open
* @param extension File extension
*/
Loader open(String directory, String extension);
/**
* Close all InputStreams opened.
*/
Loader close();
}
@@ -0,0 +1,27 @@
package com.dfsek.terra.api.config;
import ca.solostudios.strata.version.Version;
import java.util.Map;
import com.dfsek.terra.api.properties.PropertyHolder;
import com.dfsek.terra.api.registry.key.Keyed;
import com.dfsek.terra.api.registry.meta.CheckedRegistryHolder;
import com.dfsek.terra.api.registry.meta.RegistryProvider;
import com.dfsek.terra.api.tectonic.ConfigLoadingDelegate;
import com.dfsek.terra.api.tectonic.LoaderRegistrar;
public interface MetaPack extends LoaderRegistrar,
ConfigLoadingDelegate,
CheckedRegistryHolder,
RegistryProvider,
Keyed<MetaPack>,
PropertyHolder {
Map<String, ConfigPack> packs();
String getAuthor();
Version getVersion();
}
@@ -12,7 +12,6 @@ import com.dfsek.tectonic.api.config.Configuration;
import java.util.function.BiConsumer;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.Loader;
import com.dfsek.terra.api.event.events.FailThroughEvent;
import com.dfsek.terra.api.event.events.PackEvent;
@@ -25,13 +24,11 @@ import com.dfsek.terra.api.event.events.PackEvent;
*/
public class ConfigurationDiscoveryEvent implements PackEvent, FailThroughEvent {
private final ConfigPack pack;
private final Loader loader;
private final BiConsumer<String, Configuration> consumer;
public ConfigurationDiscoveryEvent(ConfigPack pack, Loader loader, BiConsumer<String, Configuration> consumer) {
public ConfigurationDiscoveryEvent(ConfigPack pack, BiConsumer<String, Configuration> consumer) {
this.pack = pack;
this.loader = loader;
this.consumer = consumer;
}
@@ -43,8 +40,4 @@ public class ConfigurationDiscoveryEvent implements PackEvent, FailThroughEvent
public ConfigPack getPack() {
return pack;
}
public Loader getLoader() {
return loader;
}
}
@@ -10,7 +10,7 @@ package com.dfsek.terra.api.structure;
import org.jetbrains.annotations.ApiStatus.Experimental;
import java.util.List;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.inventory.Inventory;
import com.dfsek.terra.api.inventory.ItemStack;
@@ -22,16 +22,16 @@ public interface LootTable {
* Fills an Inventory with loot.
*
* @param i The Inventory to fill.
* @param r The The Random instance to use.
* @param r The The RandomGenerator instance to use.
*/
void fillInventory(Inventory i, Random r);
void fillInventory(Inventory i, RandomGenerator r);
/**
* Fetches a list of ItemStacks from the loot table using the given Random instance.
* Fetches a list of ItemStacks from the loot table using the given RandomGenerator instance.
*
* @param r The Random instance to use.
* @param r The RandomGenerator instance to use.
*
* @return List&lt;ItemStack&gt; - The list of loot fetched.
*/
List<ItemStack> getLoot(Random r);
List<ItemStack> getLoot(RandomGenerator r);
}
@@ -7,7 +7,7 @@
package com.dfsek.terra.api.structure;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.util.Rotation;
import com.dfsek.terra.api.util.vector.Vector3Int;
@@ -15,5 +15,5 @@ import com.dfsek.terra.api.world.WritableWorld;
public interface Structure {
boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation);
boolean generate(Vector3Int location, WritableWorld world, RandomGenerator random, Rotation rotation);
}
@@ -10,7 +10,7 @@ package com.dfsek.terra.api.util;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
import java.util.Random;
import java.util.random.RandomGenerator;
public class ConstantRange implements Range {
@@ -36,7 +36,7 @@ public class ConstantRange implements Range {
}
@Override
public int get(Random r) {
public int get(RandomGenerator r) {
return r.nextInt(min, max);
}
@@ -0,0 +1,38 @@
package com.dfsek.terra.api.util;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.function.Function.identity;
public class FileUtil {
public static Map<String, Path> filesWithExtension(Path start, String... extensions) throws IOException {
if(Files.notExists(start) || !Files.isDirectory(start)) return Collections.emptyMap();
try(Stream<Path> paths = Files.walk(start)) {
return paths
.filter(Files::isRegularFile)
.filter(p -> Arrays.stream(extensions).anyMatch(e -> p.getFileName().toString().endsWith(e)))
.collect(Collectors.toMap(p -> start.relativize(p).toString(), identity()));
}
}
public static String fileName(String path) {
if(path.contains(File.separator)) {
return path.substring(path.lastIndexOf(File.separatorChar) + 1, path.lastIndexOf('.'));
} else if(path.contains("/")) {
return path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf('.'));
} else if(path.contains(".")) {
return path.substring(0, path.lastIndexOf('.'));
} else {
return path;
}
}
}
@@ -7,18 +7,20 @@
package com.dfsek.terra.api.util;
import java.util.Random;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.api.world.chunk.Chunk;
public final class PopulationUtil {
public static Random getRandom(Chunk c) {
public static RandomGenerator getRandom(Chunk c) {
return getRandom(c, 0);
}
public static Random getRandom(Chunk c, long salt) {
return new Random(getCarverChunkSeed(c.getX(), c.getZ(), c.getWorld().getSeed() + salt));
public static RandomGenerator getRandom(Chunk c, long salt) {
return RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of("Xoroshiro128PlusPlus").create(
getCarverChunkSeed(c.getX(), c.getZ(), c.getWorld().getSeed() + salt));
}
/**
@@ -31,7 +33,7 @@ public final class PopulationUtil {
* @return long - The carver seed.
*/
public static long getCarverChunkSeed(int chunkX, int chunkZ, long seed) {
Random r = new Random(seed);
RandomGenerator r = RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of("Xoroshiro128PlusPlus").create(seed);
return chunkX * r.nextLong() ^ chunkZ * r.nextLong() ^ seed;
}
}
@@ -10,8 +10,8 @@ package com.dfsek.terra.api.util;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
import java.util.Random;
import java.util.function.Supplier;
import java.util.random.RandomGenerator;
public interface Range extends Iterable<Integer> {
@@ -19,7 +19,7 @@ public interface Range extends Iterable<Integer> {
Range reflect(int pt);
int get(Random r);
int get(RandomGenerator r);
Range intersects(Range other);
@@ -1,25 +0,0 @@
/*
* Copyright (c) 2020-2023 Polyhedral Development
*
* The Terra API is licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in the common/api directory.
*/
package com.dfsek.terra.api.util;
import java.io.File;
public class StringUtil {
public static String fileName(String path) {
if(path.contains(File.separator)) {
return path.substring(path.lastIndexOf(File.separatorChar) + 1, path.lastIndexOf('.'));
} else if(path.contains("/")) {
return path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf('.'));
} else if(path.contains(".")) {
return path.substring(0, path.lastIndexOf('.'));
} else {
return path;
}
}
}
@@ -15,9 +15,9 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.function.Function;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.MathUtil;
@@ -43,7 +43,7 @@ public class ProbabilityCollection<E> implements Collection<E> {
}
@SuppressWarnings("unchecked")
public E get(Random r) {
public E get(RandomGenerator r) {
if(array.length == 0) return null;
return (E) array[r.nextInt(array.length)];
}
@@ -197,7 +197,7 @@ public class ProbabilityCollection<E> implements Collection<E> {
}
@Override
public T get(Random r) {
public T get(RandomGenerator r) {
return single;
}
@@ -51,6 +51,7 @@ import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.addon.bootstrap.BootstrapAddonClassLoader;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.MetaPack;
import com.dfsek.terra.api.config.PluginConfig;
import com.dfsek.terra.api.event.EventManager;
import com.dfsek.terra.api.event.events.platform.PlatformInitializationEvent;
@@ -72,6 +73,8 @@ import com.dfsek.terra.registry.CheckedRegistryImpl;
import com.dfsek.terra.registry.LockedRegistryImpl;
import com.dfsek.terra.registry.OpenRegistryImpl;
import com.dfsek.terra.registry.master.ConfigRegistry;
import com.dfsek.terra.registry.master.ConfigRegistry.PackLoadFailuresException;
import com.dfsek.terra.registry.master.MetaConfigRegistry;
/**
@@ -85,9 +88,12 @@ public abstract class AbstractPlatform implements Platform {
private static final MutableBoolean LOADED = new MutableBoolean(false);
private final EventManager eventManager = new EventManagerImpl();
private final ConfigRegistry configRegistry = new ConfigRegistry();
private final MetaConfigRegistry metaConfigRegistry = new MetaConfigRegistry();
private final CheckedRegistry<ConfigPack> checkedConfigRegistry = new CheckedRegistryImpl<>(configRegistry);
private final CheckedRegistry<MetaPack> checkedMetaConfigRegistry = new CheckedRegistryImpl<>(metaConfigRegistry);
private final Profiler profiler = new ProfilerImpl();
private final GenericLoaders loaders = new GenericLoaders(this);
@@ -102,6 +108,10 @@ public abstract class AbstractPlatform implements Platform {
return configRegistry;
}
public MetaConfigRegistry getRawMetaConfigRegistry() {
return metaConfigRegistry;
}
protected Iterable<BaseAddon> platformAddon() {
return Collections.emptySet();
}
@@ -147,11 +157,12 @@ public abstract class AbstractPlatform implements Platform {
eventManager.getHandler(FunctionalEventHandler.class)
.register(internalAddon, PlatformInitializationEvent.class)
.then(event -> {
logger.info("Loading config packs...");
configRegistry.loadAll(this);
logger.info("Loaded packs.");
})
.then(event -> loadConfigPacks())
.global();
eventManager.getHandler(FunctionalEventHandler.class)
.register(internalAddon, PlatformInitializationEvent.class)
.then(event -> loadMetaConfigPacks())
.global();
@@ -159,6 +170,38 @@ public abstract class AbstractPlatform implements Platform {
logger.info("Finished initialization.");
}
protected boolean loadConfigPacks() {
logger.info("Loading config packs...");
ConfigRegistry configRegistry = getRawConfigRegistry();
configRegistry.clear();
try {
configRegistry.loadAll(this);
} catch(IOException e) {
logger.error("Failed to load config packs", e);
return false;
} catch(PackLoadFailuresException e) {
e.getExceptions().forEach(ex -> logger.error("Failed to load config pack", ex));
return false;
}
return true;
}
protected boolean loadMetaConfigPacks() {
logger.info("Loading meta config packs...");
MetaConfigRegistry metaConfigRegistry = getRawMetaConfigRegistry();
metaConfigRegistry.clear();
try {
metaConfigRegistry.loadAll(this, configRegistry);
} catch(IOException e) {
logger.error("Failed to load meta config packs", e);
return false;
} catch(PackLoadFailuresException e) {
e.getExceptions().forEach(ex -> logger.error("Failed to meta load config pack", ex));
return false;
}
return true;
}
protected InternalAddon loadAddons() {
List<BaseAddon> addonList = new ArrayList<>();
@@ -325,6 +368,12 @@ public abstract class AbstractPlatform implements Platform {
return checkedConfigRegistry;
}
@Override
public @NotNull CheckedRegistry<MetaPack> getMetaConfigRegistry() {
return checkedMetaConfigRegistry;
}
@Override
public @NotNull Registry<BaseAddon> getAddons() {
return lockedAddonRegistry;
@@ -1,66 +0,0 @@
/*
* This file is part of Terra.
*
* Terra is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Terra is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Terra. If not, see <https://www.gnu.org/licenses/>.
*/
package com.dfsek.terra.config.fileloaders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
/**
* Load all {@code *.yml} files from a {@link java.nio.file.Path}.
*/
public class FolderLoader extends LoaderImpl {
private static final Logger logger = LoggerFactory.getLogger(FolderLoader.class);
private final Path path;
public FolderLoader(Path path) {
this.path = path;
}
@Override
public InputStream get(String singleFile) throws IOException {
return new FileInputStream(new File(path.toFile(), singleFile));
}
protected void load(String directory, String extension) {
File newPath = new File(path.toFile(), directory);
newPath.mkdirs();
try(Stream<Path> paths = Files.walk(newPath.toPath())) {
paths.filter(Files::isRegularFile).filter(file -> file.toString().toLowerCase().endsWith(extension)).forEach(file -> {
try {
String rel = newPath.toPath().relativize(file).toString();
streams.put(rel, new FileInputStream(file.toFile()));
} catch(FileNotFoundException e) {
logger.error("Could not find file to load", e);
}
});
} catch(IOException e) {
logger.error("Error while loading files", e);
}
}
}
@@ -1,83 +0,0 @@
/*
* This file is part of Terra.
*
* Terra is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Terra is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Terra. If not, see <https://www.gnu.org/licenses/>.
*/
package com.dfsek.terra.config.fileloaders;
import com.dfsek.tectonic.api.exception.ConfigException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import com.dfsek.terra.api.config.Loader;
public abstract class LoaderImpl implements Loader {
private static final Logger logger = LoggerFactory.getLogger(LoaderImpl.class);
protected final Map<String, InputStream> streams = new HashMap<>();
@Override
public Loader thenNames(Consumer<List<String>> consumer) throws ConfigException {
consumer.accept(new ArrayList<>(streams.keySet()));
return this;
}
@Override
public Loader thenEntries(Consumer<Set<Map.Entry<String, InputStream>>> consumer) throws ConfigException {
consumer.accept(streams.entrySet());
return this;
}
/**
* Open a subdirectory.
*
* @param directory Directory to open
* @param extension File extension
*/
@Override
public LoaderImpl open(String directory, String extension) {
if(!streams.isEmpty()) throw new IllegalStateException("Attempted to load new directory before closing existing InputStreams");
load(directory, extension);
return this;
}
/**
* Close all InputStreams opened.
*/
@Override
public Loader close() {
streams.forEach((name, input) -> {
try {
input.close();
} catch(IOException e) {
logger.error("Error occurred while loading", e);
}
});
streams.clear();
return this;
}
protected abstract void load(String directory, String extension);
}
@@ -1,63 +0,0 @@
/*
* This file is part of Terra.
*
* Terra is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Terra is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Terra. If not, see <https://www.gnu.org/licenses/>.
*/
package com.dfsek.terra.config.fileloaders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class ZIPLoader extends LoaderImpl {
private static final Logger logger = LoggerFactory.getLogger(ZIPLoader.class);
private final ZipFile file;
public ZIPLoader(ZipFile file) {
this.file = file;
}
@Override
public InputStream get(String singleFile) throws IOException {
Enumeration<? extends ZipEntry> entries = file.entries();
while(entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if(!entry.isDirectory() && entry.getName().equals(singleFile)) return file.getInputStream(entry);
}
throw new IllegalArgumentException("No such file: " + singleFile);
}
protected void load(String directory, String extension) {
Enumeration<? extends ZipEntry> entries = file.entries();
while(entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if(!entry.isDirectory() && entry.getName().startsWith(directory) && entry.getName().endsWith(extension)) {
try {
String rel = entry.getName().substring(directory.length());
streams.put(rel, file.getInputStream(entry));
} catch(IOException e) {
logger.error("Error while loading file from zip", e);
}
}
}
}
}
@@ -27,10 +27,10 @@ import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.lang.reflect.AnnotatedType;
import java.nio.file.Files;
import java.util.concurrent.ConcurrentHashMap;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.Loader;
import com.dfsek.terra.api.properties.Properties;
@@ -39,12 +39,10 @@ import com.dfsek.terra.api.properties.Properties;
*/
@Deprecated
public class BufferedImageLoader implements TypeLoader<BufferedImage> {
private final Loader files;
private final ConfigPack pack;
public BufferedImageLoader(Loader files, ConfigPack pack) {
this.files = files;
public BufferedImageLoader(ConfigPack pack) {
this.pack = pack;
if(!pack.getContext().has(ImageCache.class))
pack.getContext().put(new ImageCache(new ConcurrentHashMap<>()));
@@ -55,7 +53,7 @@ public class BufferedImageLoader implements TypeLoader<BufferedImage> {
throws LoadException {
return pack.getContext().get(ImageCache.class).map.computeIfAbsent((String) c, s -> {
try {
return ImageIO.read(files.get(s));
return ImageIO.read(Files.newInputStream(pack.getRootPath().resolve(s)));
} catch(IOException e) {
throw new LoadException("Unable to load image", e, depthTracker);
}
@@ -33,15 +33,15 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -51,15 +51,12 @@ import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.config.ConfigFactory;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.ConfigType;
import com.dfsek.terra.api.config.Loader;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.event.events.config.ConfigurationDiscoveryEvent;
import com.dfsek.terra.api.event.events.config.ConfigurationLoadEvent;
@@ -72,15 +69,12 @@ import com.dfsek.terra.api.registry.OpenRegistry;
import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.registry.key.RegistryKey;
import com.dfsek.terra.api.tectonic.ShortcutLoader;
import com.dfsek.terra.api.util.generic.Construct;
import com.dfsek.terra.api.util.generic.pair.Pair;
import com.dfsek.terra.api.util.reflection.ReflectionUtil;
import com.dfsek.terra.api.util.reflection.TypeKey;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import com.dfsek.terra.api.world.chunk.generation.stage.GenerationStage;
import com.dfsek.terra.api.world.chunk.generation.util.provider.ChunkGeneratorProvider;
import com.dfsek.terra.config.fileloaders.FolderLoader;
import com.dfsek.terra.config.fileloaders.ZIPLoader;
import com.dfsek.terra.config.loaders.GenericTemplateSupplierLoader;
import com.dfsek.terra.config.loaders.config.BufferedImageLoader;
import com.dfsek.terra.config.preprocessor.MetaListLikePreprocessor;
@@ -107,7 +101,7 @@ public class ConfigPackImpl implements ConfigPack {
private final AbstractConfigLoader abstractConfigLoader = new AbstractConfigLoader();
private final ConfigLoader selfLoader = new ConfigLoader();
private final Platform platform;
private final Loader loader;
private final Path rootPath;
private final Map<BaseAddon, VersionRange> addons;
@@ -121,40 +115,29 @@ public class ConfigPackImpl implements ConfigPack {
private final RegistryKey key;
public ConfigPackImpl(File folder, Platform platform) {
this(new FolderLoader(folder.toPath()), Construct.construct(() -> {
try {
return new YamlConfiguration(new FileInputStream(new File(folder, "pack.yml")), "pack.yml");
} catch(FileNotFoundException e) {
throw new UncheckedIOException("No pack.yml file found in " + folder.getAbsolutePath(), e);
}
}), platform);
}
public ConfigPackImpl(ZipFile file, Platform platform) {
this(new ZIPLoader(file), Construct.construct(() -> {
ZipEntry pack = null;
Enumeration<? extends ZipEntry> entries = file.entries();
while(entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if(entry.getName().equals("pack.yml")) pack = entry;
}
if(pack == null) throw new IllegalArgumentException("No pack.yml file found in " + file.getName());
try {
return new YamlConfiguration(file.getInputStream(pack), "pack.yml");
} catch(IOException e) {
throw new UncheckedIOException("Unable to load pack.yml from ZIP file", e);
}
}), platform);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private ConfigPackImpl(Loader loader, Configuration packManifest, Platform platform) {
@SuppressWarnings({ "rawtypes" })
public ConfigPackImpl(Path path, Platform platform) throws IOException {
long start = System.nanoTime();
this.loader = loader;
if(Files.notExists(path)) throw new FileNotFoundException("Could not load config pack, " + path + " does not exist");
if(Files.isDirectory(path)) {
this.rootPath = path;
} else if(Files.isRegularFile(path)) {
if(!path.getFileName().toString().endsWith(".zip")) {
throw new IOException("Could not load config pack, file " + path + " is not a zip");
}
FileSystem zipfs = FileSystems.newFileSystem(path);
this.rootPath = zipfs.getPath("/");
} else {
throw new IOException("Could not load config pack from " + path);
}
Path packManifestPath = rootPath.resolve("pack.yml");
if(Files.notExists(packManifestPath)) throw new IOException("No pack.yml found in " + path);
Configuration packManifest = new YamlConfiguration(Files.newInputStream(packManifestPath),
packManifestPath.getFileName().toString());
this.platform = platform;
this.configTypeRegistry = createConfigRegistry();
@@ -238,7 +221,7 @@ public class ConfigPackImpl implements ConfigPack {
private Map<String, Configuration> discoverConfigurations() {
Map<String, Configuration> configurations = new HashMap<>();
platform.getEventManager().callEvent(new ConfigurationDiscoveryEvent(this, loader,
platform.getEventManager().callEvent(new ConfigurationDiscoveryEvent(this,
(s, c) -> configurations.put(s.replace("\\", "/"),
c))); // Create all the configs.
return configurations;
@@ -283,7 +266,7 @@ public class ConfigPackImpl implements ConfigPack {
@Override
public void register(TypeRegistry registry) {
registry.registerLoader(ConfigType.class, configTypeRegistry)
.registerLoader(BufferedImage.class, new BufferedImageLoader(loader, this));
.registerLoader(BufferedImage.class, new BufferedImageLoader(this));
registryMap.forEach(registry::registerLoader);
shortcuts.forEach(registry::registerLoader); // overwrite with delegated shortcuts if present
}
@@ -309,7 +292,6 @@ public class ConfigPackImpl implements ConfigPack {
return seededBiomeProvider;
}
@SuppressWarnings("unchecked")
@Override
public <T> CheckedRegistry<T> getOrCreateRegistry(TypeKey<T> typeKey) {
return (CheckedRegistry<T>) registryMap.computeIfAbsent(typeKey.getType(), c -> {
@@ -348,8 +330,8 @@ public class ConfigPackImpl implements ConfigPack {
}
@Override
public Loader getLoader() {
return loader;
public Path getRootPath() {
return rootPath;
}
@Override
@@ -362,7 +344,7 @@ public class ConfigPackImpl implements ConfigPack {
return template.getVersion();
}
@SuppressWarnings("unchecked,rawtypes")
@SuppressWarnings("rawtypes")
@Override
public <T> ConfigPack registerShortcut(TypeKey<T> clazz, String shortcut, ShortcutLoader<T> loader) {
ShortcutHolder<?> holder = shortcuts
@@ -406,12 +388,10 @@ public class ConfigPackImpl implements ConfigPack {
}
@Override
@SuppressWarnings("unchecked")
public <T> CheckedRegistry<T> getRegistry(Type type) {
return (CheckedRegistry<T>) registryMap.get(type);
}
@SuppressWarnings("unchecked")
@Override
public <T> CheckedRegistry<T> getCheckedRegistry(Type type) throws IllegalStateException {
return (CheckedRegistry<T>) registryMap.get(type);
@@ -0,0 +1,215 @@
package com.dfsek.terra.config.pack;
import ca.solostudios.strata.version.Version;
import com.dfsek.tectonic.api.TypeRegistry;
import com.dfsek.tectonic.api.config.Configuration;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import com.dfsek.tectonic.api.loader.AbstractConfigLoader;
import com.dfsek.tectonic.api.loader.ConfigLoader;
import com.dfsek.tectonic.api.loader.type.TypeLoader;
import com.dfsek.tectonic.yaml.YamlConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.MetaPack;
import com.dfsek.terra.api.properties.Context;
import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.registry.OpenRegistry;
import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.registry.key.RegistryKey;
import com.dfsek.terra.api.util.reflection.ReflectionUtil;
import com.dfsek.terra.api.util.reflection.TypeKey;
import com.dfsek.terra.config.loaders.GenericTemplateSupplierLoader;
import com.dfsek.terra.registry.CheckedRegistryImpl;
import com.dfsek.terra.registry.OpenRegistryImpl;
import com.dfsek.terra.registry.master.ConfigRegistry;
public class MetaPackImpl implements MetaPack {
private static final Pattern PATTERN = Pattern.compile(", ");
private static final Logger logger = LoggerFactory.getLogger(MetaPackImpl.class);
private final MetaPackTemplate template = new MetaPackTemplate();
private final Platform platform;
private final Path rootPath;
private final Map<String, ConfigPack> packs = new HashMap<>();
private final ConfigLoader selfLoader = new ConfigLoader();
private final Context context = new Context();
private final RegistryKey key;
private final Map<Type, CheckedRegistryImpl<?>> registryMap = new HashMap<>();
private final AbstractConfigLoader abstractConfigLoader = new AbstractConfigLoader();
private final String author;
public MetaPackImpl(Path path, Platform platform, ConfigRegistry configRegistry) throws IOException {
long start = System.nanoTime();
if(Files.notExists(path)) throw new FileNotFoundException("Could not load metapack, " + path + " does not exist");
if(Files.isDirectory(path)) {
this.rootPath = path;
} else if(Files.isRegularFile(path)) {
if(!path.getFileName().toString().endsWith(".zip")) {
throw new IOException("Could not load metapack, file " + path + " is not a zip");
}
FileSystem zipfs = FileSystems.newFileSystem(path);
this.rootPath = zipfs.getPath("/");
} else {
throw new IOException("Could not load metapack from " + path);
}
Path packManifestPath = rootPath.resolve("metapack.yml");
if(Files.notExists(packManifestPath)) throw new IOException("No metapack.yml found in " + path);
Configuration packManifest = new YamlConfiguration(Files.newInputStream(packManifestPath),
packManifestPath.getFileName().toString());
this.platform = platform;
register(selfLoader);
platform.register(selfLoader);
register(abstractConfigLoader);
platform.register(abstractConfigLoader);
selfLoader.load(template, packManifest);
String namespace;
String id;
if(template.getID().contains(":")) {
namespace = template.getID().substring(0, template.getID().indexOf(":"));
id = template.getID().substring(template.getID().indexOf(":") + 1);
} else {
id = template.getID();
namespace = template.getID();
}
this.key = RegistryKey.of(namespace, id);
logger.info("Loading metapack \"{}:{}\"", id, namespace);
template.getPacks().forEach((k, v) -> {
RegistryKey registryKey = RegistryKey.parse(v);
if(configRegistry.contains(registryKey)) {
packs.put(k, configRegistry.get(registryKey).get());
logger.info("Linked config pack \"{}\" to metapack \"{}:{}\".", v, namespace, id);
} else {
logger.warn("Failed to link config pack \"{}\" to metapack \"{}:{}\".", v, namespace, id);
}
});
HashSet<String> authors = new HashSet<>();
packs.forEach((k, v) -> {
authors.addAll(Arrays.asList(PATTERN.split(v.getAuthor())));
});
authors.addAll(Arrays.asList(PATTERN.split(template.getAuthor())));
this.author = String.join(", ", authors);
logger.info("Loaded metapack \"{}:{}\" v{} by {} in {}ms.",
namespace, id, getVersion().getFormatted(), author, (System.nanoTime() - start) / 1000000.0D);
}
@Override
public String getAuthor() {
return author;
}
@Override
public Version getVersion() {
return template.getVersion();
}
@Override
public Map<String, ConfigPack> packs() {
return packs;
}
@Override
public Context getContext() {
return context;
}
@Override
public RegistryKey getRegistryKey() {
return key;
}
@Override
public <T> CheckedRegistry<T> getRegistry(Type type) {
return (CheckedRegistry<T>) registryMap.get(type);
}
@Override
public <T> CheckedRegistry<T> getCheckedRegistry(Type type) throws IllegalStateException {
return (CheckedRegistry<T>) registryMap.get(type);
}
@Override
public <T> CheckedRegistry<T> getOrCreateRegistry(TypeKey<T> typeKey) {
return (CheckedRegistry<T>) registryMap.computeIfAbsent(typeKey.getType(), c -> {
OpenRegistry<T> registry = new OpenRegistryImpl<>(typeKey);
selfLoader.registerLoader(c, registry);
abstractConfigLoader.registerLoader(c, registry);
logger.debug("Registered loader for registry of class {}", ReflectionUtil.typeToString(c));
if(typeKey.getType() instanceof ParameterizedType param) {
Type base = param.getRawType();
if(base instanceof Class // should always be true but we'll check anyways
&& Supplier.class.isAssignableFrom((Class<?>) base)) { // If it's a supplier
Type supplied = param.getActualTypeArguments()[0]; // Grab the supplied type
if(supplied instanceof ParameterizedType suppliedParam) {
Type suppliedBase = suppliedParam.getRawType();
if(suppliedBase instanceof Class // should always be true but we'll check anyways
&& ObjectTemplate.class.isAssignableFrom((Class<?>) suppliedBase)) {
Type templateType = suppliedParam.getActualTypeArguments()[0];
GenericTemplateSupplierLoader<?> loader = new GenericTemplateSupplierLoader<>(
(Registry<Supplier<ObjectTemplate<Supplier<ObjectTemplate<?>>>>>) registry);
selfLoader.registerLoader(templateType, loader);
abstractConfigLoader.registerLoader(templateType, loader);
logger.debug("Registered template loader for registry of class {}", ReflectionUtil.typeToString(templateType));
}
}
}
}
return new CheckedRegistryImpl<>(registry);
});
}
@Override
public <T> MetaPackImpl applyLoader(Type type, TypeLoader<T> loader) {
abstractConfigLoader.registerLoader(type, loader);
selfLoader.registerLoader(type, loader);
return this;
}
@Override
public <T> MetaPackImpl applyLoader(Type type, Supplier<ObjectTemplate<T>> loader) {
abstractConfigLoader.registerLoader(type, loader);
selfLoader.registerLoader(type, loader);
return this;
}
@Override
public void register(TypeRegistry registry) {
registryMap.forEach(registry::registerLoader);
}
}
@@ -0,0 +1,41 @@
package com.dfsek.terra.config.pack;
import ca.solostudios.strata.version.Version;
import com.dfsek.tectonic.api.config.template.ConfigTemplate;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import java.util.Map;
@SuppressWarnings({ "unused", "FieldMayBeFinal" })
public class MetaPackTemplate implements ConfigTemplate {
@Value("id")
private String id;
@Value("author")
@Default
private String author = "Anon Y. Mous";
@Value("version")
private Version version;
@Value("packs")
private Map<String, String> packs;
public String getID() {
return id;
}
public String getAuthor() {
return author;
}
public Version getVersion() {
return version;
}
public Map<String, String> getPacks() {
return packs;
}
}
@@ -17,14 +17,13 @@
package com.dfsek.terra.registry.master;
import com.dfsek.tectonic.api.exception.ConfigException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
import java.util.zip.ZipFile;
import java.io.Serial;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.config.ConfigPack;
@@ -37,44 +36,42 @@ import com.dfsek.terra.registry.OpenRegistryImpl;
* Class to hold config packs
*/
public class ConfigRegistry extends OpenRegistryImpl<ConfigPack> {
private static final Logger logger = LoggerFactory.getLogger(ConfigRegistry.class);
public ConfigRegistry() {
super(TypeKey.of(ConfigPack.class));
}
public void load(File folder, Platform platform) throws ConfigException {
ConfigPack pack = new ConfigPackImpl(folder, platform);
registerChecked(pack.getRegistryKey(), pack);
public void loadAll(Platform platform) throws IOException, PackLoadFailuresException {
Path packsDirectory = platform.getDataFolder().toPath().resolve("packs");
Files.createDirectories(packsDirectory);
List<IOException> failedLoads = new ArrayList<>();
try(Stream<Path> packs = Files.list(packsDirectory)) {
packs.forEach(path -> {
try {
ConfigPack pack = new ConfigPackImpl(path, platform);
registerChecked(pack.getRegistryKey(), pack);
} catch(IOException e) {
failedLoads.add(e);
}
});
}
if(!failedLoads.isEmpty()) {
throw new PackLoadFailuresException(failedLoads);
}
}
public boolean loadAll(Platform platform) {
boolean valid = true;
File packsFolder = new File(platform.getDataFolder(), "packs");
packsFolder.mkdirs();
for(File dir : Objects.requireNonNull(packsFolder.listFiles(File::isDirectory))) {
try {
load(dir, platform);
} catch(ConfigException e) {
logger.error("Error loading config pack {}", dir.getName(), e);
valid = false;
}
}
for(File zip : Objects.requireNonNull(
packsFolder.listFiles(file -> file.getName().endsWith(".zip") || file.getName().endsWith(".terra")))) {
try {
logger.info("Loading ZIP archive: {}", zip.getName());
load(new ZipFile(zip), platform);
} catch(IOException | ConfigException e) {
logger.error("Error loading config pack {}", zip.getName(), e);
valid = false;
}
}
return valid;
}
public static class PackLoadFailuresException extends Exception {
@Serial
private static final long serialVersionUID = 538998844645186306L;
public void load(ZipFile file, Platform platform) throws ConfigException {
ConfigPackImpl pack = new ConfigPackImpl(file, platform);
registerChecked(pack.getRegistryKey(), pack);
private final List<Throwable> exceptions;
public PackLoadFailuresException(List<? extends Throwable> exceptions) {
this.exceptions = (List<Throwable>) exceptions;
}
public List<Throwable> getExceptions() {
return exceptions;
}
}
}
@@ -0,0 +1,62 @@
/*
* This file is part of Terra.
*
* Terra is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Terra is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Terra. If not, see <https://www.gnu.org/licenses/>.
*/
package com.dfsek.terra.registry.master;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.config.MetaPack;
import com.dfsek.terra.api.util.reflection.TypeKey;
import com.dfsek.terra.config.pack.MetaPackImpl;
import com.dfsek.terra.registry.OpenRegistryImpl;
import com.dfsek.terra.registry.master.ConfigRegistry.PackLoadFailuresException;
/**
* Class to hold config packs
*/
public class MetaConfigRegistry extends OpenRegistryImpl<MetaPack> {
public MetaConfigRegistry() {
super(TypeKey.of(MetaPack.class));
}
public void loadAll(Platform platform, ConfigRegistry configRegistry) throws IOException, PackLoadFailuresException {
Path packsDirectory = platform.getDataFolder().toPath().resolve("metapacks");
Files.createDirectories(packsDirectory);
List<IOException> failedLoads = new ArrayList<>();
try(Stream<Path> packs = Files.list(packsDirectory)) {
packs.forEach(path -> {
try {
MetaPack pack = new MetaPackImpl(path, platform, configRegistry);
registerChecked(pack.getRegistryKey(), pack);
} catch(IOException e) {
failedLoads.add(e);
}
});
}
if(!failedLoads.isEmpty()) {
throw new PackLoadFailuresException(failedLoads);
}
}
}
Binary file not shown.
+2 -1
View File
@@ -1,6 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+7 -7
View File
@@ -145,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -153,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -202,11 +202,11 @@ fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
+1 -7
View File
@@ -10,19 +10,13 @@ repositories {
dependencies {
shaded(project(":platforms:bukkit:common"))
shaded(project(":platforms:bukkit:nms:v1_18_R2", configuration = "reobf"))
shaded(project(":platforms:bukkit:nms:v1_19_R1", configuration = "reobf"))
shaded(project(":platforms:bukkit:nms:v1_19_R2", configuration = "reobf"))
shaded(project(":platforms:bukkit:nms:v1_19_R3", configuration = "reobf"))
shaded(project(":platforms:bukkit:nms:v1_20_R1", configuration = "reobf"))
shaded(project(":platforms:bukkit:nms:v1_20_R2", configuration = "reobf"))
shaded(project(":platforms:bukkit:nms:v1_20_R3", configuration = "reobf"))
shaded("xyz.jpenilla", "reflection-remapper", Versions.Bukkit.reflectionRemapper)
}
tasks {
shadowJar {
relocate("io.papermc.lib", "com.dfsek.terra.lib.paperlib")
relocate("com.tcoded.folialib", "com.dfsek.terra.lib.folialib")
relocate("com.google.common", "com.dfsek.terra.lib.google.common")
relocate("org.apache.logging.slf4j", "com.dfsek.terra.lib.slf4j-over-log4j")
exclude("org/slf4j/**")
+1 -3
View File
@@ -8,9 +8,7 @@ dependencies {
compileOnly("io.papermc.paper", "paper-api", Versions.Bukkit.paper)
shadedApi("io.papermc", "paperlib", Versions.Bukkit.paperLib)
// TODO: 2023-11-08 When we drop support for 1.18 and 1.19, we can remove FoliaLib and instead use `RegionScheduler`,
// AsyncScheduler, or GlobalRegionScheduler.
shadedApi("com.tcoded", "FoliaLib", Versions.Bukkit.foliaLib)
shadedApi("com.google.guava", "guava", Versions.Libraries.Internal.guava)
shadedApi("cloud.commandframework", "cloud-paper", Versions.Libraries.cloud)
@@ -63,8 +63,7 @@ public class PlatformImpl extends AbstractPlatform {
@Override
public boolean reload() {
getTerraConfig().load(this);
getRawConfigRegistry().clear();
boolean succeed = getRawConfigRegistry().loadAll(this);
boolean succeed = loadConfigPacks();
Bukkit.getWorlds().forEach(world -> {
if(world.getGenerator() instanceof BukkitChunkGeneratorWrapper wrapper) {
@@ -84,8 +83,8 @@ public class PlatformImpl extends AbstractPlatform {
}
@Override
public void runPossiblyUnsafeTask(@NotNull Runnable task) {
plugin.getFoliaLib().getImpl().runAsync(task);
public void runPossiblyUnsafeTask(@NotNull Runnable runnable) {
plugin.getGlobalRegionScheduler().run(plugin, task -> runnable.run());
}
@Override
@@ -21,7 +21,8 @@ import cloud.commandframework.brigadier.CloudBrigadierManager;
import cloud.commandframework.bukkit.CloudBukkitCapabilities;
import cloud.commandframework.execution.CommandExecutionCoordinator;
import cloud.commandframework.paper.PaperCommandManager;
import com.tcoded.folialib.FoliaLib;
import io.papermc.paper.threadedregions.scheduler.AsyncScheduler;
import io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler;
import org.bukkit.Bukkit;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.java.JavaPlugin;
@@ -32,6 +33,7 @@ import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import com.dfsek.terra.api.command.CommandSender;
import com.dfsek.terra.api.config.ConfigPack;
@@ -51,7 +53,9 @@ public class TerraBukkitPlugin extends JavaPlugin {
private final PlatformImpl platform = new PlatformImpl(this);
private final Map<String, com.dfsek.terra.api.world.chunk.generation.ChunkGenerator> generatorMap = new HashMap<>();
private final FoliaLib foliaLib = new FoliaLib(this);
private AsyncScheduler asyncScheduler = this.getServer().getAsyncScheduler();
private GlobalRegionScheduler globalRegionScheduler = this.getServer().getGlobalRegionScheduler();
@Override
public void onEnable() {
@@ -61,6 +65,10 @@ public class TerraBukkitPlugin extends JavaPlugin {
platform.getEventManager().callEvent(new PlatformInitializationEvent());
if(!Initializer.init(platform)) {
Bukkit.getPluginManager().disablePlugin(this);
return;
}
try {
PaperCommandManager<CommandSender> commandManager = getCommandSenderPaperCommandManager();
@@ -80,8 +88,6 @@ public class TerraBukkitPlugin extends JavaPlugin {
Bukkit.getPluginManager().registerEvents(new CommonListener(), this); // Register master event listener
PaperUtil.checkPaper(this);
Initializer.init(platform);
}
@NotNull
@@ -116,6 +122,9 @@ public class TerraBukkitPlugin extends JavaPlugin {
if(!VersionUtil.getSpigotVersionInfo().isSpigot())
logger.error("YOU ARE RUNNING A CRAFTBUKKIT OR BUKKIT SERVER. PLEASE UPGRADE TO PAPER.");
if(!VersionUtil.getSpigotVersionInfo().isPaper())
logger.error("YOU ARE RUNNING A SPIGOT SERVER. PLEASE UPGRADE TO PAPER.");
if(VersionUtil.getSpigotVersionInfo().isMohist()) {
if(System.getProperty("IKnowMohistCausesLotsOfIssuesButIWillUseItAnyways") == null) {
Runnable runnable = () -> { // scary big block of text
@@ -159,7 +168,7 @@ public class TerraBukkitPlugin extends JavaPlugin {
""".strip());
};
runnable.run();
foliaLib.getImpl().runLaterAsync(runnable, 200L);
asyncScheduler.runDelayed(this, task -> runnable.run(), 200L, TimeUnit.SECONDS);
// Bukkit.shutdown(); // we're not *that* evil
Bukkit.getPluginManager().disablePlugin(this);
return false;
@@ -187,7 +196,11 @@ public class TerraBukkitPlugin extends JavaPlugin {
}), platform.getRawConfigRegistry().getByID(id).orElseThrow(), platform.getWorldHandle().air());
}
public FoliaLib getFoliaLib() {
return foliaLib;
public AsyncScheduler getAsyncScheduler() {
return asyncScheduler;
}
public GlobalRegionScheduler getGlobalRegionScheduler() {
return globalRegionScheduler;
}
}
@@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.config.ConfigPack;
@@ -20,6 +20,8 @@ package com.dfsek.terra.bukkit.handles;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Locale;
@@ -31,6 +33,7 @@ import com.dfsek.terra.bukkit.world.entity.BukkitEntityType;
public class BukkitWorldHandle implements WorldHandle {
private static final Logger logger = LoggerFactory.getLogger(BukkitWorldHandle.class);
private final BlockState air;
public BukkitWorldHandle() {
@@ -39,6 +42,13 @@ public class BukkitWorldHandle implements WorldHandle {
@Override
public synchronized @NotNull BlockState createBlockState(@NotNull String data) {
if(data.equals("minecraft:grass")) { //TODO: remove in 7.0
data = "minecraft:short_grass";
logger.warn(
"Translating minecraft:grass to minecraft:short_grass. In 1.20.3 minecraft:grass was renamed to minecraft:short_grass" +
". You are advised to perform this rename in your config backs as this translation will be removed in the next major " +
"version of Terra.");
}
org.bukkit.block.data.BlockData bukkitData = Bukkit.createBlockData(
data); // somehow bukkit managed to make this not thread safe! :)
return BukkitBlockState.newInstance(bukkitData);
@@ -51,6 +61,13 @@ public class BukkitWorldHandle implements WorldHandle {
@Override
public @NotNull EntityType getEntity(@NotNull String id) {
if(!id.contains(":")) { //TODO: remove in 7.0
String newid = "minecraft:" + id.toLowerCase();
logger.warn(
"Translating " + id + " to " + newid + ". In 1.20.3 entity parsing was reworked" +
". You are advised to perform this rename in your config backs as this translation will be removed in the next major " +
"version of Terra.");
}
if(!id.startsWith("minecraft:")) throw new IllegalArgumentException("Invalid entity identifier " + id);
String entityID = id.toUpperCase(Locale.ROOT).substring(10);
@@ -11,7 +11,7 @@ public interface Initializer {
String NMS = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
String TERRA_PACKAGE = Initializer.class.getPackageName();
static void init(PlatformImpl platform) {
static boolean init(PlatformImpl platform) {
Logger logger = LoggerFactory.getLogger(Initializer.class);
try {
Class<?> initializerClass = Class.forName(TERRA_PACKAGE + "." + NMS + ".NMSInitializer");
@@ -24,16 +24,27 @@ public interface Initializer {
} catch(ClassNotFoundException e) {
logger.error("NMS bindings for version {} do not exist. Support for this version is limited.", NMS);
logger.error("This is usually due to running Terra on an unsupported Minecraft version.");
logger.error("");
logger.error("");
for(int i = 0; i < 20; i++) {
logger.error("PROCEEDING WITH AN EXISTING TERRA WORLD WILL RESULT IN CORRUPTION!!!");
String bypassKey = "IKnowThereAreNoNMSBindingsFor" + NMS + "ButIWillProceedAnyway";
if(System.getProperty(bypassKey) == null) {
logger.error("Because of this **TERRA HAS BEEN DISABLED**.");
logger.error("Do not come ask us why it is not working.");
logger.error("If you wish to proceed anyways, you can add the JVM System Property \"{}\" to enable the plugin.", bypassKey);
return false;
} else {
logger.error("");
logger.error("");
for(int i = 0; i < 20; i++) {
logger.error("PROCEEDING WITH AN EXISTING TERRA WORLD WILL RESULT IN CORRUPTION!!!");
}
logger.error("");
logger.error("");
logger.error("NMS bindings for version {} do not exist. Support for this version is limited.", NMS);
logger.error("This is usually due to running Terra on an unsupported Minecraft version.");
logger.error("We will not give you any support for issues that may arise.");
logger.error("Since you enabled the \"{}\" flag, we won't disable Terra. But be warned.", bypassKey);
}
logger.error("");
logger.error("");
logger.error("NMS bindings for version {} do not exist. Support for this version is limited.", NMS);
logger.error("This is usually due to running Terra on an unsupported Minecraft version.");
}
return true;
}
void initialize(PlatformImpl plugin);
@@ -19,6 +19,8 @@ package com.dfsek.terra.bukkit.util;
import io.papermc.lib.PaperLib;
import java.util.concurrent.TimeUnit;
import com.dfsek.terra.bukkit.TerraBukkitPlugin;
import static io.papermc.lib.PaperLib.suggestPaper;
@@ -26,10 +28,10 @@ import static io.papermc.lib.PaperLib.suggestPaper;
public final class PaperUtil {
public static void checkPaper(TerraBukkitPlugin plugin) {
plugin.getFoliaLib().getImpl().runLaterAsync(() -> {
plugin.getAsyncScheduler().runDelayed(plugin, task -> {
if(!PaperLib.isPaper()) {
suggestPaper(plugin);
}
}, 100L);
}, 100L, TimeUnit.SECONDS);
}
}
@@ -4,6 +4,6 @@ version: "@VERSION@"
load: "STARTUP"
author: dfsek
website: "@WIKI@"
api-version: "1.13"
api-version: "1.20"
description: "@DESCRIPTION@"
folia-supported: true
@@ -1,17 +0,0 @@
apply(plugin = "io.papermc.paperweight.userdev")
repositories {
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
dependencies {
api(project(":platforms:bukkit:common"))
paperDevBundle("1.18.2-R0.1-SNAPSHOT")
implementation("xyz.jpenilla", "reflection-remapper", Versions.Bukkit.reflectionRemapper)
}
tasks {
assemble {
dependsOn("reobfJar")
}
}
@@ -1,115 +0,0 @@
package com.dfsek.terra.bukkit.nms.v1_18_R2;
import com.google.common.collect.ImmutableMap;
import com.mojang.serialization.Lifecycle;
import net.minecraft.core.Holder;
import net.minecraft.core.MappedRegistry;
import net.minecraft.core.Registry;
import net.minecraft.core.WritableRegistry;
import net.minecraft.data.BuiltinRegistries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.biome.Biome;
import org.bukkit.NamespacedKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.dfsek.terra.bukkit.world.BukkitPlatformBiome;
import com.dfsek.terra.registry.master.ConfigRegistry;
public class AwfulBukkitHacks {
private static final Logger LOGGER = LoggerFactory.getLogger(NMSBiomeInjector.class);
private static final Map<ResourceLocation, List<ResourceLocation>> terraBiomeMap = new HashMap<>();
public static void registerBiomes(ConfigRegistry configRegistry) {
try {
LOGGER.info("Hacking biome registry...");
WritableRegistry<Biome> biomeRegistry = (WritableRegistry<Biome>) Registries.biomeRegistry();
Reflection.MAPPED_REGISTRY.setFrozen((MappedRegistry<?>) biomeRegistry, false);
configRegistry.forEach(pack -> pack.getRegistry(com.dfsek.terra.api.world.biome.Biome.class).forEach((key, biome) -> {
try {
BukkitPlatformBiome platformBiome = (BukkitPlatformBiome) biome.getPlatformBiome();
NamespacedKey vanillaBukkitKey = platformBiome.getHandle().getKey();
ResourceLocation vanillaMinecraftKey = new ResourceLocation(vanillaBukkitKey.getNamespace(), vanillaBukkitKey.getKey());
Biome platform = NMSBiomeInjector.createBiome(
biome,
biomeRegistry.get(vanillaMinecraftKey) // get
);
ResourceKey<Biome> delegateKey = ResourceKey.create(Registry.BIOME_REGISTRY, new ResourceLocation("terra",
NMSBiomeInjector.createBiomeID(
pack, key)));
BuiltinRegistries.register(BuiltinRegistries.BIOME, delegateKey, platform);
biomeRegistry.register(delegateKey, platform, Lifecycle.stable());
platformBiome.getContext().put(new NMSBiomeInfo(delegateKey));
terraBiomeMap.computeIfAbsent(vanillaMinecraftKey, i -> new ArrayList<>()).add(delegateKey.location());
LOGGER.debug("Registered biome: " + delegateKey);
} catch(NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}));
Reflection.MAPPED_REGISTRY.setFrozen((MappedRegistry<?>) biomeRegistry, true); // freeze registry again :)
LOGGER.info("Doing tag garbage....");
Map<TagKey<Biome>, List<Holder<Biome>>> collect = biomeRegistry
.getTags() // streamKeysAndEntries
.collect(HashMap::new,
(map, pair) ->
map.put(pair.getFirst(), new ArrayList<>(pair.getSecond().stream().toList())),
HashMap::putAll);
terraBiomeMap
.forEach((vb, terraBiomes) ->
NMSBiomeInjector.getEntry(biomeRegistry, vb)
.ifPresentOrElse(
vanilla -> terraBiomes
.forEach(tb -> NMSBiomeInjector.getEntry(biomeRegistry, tb)
.ifPresentOrElse(
terra -> {
LOGGER.debug(
vanilla.unwrapKey()
.orElseThrow()
.location() +
" (vanilla for " +
terra.unwrapKey()
.orElseThrow()
.location() +
": " +
vanilla.tags()
.toList());
vanilla.tags()
.forEach(
tag -> collect
.computeIfAbsent(
tag,
t -> new ArrayList<>())
.add(terra));
},
() -> LOGGER.error(
"No such biome: {}",
tb))),
() -> LOGGER.error("No vanilla biome: {}", vb)));
biomeRegistry.resetTags(); // clearTags
biomeRegistry.bindTags(ImmutableMap.copyOf(collect)); // populateTags
} catch(SecurityException | IllegalArgumentException exception) {
throw new RuntimeException(exception);
}
}
}
@@ -1,10 +0,0 @@
package com.dfsek.terra.bukkit.nms.v1_18_R2;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.biome.Biome;
import com.dfsek.terra.api.properties.Properties;
public record NMSBiomeInfo(ResourceKey<Biome> biomeKey) implements Properties {
}
@@ -1,79 +0,0 @@
package com.dfsek.terra.bukkit.nms.v1_18_R2;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSpecialEffects;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.bukkit.config.VanillaBiomeProperties;
public class NMSBiomeInjector {
public static <T> Optional<Holder<T>> getEntry(Registry<T> registry, ResourceLocation identifier) {
return registry.getOptional(identifier)
.flatMap(registry::getResourceKey)
.map(registry::getOrCreateHolder);
}
public static Biome createBiome(com.dfsek.terra.api.world.biome.Biome biome, Biome vanilla)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Biome.BiomeBuilder builder = new Biome.BiomeBuilder(); // Builder
builder.biomeCategory(Reflection.BIOME.getBiomeCategory(vanilla))
.precipitation(vanilla.getPrecipitation()) // getPrecipitation
.mobSpawnSettings(vanilla.getMobSettings())
.generationSettings(vanilla.getGenerationSettings())
.temperature(vanilla.getBaseTemperature())
.downfall(vanilla.getDownfall());
BiomeSpecialEffects.Builder effects = new BiomeSpecialEffects.Builder();
effects.grassColorModifier(vanilla.getSpecialEffects().getGrassColorModifier());
VanillaBiomeProperties vanillaBiomeProperties = biome.getContext().get(VanillaBiomeProperties.class);
effects.fogColor(Objects.requireNonNullElse(vanillaBiomeProperties.getFogColor(), vanilla.getFogColor()))
.waterColor(Objects.requireNonNullElse(vanillaBiomeProperties.getWaterColor(), vanilla.getWaterColor()))
.waterFogColor(Objects.requireNonNullElse(vanillaBiomeProperties.getWaterFogColor(), vanilla.getWaterFogColor()))
.skyColor(Objects.requireNonNullElse(vanillaBiomeProperties.getSkyColor(), vanilla.getSkyColor()));
if(vanillaBiomeProperties.getFoliageColor() == null) {
vanilla.getSpecialEffects().getFoliageColorOverride().ifPresent(effects::foliageColorOverride);
} else {
effects.foliageColorOverride(vanillaBiomeProperties.getFoliageColor());
}
if(vanillaBiomeProperties.getGrassColor() == null) {
vanilla.getSpecialEffects().getGrassColorOverride().ifPresent(effects::grassColorOverride);
} else {
effects.grassColorOverride(vanillaBiomeProperties.getGrassColor());
}
vanilla.getAmbientLoop().ifPresent(effects::ambientLoopSound);
vanilla.getAmbientAdditions().ifPresent(effects::ambientAdditionsSound);
vanilla.getAmbientMood().ifPresent(effects::ambientMoodSound);
vanilla.getBackgroundMusic().ifPresent(effects::backgroundMusic);
vanilla.getAmbientParticle().ifPresent(effects::ambientParticle);
builder.specialEffects(effects.build());
return builder.build(); // build()
}
public static String createBiomeID(ConfigPack pack, com.dfsek.terra.api.registry.key.RegistryKey biomeID) {
return pack.getID()
.toLowerCase() + "/" + biomeID.getNamespace().toLowerCase(Locale.ROOT) + "/" + biomeID.getID().toLowerCase(Locale.ROOT);
}
}
@@ -1,49 +0,0 @@
package com.dfsek.terra.bukkit.nms.v1_18_R2;
import com.mojang.serialization.Codec;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.biome.Climate.Sampler;
import org.jetbrains.annotations.NotNull;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import com.dfsek.terra.bukkit.world.BukkitPlatformBiome;
public class NMSBiomeProvider extends BiomeSource {
private final BiomeProvider delegate;
private final BiomeSource vanilla;
private final long seed;
private final Registry<Biome> biomeRegistry = Registries.biomeRegistry();
public NMSBiomeProvider(BiomeProvider delegate, BiomeSource vanilla, long seed) {
super(delegate.stream()
.map(biome -> Registries.biomeRegistry()
.getOrCreateHolder(((BukkitPlatformBiome) biome.getPlatformBiome()).getContext()
.get(NMSBiomeInfo.class)
.biomeKey())));
this.delegate = delegate;
this.vanilla = vanilla;
this.seed = seed;
}
@Override
protected Codec<? extends BiomeSource> codec() {
return BiomeSource.CODEC;
}
@Override
public @NotNull BiomeSource withSeed(long seed) {
return new NMSBiomeProvider(delegate, vanilla, seed);
}
@Override
public @NotNull Holder<Biome> getNoiseBiome(int x, int y, int z, @NotNull Sampler sampler) {
return biomeRegistry.getOrCreateHolder(((BukkitPlatformBiome) delegate.getBiome(x << 2, y << 2, z << 2, seed).getPlatformBiome())
.getContext()
.get(NMSBiomeInfo.class)
.biomeKey());
}
}
@@ -1,254 +0,0 @@
package com.dfsek.terra.bukkit.nms.v1_18_R2;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Codec;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.SectionPos;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.NoiseColumn;
import net.minecraft.world.level.StructureFeatureManager;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.biome.Climate;
import net.minecraft.world.level.biome.Climate.Sampler;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.levelgen.GenerationStep;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.blending.Blender;
import net.minecraft.world.level.levelgen.structure.StructureSet;
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement;
import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.util.MathUtil;
import com.dfsek.terra.api.util.generic.Lazy;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import com.dfsek.terra.api.world.info.WorldProperties;
public class NMSChunkGeneratorDelegate extends ChunkGenerator {
private static final Logger LOGGER = LoggerFactory.getLogger(NMSChunkGeneratorDelegate.class);
private static final Lazy<List<ChunkPos>> EMPTY = Lazy.lazy(List::of);
private final NMSBiomeProvider biomeSource;
private final com.dfsek.terra.api.world.chunk.generation.ChunkGenerator delegate;
private final ChunkGenerator vanilla;
private final ConfigPack pack;
private final long seed;
private final Map<ConcentricRingsStructurePlacement, Lazy<List<ChunkPos>>> ringPositions = new Object2ObjectArrayMap<>();
private volatile boolean rings = false;
public NMSChunkGeneratorDelegate(ChunkGenerator vanilla, ConfigPack pack, NMSBiomeProvider biomeProvider, long seed) {
super(Registries.structureSet(), Optional.empty(), biomeProvider, biomeProvider, seed);
this.delegate = pack.getGeneratorProvider().newInstance(pack);
this.vanilla = vanilla;
this.biomeSource = biomeProvider;
this.pack = pack;
this.seed = seed;
}
@Override
public void applyCarvers(@NotNull WorldGenRegion chunkRegion, long seed, @NotNull BiomeManager biomeAccess,
@NotNull StructureFeatureManager structureAccessor,
@NotNull ChunkAccess chunk, GenerationStep.@NotNull Carving generationStep) {
// no-op
}
@Override
public void applyBiomeDecoration(@NotNull WorldGenLevel world, @NotNull ChunkAccess chunk,
@NotNull StructureFeatureManager structureAccessor) {
vanilla.applyBiomeDecoration(world, chunk, structureAccessor);
}
@Override
public int getSeaLevel() {
return vanilla.getSeaLevel();
}
@Override
public @NotNull CompletableFuture<ChunkAccess> fillFromNoise(@NotNull Executor executor, @NotNull Blender blender,
@NotNull StructureFeatureManager structureAccessor,
@NotNull ChunkAccess chunk) {
return vanilla.fillFromNoise(executor, blender, structureAccessor, chunk);
}
@Override
public void buildSurface(@NotNull WorldGenRegion region, @NotNull StructureFeatureManager structures, @NotNull ChunkAccess chunk) {
// no-op
}
@Override
protected @NotNull Codec<? extends ChunkGenerator> codec() {
return ChunkGenerator.CODEC;
}
@Override
public @NotNull NoiseColumn getBaseColumn(int x, int z, LevelHeightAccessor height) {
/*
BlockState[] array = new BlockState[height.getHeight()];
WorldProperties properties = new NMSWorldProperties(seed, height);
BiomeProvider biomeProvider = pack.getBiomeProvider().caching(properties);
for(int y = properties.getMaxHeight() - 1; y >= properties.getMinHeight(); y--) {
array[y - properties.getMinHeight()] = ((CraftBlockData) delegate.getBlock(properties, x, y, z, biomeProvider)
.getHandle()).getState();
}
return new NoiseColumn(getMinY(), array);
*/
return vanilla.getBaseColumn(x, z, height);
}
@Override // withSeed
public @NotNull ChunkGenerator withSeed(long seed) {
return new NMSChunkGeneratorDelegate(vanilla, pack, biomeSource, seed);
}
@Override
public void spawnOriginalMobs(@NotNull WorldGenRegion regionlimitedworldaccess) {
vanilla.spawnOriginalMobs(regionlimitedworldaccess);
}
@Override
public int getGenDepth() {
return vanilla.getGenDepth();
}
@Override
public @NotNull Sampler climateSampler() {
return Climate.empty();
}
@Override
public int getMinY() {
return vanilla.getMinY();
}
@Override
public int getBaseHeight(int x, int z, Heightmap.@NotNull Types heightmap, @NotNull LevelHeightAccessor world) {
WorldProperties properties = new NMSWorldProperties(seed, world);
int y = properties.getMaxHeight();
BiomeProvider biomeProvider = pack.getBiomeProvider();
while(y >= getMinY() && !heightmap.isOpaque().test(
((CraftBlockData) delegate.getBlock(properties, x, y - 1, z, biomeProvider).getHandle()).getState())) {
y--;
}
return y;
}
@Nullable
@Override
public List<ChunkPos> getRingPositionsFor(@NotNull ConcentricRingsStructurePlacement concentricringsstructureplacement) {
ensureStructuresGenerated();
return ringPositions.getOrDefault(concentricringsstructureplacement, EMPTY).value();
}
@Override
public synchronized void ensureStructuresGenerated() {
if(!this.rings) {
super.ensureStructuresGenerated();
this.populateStrongholdData();
this.rings = true;
}
}
private void populateStrongholdData() {
LOGGER.info("Generating safe stronghold data. This may take up to a minute.");
Set<Holder<Biome>> set = this.runtimeBiomeSource.possibleBiomes();
possibleStructureSets().map(Holder::value).forEach((holder) -> { // we dont need the spigot crap because it doesnt touch concentric.
StructurePlacement structureplacement = holder.placement();
if(structureplacement instanceof ConcentricRingsStructurePlacement concentricringsstructureplacement) {
if(holder.structures().stream().anyMatch((structureset_a1) -> structureset_a1.generatesInMatchingBiome(set::contains))) {
this.ringPositions.put(concentricringsstructureplacement,
Lazy.lazy(() -> this.generateRingPositions(holder, concentricringsstructureplacement)));
}
}
});
}
private List<ChunkPos> generateRingPositions(StructureSet holder,
ConcentricRingsStructurePlacement concentricringsstructureplacement) { // Spigot
if(concentricringsstructureplacement.count() == 0) {
return List.of();
}
List<ChunkPos> list = new ArrayList<>();
Set<Holder<Biome>> set = holder
.structures()
.stream()
.flatMap((structureset_a) -> structureset_a.structure().value().biomes().stream())
.collect(Collectors.toSet());
int i = concentricringsstructureplacement.distance();
int j = concentricringsstructureplacement.count();
int k = concentricringsstructureplacement.spread();
Random random = new Random();
// Paper start
if(this.conf.strongholdSeed != null && this.structureSets.getResourceKey(holder).orElse(null) ==
net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS) {
random.setSeed(this.conf.strongholdSeed);
} else {
// Paper end
random.setSeed(this.ringPlacementSeed);
} // Paper
double d0 = random.nextDouble() * 3.141592653589793D * 2.0D;
int l = 0;
int i1 = 0;
for(int j1 = 0; j1 < j; ++j1) {
double d1 = (double) (4 * i + i * i1 * 6) + (random.nextDouble() - 0.5D) * (double) i * 2.5D;
int k1 = (int) Math.round(MathUtil.cos(d0) * d1);
int l1 = (int) Math.round(MathUtil.sin(d0) * d1);
int i2 = SectionPos.sectionToBlockCoord(k1, 8);
int j2 = SectionPos.sectionToBlockCoord(l1, 8);
Objects.requireNonNull(set);
Pair<BlockPos, Holder<Biome>> pair = this.biomeSource.findBiomeHorizontal(i2, 0, j2, 112, set::contains, random,
this.climateSampler());
if(pair != null) {
BlockPos blockposition = pair.getFirst();
k1 = SectionPos.blockToSectionCoord(blockposition.getX());
l1 = SectionPos.blockToSectionCoord(blockposition.getZ());
}
list.add(new ChunkPos(k1, l1));
d0 += 6.283185307179586D / (double) k;
++l;
if(l == k) {
++i1;
l = 0;
k += 2 * k / (i1 + 1);
k = Math.min(k, j - j1);
d0 += random.nextDouble() * 3.141592653589793D * 2.0D;
}
}
return list;
}
@Override
public void addDebugScreenInfo(@NotNull List<String> arg0, @NotNull BlockPos arg1) {
}
}
@@ -1,15 +0,0 @@
package com.dfsek.terra.bukkit.nms.v1_18_R2;
import org.bukkit.Bukkit;
import com.dfsek.terra.bukkit.PlatformImpl;
import com.dfsek.terra.bukkit.nms.Initializer;
public class NMSInitializer implements Initializer {
@Override
public void initialize(PlatformImpl platform) {
AwfulBukkitHacks.registerBiomes(platform.getRawConfigRegistry());
Bukkit.getPluginManager().registerEvents(new NMSInjectListener(), platform.getPlugin());
}
}
@@ -1,53 +0,0 @@
package com.dfsek.terra.bukkit.nms.v1_18_R2;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkGenerator;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldInitEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.bukkit.generator.BukkitChunkGeneratorWrapper;
public class NMSInjectListener implements Listener {
private static final Logger LOGGER = LoggerFactory.getLogger(NMSInjectListener.class);
private static final Set<World> INJECTED = new HashSet<>();
private static final ReentrantLock INJECT_LOCK = new ReentrantLock();
@EventHandler
public void onWorldInit(WorldInitEvent event) {
if(!INJECTED.contains(event.getWorld()) &&
event.getWorld().getGenerator() instanceof BukkitChunkGeneratorWrapper bukkitChunkGeneratorWrapper) {
INJECT_LOCK.lock();
INJECTED.add(event.getWorld());
LOGGER.info("Preparing to take over the world: {}", event.getWorld().getName());
CraftWorld craftWorld = (CraftWorld) event.getWorld();
ServerLevel serverWorld = craftWorld.getHandle();
ConfigPack pack = bukkitChunkGeneratorWrapper.getPack();
ChunkGenerator vanilla = serverWorld.getChunkSource().getGenerator();
NMSBiomeProvider provider = new NMSBiomeProvider(pack.getBiomeProvider(), vanilla.getBiomeSource(), craftWorld.getSeed());
NMSChunkGeneratorDelegate custom = new NMSChunkGeneratorDelegate(vanilla, pack, provider, craftWorld.getSeed());
custom.conf = vanilla.conf; // world config from Spigot
serverWorld.getChunkSource().chunkMap.generator = custom;
LOGGER.info("Successfully injected into world.");
serverWorld.getChunkSource().chunkMap.generator.ensureStructuresGenerated(); // generate stronghold data now
INJECT_LOCK.unlock();
}
}
}
@@ -1,38 +0,0 @@
package com.dfsek.terra.bukkit.nms.v1_18_R2;
import net.minecraft.core.MappedRegistry;
import net.minecraft.world.level.biome.Biome;
import xyz.jpenilla.reflectionremapper.ReflectionRemapper;
import xyz.jpenilla.reflectionremapper.proxy.ReflectionProxyFactory;
import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldGetter;
import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldSetter;
import xyz.jpenilla.reflectionremapper.proxy.annotation.Proxies;
public class Reflection {
public static final MappedRegistryProxy MAPPED_REGISTRY;
public static final BiomeProxy BIOME;
static {
ReflectionRemapper reflectionRemapper = ReflectionRemapper.forReobfMappingsInPaperJar();
ReflectionProxyFactory reflectionProxyFactory = ReflectionProxyFactory.create(reflectionRemapper,
Reflection.class.getClassLoader());
MAPPED_REGISTRY = reflectionProxyFactory.reflectionProxy(MappedRegistryProxy.class);
BIOME = reflectionProxyFactory.reflectionProxy(BiomeProxy.class);
}
@Proxies(MappedRegistry.class)
public interface MappedRegistryProxy {
@FieldSetter("frozen")
void setFrozen(MappedRegistry<?> instance, boolean frozen);
}
@Proxies(Biome.class)
public interface BiomeProxy {
@FieldGetter("biomeCategory")
Biome.BiomeCategory getBiomeCategory(Biome instance);
}
}
@@ -1,30 +0,0 @@
package com.dfsek.terra.bukkit.nms.v1_18_R2;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.levelgen.structure.StructureSet;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_18_R2.CraftServer;
public class Registries {
private static <T> Registry<T> getRegistry(ResourceKey<Registry<T>> key) {
CraftServer craftserver = (CraftServer) Bukkit.getServer();
DedicatedServer dedicatedserver = craftserver.getServer();
return dedicatedserver
.registryAccess()
.registryOrThrow( // getRegistry
key
);
}
public static Registry<Biome> biomeRegistry() {
return getRegistry(Registry.BIOME_REGISTRY);
}
public static Registry<StructureSet> structureSet() {
return getRegistry(Registry.STRUCTURE_SET_REGISTRY);
}
}

Some files were not shown because too many files have changed in this diff Show More