mirror of
https://github.com/PolyhedralDev/Terra.git
synced 2026-05-20 08:40:26 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c0aaf6c6e8 | |||
| 1ab3233cba | |||
| 59ea5a69d8 | |||
| 4ba71e9c27 | |||
| 5c7441241c | |||
| ffb1198da2 | |||
| 2c211f0aa6 | |||
| 866d527d35 |
@@ -1,47 +0,0 @@
|
||||
# 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
|
||||
@@ -249,5 +249,3 @@ nbdist/
|
||||
|
||||
platforms/**/run/**
|
||||
|
||||
#Vale Config File
|
||||
**/.vale.ini
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
preRelease(true)
|
||||
|
||||
versionProjects(":common:api", version("6.4.3"))
|
||||
versionProjects(":common:implementation", version("6.4.3"))
|
||||
versionProjects(":platforms", version("6.4.3"))
|
||||
versionProjects(":common:api", version("6.5.0"))
|
||||
versionProjects(":common:implementation", version("6.5.0"))
|
||||
versionProjects(":platforms", version("6.5.0"))
|
||||
|
||||
|
||||
allprojects {
|
||||
|
||||
@@ -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.11")
|
||||
implementation("io.papermc.paperweight.userdev", "io.papermc.paperweight.userdev.gradle.plugin", "1.5.6")
|
||||
|
||||
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.ow2.asm", "asm", "9.5")
|
||||
implementation("org.ow2.asm", "asm-tree", "9.5")
|
||||
implementation("com.dfsek.tectonic", "common", "4.2.0")
|
||||
implementation("org.yaml", "snakeyaml", "2.2")
|
||||
}
|
||||
@@ -48,14 +48,17 @@ 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", Versions.Libraries.Internal.junit)
|
||||
testImplementation("org.junit.jupiter", "junit-jupiter-engine", Versions.Libraries.Internal.junit)
|
||||
compileOnly("org.jetbrains", "annotations", Versions.Libraries.Internal.jetBrainsAnnotations)
|
||||
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")
|
||||
|
||||
compileOnly("com.google.guava", "guava", Versions.Libraries.Internal.guava)
|
||||
testImplementation("com.google.guava", "guava", Versions.Libraries.Internal.guava)
|
||||
compileOnly("com.google.guava:guava:30.0-jre")
|
||||
testImplementation("com.google.guava:guava:30.0-jre")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ fun Project.configureDistribution() {
|
||||
group = "terra"
|
||||
doFirst {
|
||||
file("${buildDir}/resources/main/packs/").deleteRecursively()
|
||||
val defaultPackUrl = URL("https://github.com/PolyhedralDev/TerraOverworldConfig/releases/download/" + Versions.Terra.overworldConfig + "/default.zip")
|
||||
val defaultPackUrl = URL("https://github.com/PolyhedralDev/TerraOverworldConfig/releases/download/latest/default.zip")
|
||||
downloadPack(defaultPackUrl, project)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
object Versions {
|
||||
object Terra {
|
||||
const val overworldConfig = "v1.3.4"
|
||||
}
|
||||
|
||||
object Libraries {
|
||||
const val tectonic = "4.2.1"
|
||||
const val paralithic = "0.7.1"
|
||||
@@ -10,25 +6,21 @@ 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.11.0"
|
||||
const val apacheIO = "2.15.1"
|
||||
const val apacheText = "1.10.0"
|
||||
const val apacheIO = "2.14.0"
|
||||
const val guava = "32.1.3-jre"
|
||||
const val asm = "9.6"
|
||||
const val asm = "9.5"
|
||||
const val snakeYml = "2.2"
|
||||
const val jetBrainsAnnotations = "24.1.0"
|
||||
const val junit = "5.10.1"
|
||||
}
|
||||
}
|
||||
|
||||
object Fabric {
|
||||
const val fabricAPI = "0.91.2+${Mod.minecraft}"
|
||||
const val fabricAPI = "0.90.0+${Mod.minecraft}"
|
||||
}
|
||||
//
|
||||
// object Quilt {
|
||||
@@ -39,28 +31,30 @@ object Versions {
|
||||
object Mod {
|
||||
const val mixin = "0.12.5+mixin.0.8.5"
|
||||
|
||||
const val minecraft = "1.20.4"
|
||||
const val yarn = "$minecraft+build.1"
|
||||
const val fabricLoader = "0.15.1"
|
||||
const val minecraft = "1.20.2"
|
||||
const val yarn = "$minecraft+build.4"
|
||||
const val fabricLoader = "0.14.23"
|
||||
|
||||
const val architecuryLoom = "1.4.369"
|
||||
const val architecturyPlugin = "3.4.151"
|
||||
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"
|
||||
}
|
||||
//
|
||||
// object Forge {
|
||||
// const val forge = "${Mod.minecraft}-48.0.13"
|
||||
// const val burningwave = "12.63.0"
|
||||
// }
|
||||
|
||||
object Bukkit {
|
||||
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"
|
||||
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"
|
||||
}
|
||||
|
||||
//
|
||||
@@ -72,6 +66,6 @@ object Versions {
|
||||
//
|
||||
object CLI {
|
||||
const val nbt = "6.1"
|
||||
const val logback = "1.4.14"
|
||||
const val logback = "1.4.11"
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -1,9 +1,5 @@
|
||||
package com.dfsek.terra.addons.biome.extrusion.extrusions;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.dfsek.terra.addons.biome.extrusion.api.Extrusion;
|
||||
import com.dfsek.terra.addons.biome.extrusion.api.ReplaceableBiome;
|
||||
import com.dfsek.terra.addons.biome.query.api.BiomeQueries;
|
||||
@@ -12,6 +8,10 @@ import com.dfsek.terra.api.util.Range;
|
||||
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
|
||||
import com.dfsek.terra.api.world.biome.Biome;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
* Sets biomes at locations based on a sampler.
|
||||
|
||||
+12
-16
@@ -14,8 +14,7 @@ public class BiomeChunkImpl implements BiomeChunk {
|
||||
private final SeededVector worldOrigin;
|
||||
private final int chunkOriginArrayIndex;
|
||||
private final int worldCoordinateScale;
|
||||
private final int size;
|
||||
private PipelineBiome[] biomes;
|
||||
private PipelineBiome[][] biomes;
|
||||
|
||||
public BiomeChunkImpl(SeededVector worldOrigin, PipelineImpl pipeline) {
|
||||
|
||||
@@ -23,14 +22,14 @@ public class BiomeChunkImpl implements BiomeChunk {
|
||||
this.chunkOriginArrayIndex = pipeline.getChunkOriginArrayIndex();
|
||||
this.worldCoordinateScale = pipeline.getResolution();
|
||||
|
||||
this.size = pipeline.getArraySize();
|
||||
int size = pipeline.getArraySize();
|
||||
|
||||
int expanderCount = pipeline.getExpanderCount();
|
||||
int expansionsApplied = 0;
|
||||
|
||||
// Allocate working arrays
|
||||
this.biomes = new PipelineBiome[size * size];
|
||||
PipelineBiome[] lookupArray = new PipelineBiome[size * size];
|
||||
this.biomes = new PipelineBiome[size][size];
|
||||
PipelineBiome[][] lookupArray = new PipelineBiome[size][size];
|
||||
// A second lookup array is required such that stage application doesn't affect lookups, otherwise application may cascade
|
||||
|
||||
// Construct working grid
|
||||
@@ -44,7 +43,7 @@ public class BiomeChunkImpl implements BiomeChunk {
|
||||
for(int gridZ = 0; gridZ < gridSize; gridZ++) {
|
||||
int xIndex = gridOrigin + gridX * gridInterval;
|
||||
int zIndex = gridOrigin + gridZ * gridInterval;
|
||||
biomes[(xIndex * size) + zIndex] = pipeline.getSource().get(worldOrigin.seed(), xIndexToWorldCoordinate(xIndex),
|
||||
biomes[xIndex][zIndex] = pipeline.getSource().get(worldOrigin.seed(), xIndexToWorldCoordinate(xIndex),
|
||||
zIndexToWorldCoordinate(zIndex));
|
||||
}
|
||||
}
|
||||
@@ -66,7 +65,7 @@ public class BiomeChunkImpl implements BiomeChunk {
|
||||
|
||||
// Cycle arrays, the previously populated array is swapped to be used for lookups, and the result of the stage application
|
||||
// overwrites the previous lookup array. This saves having to allocate a new array copy each time
|
||||
PipelineBiome[] tempArray = biomes;
|
||||
PipelineBiome[][] tempArray = biomes;
|
||||
biomes = lookupArray;
|
||||
lookupArray = tempArray;
|
||||
|
||||
@@ -75,8 +74,7 @@ public class BiomeChunkImpl implements BiomeChunk {
|
||||
for(int gridX = 0; gridX < gridSize; gridX = gridX + 1) {
|
||||
int xIndex = gridOrigin + gridX * gridInterval;
|
||||
int zIndex = gridOrigin + gridZ * gridInterval;
|
||||
biomes[(xIndex * size) + zIndex] = stage.apply(
|
||||
new ViewPoint(this, gridInterval, gridX, gridZ, xIndex, zIndex, lookupArray, size));
|
||||
biomes[xIndex][zIndex] = stage.apply(new ViewPoint(this, gridInterval, gridX, gridZ, xIndex, zIndex, lookupArray));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,7 +133,7 @@ public class BiomeChunkImpl implements BiomeChunk {
|
||||
public PipelineBiome get(int xInChunk, int zInChunk) {
|
||||
int xIndex = xInChunk + chunkOriginArrayIndex;
|
||||
int zIndex = zInChunk + chunkOriginArrayIndex;
|
||||
return biomes[(xIndex * size) + zIndex];
|
||||
return biomes[xIndex][zIndex];
|
||||
}
|
||||
|
||||
private int xIndexToWorldCoordinate(int xIndex) {
|
||||
@@ -161,11 +159,10 @@ public class BiomeChunkImpl implements BiomeChunk {
|
||||
private final int gridZ;
|
||||
private final int xIndex;
|
||||
private final int zIndex;
|
||||
private final PipelineBiome[] lookupArray;
|
||||
private final int size;
|
||||
private final PipelineBiome[][] lookupArray;
|
||||
|
||||
private ViewPoint(BiomeChunkImpl chunk, int gridInterval, int gridX, int gridZ, int xIndex, int zIndex,
|
||||
PipelineBiome[] lookupArray, int size) {
|
||||
PipelineBiome[][] lookupArray) {
|
||||
this.chunk = chunk;
|
||||
this.gridInterval = gridInterval;
|
||||
this.gridX = gridX;
|
||||
@@ -173,14 +170,13 @@ public class BiomeChunkImpl implements BiomeChunk {
|
||||
this.xIndex = xIndex;
|
||||
this.zIndex = zIndex;
|
||||
this.lookupArray = lookupArray;
|
||||
this.size = size;
|
||||
this.biome = lookupArray[(this.xIndex * this.size) + this.zIndex];
|
||||
this.biome = lookupArray[xIndex][zIndex];
|
||||
}
|
||||
|
||||
public PipelineBiome getRelativeBiome(int x, int z) {
|
||||
int lookupXIndex = this.xIndex + x * gridInterval;
|
||||
int lookupZIndex = this.zIndex + z * gridInterval;
|
||||
return lookupArray[(lookupXIndex * this.size) + lookupZIndex];
|
||||
return lookupArray[lookupXIndex][lookupZIndex];
|
||||
}
|
||||
|
||||
public PipelineBiome getBiome() {
|
||||
|
||||
+2
-2
@@ -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(weightedStrength, 1.0, Math.min(noise + 1, 2) * 0.5);
|
||||
amp *= MathUtil.lerp(1.0, Math.min(noise + 1, 2) * 0.5, weightedStrength);
|
||||
|
||||
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(weightedStrength, 1.0, (noise + 1) * 0.5);
|
||||
amp *= MathUtil.lerp(1.0, (noise + 1) * 0.5, weightedStrength);
|
||||
|
||||
x *= lacunarity;
|
||||
y *= lacunarity;
|
||||
|
||||
+2
-2
@@ -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(weightedStrength, 1.0, noise);
|
||||
amp *= MathUtil.lerp(1.0, noise, weightedStrength);
|
||||
|
||||
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(weightedStrength, 1.0, noise);
|
||||
amp *= MathUtil.lerp(1.0, noise, weightedStrength);
|
||||
|
||||
x *= lacunarity;
|
||||
y *= lacunarity;
|
||||
|
||||
+2
-2
@@ -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(weightedStrength, 1.0, 1 - noise);
|
||||
amp *= MathUtil.lerp(1.0, 1 - noise, weightedStrength);
|
||||
|
||||
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(weightedStrength, 1.0, 1 - noise);
|
||||
amp *= MathUtil.lerp(1.0, 1 - noise, weightedStrength);
|
||||
|
||||
x *= lacunarity;
|
||||
y *= lacunarity;
|
||||
|
||||
+10
-10
@@ -33,10 +33,10 @@ public class PerlinSampler extends SimplexStyleSampler {
|
||||
int x1 = x0 + PRIME_X;
|
||||
int y1 = y0 + PRIME_Y;
|
||||
|
||||
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));
|
||||
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);
|
||||
|
||||
return MathUtil.lerp(ys, xf0, xf1) * 1.4247691104677813;
|
||||
return MathUtil.lerp(xf0, xf1, ys) * 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(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 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 yf0 = MathUtil.lerp(ys, xf00, xf10);
|
||||
double yf1 = MathUtil.lerp(ys, xf01, xf11);
|
||||
double yf0 = MathUtil.lerp(xf00, xf10, ys);
|
||||
double yf1 = MathUtil.lerp(xf01, xf11, ys);
|
||||
|
||||
return MathUtil.lerp(zs, yf0, yf1) * 0.964921414852142333984375;
|
||||
return MathUtil.lerp(yf0, yf1, zs) * 0.964921414852142333984375;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -25,10 +25,10 @@ public class ValueSampler extends ValueStyleNoise {
|
||||
int x1 = x0 + PRIME_X;
|
||||
int y1 = y0 + PRIME_Y;
|
||||
|
||||
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));
|
||||
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);
|
||||
|
||||
return MathUtil.lerp(ys, xf0, xf1);
|
||||
return MathUtil.lerp(xf0, xf1, ys);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,14 +49,14 @@ public class ValueSampler extends ValueStyleNoise {
|
||||
int y1 = y0 + PRIME_Y;
|
||||
int z1 = z0 + PRIME_Z;
|
||||
|
||||
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 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 yf0 = MathUtil.lerp(ys, xf00, xf10);
|
||||
double yf1 = MathUtil.lerp(ys, xf01, xf11);
|
||||
double yf0 = MathUtil.lerp(xf00, xf10, ys);
|
||||
double yf1 = MathUtil.lerp(xf01, xf11, ys);
|
||||
|
||||
return MathUtil.lerp(zs, yf0, yf1);
|
||||
return MathUtil.lerp(yf0, yf1, zs);
|
||||
}
|
||||
}
|
||||
|
||||
+19
-4
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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()
|
||||
|
||||
+8
-16
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
+2
-5
@@ -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);
|
||||
}
|
||||
|
||||
+2
-5
@@ -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);
|
||||
}
|
||||
|
||||
+16
-7
@@ -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();
|
||||
}
|
||||
|
||||
+26
-21
@@ -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();
|
||||
|
||||
+2
-2
@@ -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 ConcurrentHashMap<>();
|
||||
private final Map<String, BlockState> data = new HashMap<>();
|
||||
private final Returnable<Boolean> overwrite;
|
||||
private final Returnable<Boolean> physics;
|
||||
private final Position position;
|
||||
|
||||
@@ -6,6 +6,6 @@ dependencies {
|
||||
|
||||
api("com.dfsek.tectonic", "common", Versions.Libraries.tectonic)
|
||||
|
||||
api("com.github.ben-manes.caffeine", "caffeine", Versions.Libraries.caffeine)
|
||||
api("com.github.ben-manes.caffeine:caffeine:3.1.0")
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
+1
-8
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
|
||||
package com.dfsek.terra.api.structure;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import com.dfsek.terra.api.util.Rotation;
|
||||
import com.dfsek.terra.api.util.vector.Vector3Int;
|
||||
import com.dfsek.terra.api.world.WritableWorld;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
|
||||
public interface Structure {
|
||||
boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,9 @@
|
||||
package com.dfsek.terra;
|
||||
|
||||
import com.dfsek.tectonic.api.TypeRegistry;
|
||||
|
||||
import com.dfsek.terra.registry.master.ConfigRegistry.PackLoadFailuresException;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -147,18 +150,29 @@ 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();
|
||||
|
||||
|
||||
logger.info("Terra addons successfully loaded.");
|
||||
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 InternalAddon loadAddons() {
|
||||
List<BaseAddon> addonList = new ArrayList<>();
|
||||
|
||||
|
||||
-66
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
-83
@@ -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);
|
||||
}
|
||||
-63
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-5
@@ -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);
|
||||
}
|
||||
|
||||
+31
-51
@@ -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);
|
||||
|
||||
+35
-38
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+1
-2
@@ -1,7 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -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=SC2039,SC3045
|
||||
# shellcheck disable=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=SC2039,SC3045
|
||||
# shellcheck disable=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, 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.
|
||||
# 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.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
||||
@@ -10,13 +10,19 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
shaded(project(":platforms:bukkit:common"))
|
||||
shaded(project(":platforms:bukkit:nms:v1_20_R3", configuration = "reobf"))
|
||||
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("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/**")
|
||||
|
||||
@@ -8,7 +8,9 @@ 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)
|
||||
|
||||
@@ -20,6 +20,9 @@ package com.dfsek.terra.bukkit;
|
||||
import com.dfsek.tectonic.api.TypeRegistry;
|
||||
import com.dfsek.tectonic.api.depth.DepthTracker;
|
||||
import com.dfsek.tectonic.api.exception.LoadException;
|
||||
|
||||
import com.dfsek.terra.registry.master.ConfigRegistry.PackLoadFailuresException;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -27,6 +30,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -63,8 +67,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 +87,8 @@ public class PlatformImpl extends AbstractPlatform {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runPossiblyUnsafeTask(@NotNull Runnable runnable) {
|
||||
plugin.getGlobalRegionScheduler().run(plugin, task -> runnable.run());
|
||||
public void runPossiblyUnsafeTask(@NotNull Runnable task) {
|
||||
plugin.getFoliaLib().getImpl().runAsync(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+7
-20
@@ -21,8 +21,7 @@ import cloud.commandframework.brigadier.CloudBrigadierManager;
|
||||
import cloud.commandframework.bukkit.CloudBukkitCapabilities;
|
||||
import cloud.commandframework.execution.CommandExecutionCoordinator;
|
||||
import cloud.commandframework.paper.PaperCommandManager;
|
||||
import io.papermc.paper.threadedregions.scheduler.AsyncScheduler;
|
||||
import io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler;
|
||||
import com.tcoded.folialib.FoliaLib;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
@@ -33,7 +32,6 @@ 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;
|
||||
@@ -53,9 +51,7 @@ 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 AsyncScheduler asyncScheduler = this.getServer().getAsyncScheduler();
|
||||
|
||||
private GlobalRegionScheduler globalRegionScheduler = this.getServer().getGlobalRegionScheduler();
|
||||
private final FoliaLib foliaLib = new FoliaLib(this);
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
@@ -65,10 +61,6 @@ public class TerraBukkitPlugin extends JavaPlugin {
|
||||
|
||||
platform.getEventManager().callEvent(new PlatformInitializationEvent());
|
||||
|
||||
if(!Initializer.init(platform)) {
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
PaperCommandManager<CommandSender> commandManager = getCommandSenderPaperCommandManager();
|
||||
@@ -88,6 +80,8 @@ public class TerraBukkitPlugin extends JavaPlugin {
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(new CommonListener(), this); // Register master event listener
|
||||
PaperUtil.checkPaper(this);
|
||||
|
||||
Initializer.init(platform);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -122,9 +116,6 @@ 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
|
||||
@@ -168,7 +159,7 @@ public class TerraBukkitPlugin extends JavaPlugin {
|
||||
""".strip());
|
||||
};
|
||||
runnable.run();
|
||||
asyncScheduler.runDelayed(this, task -> runnable.run(), 200L, TimeUnit.SECONDS);
|
||||
foliaLib.getImpl().runLaterAsync(runnable, 200L);
|
||||
// Bukkit.shutdown(); // we're not *that* evil
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return false;
|
||||
@@ -196,11 +187,7 @@ public class TerraBukkitPlugin extends JavaPlugin {
|
||||
}), platform.getRawConfigRegistry().getByID(id).orElseThrow(), platform.getWorldHandle().air());
|
||||
}
|
||||
|
||||
public AsyncScheduler getAsyncScheduler() {
|
||||
return asyncScheduler;
|
||||
}
|
||||
|
||||
public GlobalRegionScheduler getGlobalRegionScheduler() {
|
||||
return globalRegionScheduler;
|
||||
public FoliaLib getFoliaLib() {
|
||||
return foliaLib;
|
||||
}
|
||||
}
|
||||
|
||||
-17
@@ -20,8 +20,6 @@ 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;
|
||||
|
||||
@@ -33,7 +31,6 @@ 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() {
|
||||
@@ -42,13 +39,6 @@ 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);
|
||||
@@ -61,13 +51,6 @@ 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 boolean init(PlatformImpl platform) {
|
||||
static void init(PlatformImpl platform) {
|
||||
Logger logger = LoggerFactory.getLogger(Initializer.class);
|
||||
try {
|
||||
Class<?> initializerClass = Class.forName(TERRA_PACKAGE + "." + NMS + ".NMSInitializer");
|
||||
@@ -24,27 +24,16 @@ 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.");
|
||||
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("");
|
||||
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.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void initialize(PlatformImpl plugin);
|
||||
|
||||
@@ -19,8 +19,6 @@ 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;
|
||||
@@ -28,10 +26,10 @@ import static io.papermc.lib.PaperLib.suggestPaper;
|
||||
|
||||
public final class PaperUtil {
|
||||
public static void checkPaper(TerraBukkitPlugin plugin) {
|
||||
plugin.getAsyncScheduler().runDelayed(plugin, task -> {
|
||||
plugin.getFoliaLib().getImpl().runLaterAsync(() -> {
|
||||
if(!PaperLib.isPaper()) {
|
||||
suggestPaper(plugin);
|
||||
}
|
||||
}, 100L, TimeUnit.SECONDS);
|
||||
}, 100L);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,6 @@ version: "@VERSION@"
|
||||
load: "STARTUP"
|
||||
author: dfsek
|
||||
website: "@WIKI@"
|
||||
api-version: "1.20"
|
||||
api-version: "1.13"
|
||||
description: "@DESCRIPTION@"
|
||||
folia-supported: true
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
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
-1
@@ -1,4 +1,4 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_20_R3;
|
||||
package com.dfsek.terra.bukkit.nms.v1_18_R2;
|
||||
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
+254
@@ -0,0 +1,254 @@
|
||||
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
-1
@@ -1,4 +1,4 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_20_R3;
|
||||
package com.dfsek.terra.bukkit.nms.v1_18_R2;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
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
-1
@@ -1,4 +1,4 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_20_R3;
|
||||
package com.dfsek.terra.bukkit.nms.v1_18_R2;
|
||||
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
apply(plugin = "io.papermc.paperweight.userdev")
|
||||
|
||||
repositories {
|
||||
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":platforms:bukkit:common"))
|
||||
paperDevBundle("1.19-R0.1-SNAPSHOT")
|
||||
implementation("xyz.jpenilla", "reflection-remapper", Versions.Bukkit.reflectionRemapper)
|
||||
}
|
||||
|
||||
tasks {
|
||||
assemble {
|
||||
dependsOn("reobfJar")
|
||||
}
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R1;
|
||||
|
||||
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 java.util.Objects;
|
||||
|
||||
import com.dfsek.terra.bukkit.world.BukkitPlatformBiome;
|
||||
import com.dfsek.terra.registry.master.ConfigRegistry;
|
||||
|
||||
|
||||
public class AwfulBukkitHacks {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AwfulBukkitHacks.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,
|
||||
Objects.requireNonNull(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();
|
||||
biomeRegistry.bindTags(ImmutableMap.copyOf(collect));
|
||||
|
||||
} catch(SecurityException | IllegalArgumentException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R1;
|
||||
|
||||
import com.dfsek.terra.api.properties.Properties;
|
||||
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
|
||||
|
||||
public record NMSBiomeInfo(ResourceKey<Biome> biomeKey) implements Properties {
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R1;
|
||||
|
||||
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::getOrCreateHolderOrThrow);
|
||||
}
|
||||
|
||||
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
|
||||
.precipitation(vanilla.getPrecipitation())
|
||||
.downfall(vanilla.getDownfall())
|
||||
.temperature(vanilla.getBaseTemperature())
|
||||
.mobSpawnSettings(vanilla.getMobSettings())
|
||||
.generationSettings(vanilla.getGenerationSettings());
|
||||
|
||||
|
||||
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 {
|
||||
// grass
|
||||
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();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R1;
|
||||
|
||||
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 long seed;
|
||||
private final Registry<Biome> biomeRegistry = Registries.biomeRegistry();
|
||||
|
||||
public NMSBiomeProvider(BiomeProvider delegate, long seed) {
|
||||
super(delegate.stream()
|
||||
.map(biome -> Registries.biomeRegistry()
|
||||
.getHolderOrThrow(((BukkitPlatformBiome) biome.getPlatformBiome()).getContext()
|
||||
.get(NMSBiomeInfo.class)
|
||||
.biomeKey())));
|
||||
this.delegate = delegate;
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull Codec<? extends BiomeSource> codec() {
|
||||
return BiomeSource.CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Holder<Biome> getNoiseBiome(int x, int y, int z, @NotNull Sampler sampler) {
|
||||
return biomeRegistry.getHolderOrThrow(((BukkitPlatformBiome) delegate.getBiome(x << 2, y << 2, z << 2, seed)
|
||||
.getPlatformBiome()).getContext()
|
||||
.get(NMSBiomeInfo.class)
|
||||
.biomeKey());
|
||||
}
|
||||
}
|
||||
+291
@@ -0,0 +1,291 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R1;
|
||||
|
||||
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.HolderSet;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.server.level.WorldGenRegion;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.NoiseColumn;
|
||||
import net.minecraft.world.level.StructureManager;
|
||||
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.block.Blocks;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.Beardifier;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction.SinglePointContext;
|
||||
import net.minecraft.world.level.levelgen.GenerationStep.Carving;
|
||||
import net.minecraft.world.level.levelgen.Heightmap.Types;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureSet.StructureSelectionEntry;
|
||||
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.block.data.CraftBlockData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
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;
|
||||
import com.dfsek.terra.bukkit.config.PreLoadCompatibilityOptions;
|
||||
import com.dfsek.terra.bukkit.world.BukkitWorldProperties;
|
||||
import com.dfsek.terra.bukkit.world.block.data.BukkitBlockState;
|
||||
|
||||
|
||||
public class NMSChunkGeneratorDelegate extends ChunkGenerator {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(NMSChunkGeneratorDelegate.class);
|
||||
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);
|
||||
this.delegate = pack.getGeneratorProvider().newInstance(pack);
|
||||
this.vanilla = vanilla;
|
||||
this.pack = pack;
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull Codec<? extends ChunkGenerator> codec() {
|
||||
return ChunkGenerator.CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyCarvers(@NotNull WorldGenRegion chunkRegion, long seed, @NotNull RandomState noiseConfig, @NotNull BiomeManager world,
|
||||
@NotNull StructureManager structureAccessor, @NotNull ChunkAccess chunk, @NotNull Carving carverStep) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildSurface(@NotNull WorldGenRegion region, @NotNull StructureManager structures, @NotNull RandomState noiseConfig,
|
||||
@NotNull ChunkAccess chunk) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyBiomeDecoration(@NotNull WorldGenLevel world, @NotNull ChunkAccess chunk,
|
||||
@NotNull StructureManager structureAccessor) {
|
||||
vanilla.applyBiomeDecoration(world, chunk, structureAccessor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnOriginalMobs(@NotNull WorldGenRegion region) {
|
||||
vanilla.spawnOriginalMobs(region);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGenDepth() {
|
||||
return vanilla.getGenDepth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<ChunkAccess> fillFromNoise(@NotNull Executor executor, @NotNull Blender blender,
|
||||
@NotNull RandomState noiseConfig,
|
||||
@NotNull StructureManager structureAccessor, @NotNull ChunkAccess chunk) {
|
||||
return vanilla.fillFromNoise(executor, blender, noiseConfig, structureAccessor, chunk)
|
||||
.thenApply(c -> {
|
||||
LevelAccessor level = Reflection.STRUCTURE_MANAGER.getLevel(structureAccessor);
|
||||
BiomeProvider biomeProvider = pack.getBiomeProvider();
|
||||
PreLoadCompatibilityOptions compatibilityOptions = pack.getContext().get(PreLoadCompatibilityOptions.class);
|
||||
if(compatibilityOptions.isBeard()) {
|
||||
beard(structureAccessor, chunk, new BukkitWorldProperties(level.getMinecraftWorld().getWorld()),
|
||||
biomeProvider, compatibilityOptions);
|
||||
}
|
||||
return c;
|
||||
});
|
||||
}
|
||||
|
||||
private void beard(StructureManager structureAccessor, ChunkAccess chunk, WorldProperties world, BiomeProvider biomeProvider,
|
||||
PreLoadCompatibilityOptions compatibilityOptions) {
|
||||
Beardifier structureWeightSampler = Beardifier.forStructuresInChunk(structureAccessor, chunk.getPos());
|
||||
double threshold = compatibilityOptions.getBeardThreshold();
|
||||
double airThreshold = compatibilityOptions.getAirThreshold();
|
||||
int xi = chunk.getPos().x << 4;
|
||||
int zi = chunk.getPos().z << 4;
|
||||
for(int x = 0; x < 16; x++) {
|
||||
for(int z = 0; z < 16; z++) {
|
||||
int depth = 0;
|
||||
for(int y = world.getMaxHeight(); y >= world.getMinHeight(); y--) {
|
||||
double noise = structureWeightSampler.compute(new SinglePointContext(x + xi, y, z + zi));
|
||||
if(noise > threshold) {
|
||||
chunk.setBlockState(new BlockPos(x, y, z), ((CraftBlockData) ((BukkitBlockState) delegate
|
||||
.getPalette(x + xi, y, z + zi, world, biomeProvider)
|
||||
.get(depth, x + xi, y, z + zi, world.getSeed())).getHandle()).getState(), false);
|
||||
depth++;
|
||||
} else if(noise < airThreshold) {
|
||||
chunk.setBlockState(new BlockPos(x, y, z), Blocks.AIR.defaultBlockState(), false);
|
||||
} else {
|
||||
depth = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSeaLevel() {
|
||||
return vanilla.getSeaLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinY() {
|
||||
return vanilla.getMinY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBaseHeight(int x, int z, @NotNull Types heightmap, @NotNull LevelHeightAccessor world, @NotNull RandomState noiseConfig) {
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NoiseColumn getBaseColumn(int x, int z, @NotNull LevelHeightAccessor world, @NotNull RandomState noiseConfig) {
|
||||
/*
|
||||
BlockState[] array = new BlockState[world.getHeight()];
|
||||
WorldProperties properties = new NMSWorldProperties(seed, world);
|
||||
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, world, noiseConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDebugScreenInfo(@NotNull List<String> text, @NotNull RandomState noiseConfig, @NotNull BlockPos pos) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ensureStructuresGenerated(@NotNull RandomState noiseConfig) {
|
||||
if(!this.rings) {
|
||||
super.ensureStructuresGenerated(noiseConfig);
|
||||
this.populateStrongholdData(noiseConfig);
|
||||
this.rings = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ChunkPos> getRingPositionsFor(@NotNull ConcentricRingsStructurePlacement structurePlacement,
|
||||
@NotNull RandomState noiseConfig) {
|
||||
ensureStructuresGenerated(noiseConfig);
|
||||
return ringPositions.get(structurePlacement).value();
|
||||
}
|
||||
|
||||
private void populateStrongholdData(RandomState noiseConfig) {
|
||||
LOGGER.info("Generating safe stronghold data. This may take up to a minute.");
|
||||
Set<Holder<Biome>> set = this.biomeSource.possibleBiomes();
|
||||
possibleStructureSets().map(Holder::value).forEach((holder) -> {
|
||||
boolean match = false;
|
||||
for(StructureSelectionEntry structureset_a : holder.structures()) {
|
||||
Structure structure = structureset_a.structure().value();
|
||||
Stream<Holder<Biome>> stream = structure.biomes().stream();
|
||||
if(stream.anyMatch(set::contains)) {
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(match) {
|
||||
if(holder.placement() instanceof ConcentricRingsStructurePlacement concentricringsstructureplacement) {
|
||||
this.ringPositions.put(concentricringsstructureplacement, Lazy.lazy(
|
||||
() -> this.generateRingPositions(holder, noiseConfig, concentricringsstructureplacement)));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private List<ChunkPos> generateRingPositions(StructureSet holder, RandomState randomstate,
|
||||
ConcentricRingsStructurePlacement concentricringsstructureplacement) { // Spigot
|
||||
if(concentricringsstructureplacement.count() == 0) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
List<ChunkPos> list = new ArrayList<>();
|
||||
int i = concentricringsstructureplacement.distance();
|
||||
int j = concentricringsstructureplacement.count();
|
||||
int k = concentricringsstructureplacement.spread();
|
||||
HolderSet<Biome> holderset = concentricringsstructureplacement.preferredBiomes();
|
||||
RandomSource randomsource = RandomSource.create();
|
||||
|
||||
if(this.conf.strongholdSeed != null && this.structureSets.getResourceKey(holder).orElse(null) ==
|
||||
net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS) {
|
||||
randomsource.setSeed(this.conf.strongholdSeed);
|
||||
} else {
|
||||
randomsource.setSeed(randomstate.legacyLevelSeed());
|
||||
}
|
||||
double d0 = randomsource.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) + (randomsource.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(holderset);
|
||||
Pair<BlockPos, Holder<Biome>> pair = this.biomeSource.findBiomeHorizontal(i2, 0, j2, 112, holderset::contains, randomsource,
|
||||
randomstate.sampler());
|
||||
|
||||
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 += randomsource.nextDouble() * 3.141592653589793D * 2.0D;
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R1;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R1;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.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(), 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.");
|
||||
|
||||
INJECT_LOCK.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R1;
|
||||
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
|
||||
import com.dfsek.terra.api.world.info.WorldProperties;
|
||||
|
||||
|
||||
public class NMSWorldProperties implements WorldProperties {
|
||||
private final long seed;
|
||||
private final LevelHeightAccessor height;
|
||||
|
||||
public NMSWorldProperties(long seed, LevelHeightAccessor height) {
|
||||
this.seed = seed;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getHandle() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSeed() {
|
||||
return seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxHeight() {
|
||||
return height.getMaxBuildHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinHeight() {
|
||||
return height.getMinBuildHeight();
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R1;
|
||||
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.StructureManager;
|
||||
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 StructureManagerProxy STRUCTURE_MANAGER;
|
||||
|
||||
static {
|
||||
ReflectionRemapper reflectionRemapper = ReflectionRemapper.forReobfMappingsInPaperJar();
|
||||
ReflectionProxyFactory reflectionProxyFactory = ReflectionProxyFactory.create(reflectionRemapper,
|
||||
Reflection.class.getClassLoader());
|
||||
|
||||
MAPPED_REGISTRY = reflectionProxyFactory.reflectionProxy(MappedRegistryProxy.class);
|
||||
STRUCTURE_MANAGER = reflectionProxyFactory.reflectionProxy(StructureManagerProxy.class);
|
||||
}
|
||||
|
||||
|
||||
@Proxies(MappedRegistry.class)
|
||||
public interface MappedRegistryProxy {
|
||||
@FieldSetter("frozen")
|
||||
void setFrozen(MappedRegistry<?> instance, boolean frozen);
|
||||
}
|
||||
|
||||
|
||||
@Proxies(StructureManager.class)
|
||||
public interface StructureManagerProxy {
|
||||
@FieldGetter("level")
|
||||
LevelAccessor getLevel(StructureManager instance);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R1;
|
||||
|
||||
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_19_R1.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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
apply(plugin = "io.papermc.paperweight.userdev")
|
||||
|
||||
repositories {
|
||||
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":platforms:bukkit:common"))
|
||||
paperDevBundle("1.19.3-R0.1-SNAPSHOT")
|
||||
implementation("xyz.jpenilla", "reflection-remapper", Versions.Bukkit.reflectionRemapper)
|
||||
}
|
||||
|
||||
tasks {
|
||||
assemble {
|
||||
dependsOn("reobfJar")
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R2;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Holder.Reference;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.core.WritableRegistry;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
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 java.util.Objects;
|
||||
|
||||
import com.dfsek.terra.bukkit.world.BukkitPlatformBiome;
|
||||
import com.dfsek.terra.registry.master.ConfigRegistry;
|
||||
|
||||
|
||||
public class AwfulBukkitHacks {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AwfulBukkitHacks.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>) RegistryFetcher.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, Objects.requireNonNull(biomeRegistry.get(vanillaMinecraftKey)));
|
||||
|
||||
ResourceKey<Biome> delegateKey = ResourceKey.create(
|
||||
Registries.BIOME,
|
||||
new ResourceLocation("terra", NMSBiomeInjector.createBiomeID(pack, key))
|
||||
);
|
||||
|
||||
Reference<Biome> holder = biomeRegistry.register(delegateKey, platform, Lifecycle.stable());
|
||||
Reflection.REFERENCE.invokeBindValue(holder, platform); // IMPORTANT: bind holder.
|
||||
|
||||
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 for {}): {}",
|
||||
vanilla.unwrapKey().orElseThrow().location(),
|
||||
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();
|
||||
biomeRegistry.bindTags(ImmutableMap.copyOf(collect));
|
||||
|
||||
} catch(SecurityException | IllegalArgumentException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_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 {
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_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)
|
||||
.flatMap(registry::getHolder);
|
||||
}
|
||||
|
||||
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
|
||||
.precipitation(vanilla.getPrecipitation())
|
||||
.downfall(vanilla.getDownfall())
|
||||
.temperature(vanilla.getBaseTemperature())
|
||||
.mobSpawnSettings(vanilla.getMobSettings())
|
||||
.generationSettings(vanilla.getGenerationSettings());
|
||||
|
||||
|
||||
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 {
|
||||
// grass
|
||||
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();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_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 long seed;
|
||||
private final Registry<Biome> biomeRegistry = RegistryFetcher.biomeRegistry();
|
||||
|
||||
public NMSBiomeProvider(BiomeProvider delegate, long seed) {
|
||||
super(delegate.stream()
|
||||
.map(biome -> RegistryFetcher.biomeRegistry()
|
||||
.getHolderOrThrow(((BukkitPlatformBiome) biome.getPlatformBiome()).getContext()
|
||||
.get(NMSBiomeInfo.class)
|
||||
.biomeKey())));
|
||||
this.delegate = delegate;
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull Codec<? extends BiomeSource> codec() {
|
||||
return BiomeSource.CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Holder<Biome> getNoiseBiome(int x, int y, int z, @NotNull Sampler sampler) {
|
||||
return biomeRegistry.getHolderOrThrow(((BukkitPlatformBiome) delegate.getBiome(x << 2, y << 2, z << 2, seed)
|
||||
.getPlatformBiome()).getContext()
|
||||
.get(NMSBiomeInfo.class)
|
||||
.biomeKey());
|
||||
}
|
||||
}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R2;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.WorldGenRegion;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.NoiseColumn;
|
||||
import net.minecraft.world.level.StructureManager;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.biome.BiomeManager;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.Beardifier;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction.SinglePointContext;
|
||||
import net.minecraft.world.level.levelgen.GenerationStep.Carving;
|
||||
import net.minecraft.world.level.levelgen.Heightmap.Types;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||
import org.bukkit.craftbukkit.v1_19_R2.block.data.CraftBlockData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import com.dfsek.terra.api.config.ConfigPack;
|
||||
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
|
||||
import com.dfsek.terra.api.world.info.WorldProperties;
|
||||
import com.dfsek.terra.bukkit.config.PreLoadCompatibilityOptions;
|
||||
import com.dfsek.terra.bukkit.world.BukkitWorldProperties;
|
||||
import com.dfsek.terra.bukkit.world.block.data.BukkitBlockState;
|
||||
|
||||
|
||||
public class NMSChunkGeneratorDelegate extends ChunkGenerator {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(NMSChunkGeneratorDelegate.class);
|
||||
private final com.dfsek.terra.api.world.chunk.generation.ChunkGenerator delegate;
|
||||
|
||||
private final ChunkGenerator vanilla;
|
||||
private final ConfigPack pack;
|
||||
|
||||
private final long seed;
|
||||
|
||||
public NMSChunkGeneratorDelegate(ChunkGenerator vanilla, ConfigPack pack, NMSBiomeProvider biomeProvider, long seed) {
|
||||
super(biomeProvider);
|
||||
this.delegate = pack.getGeneratorProvider().newInstance(pack);
|
||||
this.vanilla = vanilla;
|
||||
this.pack = pack;
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull Codec<? extends ChunkGenerator> codec() {
|
||||
return ChunkGenerator.CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyCarvers(@NotNull WorldGenRegion chunkRegion, long seed, @NotNull RandomState noiseConfig, @NotNull BiomeManager world,
|
||||
@NotNull StructureManager structureAccessor, @NotNull ChunkAccess chunk, @NotNull Carving carverStep) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildSurface(@NotNull WorldGenRegion region, @NotNull StructureManager structures, @NotNull RandomState noiseConfig,
|
||||
@NotNull ChunkAccess chunk) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyBiomeDecoration(@NotNull WorldGenLevel world, @NotNull ChunkAccess chunk,
|
||||
@NotNull StructureManager structureAccessor) {
|
||||
vanilla.applyBiomeDecoration(world, chunk, structureAccessor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnOriginalMobs(@NotNull WorldGenRegion region) {
|
||||
vanilla.spawnOriginalMobs(region);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGenDepth() {
|
||||
return vanilla.getGenDepth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<ChunkAccess> fillFromNoise(@NotNull Executor executor, @NotNull Blender blender,
|
||||
@NotNull RandomState noiseConfig,
|
||||
@NotNull StructureManager structureAccessor, @NotNull ChunkAccess chunk) {
|
||||
return vanilla.fillFromNoise(executor, blender, noiseConfig, structureAccessor, chunk)
|
||||
.thenApply(c -> {
|
||||
LevelAccessor level = Reflection.STRUCTURE_MANAGER.getLevel(structureAccessor);
|
||||
BiomeProvider biomeProvider = pack.getBiomeProvider();
|
||||
PreLoadCompatibilityOptions compatibilityOptions = pack.getContext().get(PreLoadCompatibilityOptions.class);
|
||||
if(compatibilityOptions.isBeard()) {
|
||||
beard(structureAccessor, chunk, new BukkitWorldProperties(level.getMinecraftWorld().getWorld()),
|
||||
biomeProvider, compatibilityOptions);
|
||||
}
|
||||
return c;
|
||||
});
|
||||
}
|
||||
|
||||
private void beard(StructureManager structureAccessor, ChunkAccess chunk, WorldProperties world, BiomeProvider biomeProvider,
|
||||
PreLoadCompatibilityOptions compatibilityOptions) {
|
||||
Beardifier structureWeightSampler = Beardifier.forStructuresInChunk(structureAccessor, chunk.getPos());
|
||||
double threshold = compatibilityOptions.getBeardThreshold();
|
||||
double airThreshold = compatibilityOptions.getAirThreshold();
|
||||
int xi = chunk.getPos().x << 4;
|
||||
int zi = chunk.getPos().z << 4;
|
||||
for(int x = 0; x < 16; x++) {
|
||||
for(int z = 0; z < 16; z++) {
|
||||
int depth = 0;
|
||||
for(int y = world.getMaxHeight(); y >= world.getMinHeight(); y--) {
|
||||
double noise = structureWeightSampler.compute(new SinglePointContext(x + xi, y, z + zi));
|
||||
if(noise > threshold) {
|
||||
chunk.setBlockState(new BlockPos(x, y, z), ((CraftBlockData) ((BukkitBlockState) delegate
|
||||
.getPalette(x + xi, y, z + zi, world, biomeProvider)
|
||||
.get(depth, x + xi, y, z + zi, world.getSeed())).getHandle()).getState(), false);
|
||||
depth++;
|
||||
} else if(noise < airThreshold) {
|
||||
chunk.setBlockState(new BlockPos(x, y, z), Blocks.AIR.defaultBlockState(), false);
|
||||
} else {
|
||||
depth = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSeaLevel() {
|
||||
return vanilla.getSeaLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinY() {
|
||||
return vanilla.getMinY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBaseHeight(int x, int z, @NotNull Types heightmap, @NotNull LevelHeightAccessor world, @NotNull RandomState noiseConfig) {
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NoiseColumn getBaseColumn(int x, int z, @NotNull LevelHeightAccessor world, @NotNull RandomState noiseConfig) {
|
||||
/*
|
||||
BlockState[] array = new BlockState[world.getHeight()];
|
||||
WorldProperties properties = new NMSWorldProperties(seed, world);
|
||||
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, world, noiseConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDebugScreenInfo(@NotNull List<String> text, @NotNull RandomState noiseConfig, @NotNull BlockPos pos) {
|
||||
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_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());
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_20_R3;
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R2;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.world.WorldInitEvent;
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R2;
|
||||
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
|
||||
import com.dfsek.terra.api.world.info.WorldProperties;
|
||||
|
||||
|
||||
public class NMSWorldProperties implements WorldProperties {
|
||||
private final long seed;
|
||||
private final LevelHeightAccessor height;
|
||||
|
||||
public NMSWorldProperties(long seed, LevelHeightAccessor height) {
|
||||
this.seed = seed;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getHandle() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSeed() {
|
||||
return seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxHeight() {
|
||||
return height.getMaxBuildHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinHeight() {
|
||||
return height.getMinBuildHeight();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_20_R3;
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R2;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Holder.Reference;
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_20_R3;
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R2;
|
||||
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
@@ -6,7 +6,7 @@ import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_19_R2.CraftServer;
|
||||
|
||||
|
||||
public class RegistryFetcher {
|
||||
@@ -0,0 +1,17 @@
|
||||
apply(plugin = "io.papermc.paperweight.userdev")
|
||||
|
||||
repositories {
|
||||
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":platforms:bukkit:common"))
|
||||
paperDevBundle("1.19.4-R0.1-SNAPSHOT")
|
||||
implementation("xyz.jpenilla", "reflection-remapper", Versions.Bukkit.reflectionRemapper)
|
||||
}
|
||||
|
||||
tasks {
|
||||
assemble {
|
||||
dependsOn("reobfJar")
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_20_R3;
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R3;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R3;
|
||||
|
||||
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
-1
@@ -1,4 +1,4 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_20_R3;
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R3;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_20_R3;
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R3;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import net.minecraft.core.Holder;
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_20_R3;
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R3;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import net.minecraft.core.BlockPos;
|
||||
@@ -19,7 +19,7 @@ import net.minecraft.world.level.levelgen.GenerationStep.Carving;
|
||||
import net.minecraft.world.level.levelgen.Heightmap.Types;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R3;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R3;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.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(), craftWorld.getSeed());
|
||||
|
||||
serverWorld.getChunkSource().chunkMap.generator = new NMSChunkGeneratorDelegate(vanilla, pack, provider, craftWorld.getSeed());
|
||||
|
||||
LOGGER.info("Successfully injected into world.");
|
||||
|
||||
INJECT_LOCK.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R3;
|
||||
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
|
||||
import com.dfsek.terra.api.world.info.WorldProperties;
|
||||
|
||||
|
||||
public class NMSWorldProperties implements WorldProperties {
|
||||
private final long seed;
|
||||
private final LevelHeightAccessor height;
|
||||
|
||||
public NMSWorldProperties(long seed, LevelHeightAccessor height) {
|
||||
this.seed = seed;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getHandle() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSeed() {
|
||||
return seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxHeight() {
|
||||
return height.getMaxBuildHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinHeight() {
|
||||
return height.getMinBuildHeight();
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R3;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Holder.Reference;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.StructureManager;
|
||||
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.MethodName;
|
||||
import xyz.jpenilla.reflectionremapper.proxy.annotation.Proxies;
|
||||
|
||||
|
||||
public class Reflection {
|
||||
public static final MappedRegistryProxy MAPPED_REGISTRY;
|
||||
public static final StructureManagerProxy STRUCTURE_MANAGER;
|
||||
|
||||
public static final ReferenceProxy REFERENCE;
|
||||
|
||||
static {
|
||||
ReflectionRemapper reflectionRemapper = ReflectionRemapper.forReobfMappingsInPaperJar();
|
||||
ReflectionProxyFactory reflectionProxyFactory = ReflectionProxyFactory.create(reflectionRemapper,
|
||||
Reflection.class.getClassLoader());
|
||||
|
||||
MAPPED_REGISTRY = reflectionProxyFactory.reflectionProxy(MappedRegistryProxy.class);
|
||||
STRUCTURE_MANAGER = reflectionProxyFactory.reflectionProxy(StructureManagerProxy.class);
|
||||
REFERENCE = reflectionProxyFactory.reflectionProxy(ReferenceProxy.class);
|
||||
}
|
||||
|
||||
|
||||
@Proxies(MappedRegistry.class)
|
||||
public interface MappedRegistryProxy {
|
||||
@FieldSetter("frozen")
|
||||
void setFrozen(MappedRegistry<?> instance, boolean frozen);
|
||||
}
|
||||
|
||||
|
||||
@Proxies(StructureManager.class)
|
||||
public interface StructureManagerProxy {
|
||||
@FieldGetter("level")
|
||||
LevelAccessor getLevel(StructureManager instance);
|
||||
}
|
||||
|
||||
|
||||
@Proxies(Holder.Reference.class)
|
||||
public interface ReferenceProxy {
|
||||
@MethodName("bindValue")
|
||||
<T> void invokeBindValue(Reference<T> instance, T value);
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_19_R3;
|
||||
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
|
||||
|
||||
|
||||
public class RegistryFetcher {
|
||||
private static <T> Registry<T> getRegistry(ResourceKey<Registry<T>> key) {
|
||||
CraftServer craftserver = (CraftServer) Bukkit.getServer();
|
||||
DedicatedServer dedicatedserver = craftserver.getServer();
|
||||
return dedicatedserver
|
||||
.registryAccess()
|
||||
.registryOrThrow(key);
|
||||
}
|
||||
|
||||
public static Registry<Biome> biomeRegistry() {
|
||||
return getRegistry(Registries.BIOME);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
apply(plugin = "io.papermc.paperweight.userdev")
|
||||
|
||||
repositories {
|
||||
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":platforms:bukkit:common"))
|
||||
paperDevBundle("1.20.1-R0.1-SNAPSHOT")
|
||||
implementation("xyz.jpenilla", "reflection-remapper", Versions.Bukkit.reflectionRemapper)
|
||||
}
|
||||
|
||||
tasks {
|
||||
assemble {
|
||||
dependsOn("reobfJar")
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_20_R1;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Holder.Reference;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.core.WritableRegistry;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
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 java.util.Objects;
|
||||
|
||||
import com.dfsek.terra.bukkit.world.BukkitPlatformBiome;
|
||||
import com.dfsek.terra.registry.master.ConfigRegistry;
|
||||
|
||||
|
||||
public class AwfulBukkitHacks {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AwfulBukkitHacks.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>) RegistryFetcher.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, Objects.requireNonNull(biomeRegistry.get(vanillaMinecraftKey)));
|
||||
|
||||
ResourceKey<Biome> delegateKey = ResourceKey.create(
|
||||
Registries.BIOME,
|
||||
new ResourceLocation("terra", NMSBiomeInjector.createBiomeID(pack, key))
|
||||
);
|
||||
|
||||
Reference<Biome> holder = biomeRegistry.register(delegateKey, platform, Lifecycle.stable());
|
||||
Reflection.REFERENCE.invokeBindValue(holder, platform); // IMPORTANT: bind holder.
|
||||
|
||||
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 for {}): {}",
|
||||
vanilla.unwrapKey().orElseThrow().location(),
|
||||
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();
|
||||
biomeRegistry.bindTags(ImmutableMap.copyOf(collect));
|
||||
|
||||
} catch(SecurityException | IllegalArgumentException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_20_R1;
|
||||
|
||||
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 {
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_20_R1;
|
||||
|
||||
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)
|
||||
.flatMap(registry::getHolder);
|
||||
}
|
||||
|
||||
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
|
||||
.downfall(vanilla.climateSettings.downfall())
|
||||
.temperature(vanilla.getBaseTemperature())
|
||||
.mobSpawnSettings(vanilla.getMobSettings())
|
||||
.generationSettings(vanilla.getGenerationSettings());
|
||||
|
||||
|
||||
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 {
|
||||
// grass
|
||||
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();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_20_R1;
|
||||
|
||||
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 java.util.stream.Stream;
|
||||
|
||||
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 long seed;
|
||||
private final Registry<Biome> biomeRegistry = RegistryFetcher.biomeRegistry();
|
||||
|
||||
public NMSBiomeProvider(BiomeProvider delegate, long seed) {
|
||||
super();
|
||||
this.delegate = delegate;
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<Holder<Biome>> collectPossibleBiomes() {
|
||||
return delegate.stream()
|
||||
.map(biome -> RegistryFetcher.biomeRegistry()
|
||||
.getHolderOrThrow(((BukkitPlatformBiome) biome.getPlatformBiome()).getContext()
|
||||
.get(NMSBiomeInfo.class)
|
||||
.biomeKey()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull Codec<? extends BiomeSource> codec() {
|
||||
return BiomeSource.CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Holder<Biome> getNoiseBiome(int x, int y, int z, @NotNull Sampler sampler) {
|
||||
return biomeRegistry.getHolderOrThrow(((BukkitPlatformBiome) delegate.getBiome(x << 2, y << 2, z << 2, seed)
|
||||
.getPlatformBiome()).getContext()
|
||||
.get(NMSBiomeInfo.class)
|
||||
.biomeKey());
|
||||
}
|
||||
}
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_20_R1;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.WorldGenRegion;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.NoiseColumn;
|
||||
import net.minecraft.world.level.StructureManager;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.biome.BiomeManager;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.Beardifier;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction.SinglePointContext;
|
||||
import net.minecraft.world.level.levelgen.GenerationStep.Carving;
|
||||
import net.minecraft.world.level.levelgen.Heightmap.Types;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import com.dfsek.terra.api.config.ConfigPack;
|
||||
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
|
||||
import com.dfsek.terra.api.world.info.WorldProperties;
|
||||
import com.dfsek.terra.bukkit.config.PreLoadCompatibilityOptions;
|
||||
import com.dfsek.terra.bukkit.world.BukkitWorldProperties;
|
||||
import com.dfsek.terra.bukkit.world.block.data.BukkitBlockState;
|
||||
|
||||
|
||||
public class NMSChunkGeneratorDelegate extends ChunkGenerator {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(NMSChunkGeneratorDelegate.class);
|
||||
private final com.dfsek.terra.api.world.chunk.generation.ChunkGenerator delegate;
|
||||
|
||||
private final ChunkGenerator vanilla;
|
||||
private final ConfigPack pack;
|
||||
|
||||
private final long seed;
|
||||
|
||||
public NMSChunkGeneratorDelegate(ChunkGenerator vanilla, ConfigPack pack, NMSBiomeProvider biomeProvider, long seed) {
|
||||
super(biomeProvider);
|
||||
this.delegate = pack.getGeneratorProvider().newInstance(pack);
|
||||
this.vanilla = vanilla;
|
||||
this.pack = pack;
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull Codec<? extends ChunkGenerator> codec() {
|
||||
return ChunkGenerator.CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyCarvers(@NotNull WorldGenRegion chunkRegion, long seed, @NotNull RandomState noiseConfig, @NotNull BiomeManager world,
|
||||
@NotNull StructureManager structureAccessor, @NotNull ChunkAccess chunk, @NotNull Carving carverStep) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildSurface(@NotNull WorldGenRegion region, @NotNull StructureManager structures, @NotNull RandomState noiseConfig,
|
||||
@NotNull ChunkAccess chunk) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyBiomeDecoration(@NotNull WorldGenLevel world, @NotNull ChunkAccess chunk,
|
||||
@NotNull StructureManager structureAccessor) {
|
||||
vanilla.applyBiomeDecoration(world, chunk, structureAccessor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnOriginalMobs(@NotNull WorldGenRegion region) {
|
||||
vanilla.spawnOriginalMobs(region);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGenDepth() {
|
||||
return vanilla.getGenDepth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<ChunkAccess> fillFromNoise(@NotNull Executor executor, @NotNull Blender blender,
|
||||
@NotNull RandomState noiseConfig,
|
||||
@NotNull StructureManager structureAccessor, @NotNull ChunkAccess chunk) {
|
||||
return vanilla.fillFromNoise(executor, blender, noiseConfig, structureAccessor, chunk)
|
||||
.thenApply(c -> {
|
||||
LevelAccessor level = Reflection.STRUCTURE_MANAGER.getLevel(structureAccessor);
|
||||
BiomeProvider biomeProvider = pack.getBiomeProvider();
|
||||
PreLoadCompatibilityOptions compatibilityOptions = pack.getContext().get(PreLoadCompatibilityOptions.class);
|
||||
if(compatibilityOptions.isBeard()) {
|
||||
beard(structureAccessor, chunk, new BukkitWorldProperties(level.getMinecraftWorld().getWorld()),
|
||||
biomeProvider, compatibilityOptions);
|
||||
}
|
||||
return c;
|
||||
});
|
||||
}
|
||||
|
||||
private void beard(StructureManager structureAccessor, ChunkAccess chunk, WorldProperties world, BiomeProvider biomeProvider,
|
||||
PreLoadCompatibilityOptions compatibilityOptions) {
|
||||
Beardifier structureWeightSampler = Beardifier.forStructuresInChunk(structureAccessor, chunk.getPos());
|
||||
double threshold = compatibilityOptions.getBeardThreshold();
|
||||
double airThreshold = compatibilityOptions.getAirThreshold();
|
||||
int xi = chunk.getPos().x << 4;
|
||||
int zi = chunk.getPos().z << 4;
|
||||
for(int x = 0; x < 16; x++) {
|
||||
for(int z = 0; z < 16; z++) {
|
||||
int depth = 0;
|
||||
for(int y = world.getMaxHeight(); y >= world.getMinHeight(); y--) {
|
||||
double noise = structureWeightSampler.compute(new SinglePointContext(x + xi, y, z + zi));
|
||||
if(noise > threshold) {
|
||||
chunk.setBlockState(new BlockPos(x, y, z), ((CraftBlockData) ((BukkitBlockState) delegate
|
||||
.getPalette(x + xi, y, z + zi, world, biomeProvider)
|
||||
.get(depth, x + xi, y, z + zi, world.getSeed())).getHandle()).getState(), false);
|
||||
depth++;
|
||||
} else if(noise < airThreshold) {
|
||||
chunk.setBlockState(new BlockPos(x, y, z), Blocks.AIR.defaultBlockState(), false);
|
||||
} else {
|
||||
depth = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSeaLevel() {
|
||||
return vanilla.getSeaLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinY() {
|
||||
return vanilla.getMinY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBaseHeight(int x, int z, @NotNull Types heightmap, @NotNull LevelHeightAccessor world, @NotNull RandomState noiseConfig) {
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NoiseColumn getBaseColumn(int x, int z, @NotNull LevelHeightAccessor world, @NotNull RandomState noiseConfig) {
|
||||
BlockState[] array = new BlockState[world.getHeight()];
|
||||
WorldProperties properties = new NMSWorldProperties(seed, world);
|
||||
BiomeProvider biomeProvider = pack.getBiomeProvider();
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDebugScreenInfo(@NotNull List<String> text, @NotNull RandomState noiseConfig, @NotNull BlockPos pos) {
|
||||
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_20_R1;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_20_R1;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.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(), craftWorld.getSeed());
|
||||
|
||||
serverWorld.getChunkSource().chunkMap.generator = new NMSChunkGeneratorDelegate(vanilla, pack, provider, craftWorld.getSeed());
|
||||
|
||||
LOGGER.info("Successfully injected into world.");
|
||||
|
||||
INJECT_LOCK.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.dfsek.terra.bukkit.nms.v1_20_R1;
|
||||
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
|
||||
import com.dfsek.terra.api.world.info.WorldProperties;
|
||||
|
||||
|
||||
public class NMSWorldProperties implements WorldProperties {
|
||||
private final long seed;
|
||||
private final LevelHeightAccessor height;
|
||||
|
||||
public NMSWorldProperties(long seed, LevelHeightAccessor height) {
|
||||
this.seed = seed;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getHandle() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSeed() {
|
||||
return seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxHeight() {
|
||||
return height.getMaxBuildHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinHeight() {
|
||||
return height.getMinBuildHeight();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user