Merge pull request #318 from PolyhedralDev/dev/1.19

Update to 1.19
This commit is contained in:
dfsek 2022-06-07 15:40:41 -07:00 committed by GitHub
commit 0feae25be5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 557 additions and 721 deletions

View File

@ -1,8 +1,8 @@
preRelease(true) preRelease(true)
versionProjects(":common:api", version("6.0.1")) versionProjects(":common:api", version("6.1.0"))
versionProjects(":common:implementation", version("6.0.1")) versionProjects(":common:implementation", version("6.1.0"))
versionProjects(":platforms", version("6.0.1")) versionProjects(":platforms", version("6.1.0"))
allprojects { allprojects {

View File

@ -3,6 +3,15 @@ plugins {
kotlin("jvm") version embeddedKotlinVersion kotlin("jvm") version embeddedKotlinVersion
} }
buildscript {
configurations.all {
resolutionStrategy {
force("org.ow2.asm:asm:9.3") // TODO: remove when ShadowJar updates ASM version
force("org.ow2.asm:asm-commons:9.3")
}
}
}
repositories { repositories {
mavenCentral() mavenCentral()
gradlePluginPortal() gradlePluginPortal()
@ -11,8 +20,8 @@ repositories {
dependencies { dependencies {
implementation("gradle.plugin.com.github.jengelman.gradle.plugins:shadow:+") implementation("gradle.plugin.com.github.jengelman.gradle.plugins:shadow:+")
implementation("org.ow2.asm:asm:9.2") implementation("org.ow2.asm:asm:9.3")
implementation("org.ow2.asm:asm-tree:9.2") implementation("org.ow2.asm:asm-tree:9.3")
implementation("com.dfsek.tectonic:common:4.2.0") implementation("com.dfsek.tectonic:common:4.2.0")
implementation("org.yaml:snakeyaml:1.27") implementation("org.yaml:snakeyaml:1.27")
} }

View File

@ -3,7 +3,6 @@ import java.io.File
import java.util.function.Predicate import java.util.function.Predicate
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.Task import org.gradle.api.Task
import org.gradle.jvm.tasks.Jar
import org.gradle.kotlin.dsl.extra import org.gradle.kotlin.dsl.extra
import kotlin.streams.asStream import kotlin.streams.asStream
@ -12,28 +11,33 @@ import kotlin.streams.asStream
* Configures a directory where addons will be put. * Configures a directory where addons will be put.
*/ */
fun Project.addonDir(dir: File, task: Task) { fun Project.addonDir(dir: File, task: Task) {
task.dependsOn("compileAddons") val moveAddons = tasks.register("moveAddons" + task.name) {
task.doFirst { dependsOn("compileAddons")
dir.parentFile.mkdirs() doLast {
matchingAddons(dir) { dir.parentFile.mkdirs()
it.name.startsWith("Terra-") // Assume everything that starts with Terra- is a core addon. matchingAddons(dir) {
}.forEach { it.name.startsWith("Terra-") // Assume everything that starts with Terra- is a core addon.
println("Deleting old addon: " + it.absolutePath) }.forEach {
it.delete() println("Deleting old addon: " + it.absolutePath)
it.delete()
}
forSubProjects(":common:addons") {
val jar = tasks.named("shadowJar").get() as ShadowJar
val boot = if (extra.has("bootstrap") && extra.get("bootstrap") as Boolean) "bootstrap/" else ""
val target = File(dir, boot + jar.archiveFileName.get())
val base = "${jar.archiveBaseName.get()}-${version}"
println("Copying addon ${jar.archiveFileName.get()} to ${target.absolutePath}. Base name: $base")
jar.archiveFile.orNull?.asFile?.copyTo(target)
}
}
} }
forSubProjects(":common:addons") {
val jar = tasks.named("shadowJar").get() as ShadowJar task.dependsOn(moveAddons)
val boot = if (extra.has("bootstrap") && extra.get("bootstrap") as Boolean) "bootstrap/" else ""
val target = File(dir, boot + jar.archiveFileName.get())
val base = "${jar.archiveBaseName.get()}-${version}"
println("Copying addon ${jar.archiveFileName.get()} to ${target.absolutePath}. Base name: $base")
jar.archiveFile.orNull?.asFile?.copyTo(target)
}
}
} }
fun matchingAddons(dir: File, matcher: Predicate<File>): Set<File> { fun matchingAddons(dir: File, matcher: Predicate<File>): Set<File> {

View File

@ -19,10 +19,12 @@ object Versions {
object Fabric { object Fabric {
const val fabricLoader = "0.14.2" const val fabricLoader = "0.14.2"
const val fabricAPI = "0.53.4+1.18.2" const val fabricAPI = "0.55.1+1.19"
const val minecraft = "1.18.2" const val minecraft = "1.19"
const val yarn = "$minecraft+build.3" const val yarn = "$minecraft+build.1"
const val permissionsAPI = "0.1-SNAPSHOT" const val permissionsAPI = "0.1-SNAPSHOT"
const val mixin = "0.11.2+mixin.0.8.5"
const val loom = "0.11-SNAPSHOT"
} }
object Bukkit { object Bukkit {

View File

@ -19,33 +19,6 @@ package com.dfsek.terra;
import com.dfsek.tectonic.api.TypeRegistry; import com.dfsek.tectonic.api.TypeRegistry;
import com.dfsek.terra.api.util.generic.pair.Pair;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import com.dfsek.terra.addon.BootstrapAddonLoader; import com.dfsek.terra.addon.BootstrapAddonLoader;
import com.dfsek.terra.addon.DependencySorter; import com.dfsek.terra.addon.DependencySorter;
import com.dfsek.terra.addon.EphemeralAddon; import com.dfsek.terra.addon.EphemeralAddon;
@ -64,6 +37,7 @@ import com.dfsek.terra.api.profiler.Profiler;
import com.dfsek.terra.api.registry.CheckedRegistry; import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.registry.Registry; import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.registry.key.StringIdentifiable; import com.dfsek.terra.api.registry.key.StringIdentifiable;
import com.dfsek.terra.api.util.generic.pair.Pair;
import com.dfsek.terra.api.util.mutable.MutableBoolean; import com.dfsek.terra.api.util.mutable.MutableBoolean;
import com.dfsek.terra.api.util.reflection.TypeKey; import com.dfsek.terra.api.util.reflection.TypeKey;
import com.dfsek.terra.config.GenericLoaders; import com.dfsek.terra.config.GenericLoaders;
@ -75,6 +49,21 @@ import com.dfsek.terra.registry.LockedRegistryImpl;
import com.dfsek.terra.registry.OpenRegistryImpl; import com.dfsek.terra.registry.OpenRegistryImpl;
import com.dfsek.terra.registry.master.ConfigRegistry; import com.dfsek.terra.registry.master.ConfigRegistry;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
/** /**
* Skeleton implementation of {@link Platform} * Skeleton implementation of {@link Platform}
@ -119,8 +108,10 @@ public abstract class AbstractPlatform implements Platform {
logger.info("Initializing Terra..."); logger.info("Initializing Terra...");
try(InputStream stream = getClass().getResourceAsStream("/config.yml")) { try(InputStream stream = getClass().getResourceAsStream("/config.yml")) {
logger.info("Loading config.yml");
File configFile = new File(getDataFolder(), "config.yml"); File configFile = new File(getDataFolder(), "config.yml");
if(!configFile.exists()) { if(!configFile.exists()) {
logger.info("Writing new config.yml...");
FileUtils.copyInputStreamToFile(stream, configFile); FileUtils.copyInputStreamToFile(stream, configFile);
} }
} catch(IOException e) { } catch(IOException e) {
@ -222,6 +213,7 @@ public abstract class AbstractPlatform implements Platform {
Path data = getDataFolder().toPath(); Path data = getDataFolder().toPath();
Path addonsPath = data.resolve("addons"); Path addonsPath = data.resolve("addons");
Files.createDirectories(addonsPath);
Set<Pair<Path, String>> paths = Files Set<Pair<Path, String>> paths = Files
.walk(addonsPath) .walk(addonsPath)
.map(path -> Pair.of(path, data.relativize(path).toString())) .map(path -> Pair.of(path, data.relativize(path).toString()))
@ -249,7 +241,6 @@ public abstract class AbstractPlatform implements Platform {
.collect(Collectors.toSet()); .collect(Collectors.toSet());
// Terra-aaa-aaa-1.2.3-BETA+1e6af8923d.jar
String resourceYaml = IOUtils.toString(resourcesConfig, StandardCharsets.UTF_8); String resourceYaml = IOUtils.toString(resourcesConfig, StandardCharsets.UTF_8);
Map<String, List<String>> resources = new Yaml().load(resourceYaml); Map<String, List<String>> resources = new Yaml().load(resourceYaml);
resources.forEach((dir, entries) -> entries.forEach(entry -> { resources.forEach((dir, entries) -> entries.forEach(entry -> {
@ -258,42 +249,44 @@ public abstract class AbstractPlatform implements Platform {
if(resource.exists()) if(resource.exists())
return; // dont overwrite return; // dont overwrite
paths try(InputStream is = getClass().getResourceAsStream("/" + resourcePath)) {
.stream() if(is == null) {
.filter(Pair.testRight(resourcePath::startsWith)) logger.error("Resource {} doesn't exist on the classpath!", resourcePath);
.forEach(Pair.consumeLeft(path -> { return;
logger.info("Removing outdated resource {}, replacing with {}", path, resourcePath); }
try {
Files.delete(path); paths
} catch(IOException e) { .stream()
throw new UncheckedIOException(e); .filter(Pair.testRight(resourcePath::startsWith))
} .forEach(Pair.consumeLeft(path -> {
})); logger.info("Removing outdated resource {}, replacing with {}", path, resourcePath);
try {
if(pathsNoMajor Files.delete(path);
.stream() } catch(IOException e) {
.anyMatch(resourcePath::startsWith) && // if any share name throw new UncheckedIOException(e);
paths }
.stream() }));
.map(Pair.unwrapRight())
.noneMatch(resourcePath::startsWith)) { // but dont share major version if(pathsNoMajor
logger.warn( .stream()
"Addon {} has a new major version available. It will not be automatically updated; you will need to ensure " + .anyMatch(resourcePath::startsWith) && // if any share name
"compatibility and update manually.", paths
resourcePath); .stream()
} .map(Pair.unwrapRight())
.noneMatch(resourcePath::startsWith)) { // but dont share major version
logger.info("Dumping resource {}...", resource.getAbsolutePath()); logger.warn(
try { "Addon {} has a new major version available. It will not be automatically updated; you will need to " +
"ensure " +
"compatibility and update manually.",
resourcePath);
}
logger.info("Dumping resource {}...", resource.getAbsolutePath());
resource.getParentFile().mkdirs(); resource.getParentFile().mkdirs();
resource.createNewFile(); resource.createNewFile();
} catch(IOException e) { try(OutputStream os = new FileOutputStream(resource)) {
throw new UncheckedIOException(e); IOUtils.copy(is, os);
} }
logger.debug("Copying resource {}", resourcePath);
try(InputStream is = getClass().getResourceAsStream("/" + resourcePath);
OutputStream os = new FileOutputStream(resource)) {
IOUtils.copy(is, os);
} catch(IOException e) { } catch(IOException e) {
throw new UncheckedIOException(e); throw new UncheckedIOException(e);
} }

View File

@ -74,21 +74,24 @@ public class BootstrapAddonLoader implements BootstrapBaseAddon<BootstrapBaseAdd
} }
} catch(IOException e) { } catch(IOException e) {
throw new UncheckedIOException(e); throw new AddonLoadException("Failed to load addon from path " + addonPath, e);
} }
} }
@Override @Override
public Iterable<BootstrapBaseAddon<?>> loadAddons(Path addonsFolder, BootstrapAddonClassLoader parent) { public Iterable<BootstrapBaseAddon<?>> loadAddons(Path addonsFolder, BootstrapAddonClassLoader parent) {
Path bootstrapFolder = addonsFolder.resolve("bootstrap"); try {
logger.debug("Loading bootstrap addons from {}", bootstrapFolder); Path bootstrapFolder = addonsFolder.resolve("bootstrap");
Files.createDirectories(bootstrapFolder);
try(Stream<Path> bootstrapAddons = Files.walk(bootstrapFolder, 1, FileVisitOption.FOLLOW_LINKS)) { logger.debug("Loading bootstrap addons from {}", bootstrapFolder);
return bootstrapAddons.filter(path -> path.toFile().isFile())
.filter(path -> path.toFile().canRead()) try(Stream<Path> bootstrapAddons = Files.walk(bootstrapFolder, 1, FileVisitOption.FOLLOW_LINKS)) {
.filter(path -> path.toString().endsWith(".jar")) return bootstrapAddons.filter(path -> path.toFile().isFile())
.map(path -> loadAddon(path, parent)) .filter(path -> path.toFile().canRead())
.collect(Collectors.toList()); .filter(path -> path.toString().endsWith(".jar"))
.map(path -> loadAddon(path, parent))
.collect(Collectors.toList());
}
} catch(IOException e) { } catch(IOException e) {
throw new UncheckedIOException(e); throw new UncheckedIOException(e);
} }

View File

@ -3,7 +3,7 @@ import com.modrinth.minotaur.TaskModrinthUpload
import net.fabricmc.loom.task.RemapJarTask import net.fabricmc.loom.task.RemapJarTask
plugins { plugins {
id("fabric-loom").version("0.11-SNAPSHOT") id("fabric-loom").version(Versions.Fabric.loom)
id("com.modrinth.minotaur").version("1.1.0") id("com.modrinth.minotaur").version("1.1.0")
} }
@ -15,7 +15,7 @@ dependencies {
modImplementation("net.fabricmc:fabric-loader:${Versions.Fabric.fabricLoader}") modImplementation("net.fabricmc:fabric-loader:${Versions.Fabric.fabricLoader}")
setOf("fabric-command-api-v1", "fabric-lifecycle-events-v1", "fabric-resource-loader-v0", "fabric-api-base").forEach { apiModule -> setOf("fabric-lifecycle-events-v1", "fabric-resource-loader-v0", "fabric-api-base").forEach { apiModule ->
val module = fabricApi.module(apiModule, Versions.Fabric.fabricAPI) val module = fabricApi.module(apiModule, Versions.Fabric.fabricAPI)
modImplementation(module) modImplementation(module)
include(module) include(module)
@ -24,8 +24,12 @@ dependencies {
include(modImplementation("me.lucko", "fabric-permissions-api", Versions.Fabric.permissionsAPI)) include(modImplementation("me.lucko", "fabric-permissions-api", Versions.Fabric.permissionsAPI))
include("me.lucko", "fabric-permissions-api", Versions.Fabric.permissionsAPI) include("me.lucko", "fabric-permissions-api", Versions.Fabric.permissionsAPI)
include(modImplementation("cloud.commandframework", "cloud-fabric", Versions.Libraries.cloud)) "compileOnly"("net.fabricmc:sponge-mixin:${Versions.Fabric.mixin}")
include("cloud.commandframework", "cloud-fabric", Versions.Libraries.cloud) "annotationProcessor"("net.fabricmc:sponge-mixin:${Versions.Fabric.mixin}")
"annotationProcessor"("net.fabricmc:fabric-loom:${Versions.Fabric.loom}")
//include(modImplementation("cloud.commandframework", "cloud-fabric", Versions.Libraries.cloud))
//include("cloud.commandframework", "cloud-fabric", Versions.Libraries.cloud)
} }
loom { loom {

View File

@ -17,17 +17,13 @@
package com.dfsek.terra.fabric; package com.dfsek.terra.fabric;
import cloud.commandframework.execution.CommandExecutionCoordinator;
import cloud.commandframework.fabric.FabricServerCommandManager;
import net.fabricmc.api.ModInitializer; import net.fabricmc.api.ModInitializer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.Registry;
import net.minecraft.world.gen.WorldPresets;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.dfsek.terra.api.command.CommandSender;
import com.dfsek.terra.api.event.events.platform.CommandRegistrationEvent;
import com.dfsek.terra.fabric.data.Codecs; import com.dfsek.terra.fabric.data.Codecs;
@ -49,7 +45,7 @@ public class FabricEntryPoint implements ModInitializer {
@Override @Override
public void onInitialize() { public void onInitialize() {
logger.info("Initializing Terra Fabric mod..."); logger.info("Initializing Terra Fabric mod...");
/*
FabricServerCommandManager<CommandSender> manager = new FabricServerCommandManager<>( FabricServerCommandManager<CommandSender> manager = new FabricServerCommandManager<>(
CommandExecutionCoordinator.simpleCoordinator(), CommandExecutionCoordinator.simpleCoordinator(),
serverCommandSource -> (CommandSender) serverCommandSource, serverCommandSource -> (CommandSender) serverCommandSource,
@ -60,5 +56,7 @@ public class FabricEntryPoint implements ModInitializer {
manager.brigadierManager().setNativeNumberSuggestions(false); manager.brigadierManager().setNativeNumberSuggestions(false);
TERRA_PLUGIN.getEventManager().callEvent(new CommandRegistrationEvent(manager)); TERRA_PLUGIN.getEventManager().callEvent(new CommandRegistrationEvent(manager));
TODO: re-enable when Cloud updates
*/
} }
} }

View File

@ -29,7 +29,6 @@ import net.minecraft.MinecraftVersion;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.registry.BuiltinRegistries; import net.minecraft.util.registry.BuiltinRegistries;
import net.minecraft.world.biome.Biome.Category;
import net.minecraft.world.biome.Biome.Precipitation; import net.minecraft.world.biome.Biome.Precipitation;
import net.minecraft.world.biome.BiomeEffects.GrassColorModifier; import net.minecraft.world.biome.BiomeEffects.GrassColorModifier;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -39,6 +38,7 @@ import org.slf4j.LoggerFactory;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale;
import com.dfsek.terra.AbstractPlatform; import com.dfsek.terra.AbstractPlatform;
import com.dfsek.terra.addon.EphemeralAddon; import com.dfsek.terra.addon.EphemeralAddon;
@ -68,6 +68,10 @@ public class PlatformImpl extends AbstractPlatform {
this.server = server; this.server = server;
} }
public MinecraftServer getServer() {
return server;
}
@Override @Override
public boolean reload() { public boolean reload() {
getTerraConfig().load(this); getTerraConfig().load(this);
@ -157,9 +161,10 @@ public class PlatformImpl extends AbstractPlatform {
throw new LoadException("Invalid identifier: " + o, depthTracker); throw new LoadException("Invalid identifier: " + o, depthTracker);
return identifier; return identifier;
}) })
.registerLoader(Precipitation.class, (type, o, loader, depthTracker) -> Precipitation.byName((String) o)) .registerLoader(Precipitation.class, (type, o, loader, depthTracker) -> Precipitation.valueOf(((String) o).toUpperCase(
.registerLoader(Category.class, (type, o, loader, depthTracker) -> Category.byName((String) o)) Locale.ROOT)))
.registerLoader(GrassColorModifier.class, (type, o, loader, depthTracker) -> GrassColorModifier.byName((String) o)); .registerLoader(GrassColorModifier.class, (type, o, loader, depthTracker) -> GrassColorModifier.valueOf(((String) o).toUpperCase(
Locale.ROOT)));
} }

View File

@ -38,6 +38,10 @@ public class PreLoadCompatibilityOptions implements ConfigTemplate, Properties {
@Default @Default
private double beardThreshold = 0.5; private double beardThreshold = 0.5;
@Value("fabric.beard.air-threshold")
@Default
private double airThreshold = -0.5;
public boolean useVanillaBiomes() { public boolean useVanillaBiomes() {
return vanillaBiomes; return vanillaBiomes;
} }
@ -49,4 +53,8 @@ public class PreLoadCompatibilityOptions implements ConfigTemplate, Properties {
public double getBeardThreshold() { public double getBeardThreshold() {
return beardThreshold; return beardThreshold;
} }
public double getAirThreshold() {
return airThreshold;
}
} }

View File

@ -3,7 +3,6 @@ package com.dfsek.terra.fabric.config;
import com.dfsek.tectonic.api.config.template.ConfigTemplate; import com.dfsek.tectonic.api.config.template.ConfigTemplate;
import com.dfsek.tectonic.api.config.template.annotations.Default; import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value; import com.dfsek.tectonic.api.config.template.annotations.Value;
import net.minecraft.world.biome.Biome.Category;
import net.minecraft.world.biome.Biome.Precipitation; import net.minecraft.world.biome.Biome.Precipitation;
import net.minecraft.world.biome.BiomeEffects.GrassColorModifier; import net.minecraft.world.biome.BiomeEffects.GrassColorModifier;
@ -43,10 +42,6 @@ public class VanillaBiomeProperties implements ConfigTemplate, Properties {
@Default @Default
private Precipitation precipitation = null; private Precipitation precipitation = null;
@Value("climate.category")
@Default
private Category category = null;
public Integer getFogColor() { public Integer getFogColor() {
return fogColor; return fogColor;
} }
@ -71,10 +66,6 @@ public class VanillaBiomeProperties implements ConfigTemplate, Properties {
return skyColor; return skyColor;
} }
public Category getCategory() {
return category;
}
public Precipitation getPrecipitation() { public Precipitation getPrecipitation() {
return precipitation; return precipitation;
} }

View File

@ -39,32 +39,27 @@ public final class Codecs {
.fieldOf("biome_registry") .fieldOf("biome_registry")
.stable() .stable()
.forGetter(TerraBiomeSource::getBiomeRegistry), .forGetter(TerraBiomeSource::getBiomeRegistry),
Codec.LONG.fieldOf("seed")
.stable()
.forGetter(TerraBiomeSource::getSeed),
CONFIG_PACK.fieldOf("pack") CONFIG_PACK.fieldOf("pack")
.stable() .stable()
.forGetter(TerraBiomeSource::getPack)) .forGetter(TerraBiomeSource::getPack))
.apply(instance, instance.stable(TerraBiomeSource::new))); .apply(instance, instance.stable(TerraBiomeSource::new)));
public static final Codec<FabricChunkGeneratorWrapper> FABRIC_CHUNK_GENERATOR_WRAPPER = RecordCodecBuilder.create( public static final Codec<FabricChunkGeneratorWrapper> FABRIC_CHUNK_GENERATOR_WRAPPER = RecordCodecBuilder
instance -> instance.group( .create(
RegistryOps.createRegistryCodec(Registry.STRUCTURE_SET_KEY) instance -> instance.group(
.fieldOf("structure_registry") RegistryOps.createRegistryCodec(Registry.STRUCTURE_SET_KEY)
.stable() .fieldOf("structure_registry")
.forGetter(FabricChunkGeneratorWrapper::getNoiseRegistry), .stable()
TERRA_BIOME_SOURCE.fieldOf("biome_source") .forGetter(FabricChunkGeneratorWrapper::getNoiseRegistry),
.stable() TERRA_BIOME_SOURCE.fieldOf("biome_source")
.forGetter(FabricChunkGeneratorWrapper::getBiomeSource), .stable()
Codec.LONG.fieldOf("seed") .forGetter(FabricChunkGeneratorWrapper::getBiomeSource),
.stable() CONFIG_PACK.fieldOf("pack")
.forGetter(FabricChunkGeneratorWrapper::getSeed), .stable()
CONFIG_PACK.fieldOf("pack") .forGetter(FabricChunkGeneratorWrapper::getPack),
.stable() ChunkGeneratorSettings.REGISTRY_CODEC.fieldOf("settings")
.forGetter(FabricChunkGeneratorWrapper::getPack), .stable()
ChunkGeneratorSettings.REGISTRY_CODEC.fieldOf("settings") .forGetter(FabricChunkGeneratorWrapper::getSettings)
.stable() ).apply(instance, instance.stable(FabricChunkGeneratorWrapper::new))
.forGetter(FabricChunkGeneratorWrapper::getSettings) );
).apply(instance, instance.stable(FabricChunkGeneratorWrapper::new))
);
} }

View File

@ -1,178 +0,0 @@
package com.dfsek.terra.fabric.generation;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectList;
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
import net.minecraft.block.BlockState;
import net.minecraft.structure.JigsawJunction;
import net.minecraft.structure.PoolStructurePiece;
import net.minecraft.structure.StructurePiece;
import net.minecraft.structure.StructureStart;
import net.minecraft.structure.pool.StructurePool.Projection;
import net.minecraft.util.Util;
import net.minecraft.util.math.BlockBox;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.ChunkSectionPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.gen.StructureAccessor;
import net.minecraft.world.gen.StructureWeightType;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator;
import com.dfsek.terra.api.world.info.WorldProperties;
// net.minecraft.world.gen.StructureWeightSampler
public class BeardGenerator {
private static final float[] STRUCTURE_WEIGHT_TABLE = Util.make(new float[13824], array -> {
for(int i = 0; i < 24; ++i) {
for(int j = 0; j < 24; ++j) {
for(int k = 0; k < 24; ++k) {
array[i * 24 * 24 + j * 24 + k] = (float) calculateStructureWeight(j - 12, k - 12, i - 12);
}
}
}
});
private final ObjectList<StructurePiece> pieces;
private final ObjectList<JigsawJunction> junctions;
private final ObjectListIterator<StructurePiece> pieceIterator;
private final ObjectListIterator<JigsawJunction> junctionIterator;
private final Chunk chunk;
private final int minY;
private final int maxY;
private final double threshold;
public BeardGenerator(StructureAccessor structureAccessor, Chunk chunk, double threshold) {
this.chunk = chunk;
this.threshold = threshold;
ChunkPos chunkPos = chunk.getPos();
int i = chunkPos.getStartX();
int j = chunkPos.getStartZ();
this.junctions = new ObjectArrayList<>(32);
this.pieces = new ObjectArrayList<>(10);
int minY = chunk.getBottomY();
int maxY = chunk.getTopY();
for(StructureStart start : structureAccessor.method_41035(ChunkSectionPos.from(chunk),
configuredStructureFeature -> configuredStructureFeature.field_37144)) {
for(StructurePiece structurePiece : start.getChildren()) {
if(!structurePiece.intersectsChunk(chunkPos, 12)) continue;
if(structurePiece instanceof PoolStructurePiece poolStructurePiece) {
Projection projection = poolStructurePiece.getPoolElement().getProjection();
if(projection == Projection.RIGID) {
this.pieces.add(poolStructurePiece);
}
for(JigsawJunction jigsawJunction : poolStructurePiece.getJunctions()) {
int k = jigsawJunction.getSourceX();
int l = jigsawJunction.getSourceZ();
if(k <= i - 12 || l <= j - 12 || k >= i + 15 + 12 || l >= j + 15 + 12) continue;
maxY = Math.max(maxY, jigsawJunction.getSourceGroundY());
minY = Math.min(minY, jigsawJunction.getSourceGroundY());
this.junctions.add(jigsawJunction);
}
continue;
}
maxY = Math.max(maxY, structurePiece.getCenter().getY());
minY = Math.min(minY, structurePiece.getCenter().getY());
this.pieces.add(structurePiece);
}
}
this.pieceIterator = this.pieces.iterator();
this.junctionIterator = this.junctions.iterator();
this.minY = minY;
this.maxY = maxY;
}
private static double getMagnitudeWeight(int x, int y, int z) {
double d = MathHelper.magnitude(x, (double) y / 2.0, z);
return MathHelper.clampedLerpFromProgress(d, 0.0, 6.0, 1.0, 0.0);
}
/**
* Gets the structure weight from the array from the given position, or 0 if the position is out of bounds.
*/
private static double getStructureWeight(int x, int y, int z) {
int xOffset = x + 12;
int yOffset = y + 12;
int zOffset = z + 12;
if(xOffset < 0 || xOffset >= 24) {
return 0.0;
}
if(yOffset < 0 || yOffset >= 24) {
return 0.0;
}
if(zOffset < 0 || zOffset >= 24) {
return 0.0;
}
return STRUCTURE_WEIGHT_TABLE[zOffset * 24 * 24 + xOffset * 24 + yOffset];
}
/**
* Calculates the structure weight for the given position.
* <p>The weight increases as x and z approach {@code (0, 0)}, and positive y values make the weight negative while negative y
* values make the weight positive.
*/
private static double calculateStructureWeight(int x, int y, int z) {
double horizontalDistanceSquared = x * x + z * z;
double yOffset = y + 0.5;
double verticalSquared = yOffset * yOffset;
double naturalDistance = Math.pow(Math.E, -(verticalSquared / 16.0 + horizontalDistanceSquared / 16.0));
double inverseSquareRootDistance = -yOffset * MathHelper.fastInverseSqrt(verticalSquared / 2.0 + horizontalDistanceSquared / 2.0) /
2.0;
return inverseSquareRootDistance * naturalDistance;
}
public void generate(ChunkGenerator generator, WorldProperties worldProperties, BiomeProvider biomeProvider) {
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 = maxY; y >= minY; y--) {
if(calculateNoise(x + xi, y, z + zi) > threshold) {
chunk.setBlockState(new BlockPos(x, y, z), (BlockState) generator
.getPalette(x + xi, y, z + zi, worldProperties, biomeProvider)
.get(depth, x + xi, y, z + zi, worldProperties.getSeed()), false);
depth++;
} else {
depth = 0;
}
}
}
}
}
public double calculateNoise(int x, int y, int z) {
double noise = 0.0;
while(this.pieceIterator.hasNext()) {
StructurePiece structurePiece = this.pieceIterator.next();
BlockBox blockBox = structurePiece.getBoundingBox();
int structureX = Math.max(0, Math.max(blockBox.getMinX() - x, x - blockBox.getMaxX()));
int structureY = y - (blockBox.getMinY() + (structurePiece instanceof PoolStructurePiece
? ((PoolStructurePiece) structurePiece).getGroundLevelDelta()
: 0));
int structureZ = Math.max(0, Math.max(blockBox.getMinZ() - z, z - blockBox.getMaxZ()));
StructureWeightType structureWeightType = structurePiece.getWeightType();
if(structureWeightType == StructureWeightType.BURY) {
noise += getMagnitudeWeight(structureX, structureY, structureZ);
continue;
}
if(structureWeightType != StructureWeightType.BEARD) continue;
noise += getStructureWeight(structureX, structureY, structureZ) * 0.8;
}
this.pieceIterator.back(this.pieces.size());
while(this.junctionIterator.hasNext()) {
JigsawJunction structurePiece = this.junctionIterator.next();
int structureX = x - structurePiece.getSourceX();
int structureY = y - structurePiece.getSourceGroundY();
int structureZ = z - structurePiece.getSourceZ();
noise += getStructureWeight(structureX, structureY, structureZ) * 0.4;
}
this.junctionIterator.back(this.junctions.size());
return noise;
}
}

View File

@ -17,32 +17,34 @@
package com.dfsek.terra.fabric.generation; package com.dfsek.terra.fabric.generation;
import com.dfsek.terra.api.world.info.WorldProperties;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.structure.StructureSet; import net.minecraft.structure.StructureSet;
import net.minecraft.util.Util;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.random.CheckedRandom;
import net.minecraft.util.math.random.ChunkRandom;
import net.minecraft.util.math.random.RandomSeed;
import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryEntry; import net.minecraft.util.registry.RegistryEntry;
import net.minecraft.world.ChunkRegion; import net.minecraft.world.ChunkRegion;
import net.minecraft.world.HeightLimitView; import net.minecraft.world.HeightLimitView;
import net.minecraft.world.Heightmap; import net.minecraft.world.Heightmap.Type;
import net.minecraft.world.SpawnHelper; import net.minecraft.world.SpawnHelper;
import net.minecraft.world.StructureWorldAccess; import net.minecraft.world.StructureWorldAccess;
import net.minecraft.world.biome.Biome; import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.source.BiomeAccess; import net.minecraft.world.biome.source.BiomeAccess;
import net.minecraft.world.biome.source.util.MultiNoiseUtil;
import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.gen.GenerationStep; import net.minecraft.world.gen.GenerationStep.Carver;
import net.minecraft.world.gen.StructureAccessor; import net.minecraft.world.gen.StructureAccessor;
import net.minecraft.world.gen.StructureWeightSampler;
import net.minecraft.world.gen.chunk.Blender; import net.minecraft.world.gen.chunk.Blender;
import net.minecraft.world.gen.chunk.ChunkGeneratorSettings; import net.minecraft.world.gen.chunk.ChunkGeneratorSettings;
import net.minecraft.world.gen.chunk.VerticalBlockSample; import net.minecraft.world.gen.chunk.VerticalBlockSample;
import net.minecraft.world.gen.random.AtomicSimpleRandom; import net.minecraft.world.gen.densityfunction.DensityFunction.UnblendedNoisePos;
import net.minecraft.world.gen.random.ChunkRandom; import net.minecraft.world.gen.noise.NoiseConfig;
import net.minecraft.world.gen.random.RandomSeed;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -58,6 +60,7 @@ import com.dfsek.terra.api.world.chunk.generation.ProtoChunk;
import com.dfsek.terra.api.world.chunk.generation.ProtoWorld; import com.dfsek.terra.api.world.chunk.generation.ProtoWorld;
import com.dfsek.terra.api.world.chunk.generation.stage.Chunkified; import com.dfsek.terra.api.world.chunk.generation.stage.Chunkified;
import com.dfsek.terra.api.world.chunk.generation.util.GeneratorWrapper; import com.dfsek.terra.api.world.chunk.generation.util.GeneratorWrapper;
import com.dfsek.terra.api.world.info.WorldProperties;
import com.dfsek.terra.fabric.config.PreLoadCompatibilityOptions; import com.dfsek.terra.fabric.config.PreLoadCompatibilityOptions;
import com.dfsek.terra.fabric.data.Codecs; import com.dfsek.terra.fabric.data.Codecs;
import com.dfsek.terra.fabric.mixin.access.StructureAccessorAccessor; import com.dfsek.terra.fabric.mixin.access.StructureAccessorAccessor;
@ -67,16 +70,15 @@ import com.dfsek.terra.fabric.util.FabricAdapter;
public class FabricChunkGeneratorWrapper extends net.minecraft.world.gen.chunk.ChunkGenerator implements GeneratorWrapper { public class FabricChunkGeneratorWrapper extends net.minecraft.world.gen.chunk.ChunkGenerator implements GeneratorWrapper {
private static final Logger logger = LoggerFactory.getLogger(FabricChunkGeneratorWrapper.class); private static final Logger logger = LoggerFactory.getLogger(FabricChunkGeneratorWrapper.class);
private final long seed;
private final TerraBiomeSource biomeSource; private final TerraBiomeSource biomeSource;
private final Registry<StructureSet> noiseRegistry; private final Registry<StructureSet> noiseRegistry;
private final RegistryEntry<ChunkGeneratorSettings> settings; private final RegistryEntry<ChunkGeneratorSettings> settings;
private ChunkGenerator delegate; private ChunkGenerator delegate;
private ConfigPack pack; private ConfigPack pack;
public FabricChunkGeneratorWrapper(Registry<StructureSet> noiseRegistry, TerraBiomeSource biomeSource, long seed, ConfigPack configPack, public FabricChunkGeneratorWrapper(Registry<StructureSet> noiseRegistry, TerraBiomeSource biomeSource, ConfigPack configPack,
RegistryEntry<ChunkGeneratorSettings> settingsSupplier) { RegistryEntry<ChunkGeneratorSettings> settingsSupplier) {
super(noiseRegistry, Optional.empty(), biomeSource, biomeSource, seed); super(noiseRegistry, Optional.empty(), biomeSource);
this.noiseRegistry = noiseRegistry; this.noiseRegistry = noiseRegistry;
this.pack = configPack; this.pack = configPack;
this.settings = settingsSupplier; this.settings = settingsSupplier;
@ -84,8 +86,6 @@ public class FabricChunkGeneratorWrapper extends net.minecraft.world.gen.chunk.C
this.delegate = pack.getGeneratorProvider().newInstance(pack); this.delegate = pack.getGeneratorProvider().newInstance(pack);
logger.info("Loading world with config pack {}", pack.getID()); logger.info("Loading world with config pack {}", pack.getID());
this.biomeSource = biomeSource; this.biomeSource = biomeSource;
this.seed = seed;
} }
public Registry<StructureSet> getNoiseRegistry() { public Registry<StructureSet> getNoiseRegistry() {
@ -96,29 +96,21 @@ public class FabricChunkGeneratorWrapper extends net.minecraft.world.gen.chunk.C
protected Codec<? extends net.minecraft.world.gen.chunk.ChunkGenerator> getCodec() { protected Codec<? extends net.minecraft.world.gen.chunk.ChunkGenerator> getCodec() {
return Codecs.FABRIC_CHUNK_GENERATOR_WRAPPER; return Codecs.FABRIC_CHUNK_GENERATOR_WRAPPER;
} }
@Override @Override
public net.minecraft.world.gen.chunk.ChunkGenerator withSeed(long seed) { public void buildSurface(ChunkRegion region, StructureAccessor structures, NoiseConfig noiseConfig, Chunk chunk) {
return new FabricChunkGeneratorWrapper(noiseRegistry, (TerraBiomeSource) this.biomeSource.withSeed(seed), seed, pack, settings);
}
@Override
public MultiNoiseUtil.MultiNoiseSampler getMultiNoiseSampler() {
return MultiNoiseUtil.method_40443(); // zero
}
@Override
public void buildSurface(ChunkRegion region, StructureAccessor structures, Chunk chunk) {
// no op // no op
} }
@Override @Override
public void populateEntities(ChunkRegion region) { public void populateEntities(ChunkRegion region) {
ChunkPos chunkPos = region.getCenterPos(); if (!this.settings.value().mobGenerationDisabled()) {
RegistryEntry<Biome> biome = region.getBiome(chunkPos.getStartPos().withY(region.getTopY() - 1)); ChunkPos chunkPos = region.getCenterPos();
ChunkRandom chunkRandom = new ChunkRandom(new AtomicSimpleRandom(RandomSeed.getSeed())); RegistryEntry<Biome> registryEntry = region.getBiome(chunkPos.getStartPos().withY(region.getTopY() - 1));
chunkRandom.setPopulationSeed(region.getSeed(), chunkPos.getStartX(), chunkPos.getStartZ()); ChunkRandom chunkRandom = new ChunkRandom(new CheckedRandom(RandomSeed.getSeed()));
SpawnHelper.populateEntities(region, biome, chunkPos, chunkRandom); chunkRandom.setPopulationSeed(region.getSeed(), chunkPos.getStartX(), chunkPos.getStartZ());
SpawnHelper.populateEntities(region, registryEntry, chunkPos, chunkRandom);
}
} }
@Override @Override
@ -126,20 +118,49 @@ public class FabricChunkGeneratorWrapper extends net.minecraft.world.gen.chunk.C
return settings.value().generationShapeConfig().height(); return settings.value().generationShapeConfig().height();
} }
@Override @Override
public CompletableFuture<Chunk> populateNoise(Executor executor, Blender arg, StructureAccessor structureAccessor, Chunk chunk) { public CompletableFuture<Chunk> populateNoise(Executor executor, Blender blender, NoiseConfig noiseConfig,
StructureAccessor structureAccessor, Chunk chunk) {
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
ProtoWorld world = (ProtoWorld) ((StructureAccessorAccessor) structureAccessor).getWorld(); ProtoWorld world = (ProtoWorld) ((StructureAccessorAccessor) structureAccessor).getWorld();
BiomeProvider biomeProvider = pack.getBiomeProvider().caching(world); BiomeProvider biomeProvider = pack.getBiomeProvider().caching(world);
delegate.generateChunkData((ProtoChunk) chunk, world, biomeProvider, chunk.getPos().x, chunk.getPos().z); delegate.generateChunkData((ProtoChunk) chunk, world, biomeProvider, chunk.getPos().x, chunk.getPos().z);
PreLoadCompatibilityOptions compatibilityOptions = pack.getContext().get(PreLoadCompatibilityOptions.class); PreLoadCompatibilityOptions compatibilityOptions = pack.getContext().get(PreLoadCompatibilityOptions.class);
if(compatibilityOptions.isBeard()) { if(compatibilityOptions.isBeard()) {
new BeardGenerator(structureAccessor, chunk, compatibilityOptions.getBeardThreshold()).generate(delegate, world, beard(structureAccessor, chunk, world, biomeProvider, compatibilityOptions);
biomeProvider);
} }
return chunk; return chunk;
}, executor); }, Util.getMainWorkerExecutor());
}
private void beard(StructureAccessor structureAccessor, Chunk chunk, WorldProperties world, BiomeProvider biomeProvider,
PreLoadCompatibilityOptions compatibilityOptions) {
StructureWeightSampler structureWeightSampler = StructureWeightSampler.method_42695(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.sample(new UnblendedNoisePos(x + xi, y, z + zi));
if(noise > threshold) {
chunk.setBlockState(new BlockPos(x, y, z), (BlockState) delegate
.getPalette(x + xi, y, z + zi, world, biomeProvider)
.get(depth, x + xi, y, z + zi, world.getSeed()), false);
depth++;
} else if(noise < airThreshold) {
chunk.setBlockState(new BlockPos(x, y, z), Blocks.AIR.getDefaultState(), false);
} else {
depth = 0;
}
}
}
}
} }
@Override @Override
@ -162,10 +183,11 @@ public class FabricChunkGeneratorWrapper extends net.minecraft.world.gen.chunk.C
return settings.value().generationShapeConfig().minimumY(); return settings.value().generationShapeConfig().minimumY();
} }
@Override @Override
public int getHeight(int x, int z, Heightmap.Type heightmap, HeightLimitView height) { public int getHeight(int x, int z, Type heightmap, HeightLimitView height, NoiseConfig noiseConfig) {
int y = height.getTopY(); int y = height.getTopY();
WorldProperties properties = FabricAdapter.adapt(height, seed); WorldProperties properties = FabricAdapter.adapt(height, noiseConfig.getLegacyWorldSeed());
BiomeProvider biomeProvider = pack.getBiomeProvider().caching(properties); BiomeProvider biomeProvider = pack.getBiomeProvider().caching(properties);
while(y >= getMinimumY() && !heightmap.getBlockPredicate().test( while(y >= getMinimumY() && !heightmap.getBlockPredicate().test(
(BlockState) delegate.getBlock(properties, x, y - 1, z, biomeProvider))) { (BlockState) delegate.getBlock(properties, x, y - 1, z, biomeProvider))) {
@ -175,9 +197,9 @@ public class FabricChunkGeneratorWrapper extends net.minecraft.world.gen.chunk.C
} }
@Override @Override
public VerticalBlockSample getColumnSample(int x, int z, HeightLimitView height) { public VerticalBlockSample getColumnSample(int x, int z, HeightLimitView height, NoiseConfig noiseConfig) {
BlockState[] array = new BlockState[height.getHeight()]; BlockState[] array = new BlockState[height.getHeight()];
WorldProperties properties = FabricAdapter.adapt(height, seed); WorldProperties properties = FabricAdapter.adapt(height, noiseConfig.getLegacyWorldSeed());
BiomeProvider biomeProvider = pack.getBiomeProvider().caching(properties); BiomeProvider biomeProvider = pack.getBiomeProvider().caching(properties);
for(int y = height.getTopY() - 1; y >= height.getBottomY(); y--) { for(int y = height.getTopY() - 1; y >= height.getBottomY(); y--) {
array[y - height.getBottomY()] = (BlockState) delegate.getBlock(properties, x, y, z, biomeProvider); array[y - height.getBottomY()] = (BlockState) delegate.getBlock(properties, x, y, z, biomeProvider);
@ -186,7 +208,7 @@ public class FabricChunkGeneratorWrapper extends net.minecraft.world.gen.chunk.C
} }
@Override @Override
public void getDebugHudText(List<String> text, BlockPos pos) { public void getDebugHudText(List<String> text, NoiseConfig noiseConfig, BlockPos pos) {
} }
@ -203,9 +225,9 @@ public class FabricChunkGeneratorWrapper extends net.minecraft.world.gen.chunk.C
} }
@Override @Override
public void carve(ChunkRegion chunkRegion, long seed, BiomeAccess biomeAccess, StructureAccessor structureAccessor, Chunk chunk, public void carve(ChunkRegion chunkRegion, long seed, NoiseConfig noiseConfig, BiomeAccess world, StructureAccessor structureAccessor,
GenerationStep.Carver generationStep) { Chunk chunk, Carver carverStep) {
// no op
} }
@Override @Override
@ -213,10 +235,6 @@ public class FabricChunkGeneratorWrapper extends net.minecraft.world.gen.chunk.C
return delegate; return delegate;
} }
public long getSeed() {
return seed;
}
public RegistryEntry<ChunkGeneratorSettings> getSettings() { public RegistryEntry<ChunkGeneratorSettings> getSettings() {
return settings; return settings;
} }

View File

@ -22,7 +22,11 @@ import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import com.dfsek.terra.fabric.data.Codecs; import com.dfsek.terra.fabric.data.Codecs;
import com.dfsek.terra.fabric.util.ProtoPlatformBiome; import com.dfsek.terra.fabric.util.ProtoPlatformBiome;
import com.dfsek.terra.fabric.util.SeedHack;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import it.unimi.dsi.fastutil.objects.Object2LongMap;
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryEntry; import net.minecraft.util.registry.RegistryEntry;
import net.minecraft.world.biome.source.BiomeSource; import net.minecraft.world.biome.source.BiomeSource;
@ -30,6 +34,7 @@ import net.minecraft.world.biome.source.util.MultiNoiseUtil.MultiNoiseSampler;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
@ -37,17 +42,15 @@ public class TerraBiomeSource extends BiomeSource {
private static final Logger LOGGER = LoggerFactory.getLogger(TerraBiomeSource.class); private static final Logger LOGGER = LoggerFactory.getLogger(TerraBiomeSource.class);
private final Registry<net.minecraft.world.biome.Biome> biomeRegistry; private final Registry<net.minecraft.world.biome.Biome> biomeRegistry;
private final long seed;
private ConfigPack pack; private ConfigPack pack;
public TerraBiomeSource(Registry<net.minecraft.world.biome.Biome> biomes, long seed, ConfigPack pack) { public TerraBiomeSource(Registry<net.minecraft.world.biome.Biome> biomes, ConfigPack pack) {
super(StreamSupport super(StreamSupport
.stream(pack.getBiomeProvider() .stream(pack.getBiomeProvider()
.getBiomes() .getBiomes()
.spliterator(), false) .spliterator(), false)
.map(b -> biomes.getOrCreateEntry(((ProtoPlatformBiome) b.getPlatformBiome()).getDelegate()))); .map(b -> biomes.getOrCreateEntry(((ProtoPlatformBiome) b.getPlatformBiome()).getDelegate())));
this.biomeRegistry = biomes; this.biomeRegistry = biomes;
this.seed = seed;
this.pack = pack; this.pack = pack;
LOGGER.debug("Biomes: " + getBiomes()); LOGGER.debug("Biomes: " + getBiomes());
@ -58,17 +61,12 @@ public class TerraBiomeSource extends BiomeSource {
return Codecs.TERRA_BIOME_SOURCE; return Codecs.TERRA_BIOME_SOURCE;
} }
@Override
public BiomeSource withSeed(long seed) {
return new TerraBiomeSource(this.biomeRegistry, seed, pack);
}
@Override @Override
public RegistryEntry<net.minecraft.world.biome.Biome> getBiome(int biomeX, int biomeY, int biomeZ, MultiNoiseSampler noiseSampler) { public RegistryEntry<net.minecraft.world.biome.Biome> getBiome(int biomeX, int biomeY, int biomeZ, MultiNoiseSampler noiseSampler) {
return biomeRegistry return biomeRegistry
.entryOf(((ProtoPlatformBiome) pack .entryOf(((ProtoPlatformBiome) pack
.getBiomeProvider() .getBiomeProvider()
.getBiome(biomeX << 2, biomeY << 2, biomeZ << 2, seed) .getBiome(biomeX << 2, biomeY << 2, biomeZ << 2, SeedHack.getSeed(noiseSampler))
.getPlatformBiome()).getDelegate() .getPlatformBiome()).getDelegate()
); );
} }
@ -88,8 +86,4 @@ public class TerraBiomeSource extends BiomeSource {
public void setPack(ConfigPack pack) { public void setPack(ConfigPack pack) {
this.pack = pack; this.pack = pack;
} }
public long getSeed() {
return seed;
}
} }

View File

@ -1,51 +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.fabric.generation;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.world.GeneratorType;
import net.minecraft.structure.StructureSet;
import net.minecraft.util.registry.DynamicRegistryManager;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryEntry;
import net.minecraft.world.gen.chunk.ChunkGenerator;
import net.minecraft.world.gen.chunk.ChunkGeneratorSettings;
import com.dfsek.terra.api.config.ConfigPack;
@Environment(EnvType.CLIENT)
public class TerraGeneratorType extends GeneratorType {
private final ConfigPack pack;
public TerraGeneratorType(ConfigPack pack) {
super("terra." + pack.getID());
this.pack = pack;
}
@Override
protected ChunkGenerator getChunkGenerator(DynamicRegistryManager manager, long seed) {
Registry<ChunkGeneratorSettings> chunkGeneratorSettingsRegistry = manager.get(Registry.CHUNK_GENERATOR_SETTINGS_KEY);
RegistryEntry<ChunkGeneratorSettings>
settingsSupplier = chunkGeneratorSettingsRegistry.getEntry(ChunkGeneratorSettings.OVERWORLD).orElseThrow();
Registry<StructureSet> noiseRegistry = manager.get(Registry.STRUCTURE_SET_KEY);
return new FabricChunkGeneratorWrapper(noiseRegistry, new TerraBiomeSource(manager.get(Registry.BIOME_KEY), seed, pack), seed, pack,
settingsSupplier);
}
}

View File

@ -17,8 +17,11 @@
package com.dfsek.terra.fabric.handle; package com.dfsek.terra.fabric.handle;
import com.dfsek.terra.fabric.FabricEntryPoint;
import com.mojang.brigadier.StringReader; import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.command.CommandRegistryAccess;
import net.minecraft.command.argument.ItemStackArgumentType; import net.minecraft.command.argument.ItemStackArgumentType;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.Registry;
@ -36,7 +39,7 @@ public class FabricItemHandle implements ItemHandle {
@Override @Override
public Item createItem(String data) { public Item createItem(String data) {
try { try {
return (Item) new ItemStackArgumentType().parse(new StringReader(data)).getItem(); return (Item) new ItemStackArgumentType(new CommandRegistryAccess(FabricEntryPoint.getPlatform().getServer().getRegistryManager())).parse(new StringReader(data)).getItem();
} catch(CommandSyntaxException e) { } catch(CommandSyntaxException e) {
throw new IllegalArgumentException("Invalid item data \"" + data + "\"", e); throw new IllegalArgumentException("Invalid item data \"" + data + "\"", e);
} }

View File

@ -17,11 +17,14 @@
package com.dfsek.terra.fabric.handle; package com.dfsek.terra.fabric.handle;
import com.dfsek.terra.fabric.FabricEntryPoint;
import com.mojang.brigadier.StringReader; import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.command.argument.BlockArgumentParser; import net.minecraft.command.argument.BlockArgumentParser;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.registry.BuiltinRegistries;
import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.Registry;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -36,9 +39,8 @@ public class FabricWorldHandle implements WorldHandle {
@Override @Override
public @NotNull BlockState createBlockState(@NotNull String data) { public @NotNull BlockState createBlockState(@NotNull String data) {
BlockArgumentParser parser = new BlockArgumentParser(new StringReader(data), true);
try { try {
net.minecraft.block.BlockState state = parser.parse(true).getBlockState(); net.minecraft.block.BlockState state = BlockArgumentParser.block(Registry.BLOCK, data, true).blockState();
if(state == null) throw new IllegalArgumentException("Invalid data: " + data); if(state == null) throw new IllegalArgumentException("Invalid data: " + data);
return (BlockState) state; return (BlockState) state;
} catch(CommandSyntaxException e) { } catch(CommandSyntaxException e) {

View File

@ -0,0 +1,25 @@
package com.dfsek.terra.fabric.mixin;
import com.dfsek.terra.fabric.FabricEntryPoint;
import net.minecraft.server.command.ReloadCommand;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.Text;
import org.checkerframework.checker.units.qual.A;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Collection;
@Mixin(ReloadCommand.class)
public class ReloadCommandMixin {
@Inject(method = "tryReloadDataPacks", at = @At("HEAD"))
private static void inject(Collection<String> dataPacks, ServerCommandSource source, CallbackInfo ci) {
source.sendFeedback(Text.literal("Reloading Terra..."), true);
FabricEntryPoint.getPlatform().reload();
source.sendFeedback(Text.literal("Done."), true);
}
}

View File

@ -1,39 +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.fabric.mixin.access;
import net.minecraft.client.world.GeneratorType;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.List;
@Mixin(GeneratorType.class)
public interface GeneratorTypeAccessor {
@Accessor("VALUES")
static List<GeneratorType> getValues() {
throw new UnsupportedOperationException();
}
@Mutable
@Accessor("displayName")
void setDisplayName(Text translationKey);
}

View File

@ -18,7 +18,6 @@
package com.dfsek.terra.fabric.mixin.implementations.block.entity; package com.dfsek.terra.fabric.mixin.implementations.block.entity;
import net.minecraft.block.entity.SignBlockEntity; import net.minecraft.block.entity.SignBlockEntity;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
@ -42,19 +41,19 @@ public abstract class SignBlockEntityMixin {
public abstract void setTextOnRow(int row, Text text); public abstract void setTextOnRow(int row, Text text);
public void terra$setLine(int index, @NotNull String line) throws IndexOutOfBoundsException { public void terra$setLine(int index, @NotNull String line) throws IndexOutOfBoundsException {
setTextOnRow(index, new LiteralText(line)); setTextOnRow(index, Text.literal(line));
} }
public @NotNull String[] terra$getLines() { public @NotNull String[] terra$getLines() {
String[] lines = new String[texts.length]; String[] lines = new String[texts.length];
for(int i = 0; i < texts.length; i++) { for(int i = 0; i < texts.length; i++) {
lines[i] = texts[i].asString(); lines[i] = texts[i].getString();
} }
return lines; return lines;
} }
public @NotNull String terra$getLine(int index) throws IndexOutOfBoundsException { public @NotNull String terra$getLine(int index) throws IndexOutOfBoundsException {
return texts[index].asString(); return texts[index].getString();
} }
public void terra$applyState(String state) { public void terra$applyState(String state) {

View File

@ -44,9 +44,6 @@ public abstract class EntityMixin {
@Shadow @Shadow
public abstract void teleport(double destX, double destY, double destZ); public abstract void teleport(double destX, double destY, double destZ);
@Shadow
public abstract void sendSystemMessage(Text message, UUID senderUuid);
public Vector3 terra$position() { public Vector3 terra$position() {
return FabricAdapter.adapt(blockPos); return FabricAdapter.adapt(blockPos);
} }

View File

@ -20,7 +20,6 @@ package com.dfsek.terra.fabric.mixin.implementations.entity;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Implements; import org.spongepowered.asm.mixin.Implements;
@ -49,7 +48,7 @@ public abstract class ServerCommandSourceMixin {
public abstract net.minecraft.entity.@Nullable Entity getEntity(); public abstract net.minecraft.entity.@Nullable Entity getEntity();
public void terra$sendMessage(String message) { public void terra$sendMessage(String message) {
sendFeedback(new LiteralText(message), true); sendFeedback(Text.literal(message), true);
} }
@Nullable @Nullable

View File

@ -81,10 +81,8 @@ public abstract class ChunkRegionMixin {
this.config = ((ServerWorld) world).getPack(); this.config = ((ServerWorld) world).getPack();
} }
@SuppressWarnings("deprecation")
public Entity terraWorld$spawnEntity(Vector3 location, EntityType entityType) { public Entity terraWorld$spawnEntity(Vector3 location, EntityType entityType) {
net.minecraft.entity.Entity entity = ((net.minecraft.entity.EntityType<?>) entityType).create( net.minecraft.entity.Entity entity = ((net.minecraft.entity.EntityType<?>) entityType).create(null);
((ChunkRegion) (Object) this).toServerWorld());
entity.setPos(location.getX(), location.getY(), location.getZ()); entity.setPos(location.getX(), location.getY(), location.getZ());
((ChunkRegion) (Object) this).spawnEntity(entity); ((ChunkRegion) (Object) this).spawnEntity(entity);
return (Entity) entity; return (Entity) entity;

View File

@ -42,8 +42,7 @@ import com.dfsek.terra.fabric.util.FabricUtil;
@Implements(@Interface(iface = ServerWorld.class, prefix = "terra$")) @Implements(@Interface(iface = ServerWorld.class, prefix = "terra$"))
public abstract class ServerWorldMixin { public abstract class ServerWorldMixin {
public Entity terra$spawnEntity(double x, double y, double z, EntityType entityType) { public Entity terra$spawnEntity(double x, double y, double z, EntityType entityType) {
net.minecraft.entity.Entity entity = ((net.minecraft.entity.EntityType<?>) entityType).create( net.minecraft.entity.Entity entity = ((net.minecraft.entity.EntityType<?>) entityType).create(null);
((net.minecraft.server.world.ServerWorld) (Object) this));
entity.setPos(x, y, z); entity.setPos(x, y, z);
((net.minecraft.server.world.ServerWorld) (Object) this).spawnEntity(entity); ((net.minecraft.server.world.ServerWorld) (Object) this).spawnEntity(entity);
return (Entity) entity; return (Entity) entity;

View File

@ -19,8 +19,10 @@ public class DataPackContentsMixin {
*/ */
@Inject(method = "refresh(Lnet/minecraft/util/registry/DynamicRegistryManager;)V", at = @At("RETURN")) @Inject(method = "refresh(Lnet/minecraft/util/registry/DynamicRegistryManager;)V", at = @At("RETURN"))
private void injectReload(DynamicRegistryManager dynamicRegistryManager, CallbackInfo ci) { private void injectReload(DynamicRegistryManager dynamicRegistryManager, CallbackInfo ci) {
TagUtil.registerWorldPresetTags(dynamicRegistryManager.get(Registry.WORLD_PRESET_KEY));
Registry<Biome> biomeRegistry = dynamicRegistryManager.get(Registry.BIOME_KEY); Registry<Biome> biomeRegistry = dynamicRegistryManager.get(Registry.BIOME_KEY);
TagUtil.registerTags(biomeRegistry); TagUtil.registerBiomeTags(biomeRegistry);
BiomeUtil.registerFlora(biomeRegistry); BiomeUtil.registerFlora(biomeRegistry);
} }
} }

View File

@ -7,6 +7,7 @@ import net.minecraft.resource.ResourcePackManager;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.SaveLoader; import net.minecraft.server.SaveLoader;
import net.minecraft.server.WorldGenerationProgressListenerFactory; import net.minecraft.server.WorldGenerationProgressListenerFactory;
import net.minecraft.util.ApiServices;
import net.minecraft.util.UserCache; import net.minecraft.util.UserCache;
import net.minecraft.world.level.storage.LevelStorage; import net.minecraft.world.level.storage.LevelStorage;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@ -23,13 +24,11 @@ import com.dfsek.terra.fabric.FabricEntryPoint;
public class MinecraftServerMixin { public class MinecraftServerMixin {
@Inject(method = "<init>(Ljava/lang/Thread;Lnet/minecraft/world/level/storage/LevelStorage$Session;" + @Inject(method = "<init>(Ljava/lang/Thread;Lnet/minecraft/world/level/storage/LevelStorage$Session;" +
"Lnet/minecraft/resource/ResourcePackManager;Lnet/minecraft/server/SaveLoader;Ljava/net/Proxy;" + "Lnet/minecraft/resource/ResourcePackManager;Lnet/minecraft/server/SaveLoader;Ljava/net/Proxy;" +
"Lcom/mojang/datafixers/DataFixer;Lcom/mojang/authlib/minecraft/MinecraftSessionService;" + "Lcom/mojang/datafixers/DataFixer;Lnet/minecraft/util/ApiServices;" +
"Lcom/mojang/authlib/GameProfileRepository;Lnet/minecraft/util/UserCache;" +
"Lnet/minecraft/server/WorldGenerationProgressListenerFactory;)V", "Lnet/minecraft/server/WorldGenerationProgressListenerFactory;)V",
at = @At("RETURN")) at = @At("RETURN"))
private void injectConstructor(Thread serverThread, LevelStorage.Session session, ResourcePackManager dataPackManager, private void injectConstructor(Thread serverThread, LevelStorage.Session session, ResourcePackManager dataPackManager,
SaveLoader saveLoader, Proxy proxy, DataFixer dataFixer, MinecraftSessionService sessionService, SaveLoader saveLoader, Proxy proxy, DataFixer dataFixer, ApiServices apiServices,
GameProfileRepository gameProfileRepo, UserCache userCache,
WorldGenerationProgressListenerFactory worldGenerationProgressListenerFactory, CallbackInfo ci) { WorldGenerationProgressListenerFactory worldGenerationProgressListenerFactory, CallbackInfo ci) {
FabricEntryPoint.getPlatform().setServer((MinecraftServer) (Object) this); FabricEntryPoint.getPlatform().setServer((MinecraftServer) (Object) this);
} }

View File

@ -0,0 +1,32 @@
package com.dfsek.terra.fabric.mixin.lifecycle;
import com.dfsek.terra.fabric.util.SeedHack;
import net.minecraft.util.math.noise.DoublePerlinNoiseSampler;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.biome.source.util.MultiNoiseUtil;
import net.minecraft.world.biome.source.util.MultiNoiseUtil.MultiNoiseSampler;
import net.minecraft.world.gen.chunk.ChunkGeneratorSettings;
import net.minecraft.world.gen.noise.NoiseConfig;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* Hack to map noise sampler to seeds
*/
@Mixin(NoiseConfig.class)
public class NoiseConfigMixin {
@Shadow
@Final
private MultiNoiseSampler multiNoiseSampler;
@Inject(method = "<init>(Lnet/minecraft/world/gen/chunk/ChunkGeneratorSettings;Lnet/minecraft/util/registry/Registry;J)V", at = @At("TAIL"))
private void mapMultiNoise(ChunkGeneratorSettings chunkGeneratorSettings, Registry<DoublePerlinNoiseSampler.NoiseParameters> noiseRegistry, long seed, CallbackInfo ci) {
SeedHack.register(multiNoiseSampler, seed);
}
}

View File

@ -17,11 +17,10 @@
package com.dfsek.terra.fabric.mixin.lifecycle.client; package com.dfsek.terra.fabric.mixin.lifecycle.client;
import com.dfsek.terra.fabric.util.BiomeUtil; import com.dfsek.terra.fabric.util.LifecycleUtil;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.RunArgs; import net.minecraft.client.RunArgs;
import net.minecraft.client.world.GeneratorType;
import net.minecraft.text.LiteralText;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
@ -29,8 +28,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.dfsek.terra.api.event.events.platform.PlatformInitializationEvent; import com.dfsek.terra.api.event.events.platform.PlatformInitializationEvent;
import com.dfsek.terra.fabric.FabricEntryPoint; import com.dfsek.terra.fabric.FabricEntryPoint;
import com.dfsek.terra.fabric.generation.TerraGeneratorType; import com.dfsek.terra.fabric.util.BiomeUtil;
import com.dfsek.terra.fabric.mixin.access.GeneratorTypeAccessor;
@Mixin(MinecraftClient.class) @Mixin(MinecraftClient.class)
@ -42,13 +40,6 @@ public class MinecraftClientMixin {
// sorta arbitrary position, after mod init, before window opens // sorta arbitrary position, after mod init, before window opens
shift = At.Shift.BEFORE)) shift = At.Shift.BEFORE))
public void injectConstructor(RunArgs args, CallbackInfo callbackInfo) { public void injectConstructor(RunArgs args, CallbackInfo callbackInfo) {
FabricEntryPoint.getPlatform().getEventManager().callEvent(new PlatformInitializationEvent()); LifecycleUtil.initialize();
FabricEntryPoint.getPlatform().getConfigRegistry().forEach(pack -> {
final GeneratorType generatorType = new TerraGeneratorType(pack);
//noinspection ConstantConditions
((GeneratorTypeAccessor) generatorType).setDisplayName(new LiteralText("Terra:" + pack.getID()));
GeneratorTypeAccessor.getValues().add(1, generatorType);
});
BiomeUtil.registerBiomes();
} }
} }

View File

@ -1,111 +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.fabric.mixin.lifecycle.server;
import net.minecraft.server.dedicated.ServerPropertiesHandler;
import net.minecraft.structure.StructureSet;
import net.minecraft.util.registry.DynamicRegistryManager;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryEntry;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.dimension.DimensionOptions;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.gen.GeneratorOptions;
import net.minecraft.world.gen.chunk.ChunkGeneratorSettings;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Locale;
import java.util.Random;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.fabric.FabricEntryPoint;
import com.dfsek.terra.fabric.PlatformImpl;
import com.dfsek.terra.fabric.generation.FabricChunkGeneratorWrapper;
import com.dfsek.terra.fabric.generation.TerraBiomeSource;
@Mixin(GeneratorOptions.class)
public abstract class GeneratorOptionsMixin {
@Inject(method = "fromProperties(Lnet/minecraft/util/registry/DynamicRegistryManager;" +
"Lnet/minecraft/server/dedicated/ServerPropertiesHandler$WorldGenProperties;)" +
"Lnet/minecraft/world/gen/GeneratorOptions;",
at = @At("HEAD"),
cancellable = true)
private static void fromProperties(DynamicRegistryManager manager,
ServerPropertiesHandler.WorldGenProperties properties,
CallbackInfoReturnable<GeneratorOptions> cir) {
if(properties.levelType() == null) {
return;
}
PlatformImpl main = FabricEntryPoint.getPlatform();
String levelType = properties.levelType();
if(levelType.toLowerCase(Locale.ROOT).startsWith("terra")) {
String seedProperty = properties.levelSeed();
long seed = new Random().nextLong();
if(seedProperty != null) {
try {
long m = Long.parseLong(seedProperty);
if(m != 0L) {
seed = m;
}
} catch(NumberFormatException exception) {
seed = seedProperty.hashCode();
}
}
boolean generateStructures = properties.generateStructures();
Registry<DimensionType> dimensionTypes = manager.get(Registry.DIMENSION_TYPE_KEY);
Registry<Biome> biomeRegistry = manager.get(Registry.BIOME_KEY);
Registry<DimensionOptions> dimensionOptions = DimensionType.createDefaultDimensionOptions(manager, seed, false);
Registry<ChunkGeneratorSettings> chunkGeneratorSettingsRegistry = manager.get(Registry.CHUNK_GENERATOR_SETTINGS_KEY);
RegistryEntry<ChunkGeneratorSettings>
settingsSupplier = chunkGeneratorSettingsRegistry.getEntry(ChunkGeneratorSettings.OVERWORLD).orElseThrow();
Registry<StructureSet> noiseRegistry = manager.get(Registry.STRUCTURE_SET_KEY);
String pack = levelType.substring(levelType.indexOf(":") + 1);
CheckedRegistry<ConfigPack> configRegistry = main.getConfigRegistry();
ConfigPack config = configRegistry
.getByID(pack)
.or(() -> configRegistry.getByID(pack.toUpperCase(Locale.ROOT)))
.orElseThrow(() -> new IllegalArgumentException("No such pack " + pack));
cir.setReturnValue(
new GeneratorOptions(seed,
generateStructures,
false,
GeneratorOptions
.getRegistryWithReplacedOverworldGenerator(
dimensionTypes,
dimensionOptions,
new FabricChunkGeneratorWrapper(noiseRegistry,
new TerraBiomeSource(biomeRegistry, seed, config),
seed,
config,
settingsSupplier))));
}
}
}

View File

@ -18,6 +18,8 @@
package com.dfsek.terra.fabric.mixin.lifecycle.server; package com.dfsek.terra.fabric.mixin.lifecycle.server;
import com.dfsek.terra.fabric.util.BiomeUtil; import com.dfsek.terra.fabric.util.BiomeUtil;
import com.dfsek.terra.fabric.util.LifecycleUtil;
import net.minecraft.server.Main; import net.minecraft.server.Main;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@ -37,9 +39,6 @@ public class ServerMainMixin {
// after registry manager creation // after registry manager creation
) )
private static void injectConstructor(String[] args, CallbackInfo ci) { private static void injectConstructor(String[] args, CallbackInfo ci) {
FabricEntryPoint.getPlatform().getEventManager().callEvent( LifecycleUtil.initialize();
new PlatformInitializationEvent()); // Load during MinecraftServer construction, after other mods have registered blocks
// and stuff
BiomeUtil.registerBiomes();
} }
} }

View File

@ -137,12 +137,9 @@ public final class BiomeUtil {
effects.foliageColor(vanillaBiomeProperties.getFoliageColor()); effects.foliageColor(vanillaBiomeProperties.getFoliageColor());
} }
builder.precipitation(Objects.requireNonNullElse(vanillaBiomeProperties.getPrecipitation(), vanilla.getPrecipitation()));
builder.precipitation(Objects.requireNonNullElse(vanillaBiomeProperties.getPrecipitation(), vanilla.getPrecipitation()))
.category(Objects.requireNonNullElse(vanillaBiomeProperties.getCategory(), vanilla.getCategory()));
} else { } else {
effects.waterColor(vanilla.getWaterColor()) effects.waterColor(vanilla.getWaterColor())
.waterFogColor(vanilla.getWaterFogColor()) .waterFogColor(vanilla.getWaterFogColor())
.fogColor(vanilla.getFogColor()) .fogColor(vanilla.getFogColor())
@ -150,11 +147,9 @@ public final class BiomeUtil {
vanilla.getEffects().getFoliageColor().ifPresent(effects::foliageColor); vanilla.getEffects().getFoliageColor().ifPresent(effects::foliageColor);
vanilla.getEffects().getGrassColor().ifPresent(effects::grassColor); vanilla.getEffects().getGrassColor().ifPresent(effects::grassColor);
builder.precipitation(vanilla.getPrecipitation()) builder.precipitation(vanilla.getPrecipitation());
.category(vanilla.getCategory());
} }
return builder return builder
.temperature(vanilla.getTemperature()) .temperature(vanilla.getTemperature())
.downfall(vanilla.getDownfall()) .downfall(vanilla.getDownfall())

View File

@ -0,0 +1,100 @@
package com.dfsek.terra.fabric.util;
import com.dfsek.terra.api.event.events.platform.PlatformInitializationEvent;
import com.dfsek.terra.fabric.FabricEntryPoint;
import com.dfsek.terra.fabric.generation.FabricChunkGeneratorWrapper;
import com.dfsek.terra.fabric.generation.TerraBiomeSource;
import net.minecraft.structure.StructureSet;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.noise.DoublePerlinNoiseSampler.NoiseParameters;
import net.minecraft.util.registry.BuiltinRegistries;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryEntry;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.source.MultiNoiseBiomeSource;
import net.minecraft.world.biome.source.TheEndBiomeSource;
import net.minecraft.world.dimension.DimensionOptions;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.dimension.DimensionTypes;
import net.minecraft.world.gen.WorldPreset;
import net.minecraft.world.gen.chunk.ChunkGenerator;
import net.minecraft.world.gen.chunk.ChunkGeneratorSettings;
import net.minecraft.world.gen.chunk.NoiseChunkGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class LifecycleUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleUtil.class);
private static final List<Identifier> PRESETS = new ArrayList<>();
public static void initialize() {
FabricEntryPoint.getPlatform().getEventManager().callEvent(
new PlatformInitializationEvent());
BiomeUtil.registerBiomes();
LOGGER.info("Registering Terra world types...");
Registry<DimensionType> dimensionTypeRegistry = BuiltinRegistries.DIMENSION_TYPE;
Registry<ChunkGeneratorSettings> chunkGeneratorSettingsRegistry = BuiltinRegistries.CHUNK_GENERATOR_SETTINGS;
Registry<StructureSet> structureSetRegistry = BuiltinRegistries.STRUCTURE_SET;
Registry<NoiseParameters> noiseParametersRegistry = BuiltinRegistries.NOISE_PARAMETERS;
Registry<Biome> biomeRegistry = BuiltinRegistries.BIOME;
RegistryEntry<DimensionType> theNetherDimensionType = dimensionTypeRegistry.getOrCreateEntry(DimensionTypes.THE_NETHER);
RegistryEntry<ChunkGeneratorSettings>
netherChunkGeneratorSettings = chunkGeneratorSettingsRegistry.getOrCreateEntry(ChunkGeneratorSettings.NETHER);
DimensionOptions netherDimensionOptions = new DimensionOptions(theNetherDimensionType,
new NoiseChunkGenerator(structureSetRegistry,
noiseParametersRegistry,
MultiNoiseBiomeSource.Preset.NETHER.getBiomeSource(
biomeRegistry),
netherChunkGeneratorSettings));
RegistryEntry<DimensionType> theEndDimensionType = dimensionTypeRegistry.getOrCreateEntry(DimensionTypes.THE_END);
RegistryEntry<ChunkGeneratorSettings> endChunkGeneratorSettings = chunkGeneratorSettingsRegistry.getOrCreateEntry(
ChunkGeneratorSettings.END);
DimensionOptions endDimensionOptions = new DimensionOptions(theEndDimensionType,
new NoiseChunkGenerator(structureSetRegistry, noiseParametersRegistry,
new TheEndBiomeSource(biomeRegistry),
endChunkGeneratorSettings));
RegistryEntry<DimensionType> overworldDimensionType = dimensionTypeRegistry.getOrCreateEntry(DimensionTypes.OVERWORLD);
RegistryEntry<ChunkGeneratorSettings> overworld = chunkGeneratorSettingsRegistry.getOrCreateEntry(ChunkGeneratorSettings.OVERWORLD);
FabricEntryPoint
.getPlatform()
.getRawConfigRegistry()
.forEach((id, pack) -> {
Identifier generatorID = Identifier.of("terra", pack.getID().toLowerCase(Locale.ROOT) + "/" + pack.getNamespace().toLowerCase(
Locale.ROOT));
PRESETS.add(generatorID);
TerraBiomeSource biomeSource = new TerraBiomeSource(biomeRegistry, pack);
ChunkGenerator generator = new FabricChunkGeneratorWrapper(structureSetRegistry, biomeSource, pack, overworld);
DimensionOptions dimensionOptions = new DimensionOptions(overworldDimensionType, generator);
WorldPreset preset = new WorldPreset(
Map.of(
DimensionOptions.OVERWORLD, dimensionOptions,
DimensionOptions.NETHER, netherDimensionOptions,
DimensionOptions.END, endDimensionOptions
)
);
BuiltinRegistries.add(BuiltinRegistries.WORLD_PRESET, generatorID, preset);
LOGGER.info("Registered world type \"{}\"", generatorID);
}
);
}
public static List<Identifier> getPresets() {
return PRESETS;
}
}

View File

@ -0,0 +1,29 @@
package com.dfsek.terra.fabric.util;
import it.unimi.dsi.fastutil.objects.Object2LongMap;
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
import net.minecraft.world.biome.source.util.MultiNoiseUtil.MultiNoiseSampler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Holder for hacky biome source seed workaround
*/
public class SeedHack {
private static final Logger LOGGER = LoggerFactory.getLogger(SeedHack.class);
private static final Object2LongMap<MultiNoiseSampler> seedMap = new Object2LongOpenHashMap<>();
public static long getSeed(MultiNoiseSampler sampler) {
if(!seedMap.containsKey(sampler)) {
throw new IllegalArgumentException("Sampler is not registered: " + sampler);
}
return seedMap.getLong(sampler);
}
public static void register(MultiNoiseSampler sampler, long seed) {
LOGGER.info("Registered seed {} to sampler {}", seed, sampler.hashCode());
seedMap.put(sampler, seed);
}
}

View File

@ -2,9 +2,11 @@ package com.dfsek.terra.fabric.util;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import net.minecraft.tag.TagKey; import net.minecraft.tag.TagKey;
import net.minecraft.tag.WorldPresetTags;
import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryEntry; import net.minecraft.util.registry.RegistryEntry;
import net.minecraft.world.biome.Biome; import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.WorldPreset;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -21,48 +23,72 @@ public final class TagUtil {
} }
public static void registerTags(Registry<Biome> registry) { private static <T> Map<TagKey<T>, List<RegistryEntry<T>>> tagsToMutableMap(Registry<T> registry) {
logger.info("Doing tag garbage...."); return registry
Map<TagKey<Biome>, List<RegistryEntry<Biome>>> collect = registry
.streamTagsAndEntries() .streamTagsAndEntries()
.collect(HashMap::new, .collect(HashMap::new,
(map, pair) -> (map, pair) ->
map.put(pair.getFirst(), new ArrayList<>(pair.getSecond().stream().toList())), map.put(pair.getFirst(), new ArrayList<>(pair.getSecond().stream().toList())),
HashMap::putAll); HashMap::putAll);
}
public static void registerWorldPresetTags(Registry<WorldPreset> registry) {
logger.info("Doing preset tag garbage....");
Map<TagKey<WorldPreset>, List<RegistryEntry<WorldPreset>>> collect = tagsToMutableMap(registry);
LifecycleUtil
.getPresets()
.forEach(id -> FabricUtil
.getEntry(registry, id)
.ifPresentOrElse(
preset -> collect
.computeIfAbsent(WorldPresetTags.NORMAL, tag -> new ArrayList<>())
.add(preset),
() -> logger.error("Preset {} does not exist!", id)));
registry.clearTags();
registry.populateTags(ImmutableMap.copyOf(collect));
}
public static void registerBiomeTags(Registry<Biome> registry) {
logger.info("Doing biome tag garbage....");
Map<TagKey<Biome>, List<RegistryEntry<Biome>>> collect = tagsToMutableMap(registry);
BiomeUtil BiomeUtil
.getTerraBiomeMap() .getTerraBiomeMap()
.forEach((vb, terraBiomes) -> .forEach((vb, terraBiomes) ->
FabricUtil.getEntry(registry, vb) FabricUtil
.ifPresentOrElse(vanilla -> terraBiomes .getEntry(registry, vb)
.forEach(tb -> FabricUtil .ifPresentOrElse(
.getEntry(registry, tb) vanilla -> terraBiomes
.ifPresentOrElse( .forEach(tb -> FabricUtil
terra -> { .getEntry(registry, tb)
logger.debug( .ifPresentOrElse(
vanilla.getKey() terra -> {
.orElseThrow() logger.debug(
.getValue() + vanilla.getKey()
" (vanilla for " + .orElseThrow()
terra.getKey() .getValue() +
.orElseThrow() " (vanilla for " +
.getValue() + terra.getKey()
": " + .orElseThrow()
vanilla.streamTags() .getValue() +
.toList()); ": " +
vanilla.streamTags()
vanilla.streamTags() .toList());
.forEach(
tag -> collect vanilla.streamTags()
.computeIfAbsent( .forEach(
tag, tag -> collect
t -> new ArrayList<>()) .computeIfAbsent(
.add(terra)); tag,
}, t -> new ArrayList<>())
() -> logger.error( .add(terra));
"No such biome: {}", },
tb))), () -> logger.error(
() -> logger.error("No vanilla biome: {}", vb))); "No such biome: {}",
tb))),
() -> logger.error("No vanilla biome: {}", vb)));
registry.clearTags(); registry.clearTags();
registry.populateTags(ImmutableMap.copyOf(collect)); registry.populateTags(ImmutableMap.copyOf(collect));

View File

@ -26,7 +26,7 @@
"depends": { "depends": {
"fabricloader": ">=0.14.2", "fabricloader": ">=0.14.2",
"java": ">=17", "java": ">=17",
"minecraft": "1.18.x" "minecraft": "1.19.x"
}, },
"accessWidener": "terra.accesswidener" "accessWidener": "terra.accesswidener"
} }

View File

@ -1,5 +1 @@
accessWidener v1 named accessWidener v1 named
extendable method net/minecraft/client/world/GeneratorType <init> (Ljava/lang/String;)V
accessible method net/minecraft/world/biome/Biome getCategory ()Lnet/minecraft/world/biome/Biome$Category;

View File

@ -1,51 +1,51 @@
{ {
"required": true, "required": true,
"minVersion": "0.8", "minVersion": "0.8",
"package": "com.dfsek.terra.fabric.mixin", "package": "com.dfsek.terra.fabric.mixin",
"compatibilityLevel": "JAVA_17", "compatibilityLevel": "JAVA_17",
"mixins": [ "mixins": [
"access.MobSpawnerLogicAccessor", "ReloadCommandMixin",
"access.StateAccessor", "access.MobSpawnerLogicAccessor",
"access.StructureAccessorAccessor", "access.StateAccessor",
"compat.GenerationSettingsFloraFeaturesMixin", "access.StructureAccessorAccessor",
"implementations.BiomeMixin", "compat.GenerationSettingsFloraFeaturesMixin",
"implementations.HandleImplementationMixin", "implementations.BiomeMixin",
"implementations.block.BlockMixin", "implementations.HandleImplementationMixin",
"implementations.block.entity.BlockEntityMixin", "implementations.block.BlockMixin",
"implementations.block.entity.LootableContainerBlockEntityMixin", "implementations.block.entity.BlockEntityMixin",
"implementations.block.entity.MobSpawnerBlockEntityMixin", "implementations.block.entity.LootableContainerBlockEntityMixin",
"implementations.block.entity.SignBlockEntityMixin", "implementations.block.entity.MobSpawnerBlockEntityMixin",
"implementations.block.state.BlockStateMixin", "implementations.block.entity.SignBlockEntityMixin",
"implementations.block.state.PropertyMixin", "implementations.block.state.BlockStateMixin",
"implementations.chunk.ChunkRegionMixin", "implementations.block.state.PropertyMixin",
"implementations.chunk.WorldChunkMixin", "implementations.chunk.ChunkRegionMixin",
"implementations.chunk.data.ProtoChunkMixin", "implementations.chunk.WorldChunkMixin",
"implementations.entity.EntityMixin", "implementations.chunk.data.ProtoChunkMixin",
"implementations.entity.EntityTypeMixin", "implementations.entity.EntityMixin",
"implementations.entity.PlayerEntityMixin", "implementations.entity.EntityTypeMixin",
"implementations.entity.ServerCommandSourceMixin", "implementations.entity.PlayerEntityMixin",
"implementations.inventory.LockableContainerBlockEntityMixin", "implementations.entity.ServerCommandSourceMixin",
"implementations.inventory.item.ItemMixin", "implementations.inventory.LockableContainerBlockEntityMixin",
"implementations.inventory.item.ItemStackMixin", "implementations.inventory.item.ItemMixin",
"implementations.inventory.meta.EnchantmentMixin", "implementations.inventory.item.ItemStackMixin",
"implementations.inventory.meta.ItemStackDamageableMixin", "implementations.inventory.meta.EnchantmentMixin",
"implementations.inventory.meta.ItemStackMetaMixin", "implementations.inventory.meta.ItemStackDamageableMixin",
"implementations.world.ChunkRegionMixin", "implementations.inventory.meta.ItemStackMetaMixin",
"implementations.world.ServerWorldMixin", "implementations.world.ChunkRegionMixin",
"lifecycle.DataPackContentsMixin", "implementations.world.ServerWorldMixin",
"lifecycle.MinecraftServerMixin", "lifecycle.DataPackContentsMixin",
"lifecycle.RegistryMixin" "lifecycle.MinecraftServerMixin",
], "lifecycle.NoiseConfigMixin",
"client": [ "lifecycle.RegistryMixin"
"access.GeneratorTypeAccessor", ],
"lifecycle.client.MinecraftClientMixin" "client": [
], "lifecycle.client.MinecraftClientMixin"
"server": [ ],
"lifecycle.server.GeneratorOptionsMixin", "server": [
"lifecycle.server.ServerMainMixin" "lifecycle.server.ServerMainMixin"
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1
}, },
"refmap": "terra-refmap.json" "refmap": "terra-refmap.json"
} }