diff --git a/buildSrc/src/main/kotlin/DependencyConfig.kt b/buildSrc/src/main/kotlin/DependencyConfig.kt index ecc0a8229..a507eb96c 100644 --- a/buildSrc/src/main/kotlin/DependencyConfig.kt +++ b/buildSrc/src/main/kotlin/DependencyConfig.kt @@ -34,6 +34,7 @@ fun Project.configureDependencies() { maven("https://repo.codemc.org/repository/maven-public") maven("https://repo.codemc.io/repository/nms/") maven("https://papermc.io/repo/repository/maven-public/") + maven ( "https://files.minecraftforge.net/maven/" ) } dependencies { diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index ca7ad4360..42a262e15 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -28,6 +28,13 @@ object Versions { const val minotaur = "1.1.0" } + object Forge { + const val minecraft = "1.19" + const val forge = "$minecraft-41.0.38" + const val yarn = "$minecraft+build.1" + const val architecuryLoom = "0.12.0-SNAPSHOT" + } + object Bukkit { const val paper = "1.18.2-R0.1-SNAPSHOT" const val paperLib = "1.0.5" diff --git a/platforms/forge/build.gradle.kts b/platforms/forge/build.gradle.kts new file mode 100644 index 000000000..73aee97a0 --- /dev/null +++ b/platforms/forge/build.gradle.kts @@ -0,0 +1,88 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.modrinth.minotaur.TaskModrinthUpload +import net.fabricmc.loom.task.RemapJarTask +import java.util.Date + +plugins { + id("dev.architectury.loom") version Versions.Forge.architecuryLoom + id("com.modrinth.minotaur") version Versions.Fabric.minotaur +} + +loom { + mixin { + defaultRefmapName.set("terra-refmap.json") + } + + forge { + mixinConfigs.set(listOf("terra.mixins.json")) + + } + +} + +dependencies { + shadedApi(project(":common:implementation:base")) + + forge("net.minecraftforge:forge:${Versions.Forge.forge}") + + minecraft("com.mojang:minecraft:${Versions.Forge.minecraft}") + mappings("net.fabricmc:yarn:${Versions.Forge.yarn}:v2") +} + + + + + +addonDir(project.file("./run/config/Terra/addons"), tasks.named("runClient").get()) +addonDir(project.file("./run/config/Terra/addons"), tasks.named("runServer").get()) + +tasks.withType().configureEach { + options.release.set(17) +} + +tasks.getByName("shadowJar") { + exclude("org/slf4j/**") +} + +val remapped = tasks.register("remapShadedJar") { + dependsOn("installAddons") + group = "loom" + val shadowJar = tasks.getByName("shadowJar") + dependsOn(shadowJar) + inputFile.set(shadowJar.archiveFile) + archiveFileName.set(shadowJar.archiveFileName.get().replace(Regex("-shaded\\.jar$"), "-shaded-mapped.jar")) + addNestedDependencies.set(true) +} + +tasks.named("assemble").configure { + dependsOn("remapShadedJar") +} + +tasks.withType { + finalizedBy(remapped) + manifest { + attributes( + mapOf( + "Specification-Title" to "terra", + "Specification-Vendor" to "Terra Contributors", + "Specification-Version" to "1", + "Implementation-Title" to project.name, + "Implementation-Version" to "@VERSION@", + "Implementation-Vendor" to "Terra Contributors", + "Implementation-Timestamp" to Date().toString() + ) + ) + } +} + +tasks.register("publishModrinth") { + dependsOn("remapShadedJar") + group = "loom" + token = System.getenv("MODRINTH_SECRET") + projectId = "FIlZB9L0" + versionNumber = "${project.version}-forge" + uploadFile = remapped.get().archiveFile.get().asFile + releaseType = "beta" + addGameVersion(Versions.Forge.minecraft) + addLoader("forge") +} \ No newline at end of file diff --git a/platforms/forge/gradle.properties b/platforms/forge/gradle.properties new file mode 100644 index 000000000..82425854e --- /dev/null +++ b/platforms/forge/gradle.properties @@ -0,0 +1 @@ +loom.platform=forge diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/ForgeAddon.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/ForgeAddon.java new file mode 100644 index 000000000..320c03fe2 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/ForgeAddon.java @@ -0,0 +1,80 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge; + +import ca.solostudios.strata.Versions; +import ca.solostudios.strata.version.Version; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.dfsek.terra.api.addon.BaseAddon; +import com.dfsek.terra.api.event.events.config.ConfigurationLoadEvent; +import com.dfsek.terra.api.event.events.config.pack.ConfigPackPostLoadEvent; +import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent; +import com.dfsek.terra.api.event.functional.FunctionalEventHandler; +import com.dfsek.terra.api.world.biome.Biome; +import com.dfsek.terra.forge.config.PostLoadCompatibilityOptions; +import com.dfsek.terra.forge.config.PreLoadCompatibilityOptions; +import com.dfsek.terra.forge.config.VanillaBiomeProperties; + + +public final class ForgeAddon implements BaseAddon { + private static final Version VERSION = Versions.getVersion(1, 0, 0); + private static final Logger logger = LoggerFactory.getLogger(ForgeAddon.class); + private final PlatformImpl terraForgePlugin; + + public ForgeAddon(PlatformImpl terraForgePlugin) { + this.terraForgePlugin = terraForgePlugin; + } + + @Override + public void initialize() { + terraForgePlugin.getEventManager() + .getHandler(FunctionalEventHandler.class) + .register(this, ConfigPackPreLoadEvent.class) + .then(event -> event.getPack().getContext().put(event.loadTemplate(new PreLoadCompatibilityOptions()))) + .global(); + + terraForgePlugin.getEventManager() + .getHandler(FunctionalEventHandler.class) + .register(this, ConfigPackPostLoadEvent.class) + .then(event -> event.getPack().getContext().put(event.loadTemplate(new PostLoadCompatibilityOptions()))) + .priority(100) + .global(); + + terraForgePlugin.getEventManager() + .getHandler(FunctionalEventHandler.class) + .register(this, ConfigurationLoadEvent.class) + .then(event -> { + if(event.is(Biome.class)) { + event.getLoadedObject(Biome.class).getContext().put(event.load(new VanillaBiomeProperties())); + } + }) + .global(); + } + + @Override + public Version getVersion() { + return VERSION; + } + + @Override + public String getID() { + return "terra-fabric"; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/ForgeEntryPoint.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/ForgeEntryPoint.java new file mode 100644 index 000000000..22194ac6e --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/ForgeEntryPoint.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge; + +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.dfsek.terra.forge.data.Codecs; + +@Mod("terra") +public class ForgeEntryPoint { + private static final Logger logger = LoggerFactory.getLogger(ForgeEntryPoint.class); + + private static final PlatformImpl TERRA_PLUGIN = new PlatformImpl(); + + + public static PlatformImpl getPlatform() { + return TERRA_PLUGIN; + } + + public static void register() { // register the things + Registry.register(Registry.CHUNK_GENERATOR, new Identifier("terra:terra"), Codecs.FABRIC_CHUNK_GENERATOR_WRAPPER); + Registry.register(Registry.BIOME_SOURCE, new Identifier("terra:terra"), Codecs.TERRA_BIOME_SOURCE); + } + + public ForgeEntryPoint() { + IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); + + modEventBus.addListener(this::commonSetup); + } + + private void commonSetup(final FMLCommonSetupEvent event) { + logger.info("Initializing Terra Forge mod..."); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/PlatformImpl.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/PlatformImpl.java new file mode 100644 index 000000000..3e3791142 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/PlatformImpl.java @@ -0,0 +1,169 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge; + +import ca.solostudios.strata.Versions; +import ca.solostudios.strata.parser.tokenizer.ParseException; +import ca.solostudios.strata.version.Version; +import com.dfsek.tectonic.api.TypeRegistry; +import com.dfsek.tectonic.api.depth.DepthTracker; +import com.dfsek.tectonic.api.exception.LoadException; +import net.minecraft.MinecraftVersion; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.BuiltinRegistries; +import net.minecraft.world.biome.Biome.Precipitation; +import net.minecraft.world.biome.BiomeEffects.GrassColorModifier; +import net.minecraftforge.fml.loading.FMLLoader; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import com.dfsek.terra.AbstractPlatform; +import com.dfsek.terra.addon.EphemeralAddon; +import com.dfsek.terra.api.addon.BaseAddon; +import com.dfsek.terra.api.handle.ItemHandle; +import com.dfsek.terra.api.handle.WorldHandle; +import com.dfsek.terra.api.util.generic.Lazy; +import com.dfsek.terra.api.world.biome.PlatformBiome; +import com.dfsek.terra.forge.generation.FabricChunkGeneratorWrapper; +import com.dfsek.terra.forge.handle.FabricItemHandle; +import com.dfsek.terra.forge.handle.FabricWorldHandle; +import com.dfsek.terra.forge.util.BiomeUtil; +import com.dfsek.terra.forge.util.ProtoPlatformBiome; + + +public class PlatformImpl extends AbstractPlatform { + private static final Logger LOGGER = LoggerFactory.getLogger(PlatformImpl.class); + private final ItemHandle itemHandle = new FabricItemHandle(); + private final WorldHandle worldHandle = new FabricWorldHandle(); + private final Lazy dataFolder = Lazy.lazy(() -> new File("./config/Terra")); + private MinecraftServer server; + + public PlatformImpl() { + load(); + } + + public void setServer(MinecraftServer server) { + this.server = server; + } + + public MinecraftServer getServer() { + return server; + } + + @Override + public boolean reload() { + getTerraConfig().load(this); + getRawConfigRegistry().clear(); + boolean succeed = getRawConfigRegistry().loadAll(this); + + + if(server != null) { + server.reloadResources(server.getDataPackManager().getNames()).exceptionally(throwable -> { + LOGGER.warn("Failed to execute reload", throwable); + return null; + }).join(); + BiomeUtil.registerBiomes(); + server.getWorlds().forEach(world -> { + if(world.getChunkManager().getChunkGenerator() instanceof FabricChunkGeneratorWrapper chunkGeneratorWrapper) { + getConfigRegistry().get(chunkGeneratorWrapper.getPack().getRegistryKey()).ifPresent(pack -> { + chunkGeneratorWrapper.setPack(pack); + LOGGER.info("Replaced pack in chunk generator for world {}", world); + }); + } + }); + } + return succeed; + } + + @Override + protected Iterable platformAddon() { + List addons = new ArrayList<>(); + + addons.add(new ForgeAddon(this)); + + String mcVersion = MinecraftVersion.CURRENT.getReleaseTarget(); + try { + addons.add(new EphemeralAddon(Versions.parseVersion(mcVersion), "minecraft")); + } catch(ParseException e) { + try { + addons.add(new EphemeralAddon(Versions.parseVersion(mcVersion + ".0"), "minecraft")); + } catch(ParseException ex) { + LOGGER.warn("Failed to parse Minecraft version", e); + } + } + + FMLLoader.getLoadingModList().getMods().forEach(mod -> { + String id = mod.getModId(); + if(id.equals("terra") || id.equals("minecraft") || id.equals("java")) return; + Version version = Versions.getVersion(mod.getVersion().getMajorVersion(), mod.getVersion().getMinorVersion(), mod.getVersion().getIncrementalVersion()); + addons.add(new EphemeralAddon(version, "forge:" + id)); + }); + + return addons; + } + + @Override + public @NotNull String platformName() { + return "Fabric"; + } + + @Override + public @NotNull WorldHandle getWorldHandle() { + return worldHandle; + } + + @Override + public @NotNull File getDataFolder() { + return dataFolder.value(); + } + + @Override + public @NotNull ItemHandle getItemHandle() { + return itemHandle; + } + + @Override + public void register(TypeRegistry registry) { + super.register(registry); + registry.registerLoader(PlatformBiome.class, (type, o, loader, depthTracker) -> parseBiome((String) o, depthTracker)) + .registerLoader(Identifier.class, (type, o, loader, depthTracker) -> { + Identifier identifier = Identifier.tryParse((String) o); + if(identifier == null) + throw new LoadException("Invalid identifier: " + o, depthTracker); + return identifier; + }) + .registerLoader(Precipitation.class, (type, o, loader, depthTracker) -> Precipitation.valueOf(((String) o).toUpperCase( + Locale.ROOT))) + .registerLoader(GrassColorModifier.class, (type, o, loader, depthTracker) -> GrassColorModifier.valueOf(((String) o).toUpperCase( + Locale.ROOT))); + } + + + private ProtoPlatformBiome parseBiome(String id, DepthTracker tracker) throws LoadException { + Identifier identifier = Identifier.tryParse(id); + if(BuiltinRegistries.BIOME.get(identifier) == null) throw new LoadException("Invalid Biome ID: " + identifier, tracker); // failure. + return new ProtoPlatformBiome(identifier); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/config/PostLoadCompatibilityOptions.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/config/PostLoadCompatibilityOptions.java new file mode 100644 index 000000000..1a8ad12ba --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/config/PostLoadCompatibilityOptions.java @@ -0,0 +1,28 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.config; + +import com.dfsek.tectonic.api.config.template.ConfigTemplate; + +import com.dfsek.terra.api.properties.Properties; + + +@SuppressWarnings("FieldMayBeFinal") +public class PostLoadCompatibilityOptions implements ConfigTemplate, Properties { + +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/config/PreLoadCompatibilityOptions.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/config/PreLoadCompatibilityOptions.java new file mode 100644 index 000000000..bf7c3b7bc --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/config/PreLoadCompatibilityOptions.java @@ -0,0 +1,60 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.config; + +import com.dfsek.tectonic.api.config.template.ConfigTemplate; +import com.dfsek.tectonic.api.config.template.annotations.Default; +import com.dfsek.tectonic.api.config.template.annotations.Value; + +import com.dfsek.terra.api.properties.Properties; + + +@SuppressWarnings("FieldMayBeFinal") +public class PreLoadCompatibilityOptions implements ConfigTemplate, Properties { + @Value("fabric.use-vanilla-biomes") + @Default + private boolean vanillaBiomes = false; + + @Value("fabric.beard.enable") + @Default + private boolean beard = true; + + @Value("fabric.beard.threshold") + @Default + private double beardThreshold = 0.5; + + @Value("fabric.beard.air-threshold") + @Default + private double airThreshold = -0.5; + + public boolean useVanillaBiomes() { + return vanillaBiomes; + } + + public boolean isBeard() { + return beard; + } + + public double getBeardThreshold() { + return beardThreshold; + } + + public double getAirThreshold() { + return airThreshold; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/config/VanillaBiomeProperties.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/config/VanillaBiomeProperties.java new file mode 100644 index 000000000..b4baa1882 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/config/VanillaBiomeProperties.java @@ -0,0 +1,76 @@ +package com.dfsek.terra.forge.config; + +import com.dfsek.tectonic.api.config.template.ConfigTemplate; +import com.dfsek.tectonic.api.config.template.annotations.Default; +import com.dfsek.tectonic.api.config.template.annotations.Value; +import net.minecraft.world.biome.Biome.Precipitation; +import net.minecraft.world.biome.BiomeEffects.GrassColorModifier; + +import com.dfsek.terra.api.properties.Properties; + + +public class VanillaBiomeProperties implements ConfigTemplate, Properties { + @Value("colors.grass") + @Default + private Integer grassColor = null; + + @Value("colors.fog") + @Default + private Integer fogColor = null; + + @Value("colors.water") + @Default + private Integer waterColor = null; + + @Value("colors.water-fog") + @Default + private Integer waterFogColor = null; + + @Value("colors.foliage") + @Default + private Integer foliageColor = null; + + @Value("colors.sky") + @Default + private Integer skyColor = null; + + @Value("colors.modifier") + @Default + private GrassColorModifier modifier = null; + + @Value("climate.precipitation") + @Default + private Precipitation precipitation = null; + + public Integer getFogColor() { + return fogColor; + } + + public Integer getFoliageColor() { + return foliageColor; + } + + public Integer getGrassColor() { + return grassColor; + } + + public Integer getWaterColor() { + return waterColor; + } + + public Integer getWaterFogColor() { + return waterFogColor; + } + + public Integer getSkyColor() { + return skyColor; + } + + public Precipitation getPrecipitation() { + return precipitation; + } + + public GrassColorModifier getModifier() { + return modifier; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/data/Codecs.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/data/Codecs.java new file mode 100644 index 000000000..95bf25e1a --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/data/Codecs.java @@ -0,0 +1,65 @@ +package com.dfsek.terra.forge.data; + +import com.dfsek.terra.api.config.ConfigPack; +import com.dfsek.terra.api.registry.key.RegistryKey; +import com.dfsek.terra.forge.ForgeEntryPoint; +import com.dfsek.terra.forge.generation.FabricChunkGeneratorWrapper; +import com.dfsek.terra.forge.generation.TerraBiomeSource; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.util.dynamic.RegistryOps; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.gen.chunk.ChunkGeneratorSettings; + + +public final class Codecs { + public static final Codec TERRA_REGISTRY_KEY = RecordCodecBuilder + .create(registryKey -> registryKey.group(Codec.STRING.fieldOf("namespace") + .stable() + .forGetter(RegistryKey::getNamespace), + Codec.STRING.fieldOf("id") + .stable() + .forGetter(RegistryKey::getID)) + .apply(registryKey, registryKey.stable(RegistryKey::of))); + + public static final Codec CONFIG_PACK = RecordCodecBuilder + .create(config -> config.group(TERRA_REGISTRY_KEY.fieldOf("pack") + .stable() + .forGetter(ConfigPack::getRegistryKey)) + .apply(config, config.stable(id -> ForgeEntryPoint.getPlatform() + .getConfigRegistry() + .get(id) + .orElseThrow(() -> new IllegalArgumentException( + "No such config pack " + + id))))); + + public static final Codec TERRA_BIOME_SOURCE = RecordCodecBuilder + .create(instance -> instance.group(RegistryOps.createRegistryCodec(Registry.BIOME_KEY) + .fieldOf("biome_registry") + .stable() + .forGetter(TerraBiomeSource::getBiomeRegistry), + CONFIG_PACK.fieldOf("pack") + .stable() + .forGetter(TerraBiomeSource::getPack)) + .apply(instance, instance.stable(TerraBiomeSource::new))); + + public static final Codec FABRIC_CHUNK_GENERATOR_WRAPPER = RecordCodecBuilder + .create( + instance -> instance.group( + RegistryOps.createRegistryCodec(Registry.STRUCTURE_SET_KEY) + .fieldOf("structure_registry") + .stable() + .forGetter(FabricChunkGeneratorWrapper::getNoiseRegistry), + TERRA_BIOME_SOURCE.fieldOf("biome_source") + .stable() + .forGetter(FabricChunkGeneratorWrapper::getBiomeSource), + CONFIG_PACK.fieldOf("pack") + .stable() + .forGetter(FabricChunkGeneratorWrapper::getPack), + ChunkGeneratorSettings.REGISTRY_CODEC.fieldOf("settings") + .stable() + .forGetter(FabricChunkGeneratorWrapper::getSettings) + ).apply(instance, instance.stable(FabricChunkGeneratorWrapper::new)) + ); +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/generation/FabricChunkGeneratorWrapper.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/generation/FabricChunkGeneratorWrapper.java new file mode 100644 index 000000000..e31bb40b2 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/generation/FabricChunkGeneratorWrapper.java @@ -0,0 +1,246 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.generation; + +import com.mojang.serialization.Codec; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.structure.StructureSet; +import net.minecraft.util.Util; +import net.minecraft.util.math.BlockPos; +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.RegistryEntry; +import net.minecraft.world.ChunkRegion; +import net.minecraft.world.HeightLimitView; +import net.minecraft.world.Heightmap.Type; +import net.minecraft.world.SpawnHelper; +import net.minecraft.world.StructureWorldAccess; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.source.BiomeAccess; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.gen.GenerationStep.Carver; +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.ChunkGeneratorSettings; +import net.minecraft.world.gen.chunk.VerticalBlockSample; +import net.minecraft.world.gen.densityfunction.DensityFunction.UnblendedNoisePos; +import net.minecraft.world.gen.noise.NoiseConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.Optional; +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.chunk.generation.ChunkGenerator; +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.stage.Chunkified; +import com.dfsek.terra.api.world.chunk.generation.util.GeneratorWrapper; +import com.dfsek.terra.api.world.info.WorldProperties; +import com.dfsek.terra.forge.config.PreLoadCompatibilityOptions; +import com.dfsek.terra.forge.data.Codecs; +import com.dfsek.terra.forge.mixin.access.StructureAccessorAccessor; +import com.dfsek.terra.forge.util.FabricAdapter; + + +public class FabricChunkGeneratorWrapper extends net.minecraft.world.gen.chunk.ChunkGenerator implements GeneratorWrapper { + private static final Logger logger = LoggerFactory.getLogger(FabricChunkGeneratorWrapper.class); + + private final TerraBiomeSource biomeSource; + private final Registry noiseRegistry; + private final RegistryEntry settings; + private ChunkGenerator delegate; + private ConfigPack pack; + + public FabricChunkGeneratorWrapper(Registry noiseRegistry, TerraBiomeSource biomeSource, ConfigPack configPack, + RegistryEntry settingsSupplier) { + super(noiseRegistry, Optional.empty(), biomeSource); + this.noiseRegistry = noiseRegistry; + this.pack = configPack; + this.settings = settingsSupplier; + + this.delegate = pack.getGeneratorProvider().newInstance(pack); + logger.info("Loading world with config pack {}", pack.getID()); + this.biomeSource = biomeSource; + } + + public Registry getNoiseRegistry() { + return noiseRegistry; + } + + @Override + protected Codec getCodec() { + return Codecs.FABRIC_CHUNK_GENERATOR_WRAPPER; + } + + @Override + public void buildSurface(ChunkRegion region, StructureAccessor structures, NoiseConfig noiseConfig, Chunk chunk) { + // no op + } + + @Override + public void populateEntities(ChunkRegion region) { + if(!this.settings.value().mobGenerationDisabled()) { + ChunkPos chunkPos = region.getCenterPos(); + RegistryEntry registryEntry = region.getBiome(chunkPos.getStartPos().withY(region.getTopY() - 1)); + ChunkRandom chunkRandom = new ChunkRandom(new CheckedRandom(RandomSeed.getSeed())); + chunkRandom.setPopulationSeed(region.getSeed(), chunkPos.getStartX(), chunkPos.getStartZ()); + SpawnHelper.populateEntities(region, registryEntry, chunkPos, chunkRandom); + } + } + + @Override + public int getWorldHeight() { + return settings.value().generationShapeConfig().height(); + } + + + @Override + public CompletableFuture populateNoise(Executor executor, Blender blender, NoiseConfig noiseConfig, + StructureAccessor structureAccessor, Chunk chunk) { + return CompletableFuture.supplyAsync(() -> { + ProtoWorld world = (ProtoWorld) ((StructureAccessorAccessor) structureAccessor).getWorld(); + BiomeProvider biomeProvider = pack.getBiomeProvider(); + delegate.generateChunkData((ProtoChunk) chunk, world, biomeProvider, chunk.getPos().x, chunk.getPos().z); + + PreLoadCompatibilityOptions compatibilityOptions = pack.getContext().get(PreLoadCompatibilityOptions.class); + if(compatibilityOptions.isBeard()) { + beard(structureAccessor, chunk, world, biomeProvider, compatibilityOptions); + } + return chunk; + }, 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 + public void generateFeatures(StructureWorldAccess world, Chunk chunk, StructureAccessor structureAccessor) { + super.generateFeatures(world, chunk, structureAccessor); + pack.getStages().forEach(populator -> { + if(!(populator instanceof Chunkified)) { + populator.populate((ProtoWorld) world); + } + }); + } + + @Override + public int getSeaLevel() { + return settings.value().seaLevel(); + } + + @Override + public int getMinimumY() { + return settings.value().generationShapeConfig().minimumY(); + } + + + @Override + public int getHeight(int x, int z, Type heightmap, HeightLimitView height, NoiseConfig noiseConfig) { + WorldProperties properties = FabricAdapter.adapt(height, noiseConfig.getLegacyWorldSeed()); + BiomeProvider biomeProvider = pack.getBiomeProvider(); + int min = height.getBottomY(); + for(int y = height.getTopY() - 1; y >= min; y--) { + if(heightmap + .getBlockPredicate() + .test((BlockState) delegate.getBlock(properties, x, y, z, biomeProvider))) return y + 1; + } + return min; + } + + @Override + public VerticalBlockSample getColumnSample(int x, int z, HeightLimitView height, NoiseConfig noiseConfig) { + BlockState[] array = new BlockState[height.getHeight()]; + WorldProperties properties = FabricAdapter.adapt(height, noiseConfig.getLegacyWorldSeed()); + BiomeProvider biomeProvider = pack.getBiomeProvider(); + for(int y = height.getTopY() - 1; y >= height.getBottomY(); y--) { + array[y - height.getBottomY()] = (BlockState) delegate.getBlock(properties, x, y, z, biomeProvider); + } + return new VerticalBlockSample(height.getBottomY(), array); + } + + @Override + public void getDebugHudText(List text, NoiseConfig noiseConfig, BlockPos pos) { + + } + + public ConfigPack getPack() { + return pack; + } + + public void setPack(ConfigPack pack) { + this.pack = pack; + this.delegate = pack.getGeneratorProvider().newInstance(pack); + biomeSource.setPack(pack); + + logger.debug("Loading world with config pack {}", pack.getID()); + } + + @Override + public void carve(ChunkRegion chunkRegion, long seed, NoiseConfig noiseConfig, BiomeAccess world, StructureAccessor structureAccessor, + Chunk chunk, Carver carverStep) { + // no op + } + + @Override + public ChunkGenerator getHandle() { + return delegate; + } + + public RegistryEntry getSettings() { + return settings; + } + + @Override + public TerraBiomeSource getBiomeSource() { + return biomeSource; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/generation/TerraBiomeSource.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/generation/TerraBiomeSource.java new file mode 100644 index 000000000..b43b38ce8 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/generation/TerraBiomeSource.java @@ -0,0 +1,86 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.generation; + +import com.dfsek.terra.api.config.ConfigPack; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; +import com.dfsek.terra.forge.data.Codecs; +import com.dfsek.terra.forge.util.ProtoPlatformBiome; + +import com.dfsek.terra.forge.util.SeedHack; + +import com.mojang.serialization.Codec; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.RegistryEntry; +import net.minecraft.world.biome.source.BiomeSource; +import net.minecraft.world.biome.source.util.MultiNoiseUtil.MultiNoiseSampler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.stream.StreamSupport; + + +public class TerraBiomeSource extends BiomeSource { + + private static final Logger LOGGER = LoggerFactory.getLogger(TerraBiomeSource.class); + private final Registry biomeRegistry; + private ConfigPack pack; + + public TerraBiomeSource(Registry biomes, ConfigPack pack) { + super(StreamSupport + .stream(pack.getBiomeProvider() + .getBiomes() + .spliterator(), false) + .map(b -> biomes.getOrCreateEntry(((ProtoPlatformBiome) b.getPlatformBiome()).getDelegate()))); + this.biomeRegistry = biomes; + this.pack = pack; + + LOGGER.debug("Biomes: " + getBiomes()); + } + + @Override + protected Codec getCodec() { + return Codecs.TERRA_BIOME_SOURCE; + } + + @Override + public RegistryEntry getBiome(int biomeX, int biomeY, int biomeZ, MultiNoiseSampler noiseSampler) { + return biomeRegistry + .entryOf(((ProtoPlatformBiome) pack + .getBiomeProvider() + .getBiome(biomeX << 2, biomeY << 2, biomeZ << 2, SeedHack.getSeed(noiseSampler)) + .getPlatformBiome()).getDelegate() + ); + } + + public BiomeProvider getProvider() { + return pack.getBiomeProvider(); + } + + public Registry getBiomeRegistry() { + return biomeRegistry; + } + + public ConfigPack getPack() { + return pack; + } + + public void setPack(ConfigPack pack) { + this.pack = pack; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/handle/FabricItemHandle.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/handle/FabricItemHandle.java new file mode 100644 index 000000000..cc7b0d1a6 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/handle/FabricItemHandle.java @@ -0,0 +1,58 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.handle; + +import com.dfsek.terra.forge.ForgeEntryPoint; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.command.CommandRegistryAccess; +import net.minecraft.command.argument.ItemStackArgumentType; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Set; +import java.util.stream.Collectors; + +import com.dfsek.terra.api.handle.ItemHandle; +import com.dfsek.terra.api.inventory.Item; +import com.dfsek.terra.api.inventory.item.Enchantment; + + +public class FabricItemHandle implements ItemHandle { + + @Override + public Item createItem(String data) { + try { + return (Item) new ItemStackArgumentType(new CommandRegistryAccess( + ForgeEntryPoint.getPlatform().getServer().getRegistryManager())).parse(new StringReader(data)).getItem(); + } catch(CommandSyntaxException e) { + throw new IllegalArgumentException("Invalid item data \"" + data + "\"", e); + } + } + + @Override + public Enchantment getEnchantment(String id) { + return (Enchantment) (Registry.ENCHANTMENT.get(Identifier.tryParse(id))); + } + + @Override + public Set getEnchantments() { + return Registry.ENCHANTMENT.stream().map(enchantment -> (Enchantment) enchantment).collect(Collectors.toSet()); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/handle/FabricWorldHandle.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/handle/FabricWorldHandle.java new file mode 100644 index 000000000..dd47858b8 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/handle/FabricWorldHandle.java @@ -0,0 +1,59 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.handle; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.block.Blocks; +import net.minecraft.command.argument.BlockArgumentParser; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import net.minecraftforge.registries.ForgeRegistries; +import org.jetbrains.annotations.NotNull; + +import com.dfsek.terra.api.block.state.BlockState; +import com.dfsek.terra.api.entity.EntityType; +import com.dfsek.terra.api.handle.WorldHandle; + + +public class FabricWorldHandle implements WorldHandle { + + private static final BlockState AIR = (BlockState) Blocks.AIR.getDefaultState(); + + @Override + public @NotNull BlockState createBlockState(@NotNull String data) { + try { + net.minecraft.block.BlockState state = BlockArgumentParser.block(Registry.BLOCK, data, true).blockState(); + if(state == null) throw new IllegalArgumentException("Invalid data: " + data); + return (BlockState) state; + } catch(CommandSyntaxException e) { + throw new IllegalArgumentException(e); + } + } + + @Override + public @NotNull BlockState air() { + return AIR; + } + + @Override + public @NotNull EntityType getEntity(@NotNull String id) { + Identifier identifier = Identifier.tryParse(id); + if(identifier == null) identifier = Identifier.tryParse(id); + return (EntityType) ForgeRegistries.ENTITIES.getHolder(identifier).orElseThrow().value(); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/access/ChunkRegionAccessor.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/access/ChunkRegionAccessor.java new file mode 100644 index 000000000..58624dfe9 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/access/ChunkRegionAccessor.java @@ -0,0 +1,15 @@ +package com.dfsek.terra.forge.mixin.access; + +import net.minecraft.world.ChunkRegion; +import net.minecraft.world.chunk.Chunk; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + + +@Mixin(ChunkRegion.class) +public interface ChunkRegionAccessor { + @Accessor("chunks") + List getChunks(); +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/access/MobSpawnerLogicAccessor.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/access/MobSpawnerLogicAccessor.java new file mode 100644 index 000000000..20a900005 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/access/MobSpawnerLogicAccessor.java @@ -0,0 +1,30 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.access; + +import net.minecraft.world.MobSpawnerEntry; +import net.minecraft.world.MobSpawnerLogic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + + +@Mixin(MobSpawnerLogic.class) +public interface MobSpawnerLogicAccessor { + @Accessor("spawnEntry") + MobSpawnerEntry getSpawnEntry(); +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/access/StateAccessor.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/access/StateAccessor.java new file mode 100644 index 000000000..947118da9 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/access/StateAccessor.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.access; + +import net.minecraft.state.State; +import net.minecraft.state.property.Property; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; +import java.util.function.Function; + + +@Mixin(State.class) +public interface StateAccessor { + @Accessor("PROPERTY_MAP_PRINTER") + static Function, Comparable>, String> getPropertyMapPrinter() { + throw new UnsupportedOperationException(); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/access/StructureAccessorAccessor.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/access/StructureAccessorAccessor.java new file mode 100644 index 000000000..12ca75c9d --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/access/StructureAccessorAccessor.java @@ -0,0 +1,30 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.access; + +import net.minecraft.world.WorldAccess; +import net.minecraft.world.gen.StructureAccessor; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + + +@Mixin(StructureAccessor.class) +public interface StructureAccessorAccessor { + @Accessor + WorldAccess getWorld(); +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/fix/BeeMoveGoalsUnsynchronizedRandomAccessFix.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/fix/BeeMoveGoalsUnsynchronizedRandomAccessFix.java new file mode 100644 index 000000000..53b2ac21d --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/fix/BeeMoveGoalsUnsynchronizedRandomAccessFix.java @@ -0,0 +1,27 @@ +package com.dfsek.terra.forge.mixin.fix; + +import com.dfsek.terra.forge.ForgeEntryPoint; + +import net.minecraft.entity.passive.BeeEntity.MoveToFlowerGoal; +import net.minecraft.entity.passive.BeeEntity.MoveToHiveGoal; +import net.minecraft.util.math.random.CheckedRandom; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + + +/** + * Bees spawning uses world.random without synchronization. This causes issues when spawning bees during world generation. + */ +@Mixin({ + MoveToHiveGoal.class, + MoveToFlowerGoal.class +}) +public class BeeMoveGoalsUnsynchronizedRandomAccessFix { + @Redirect(method = "", at = @At(value = "FIELD", target = "Lnet/minecraft/world/World;random:Lnet/minecraft/util/math/random/Random;")) + public Random redirectRandomAccess(World instance) { + return new CheckedRandom(ForgeEntryPoint.getPlatform().getServer().getTicks()); // replace with new random seeded by tick time. + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/fix/NetherFossilOptimization.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/fix/NetherFossilOptimization.java new file mode 100644 index 000000000..c8def7e88 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/fix/NetherFossilOptimization.java @@ -0,0 +1,29 @@ +package com.dfsek.terra.forge.mixin.fix; + +import com.dfsek.terra.forge.generation.FabricChunkGeneratorWrapper; + +import net.minecraft.world.gen.structure.NetherFossilStructure; +import net.minecraft.world.gen.structure.Structure.Context; +import net.minecraft.world.gen.structure.Structure.StructurePosition; +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.Optional; + + +/** + * Disable fossil generation in Terra worlds, as they are very expensive due to consistently triggering cache misses. + * + * Currently, on Fabric, Terra cannot be specified as a Nether generator. TODO: logic to turn fossils back on if chunk generator is in nether. + */ +@Mixin(NetherFossilStructure.class) +public class NetherFossilOptimization { + @Inject(method = "getStructurePosition", at = @At("HEAD"), cancellable = true) + public void injectFossilPositions(Context context, CallbackInfoReturnable> cir) { + if(context.chunkGenerator() instanceof FabricChunkGeneratorWrapper) { + cir.setReturnValue(Optional.empty()); + } + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/compat/GenerationSettingsFloraFeaturesMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/compat/GenerationSettingsFloraFeaturesMixin.java new file mode 100644 index 000000000..19639e48a --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/compat/GenerationSettingsFloraFeaturesMixin.java @@ -0,0 +1,32 @@ +package com.dfsek.terra.forge.mixin.implementations.compat; + +import com.dfsek.terra.forge.mixin_ifaces.FloraFeatureHolder; + +import net.minecraft.world.biome.GenerationSettings; +import net.minecraft.world.gen.feature.ConfiguredFeature; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +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.List; + + +@Mixin(GenerationSettings.class) +@Implements(@Interface(iface = FloraFeatureHolder.class, prefix = "terra$")) +public class GenerationSettingsFloraFeaturesMixin { + private List> flora; + + public void terra$setFloraFeatures(List> features) { + this.flora = features; + } + + @Inject(method = "getFlowerFeatures", cancellable = true, at = @At("HEAD")) + public void inject(CallbackInfoReturnable>> cir) { + if(flora != null) { + cir.setReturnValue(flora); + } + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/BiomeMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/BiomeMixin.java new file mode 100644 index 000000000..3bf07cffc --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/BiomeMixin.java @@ -0,0 +1,31 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra; + +import net.minecraft.world.biome.Biome; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + +import com.dfsek.terra.api.world.biome.PlatformBiome; + + +@Mixin(Biome.class) +@Implements(@Interface(iface = PlatformBiome.class, prefix = "terra$")) +public abstract class BiomeMixin { +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/HandleImplementationMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/HandleImplementationMixin.java new file mode 100644 index 000000000..936af09b3 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/HandleImplementationMixin.java @@ -0,0 +1,61 @@ +package com.dfsek.terra.forge.mixin.implementations.terra; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.LockableContainerBlockEntity; +import net.minecraft.block.entity.LootableContainerBlockEntity; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.world.ChunkRegion; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.chunk.ProtoChunk; +import net.minecraft.world.chunk.WorldChunk; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; + +import com.dfsek.terra.api.Handle; + + +/** + * A ton of Minecraft classes must implement Handle identically, we can just take care of it here + */ +@Mixin({ + ServerWorld.class, + ChunkRegion.class, + + Block.class, + BlockState.class, + + BlockEntity.class, + LootableContainerBlockEntity.class, + LockableContainerBlockEntity.class, + + ProtoChunk.class, + WorldChunk.class, + + Entity.class, + EntityType.class, + + ServerCommandSource.class, + + Item.class, + ItemStack.class, + Enchantment.class, + + Biome.class +}) +@Implements(@Interface(iface = Handle.class, prefix = "terra$")) +public class HandleImplementationMixin { + @Intrinsic + public Object terra$getHandle() { + return this; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/BlockMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/BlockMixin.java new file mode 100644 index 000000000..8df67e40a --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/BlockMixin.java @@ -0,0 +1,44 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.block; + +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + +import com.dfsek.terra.api.block.BlockType; + + +@Mixin(Block.class) +@Implements(@Interface(iface = BlockType.class, prefix = "terra$")) +public abstract class BlockMixin { + public com.dfsek.terra.api.block.state.BlockState terra$getDefaultState() { + return (com.dfsek.terra.api.block.state.BlockState) ((Block) (Object) this).getDefaultState(); + } + + public boolean terra$isSolid() { + return ((Block) (Object) this).getDefaultState().isOpaque(); + } + + @SuppressWarnings("ConstantConditions") + public boolean terra$isWater() { + return ((Object) this) == Blocks.WATER; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/entity/BlockEntityMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/entity/BlockEntityMixin.java new file mode 100644 index 000000000..272c18812 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/entity/BlockEntityMixin.java @@ -0,0 +1,54 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.block.entity; + +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + +import com.dfsek.terra.api.block.entity.BlockEntity; +import com.dfsek.terra.api.block.state.BlockState; + + +@Mixin(net.minecraft.block.entity.BlockEntity.class) +@Implements(@Interface(iface = BlockEntity.class, prefix = "terra$")) +public abstract class BlockEntityMixin { + public boolean terra$update(boolean applyPhysics) { + if(((net.minecraft.block.entity.BlockEntity) (Object) this).hasWorld()) //noinspection ConstantConditions + ((net.minecraft.block.entity.BlockEntity) (Object) this).getWorld().getChunk( + ((net.minecraft.block.entity.BlockEntity) (Object) this).getPos()).setBlockEntity( + (net.minecraft.block.entity.BlockEntity) (Object) this); + return true; + } + + public int terra$getX() { + return ((net.minecraft.block.entity.BlockEntity) (Object) this).getPos().getX(); + } + + public int terra$getY() { + return ((net.minecraft.block.entity.BlockEntity) (Object) this).getPos().getY(); + } + + public int terra$getZ() { + return ((net.minecraft.block.entity.BlockEntity) (Object) this).getPos().getZ(); + } + + public BlockState terra$getBlockState() { + return (BlockState) ((net.minecraft.block.entity.BlockEntity) (Object) this).getCachedState(); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/entity/LootableContainerBlockEntityMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/entity/LootableContainerBlockEntityMixin.java new file mode 100644 index 000000000..123541696 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/entity/LootableContainerBlockEntityMixin.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.block.entity; + +import net.minecraft.block.entity.LootableContainerBlockEntity; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + +import com.dfsek.terra.api.block.entity.Container; +import com.dfsek.terra.api.inventory.Inventory; + + +@Mixin(LootableContainerBlockEntity.class) +@Implements(@Interface(iface = Container.class, prefix = "terra$")) +public abstract class LootableContainerBlockEntityMixin extends BlockEntityMixin { + public Inventory terra$getInventory() { + return (Inventory) this; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/entity/MobSpawnerBlockEntityMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/entity/MobSpawnerBlockEntityMixin.java new file mode 100644 index 000000000..60bafaefb --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/entity/MobSpawnerBlockEntityMixin.java @@ -0,0 +1,131 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.block.entity; + +import com.dfsek.terra.forge.ForgeEntryPoint; +import com.dfsek.terra.forge.mixin.access.MobSpawnerLogicAccessor; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.block.entity.MobSpawnerBlockEntity; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.MobSpawnerLogic; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import com.dfsek.terra.api.block.entity.MobSpawner; +import com.dfsek.terra.api.block.entity.SerialState; +import com.dfsek.terra.api.entity.EntityType; + + +@Mixin(MobSpawnerBlockEntity.class) +@Implements(@Interface(iface = MobSpawner.class, prefix = "terra$")) +public abstract class MobSpawnerBlockEntityMixin extends BlockEntity { + private MobSpawnerBlockEntityMixin(BlockEntityType type, BlockPos pos, BlockState state) { + super(type, pos, state); + } + + @Shadow + public abstract MobSpawnerLogic getLogic(); + + public EntityType terra$getSpawnedType() { + return (EntityType) Registry.ENTITY_TYPE.get( + Identifier.tryParse(((MobSpawnerLogicAccessor) getLogic()).getSpawnEntry().getNbt().getString("id"))); + } + + public void terra$setSpawnedType(@NotNull EntityType creatureType) { + getLogic().setEntityId((net.minecraft.entity.EntityType) creatureType); + } + + public int terra$getDelay() { + return 0; + } + + public void terra$setDelay(int delay) { + + } + + public int terra$getMinSpawnDelay() { + return 0; + } + + public void terra$setMinSpawnDelay(int delay) { + + } + + public int terra$getMaxSpawnDelay() { + return 0; + } + + public void terra$setMaxSpawnDelay(int delay) { + + } + + public int terra$getSpawnCount() { + return 0; + } + + public void terra$setSpawnCount(int spawnCount) { + + } + + public int terra$getMaxNearbyEntities() { + return 0; + } + + public void terra$setMaxNearbyEntities(int maxNearbyEntities) { + + } + + public int terra$getRequiredPlayerRange() { + return 0; + } + + public void terra$setRequiredPlayerRange(int requiredPlayerRange) { + + } + + public int terra$getSpawnRange() { + return 0; + } + + public void terra$setSpawnRange(int spawnRange) { + + } + + public void terra$applyState(String state) { + SerialState.parse(state).forEach((k, v) -> { + switch(k) { + case "type" -> terra$setSpawnedType(ForgeEntryPoint.getPlatform().getWorldHandle().getEntity(v)); + case "delay" -> terra$setDelay(Integer.parseInt(v)); + case "min_delay" -> terra$setMinSpawnDelay(Integer.parseInt(v)); + case "max_delay" -> terra$setMaxSpawnDelay(Integer.parseInt(v)); + case "spawn_count" -> terra$setSpawnCount(Integer.parseInt(v)); + case "spawn_range" -> terra$setSpawnRange(Integer.parseInt(v)); + case "max_nearby" -> terra$setMaxNearbyEntities(Integer.parseInt(v)); + case "required_player_range" -> terra$setRequiredPlayerRange(Integer.parseInt(v)); + default -> throw new IllegalArgumentException("Invalid property: " + k); + } + }); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/entity/SignBlockEntityMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/entity/SignBlockEntityMixin.java new file mode 100644 index 000000000..400a176ca --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/entity/SignBlockEntityMixin.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.block.entity; + +import net.minecraft.block.entity.SignBlockEntity; +import net.minecraft.text.Text; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import com.dfsek.terra.api.block.entity.SerialState; +import com.dfsek.terra.api.block.entity.Sign; + + +@Mixin(SignBlockEntity.class) +@Implements(@Interface(iface = Sign.class, prefix = "terra$")) +public abstract class SignBlockEntityMixin { + @Shadow + @Final + private Text[] texts; + + @Shadow + public abstract void setTextOnRow(int row, Text text); + + public void terra$setLine(int index, @NotNull String line) throws IndexOutOfBoundsException { + setTextOnRow(index, Text.literal(line)); + } + + public @NotNull String[] terra$getLines() { + String[] lines = new String[texts.length]; + for(int i = 0; i < texts.length; i++) { + lines[i] = texts[i].getString(); + } + return lines; + } + + public @NotNull String terra$getLine(int index) throws IndexOutOfBoundsException { + return texts[index].getString(); + } + + public void terra$applyState(String state) { + SerialState.parse(state).forEach((k, v) -> { + if(!k.startsWith("text")) throw new IllegalArgumentException("Invalid property: " + k); + terra$setLine(Integer.parseInt(k.substring(4)), v); + }); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/state/BlockStateMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/state/BlockStateMixin.java new file mode 100644 index 000000000..c0076820c --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/state/BlockStateMixin.java @@ -0,0 +1,83 @@ +package com.dfsek.terra.forge.mixin.implementations.terra.block.state; + + +import com.dfsek.terra.forge.mixin.access.StateAccessor; +import com.google.common.collect.ImmutableMap; +import com.mojang.serialization.MapCodec; +import net.minecraft.block.AbstractBlock.AbstractBlockState; +import net.minecraft.block.Block; +import net.minecraft.state.State; +import net.minecraft.util.registry.Registry; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.stream.Collectors; + +import com.dfsek.terra.api.block.BlockType; +import com.dfsek.terra.api.block.state.BlockState; +import com.dfsek.terra.api.block.state.properties.Property; + + +@Mixin(AbstractBlockState.class) +@Implements(@Interface(iface = BlockState.class, prefix = "terra$")) +public abstract class BlockStateMixin extends State { + private BlockStateMixin(Block owner, ImmutableMap, Comparable> entries, + MapCodec codec) { + super(owner, entries, codec); + } + + @Shadow + public abstract Block getBlock(); + + @Shadow + public abstract boolean isAir(); + + public boolean terra$matches(BlockState other) { + return getBlock() == ((net.minecraft.block.BlockState) other).getBlock(); + } + + @Intrinsic + public > boolean terra$has(Property property) { + if(property instanceof net.minecraft.state.property.Property minecraftProperty) { + return contains(minecraftProperty); + } + return false; + } + + @SuppressWarnings("unchecked") + @Intrinsic + public > T terra$get(Property property) { + return get((net.minecraft.state.property.Property) property); + } + + @SuppressWarnings("unchecked") + @Intrinsic + public > BlockState terra$set(Property property, T value) { + return (BlockState) with((net.minecraft.state.property.Property) property, value); + } + + @Intrinsic + public BlockType terra$getBlockType() { + return (BlockType) getBlock(); + } + + @Intrinsic + public String terra$getAsString(boolean properties) { + StringBuilder data = new StringBuilder(Registry.BLOCK.getId(getBlock()).toString()); + if(properties && !getEntries().isEmpty()) { + data.append('['); + data.append( + getEntries().entrySet().stream().map(StateAccessor.getPropertyMapPrinter()).collect(Collectors.joining(","))); + data.append(']'); + } + return data.toString(); + } + + @Intrinsic + public boolean terra$isAir() { + return isAir(); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/state/PropertyMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/state/PropertyMixin.java new file mode 100644 index 000000000..ce7d41e20 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/block/state/PropertyMixin.java @@ -0,0 +1,43 @@ +package com.dfsek.terra.forge.mixin.implementations.terra.block.state; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Interface.Remap; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Collection; + +import com.dfsek.terra.api.block.state.properties.Property; + + +@Mixin(net.minecraft.state.property.Property.class) +@Implements(@Interface(iface = Property.class, prefix = "terra$", remap = Remap.NONE)) +public abstract class PropertyMixin { + @Shadow + @Final + private Class type; + @Shadow + @Final + private String name; + + @Shadow + public abstract Collection getValues(); + + @Intrinsic + public Collection terra$values() { + return getValues(); + } + + @Intrinsic + public Class terra$getType() { + return type; + } + + @Intrinsic + public String terra$getID() { + return name; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/chunk/ChunkRegionMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/chunk/ChunkRegionMixin.java new file mode 100644 index 000000000..96bbe0715 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/chunk/ChunkRegionMixin.java @@ -0,0 +1,58 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.chunk; + +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ChunkRegion; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import com.dfsek.terra.api.block.state.BlockState; +import com.dfsek.terra.api.world.chunk.Chunk; + + +@Mixin(ChunkRegion.class) +@Implements(@Interface(iface = Chunk.class, prefix = "terraChunk$")) +public abstract class ChunkRegionMixin { + + @Shadow + @Final + private net.minecraft.world.chunk.Chunk centerPos; + + public void terraChunk$setBlock(int x, int y, int z, @NotNull BlockState blockState, boolean physics) { + ((ChunkRegion) (Object) this).setBlockState(new BlockPos(x + (centerPos.getPos().x << 4), y, z + (centerPos.getPos().z << 4)), + (net.minecraft.block.BlockState) blockState, 0); + } + + public @NotNull BlockState terraChunk$getBlock(int x, int y, int z) { + return (BlockState) ((ChunkRegion) (Object) this).getBlockState( + new BlockPos(x + (centerPos.getPos().x << 4), y, z + (centerPos.getPos().z << 4))); + } + + public int terraChunk$getX() { + return centerPos.getPos().x; + } + + public int terraChunk$getZ() { + return centerPos.getPos().z; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/chunk/WorldChunkMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/chunk/WorldChunkMixin.java new file mode 100644 index 000000000..b6aecff61 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/chunk/WorldChunkMixin.java @@ -0,0 +1,75 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.chunk; + +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.chunk.WorldChunk; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import com.dfsek.terra.api.block.state.BlockState; +import com.dfsek.terra.api.world.ServerWorld; +import com.dfsek.terra.api.world.chunk.Chunk; + + +@Mixin(WorldChunk.class) +@Implements(@Interface(iface = Chunk.class, prefix = "terra$")) +public abstract class WorldChunkMixin { + @Final + @Shadow + net.minecraft.world.World world; + + @Shadow + public abstract net.minecraft.block.BlockState getBlockState(BlockPos pos); + + @Shadow + @Nullable + public abstract net.minecraft.block.BlockState setBlockState(BlockPos pos, net.minecraft.block.BlockState state, boolean moved); + + public void terra$setBlock(int x, int y, int z, BlockState data, boolean physics) { + setBlockState(new BlockPos(x, y, z), (net.minecraft.block.BlockState) data, false); + } + + public void terra$setBlock(int x, int y, int z, @NotNull BlockState blockState) { + ((net.minecraft.world.chunk.Chunk) (Object) this).setBlockState(new BlockPos(x, y, z), (net.minecraft.block.BlockState) blockState, + false); + } + + @Intrinsic + public @NotNull BlockState terra$getBlock(int x, int y, int z) { + return (BlockState) getBlockState(new BlockPos(x, y, z)); + } + + public int terra$getX() { + return ((net.minecraft.world.chunk.Chunk) (Object) this).getPos().x; + } + + public int terra$getZ() { + return ((net.minecraft.world.chunk.Chunk) (Object) this).getPos().z; + } + + public ServerWorld terra$getWorld() { + return (ServerWorld) world; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/chunk/data/ProtoChunkMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/chunk/data/ProtoChunkMixin.java new file mode 100644 index 000000000..918df4f42 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/chunk/data/ProtoChunkMixin.java @@ -0,0 +1,53 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.chunk.data; + +import com.dfsek.terra.api.block.state.BlockState; +import com.dfsek.terra.api.world.chunk.generation.ProtoChunk; + +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.HeightLimitView; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + + +@Mixin(net.minecraft.world.chunk.ProtoChunk.class) +@Implements(@Interface(iface = ProtoChunk.class, prefix = "terra$")) +public abstract class ProtoChunkMixin { + @Shadow + public abstract net.minecraft.block.BlockState getBlockState(BlockPos pos); + + @Shadow + public abstract HeightLimitView getHeightLimitView(); + + public void terra$setBlock(int x, int y, int z, @NotNull BlockState blockState) { + ((net.minecraft.world.chunk.Chunk) (Object) this).setBlockState(new BlockPos(x, y, z), (net.minecraft.block.BlockState) blockState, + false); + } + + public @NotNull BlockState terra$getBlock(int x, int y, int z) { + return (BlockState) getBlockState(new BlockPos(x, y, z)); + } + + public int terra$getMaxHeight() { + return getHeightLimitView().getTopY(); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/entity/EntityMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/entity/EntityMixin.java new file mode 100644 index 000000000..96e7be2d5 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/entity/EntityMixin.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.entity; + +import com.dfsek.terra.forge.util.FabricAdapter; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.BlockPos; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import com.dfsek.terra.api.util.vector.Vector3; +import com.dfsek.terra.api.world.ServerWorld; + + +@Mixin(Entity.class) +@Implements(@Interface(iface = com.dfsek.terra.api.entity.Entity.class, prefix = "terra$")) +public abstract class EntityMixin { + @Shadow + public net.minecraft.world.World world; + + @Shadow + private BlockPos blockPos; + + @Shadow + public abstract void teleport(double destX, double destY, double destZ); + + public Vector3 terra$position() { + return FabricAdapter.adapt(blockPos); + } + + public void terra$position(Vector3 location) { + teleport(location.getX(), location.getY(), location.getZ()); + } + + public ServerWorld terra$world() { + return (ServerWorld) world; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/entity/EntityTypeMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/entity/EntityTypeMixin.java new file mode 100644 index 000000000..1c73c1efb --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/entity/EntityTypeMixin.java @@ -0,0 +1,29 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.entity; + +import net.minecraft.entity.EntityType; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + + +@Mixin(EntityType.class) +@Implements(@Interface(iface = com.dfsek.terra.api.entity.EntityType.class, prefix = "terra$")) +public abstract class EntityTypeMixin { +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/entity/PlayerEntityMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/entity/PlayerEntityMixin.java new file mode 100644 index 000000000..f08df6293 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/entity/PlayerEntityMixin.java @@ -0,0 +1,31 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.entity; + +import net.minecraft.entity.player.PlayerEntity; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + +import com.dfsek.terra.api.entity.Player; + + +@Mixin(PlayerEntity.class) +@Implements(@Interface(iface = Player.class, prefix = "terra$")) +public abstract class PlayerEntityMixin extends EntityMixin { +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/entity/ServerCommandSourceMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/entity/ServerCommandSourceMixin.java new file mode 100644 index 000000000..ce91be261 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/entity/ServerCommandSourceMixin.java @@ -0,0 +1,66 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.entity; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Optional; + +import com.dfsek.terra.api.command.CommandSender; +import com.dfsek.terra.api.entity.Entity; +import com.dfsek.terra.api.entity.Player; + + +@Mixin(ServerCommandSource.class) +@Implements(@Interface(iface = CommandSender.class, prefix = "terra$")) +public abstract class ServerCommandSourceMixin { + @Shadow + public abstract void sendFeedback(Text message, boolean broadcastToOps); + + @Shadow + public abstract ServerPlayerEntity getPlayer() throws CommandSyntaxException; + + @Shadow + @Nullable + public abstract net.minecraft.entity.@Nullable Entity getEntity(); + + public void terra$sendMessage(String message) { + sendFeedback(Text.literal(message), true); + } + + @Nullable + public Optional terra$getEntity() { + return Optional.ofNullable((Entity) getEntity()); + } + + public Optional terra$getPlayer() { + try { + return Optional.ofNullable((Player) getPlayer()); + } catch(CommandSyntaxException e) { + return Optional.empty(); + } + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/inventory/LockableContainerBlockEntityMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/inventory/LockableContainerBlockEntityMixin.java new file mode 100644 index 000000000..40f6111db --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/inventory/LockableContainerBlockEntityMixin.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.inventory; + +import net.minecraft.block.entity.LockableContainerBlockEntity; +import net.minecraft.item.Items; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; + +import com.dfsek.terra.api.inventory.Inventory; +import com.dfsek.terra.api.inventory.ItemStack; + + +@Mixin(LockableContainerBlockEntity.class) +@Implements(@Interface(iface = Inventory.class, prefix = "terra$")) +public class LockableContainerBlockEntityMixin { + @SuppressWarnings("ConstantConditions") + public void terra$setItem(int slot, ItemStack newStack) { + ((LockableContainerBlockEntity) (Object) this).setStack(slot, (net.minecraft.item.ItemStack) (Object) newStack); + } + + public int terra$getSize() { + return ((LockableContainerBlockEntity) (Object) this).size(); + } + + @SuppressWarnings("ConstantConditions") + public ItemStack terra$getItem(int slot) { + net.minecraft.item.ItemStack itemStack = ((LockableContainerBlockEntity) (Object) this).getStack(slot); + return itemStack.getItem() == Items.AIR ? null : (ItemStack) (Object) itemStack; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/inventory/item/ItemMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/inventory/item/ItemMixin.java new file mode 100644 index 000000000..e48b5341c --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/inventory/item/ItemMixin.java @@ -0,0 +1,43 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.inventory.item; + +import net.minecraft.item.Item; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import com.dfsek.terra.api.inventory.ItemStack; + + +@Mixin(Item.class) +@Implements(@Interface(iface = com.dfsek.terra.api.inventory.Item.class, prefix = "terra$")) +public abstract class ItemMixin { + @Shadow + public abstract int getMaxDamage(); + + @SuppressWarnings("ConstantConditions") + public ItemStack terra$newItemStack(int amount) { + return (ItemStack) (Object) new net.minecraft.item.ItemStack((Item) (Object) this, amount); + } + + public double terra$getMaxDurability() { + return getMaxDamage(); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/inventory/item/ItemStackMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/inventory/item/ItemStackMixin.java new file mode 100644 index 000000000..7bb42595a --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/inventory/item/ItemStackMixin.java @@ -0,0 +1,76 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.inventory.item; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import com.dfsek.terra.api.inventory.Item; +import com.dfsek.terra.api.inventory.item.ItemMeta; + + +@Mixin(ItemStack.class) +@Implements(@Interface(iface = com.dfsek.terra.api.inventory.ItemStack.class, prefix = "terra$")) +public abstract class ItemStackMixin { + @Shadow + public abstract int getCount(); + + @Shadow + public abstract void setCount(int count); + + @Shadow + public abstract net.minecraft.item.Item getItem(); + + @Shadow + public abstract boolean isDamageable(); + + @Shadow + public abstract void setNbt(@Nullable NbtCompound tag); + + public int terra$getAmount() { + return getCount(); + } + + public void terra$setAmount(int i) { + setCount(i); + } + + public Item terra$getType() { + return (Item) getItem(); + } + + public ItemMeta terra$getItemMeta() { + return (ItemMeta) this; + } + + @SuppressWarnings("ConstantConditions") + public void terra$setItemMeta(ItemMeta meta) { + setNbt(((ItemStack) (Object) meta).getNbt()); + } + + @Intrinsic + public boolean terra$isDamageable() { + return isDamageable(); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/inventory/meta/EnchantmentMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/inventory/meta/EnchantmentMixin.java new file mode 100644 index 000000000..358cd7a95 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/inventory/meta/EnchantmentMixin.java @@ -0,0 +1,53 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.inventory.meta; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.util.registry.Registry; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Objects; + +import com.dfsek.terra.api.inventory.ItemStack; + + +@Mixin(Enchantment.class) +@Implements(@Interface(iface = com.dfsek.terra.api.inventory.item.Enchantment.class, prefix = "terra$")) +public abstract class EnchantmentMixin { + @Shadow + public abstract boolean isAcceptableItem(net.minecraft.item.ItemStack stack); + + @Shadow + public abstract boolean canCombine(Enchantment other); + + @SuppressWarnings("ConstantConditions") + public boolean terra$canEnchantItem(ItemStack itemStack) { + return isAcceptableItem((net.minecraft.item.ItemStack) (Object) itemStack); + } + + public boolean terra$conflictsWith(com.dfsek.terra.api.inventory.item.Enchantment other) { + return !canCombine((Enchantment) other); + } + + public String terra$getID() { + return Objects.requireNonNull(Registry.ENCHANTMENT.getId((Enchantment) (Object) this)).toString(); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/inventory/meta/ItemStackDamageableMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/inventory/meta/ItemStackDamageableMixin.java new file mode 100644 index 000000000..d70b1732d --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/inventory/meta/ItemStackDamageableMixin.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.inventory.meta; + +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import com.dfsek.terra.api.inventory.item.Damageable; + + +@Mixin(ItemStack.class) +@Implements(@Interface(iface = Damageable.class, prefix = "terra$")) +public abstract class ItemStackDamageableMixin { + @Shadow + public abstract boolean isDamaged(); + + @Shadow + public abstract int getDamage(); + + @Shadow + public abstract void setDamage(int damage); + + @Intrinsic + public int terra$getDamage() { + return getDamage(); + } + + @Intrinsic + public void terra$setDamage(int damage) { + setDamage(damage); + } + + public boolean terra$hasDamage() { + return isDamaged(); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/inventory/meta/ItemStackMetaMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/inventory/meta/ItemStackMetaMixin.java new file mode 100644 index 000000000..17bf068a1 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/inventory/meta/ItemStackMetaMixin.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.inventory.meta; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtList; +import net.minecraft.util.registry.Registry; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import com.dfsek.terra.api.inventory.item.Enchantment; +import com.dfsek.terra.api.inventory.item.ItemMeta; + + +@Mixin(ItemStack.class) +@Implements(@Interface(iface = ItemMeta.class, prefix = "terra$")) +public abstract class ItemStackMetaMixin { + @Shadow + public abstract boolean hasEnchantments(); + + @Shadow + public abstract NbtList getEnchantments(); + + @Shadow + public abstract void addEnchantment(net.minecraft.enchantment.Enchantment enchantment, int level); + + public void terra$addEnchantment(Enchantment enchantment, int level) { + addEnchantment((net.minecraft.enchantment.Enchantment) enchantment, level); + } + + @Intrinsic(displace = true) + public Map terra$getEnchantments() { + if(!hasEnchantments()) return Collections.emptyMap(); + Map map = new HashMap<>(); + + getEnchantments().forEach(enchantment -> { + NbtCompound eTag = (NbtCompound) enchantment; + map.put((Enchantment) Registry.ENCHANTMENT.get(eTag.getInt("id")), eTag.getInt("lvl")); + }); + return map; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/package-info.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/package-info.java new file mode 100644 index 000000000..0899793d3 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/package-info.java @@ -0,0 +1,23 @@ +/* + * 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 . + */ + +/** + * Mixins in this package implement Terra + * interfaces in Minecraft classes. + */ + +package com.dfsek.terra.forge.mixin.implementations.terra; \ No newline at end of file diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/world/ChunkRegionMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/world/ChunkRegionMixin.java new file mode 100644 index 000000000..6ba8768ab --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/world/ChunkRegionMixin.java @@ -0,0 +1,149 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.world; + +import com.dfsek.terra.forge.generation.FabricChunkGeneratorWrapper; +import net.minecraft.block.FluidBlock; +import net.minecraft.fluid.Fluid; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ChunkRegion; +import net.minecraft.world.WorldAccess; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.ChunkStatus; +import net.minecraft.world.tick.MultiTickScheduler; +import net.minecraft.world.tick.OrderedTick; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +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; + +import java.util.List; + +import com.dfsek.terra.api.block.entity.BlockEntity; +import com.dfsek.terra.api.block.state.BlockState; +import com.dfsek.terra.api.config.ConfigPack; +import com.dfsek.terra.api.entity.Entity; +import com.dfsek.terra.api.entity.EntityType; +import com.dfsek.terra.api.world.ServerWorld; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; +import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator; +import com.dfsek.terra.api.world.chunk.generation.ProtoWorld; +import com.dfsek.terra.forge.util.FabricUtil; + + +@Mixin(ChunkRegion.class) +@Implements(@Interface(iface = ProtoWorld.class, prefix = "terraWorld$")) +public abstract class ChunkRegionMixin { + private ConfigPack terra$config; + + + @Shadow + @Final + private net.minecraft.server.world.ServerWorld world; + + @Shadow + @Final + private long seed; + @Shadow + @Final + private Chunk centerPos; + + @Shadow + @Final + private MultiTickScheduler fluidTickScheduler; + + + @Inject(at = @At("RETURN"), + method = "(Lnet/minecraft/server/world/ServerWorld;Ljava/util/List;Lnet/minecraft/world/chunk/ChunkStatus;I)V") + public void injectConstructor(net.minecraft.server.world.ServerWorld world, List list, + ChunkStatus chunkStatus, int i, + CallbackInfo ci) { + this.terra$config = ((ServerWorld) world).getPack(); + } + + + @Intrinsic(displace = true) + public void terraWorld$setBlockState(int x, int y, int z, BlockState data, boolean physics) { + BlockPos pos = new BlockPos(x, y, z); + ((ChunkRegion) (Object) this).setBlockState(pos, (net.minecraft.block.BlockState) data, physics ? 3 : 1042); + if(physics && ((net.minecraft.block.BlockState) data).getBlock() instanceof FluidBlock) { + fluidTickScheduler.scheduleTick( + OrderedTick.create(((FluidBlock) ((net.minecraft.block.BlockState) data).getBlock()).getFluidState( + (net.minecraft.block.BlockState) data).getFluid(), pos)); + } + } + + @Intrinsic + public long terraWorld$getSeed() { + return seed; + } + + public int terraWorld$getMaxHeight() { + return world.getTopY(); + } + + @Intrinsic(displace = true) + public BlockState terraWorld$getBlockState(int x, int y, int z) { + BlockPos pos = new BlockPos(x, y, z); + return (BlockState) ((ChunkRegion) (Object) this).getBlockState(pos); + } + + public BlockEntity terraWorld$getBlockEntity(int x, int y, int z) { + return FabricUtil.createState((WorldAccess) this, new BlockPos(x, y, z)); + } + + public int terraWorld$getMinHeight() { + return world.getBottomY(); + } + + public ChunkGenerator terraWorld$getGenerator() { + return ((FabricChunkGeneratorWrapper) world.getChunkManager().getChunkGenerator()).getHandle(); + } + + public BiomeProvider terraWorld$getBiomeProvider() { + return terra$config.getBiomeProvider(); + } + + public Entity terraWorld$spawnEntity(double x, double y, double z, EntityType entityType) { + net.minecraft.entity.Entity entity = ((net.minecraft.entity.EntityType) entityType).create(world); + entity.setPos(x, y, z); + ((ChunkRegion) (Object) this).spawnEntity(entity); + return (Entity) entity; + } + + public int terraWorld$centerChunkX() { + return centerPos.getPos().x; + } + + public int terraWorld$centerChunkZ() { + return centerPos.getPos().z; + } + + public ServerWorld terraWorld$getWorld() { + return (ServerWorld) world; + } + + public ConfigPack terraWorld$getPack() { + return terra$config; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/world/ServerWorldMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/world/ServerWorldMixin.java new file mode 100644 index 000000000..fc9158d4c --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/implementations/terra/world/ServerWorldMixin.java @@ -0,0 +1,102 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.implementations.terra.world; + +import com.dfsek.terra.forge.generation.FabricChunkGeneratorWrapper; +import com.dfsek.terra.forge.generation.TerraBiomeSource; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.WorldAccess; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; + +import com.dfsek.terra.api.block.entity.BlockEntity; +import com.dfsek.terra.api.block.state.BlockState; +import com.dfsek.terra.api.config.ConfigPack; +import com.dfsek.terra.api.entity.Entity; +import com.dfsek.terra.api.entity.EntityType; +import com.dfsek.terra.api.world.ServerWorld; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; +import com.dfsek.terra.api.world.chunk.Chunk; +import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator; +import com.dfsek.terra.forge.util.FabricUtil; + + +@Mixin(net.minecraft.server.world.ServerWorld.class) +@Implements(@Interface(iface = ServerWorld.class, prefix = "terra$")) +public abstract class ServerWorldMixin { + public Entity terra$spawnEntity(double x, double y, double z, EntityType entityType) { + net.minecraft.entity.Entity entity = ((net.minecraft.entity.EntityType) entityType).create(null); + entity.setPos(x, y, z); + ((net.minecraft.server.world.ServerWorld) (Object) this).spawnEntity(entity); + return (Entity) entity; + } + + public void terra$setBlockState(int x, int y, int z, BlockState data, boolean physics) { + BlockPos pos = new BlockPos(x, y, z); + ((net.minecraft.server.world.ServerWorld) (Object) this).setBlockState(pos, (net.minecraft.block.BlockState) data, + physics ? 3 : 1042); + } + + @Intrinsic + public long terra$getSeed() { + return ((net.minecraft.server.world.ServerWorld) (Object) this).getSeed(); + } + + public int terra$getMaxHeight() { + return (((net.minecraft.server.world.ServerWorld) (Object) this).getBottomY()) + + ((net.minecraft.server.world.ServerWorld) (Object) this).getHeight(); + } + + public Chunk terra$getChunkAt(int x, int z) { + return (Chunk) ((net.minecraft.server.world.ServerWorld) (Object) this).getChunk(x, z); + } + + public BlockState terra$getBlockState(int x, int y, int z) { + return (BlockState) ((net.minecraft.server.world.ServerWorld) (Object) this).getBlockState(new BlockPos(x, y, z)); + } + + public BlockEntity terra$getBlockEntity(int x, int y, int z) { + return FabricUtil.createState((WorldAccess) this, new BlockPos(x, y, z)); + } + + public int terra$getMinHeight() { + return ((net.minecraft.server.world.ServerWorld) (Object) this).getBottomY(); + } + + public ChunkGenerator terra$getGenerator() { + return ((FabricChunkGeneratorWrapper) ((net.minecraft.server.world.ServerWorld) (Object) this).getChunkManager() + .getChunkGenerator()).getHandle(); + } + + public BiomeProvider terra$getBiomeProvider() { + return ((TerraBiomeSource) ((net.minecraft.server.world.ServerWorld) (Object) this).getChunkManager() + .getChunkGenerator() + .getBiomeSource()).getProvider(); + } + + public ConfigPack terra$getPack() { + net.minecraft.world.gen.chunk.ChunkGenerator generator = + (((net.minecraft.server.world.ServerWorld) (Object) this).getChunkManager()).getChunkGenerator(); + if(generator instanceof FabricChunkGeneratorWrapper fabricChunkGeneratorWrapper) { + return fabricChunkGeneratorWrapper.getPack(); + } + return null; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/DataPackContentsMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/DataPackContentsMixin.java new file mode 100644 index 000000000..fefb67df4 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/DataPackContentsMixin.java @@ -0,0 +1,28 @@ +package com.dfsek.terra.forge.mixin.lifecycle; + +import com.dfsek.terra.forge.util.BiomeUtil; +import com.dfsek.terra.forge.util.TagUtil; +import net.minecraft.server.DataPackContents; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; +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; + + +@Mixin(DataPackContents.class) +public class DataPackContentsMixin { + /* + * #refresh populates all tags in the registries + */ + @Inject(method = "refresh(Lnet/minecraft/util/registry/DynamicRegistryManager;)V", at = @At("RETURN")) + private void injectReload(DynamicRegistryManager dynamicRegistryManager, CallbackInfo ci) { + TagUtil.registerWorldPresetTags(dynamicRegistryManager.get(Registry.WORLD_PRESET_KEY)); + + Registry biomeRegistry = dynamicRegistryManager.get(Registry.BIOME_KEY); + TagUtil.registerBiomeTags(biomeRegistry); + BiomeUtil.registerFlora(biomeRegistry); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/MinecraftServerMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/MinecraftServerMixin.java new file mode 100644 index 000000000..a524a9df7 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/MinecraftServerMixin.java @@ -0,0 +1,32 @@ +package com.dfsek.terra.forge.mixin.lifecycle; + +import com.dfsek.terra.forge.ForgeEntryPoint; + +import com.mojang.datafixers.DataFixer; +import net.minecraft.resource.ResourcePackManager; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.SaveLoader; +import net.minecraft.server.WorldGenerationProgressListenerFactory; +import net.minecraft.util.ApiServices; +import net.minecraft.world.level.storage.LevelStorage; +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.net.Proxy; + + +@Mixin(MinecraftServer.class) +public class MinecraftServerMixin { + @Inject(method = "(Ljava/lang/Thread;Lnet/minecraft/world/level/storage/LevelStorage$Session;" + + "Lnet/minecraft/resource/ResourcePackManager;Lnet/minecraft/server/SaveLoader;Ljava/net/Proxy;" + + "Lcom/mojang/datafixers/DataFixer;Lnet/minecraft/util/ApiServices;" + + "Lnet/minecraft/server/WorldGenerationProgressListenerFactory;)V", + at = @At("RETURN")) + private void injectConstructor(Thread serverThread, LevelStorage.Session session, ResourcePackManager dataPackManager, + SaveLoader saveLoader, Proxy proxy, DataFixer dataFixer, ApiServices apiServices, + WorldGenerationProgressListenerFactory worldGenerationProgressListenerFactory, CallbackInfo ci) { + ForgeEntryPoint.getPlatform().setServer((MinecraftServer) (Object) this); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/NoiseConfigMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/NoiseConfigMixin.java new file mode 100644 index 000000000..67008fbba --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/NoiseConfigMixin.java @@ -0,0 +1,31 @@ +package com.dfsek.terra.forge.mixin.lifecycle; + +import com.dfsek.terra.forge.util.SeedHack; + +import net.minecraft.util.math.noise.DoublePerlinNoiseSampler; +import net.minecraft.util.registry.Registry; +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 = "(Lnet/minecraft/world/gen/chunk/ChunkGeneratorSettings;Lnet/minecraft/util/registry/Registry;J)V", at = @At("TAIL")) + private void mapMultiNoise(ChunkGeneratorSettings chunkGeneratorSettings, Registry noiseRegistry, long seed, CallbackInfo ci) { + SeedHack.register(multiNoiseSampler, seed); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/RegistryMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/RegistryMixin.java new file mode 100644 index 000000000..18633dc0f --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/RegistryMixin.java @@ -0,0 +1,20 @@ +package com.dfsek.terra.forge.mixin.lifecycle; + + +import net.minecraft.util.registry.Registry; +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 com.dfsek.terra.forge.ForgeEntryPoint; + + +// Register Terra things to the builtin registries. +@Mixin(Registry.class) +public class RegistryMixin { + @Inject(method = "", at = @At("RETURN")) + private static void registerTerraGenerators(CallbackInfo ci) { + ForgeEntryPoint.register(); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/client/MinecraftClientMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/client/MinecraftClientMixin.java new file mode 100644 index 000000000..ee2e01268 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/client/MinecraftClientMixin.java @@ -0,0 +1,41 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.lifecycle.client; + +import com.dfsek.terra.forge.util.LifecycleUtil; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.RunArgs; +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; + + +@Mixin(MinecraftClient.class) +public class MinecraftClientMixin { + @Inject(method = "", at = @At(value = "INVOKE", + target = "Lnet/minecraft/client/util/WindowProvider;createWindow" + + "(Lnet/minecraft/client/WindowSettings;Ljava/lang/String;Ljava/lang/String;)" + + "Lnet/minecraft/client/util/Window;", + // sorta arbitrary position, after mod init, before window opens + shift = At.Shift.BEFORE)) + public void injectConstructor(RunArgs args, CallbackInfo callbackInfo) { + LifecycleUtil.initialize(); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/package-info.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/package-info.java new file mode 100644 index 000000000..eba8981f7 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/package-info.java @@ -0,0 +1,22 @@ +/* + * 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 . + */ + +/** + * Mixins that inject behavior into the client/server lifecycle. + */ + +package com.dfsek.terra.forge.mixin.lifecycle; \ No newline at end of file diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/server/ServerMainMixin.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/server/ServerMainMixin.java new file mode 100644 index 000000000..70d93969e --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin/lifecycle/server/ServerMainMixin.java @@ -0,0 +1,40 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.mixin.lifecycle.server; + +import com.dfsek.terra.forge.util.LifecycleUtil; + +import net.minecraft.server.Main; +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; + + +@Mixin(Main.class) +public class ServerMainMixin { + @Inject(method = "main([Ljava/lang/String;)V", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/resource/ResourcePackManager;(Lnet/minecraft/resource/ResourceType;" + + "[Lnet/minecraft/resource/ResourcePackProvider;)V") + // after registry manager creation + ) + private static void injectConstructor(String[] args, CallbackInfo ci) { + LifecycleUtil.initialize(); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin_ifaces/FloraFeatureHolder.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin_ifaces/FloraFeatureHolder.java new file mode 100644 index 000000000..2797691f4 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/mixin_ifaces/FloraFeatureHolder.java @@ -0,0 +1,10 @@ +package com.dfsek.terra.forge.mixin_ifaces; + +import net.minecraft.world.gen.feature.ConfiguredFeature; + +import java.util.List; + + +public interface FloraFeatureHolder { + void setFloraFeatures(List> features); +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/util/BiomeUtil.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/util/BiomeUtil.java new file mode 100644 index 000000000..9e4c1e257 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/util/BiomeUtil.java @@ -0,0 +1,169 @@ +package com.dfsek.terra.forge.util; + +import com.dfsek.terra.api.config.ConfigPack; +import com.dfsek.terra.api.world.biome.Biome; +import com.dfsek.terra.forge.ForgeEntryPoint; +import com.dfsek.terra.forge.config.PreLoadCompatibilityOptions; +import com.dfsek.terra.forge.config.VanillaBiomeProperties; + +import com.dfsek.terra.forge.mixin_ifaces.FloraFeatureHolder; + +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.BuiltinRegistries; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.RegistryKey; +import net.minecraft.world.biome.Biome.Builder; +import net.minecraft.world.biome.BiomeEffects; +import net.minecraft.world.biome.GenerationSettings; +import net.minecraft.world.gen.feature.ConfiguredFeature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + + +public final class BiomeUtil { + private static final Logger logger = LoggerFactory.getLogger(BiomeUtil.class); + + private static final Map> + TERRA_BIOME_MAP = new HashMap<>(); + + private BiomeUtil() { + + } + + 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); + } + + public static void registerBiomes() { + logger.info("Registering biomes..."); + ForgeEntryPoint.getPlatform().getConfigRegistry().forEach(pack -> { // Register all Terra biomes. + pack.getCheckedRegistry(Biome.class) + .forEach((id, biome) -> registerBiome(biome, pack, id)); + }); + registerFlora(BuiltinRegistries.BIOME); + logger.info("Terra biomes registered."); + } + + /** + * Clones a Vanilla biome and injects Terra data to create a Terra-vanilla biome delegate. + * + * @param biome The Terra BiomeBuilder. + * @param pack The ConfigPack this biome belongs to. + */ + private static void registerBiome(Biome biome, ConfigPack pack, + com.dfsek.terra.api.registry.key.RegistryKey id) { + Registry registry = BuiltinRegistries.BIOME; + RegistryKey vanilla = ((ProtoPlatformBiome) biome.getPlatformBiome()).get(registry); + + + if(pack.getContext().get(PreLoadCompatibilityOptions.class).useVanillaBiomes()) { + ((ProtoPlatformBiome) biome.getPlatformBiome()).setDelegate(vanilla); + } else { + net.minecraft.world.biome.Biome minecraftBiome = createBiome(biome, registry.get(vanilla)); + + Identifier identifier = new Identifier("terra", createBiomeID(pack, id)); + + if(registry.containsId(identifier)) { + ((ProtoPlatformBiome) biome.getPlatformBiome()).setDelegate(FabricUtil.getEntry(registry, identifier) + .orElseThrow() + .getKey() + .orElseThrow()); + } else { + ((ProtoPlatformBiome) biome.getPlatformBiome()).setDelegate(BuiltinRegistries.add(registry, + registerKey(identifier).getValue(), + minecraftBiome).getKey().orElseThrow()); + } + + TERRA_BIOME_MAP.computeIfAbsent(vanilla.getValue(), i -> new ArrayList<>()).add(identifier); + } + } + + public static void registerFlora(Registry biomes) { + logger.info("Injecting flora into Terra biomes..."); + TERRA_BIOME_MAP + .forEach((vb, terraBiomes) -> + biomes.getOrEmpty(vb) + .ifPresentOrElse(vanilla -> terraBiomes + .forEach(tb -> biomes.getOrEmpty(tb) + .ifPresentOrElse( + terra -> { + List> flowerFeatures = List.copyOf(vanilla.getGenerationSettings().getFlowerFeatures()); + logger.debug("Injecting flora into biome {} : {}", tb, flowerFeatures); + ((FloraFeatureHolder) terra.getGenerationSettings()).setFloraFeatures(flowerFeatures); + }, + () -> logger.error( + "No such biome: {}", + tb))), + () -> logger.error("No vanilla biome: {}", vb))); + + } + + public static Map> getTerraBiomeMap() { + return Map.copyOf(TERRA_BIOME_MAP); + } + + private static RegistryKey registerKey(Identifier identifier) { + return RegistryKey.of(Registry.BIOME_KEY, identifier); + } + + public static net.minecraft.world.biome.Biome createBiome(Biome biome, net.minecraft.world.biome.Biome vanilla) { + GenerationSettings.Builder generationSettings = new GenerationSettings.Builder(); + + BiomeEffects.Builder effects = new BiomeEffects.Builder(); + + net.minecraft.world.biome.Biome.Builder builder = new Builder(); + + if(biome.getContext().has(VanillaBiomeProperties.class)) { + VanillaBiomeProperties vanillaBiomeProperties = biome.getContext().get(VanillaBiomeProperties.class); + + effects.waterColor(Objects.requireNonNullElse(vanillaBiomeProperties.getWaterColor(), vanilla.getWaterColor())) + .waterFogColor(Objects.requireNonNullElse(vanillaBiomeProperties.getWaterFogColor(), vanilla.getWaterFogColor())) + .fogColor(Objects.requireNonNullElse(vanillaBiomeProperties.getFogColor(), vanilla.getFogColor())) + .skyColor(Objects.requireNonNullElse(vanillaBiomeProperties.getSkyColor(), vanilla.getSkyColor())) + .grassColorModifier( + Objects.requireNonNullElse(vanillaBiomeProperties.getModifier(), vanilla.getEffects().getGrassColorModifier())); + + + if(vanillaBiomeProperties.getGrassColor() == null) { + vanilla.getEffects().getGrassColor().ifPresent(effects::grassColor); + } else { + effects.grassColor(vanillaBiomeProperties.getGrassColor()); + } + + if(vanillaBiomeProperties.getFoliageColor() == null) { + vanilla.getEffects().getFoliageColor().ifPresent(effects::foliageColor); + } else { + effects.foliageColor(vanillaBiomeProperties.getFoliageColor()); + } + + builder.precipitation(Objects.requireNonNullElse(vanillaBiomeProperties.getPrecipitation(), vanilla.getPrecipitation())); + + } else { + effects.waterColor(vanilla.getWaterColor()) + .waterFogColor(vanilla.getWaterFogColor()) + .fogColor(vanilla.getFogColor()) + .skyColor(vanilla.getSkyColor()); + vanilla.getEffects().getFoliageColor().ifPresent(effects::foliageColor); + vanilla.getEffects().getGrassColor().ifPresent(effects::grassColor); + + builder.precipitation(vanilla.getPrecipitation()); + } + + vanilla.getLoopSound().ifPresent(effects::loopSound); + vanilla.getAdditionsSound().ifPresent(effects::additionsSound); + vanilla.getMoodSound().ifPresent(effects::moodSound); + vanilla.getMusic().ifPresent(effects::music); + vanilla.getParticleConfig().ifPresent(effects::particleConfig); + + return builder + .temperature(vanilla.getTemperature()) + .downfall(vanilla.getDownfall()) + .effects(effects.build()) + .spawnSettings(vanilla.getSpawnSettings()) + .generationSettings(generationSettings.build()) + .build(); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/util/FabricAdapter.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/util/FabricAdapter.java new file mode 100644 index 000000000..b7bc9f010 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/util/FabricAdapter.java @@ -0,0 +1,185 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.util; + +import net.minecraft.block.enums.BlockHalf; +import net.minecraft.block.enums.WallShape; +import net.minecraft.block.enums.WireConnection; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.HeightLimitView; + +import com.dfsek.terra.api.block.state.properties.enums.Axis; +import com.dfsek.terra.api.block.state.properties.enums.Half; +import com.dfsek.terra.api.block.state.properties.enums.RailShape; +import com.dfsek.terra.api.block.state.properties.enums.RedstoneConnection; +import com.dfsek.terra.api.block.state.properties.enums.WallHeight; +import com.dfsek.terra.api.util.vector.Vector3; +import com.dfsek.terra.api.world.info.WorldProperties; + + +public final class FabricAdapter { + public static BlockPos adapt(Vector3 v) { + return new BlockPos(v.getBlockX(), v.getBlockY(), v.getBlockZ()); + } + + public static Vector3 adapt(BlockPos pos) { + return Vector3.of(pos.getX(), pos.getY(), pos.getZ()); + } + + public static Direction adapt(com.dfsek.terra.api.block.state.properties.enums.Direction direction) { + return switch(direction) { + case SOUTH -> Direction.SOUTH; + case NORTH -> Direction.NORTH; + case WEST -> Direction.WEST; + case EAST -> Direction.EAST; + case UP -> Direction.UP; + case DOWN -> Direction.DOWN; + }; + } + + public static WorldProperties adapt(HeightLimitView height, long seed) { + return new WorldProperties() { + @Override + public long getSeed() { + return seed; + } + + @Override + public int getMaxHeight() { + return height.getTopY(); + } + + @Override + public int getMinHeight() { + return height.getBottomY(); + } + + @Override + public Object getHandle() { + return height; + } + }; + } + + public static com.dfsek.terra.api.block.state.properties.enums.Direction adapt(Direction direction) { + return switch(direction) { + case SOUTH -> com.dfsek.terra.api.block.state.properties.enums.Direction.SOUTH; + case NORTH -> com.dfsek.terra.api.block.state.properties.enums.Direction.NORTH; + case WEST -> com.dfsek.terra.api.block.state.properties.enums.Direction.WEST; + case EAST -> com.dfsek.terra.api.block.state.properties.enums.Direction.EAST; + case UP -> com.dfsek.terra.api.block.state.properties.enums.Direction.UP; + case DOWN -> com.dfsek.terra.api.block.state.properties.enums.Direction.DOWN; + }; + } + + public static WallHeight adapt(WallShape shape) { + return switch(shape) { + case LOW -> WallHeight.LOW; + case NONE -> WallHeight.NONE; + case TALL -> WallHeight.TALL; + }; + } + + public static WallShape adapt(WallHeight shape) { + return switch(shape) { + case LOW -> WallShape.LOW; + case NONE -> WallShape.NONE; + case TALL -> WallShape.TALL; + }; + } + + public static RedstoneConnection adapt(WireConnection connection) { + return switch(connection) { + case NONE -> RedstoneConnection.NONE; + case UP -> RedstoneConnection.UP; + case SIDE -> RedstoneConnection.SIDE; + }; + } + + public static WireConnection adapt(RedstoneConnection connection) { + return switch(connection) { + case NONE -> WireConnection.NONE; + case UP -> WireConnection.UP; + case SIDE -> WireConnection.SIDE; + }; + } + + + public static Half adapt(BlockHalf half) { + return switch(half) { + case BOTTOM -> Half.BOTTOM; + case TOP -> Half.TOP; + }; + } + + public static BlockHalf adapt(Half half) { + return switch(half) { + case TOP -> BlockHalf.TOP; + case BOTTOM -> BlockHalf.BOTTOM; + default -> throw new IllegalStateException(); + }; + } + + public static RailShape adapt(net.minecraft.block.enums.RailShape railShape) { + return switch(railShape) { + case EAST_WEST -> RailShape.EAST_WEST; + case NORTH_EAST -> RailShape.NORTH_EAST; + case NORTH_WEST -> RailShape.NORTH_WEST; + case SOUTH_EAST -> RailShape.SOUTH_EAST; + case SOUTH_WEST -> RailShape.SOUTH_WEST; + case NORTH_SOUTH -> RailShape.NORTH_SOUTH; + case ASCENDING_EAST -> RailShape.ASCENDING_EAST; + case ASCENDING_NORTH -> RailShape.ASCENDING_NORTH; + case ASCENDING_SOUTH -> RailShape.ASCENDING_SOUTH; + case ASCENDING_WEST -> RailShape.ASCENDING_WEST; + }; + } + + public static net.minecraft.block.enums.RailShape adapt(RailShape railShape) { + return switch(railShape) { + case EAST_WEST -> net.minecraft.block.enums.RailShape.EAST_WEST; + case NORTH_EAST -> net.minecraft.block.enums.RailShape.NORTH_EAST; + case NORTH_WEST -> net.minecraft.block.enums.RailShape.NORTH_WEST; + case SOUTH_EAST -> net.minecraft.block.enums.RailShape.SOUTH_EAST; + case SOUTH_WEST -> net.minecraft.block.enums.RailShape.SOUTH_WEST; + case NORTH_SOUTH -> net.minecraft.block.enums.RailShape.NORTH_SOUTH; + case ASCENDING_EAST -> net.minecraft.block.enums.RailShape.ASCENDING_EAST; + case ASCENDING_NORTH -> net.minecraft.block.enums.RailShape.ASCENDING_NORTH; + case ASCENDING_SOUTH -> net.minecraft.block.enums.RailShape.ASCENDING_SOUTH; + case ASCENDING_WEST -> net.minecraft.block.enums.RailShape.ASCENDING_WEST; + }; + } + + + public static Axis adapt(Direction.Axis axis) { + return switch(axis) { + case X -> Axis.X; + case Y -> Axis.Y; + case Z -> Axis.Z; + }; + } + + public static Direction.Axis adapt(Axis axis) { + return switch(axis) { + case Z -> Direction.Axis.Z; + case Y -> Direction.Axis.Y; + case X -> Direction.Axis.X; + }; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/util/FabricUtil.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/util/FabricUtil.java new file mode 100644 index 000000000..8f854186f --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/util/FabricUtil.java @@ -0,0 +1,61 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.util; + +import net.minecraft.block.entity.LootableContainerBlockEntity; +import net.minecraft.block.entity.MobSpawnerBlockEntity; +import net.minecraft.block.entity.SignBlockEntity; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.RegistryEntry; +import net.minecraft.world.WorldAccess; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Optional; + +import com.dfsek.terra.api.block.entity.BlockEntity; +import com.dfsek.terra.api.block.entity.Container; +import com.dfsek.terra.api.block.entity.MobSpawner; +import com.dfsek.terra.api.block.entity.Sign; + + +public final class FabricUtil { + private FabricUtil() { + + } + + public static BlockEntity createState(WorldAccess worldAccess, BlockPos pos) { + net.minecraft.block.entity.BlockEntity entity = worldAccess.getBlockEntity(pos); + if(entity instanceof SignBlockEntity) { + return (Sign) entity; + } else if(entity instanceof MobSpawnerBlockEntity) { + return (MobSpawner) entity; + } else if(entity instanceof LootableContainerBlockEntity) { + return (Container) entity; + } + return null; + } + + public static Optional> getEntry(Registry registry, Identifier identifier) { + return registry.getOrEmpty(identifier) + .flatMap(registry::getKey) + .map(registry::getOrCreateEntry); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/util/LifecycleUtil.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/util/LifecycleUtil.java new file mode 100644 index 000000000..560a200bc --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/util/LifecycleUtil.java @@ -0,0 +1,100 @@ +package com.dfsek.terra.forge.util; + +import com.dfsek.terra.api.event.events.platform.PlatformInitializationEvent; +import com.dfsek.terra.forge.ForgeEntryPoint; +import com.dfsek.terra.forge.generation.FabricChunkGeneratorWrapper; +import com.dfsek.terra.forge.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 PRESETS = new ArrayList<>(); + public static void initialize() { + ForgeEntryPoint.getPlatform().getEventManager().callEvent( + new PlatformInitializationEvent()); + BiomeUtil.registerBiomes(); + + + LOGGER.info("Registering Terra world types..."); + + Registry dimensionTypeRegistry = BuiltinRegistries.DIMENSION_TYPE; + Registry chunkGeneratorSettingsRegistry = BuiltinRegistries.CHUNK_GENERATOR_SETTINGS; + Registry structureSetRegistry = BuiltinRegistries.STRUCTURE_SET; + Registry noiseParametersRegistry = BuiltinRegistries.NOISE_PARAMETERS; + Registry biomeRegistry = BuiltinRegistries.BIOME; + + RegistryEntry theNetherDimensionType = dimensionTypeRegistry.getOrCreateEntry(DimensionTypes.THE_NETHER); + RegistryEntry + netherChunkGeneratorSettings = chunkGeneratorSettingsRegistry.getOrCreateEntry(ChunkGeneratorSettings.NETHER); + DimensionOptions netherDimensionOptions = new DimensionOptions(theNetherDimensionType, + new NoiseChunkGenerator(structureSetRegistry, + noiseParametersRegistry, + MultiNoiseBiomeSource.Preset.NETHER.getBiomeSource( + biomeRegistry), + netherChunkGeneratorSettings)); + RegistryEntry theEndDimensionType = dimensionTypeRegistry.getOrCreateEntry(DimensionTypes.THE_END); + RegistryEntry endChunkGeneratorSettings = chunkGeneratorSettingsRegistry.getOrCreateEntry( + ChunkGeneratorSettings.END); + DimensionOptions endDimensionOptions = new DimensionOptions(theEndDimensionType, + new NoiseChunkGenerator(structureSetRegistry, noiseParametersRegistry, + new TheEndBiomeSource(biomeRegistry), + endChunkGeneratorSettings)); + + RegistryEntry overworldDimensionType = dimensionTypeRegistry.getOrCreateEntry(DimensionTypes.OVERWORLD); + + RegistryEntry overworld = chunkGeneratorSettingsRegistry.getOrCreateEntry(ChunkGeneratorSettings.OVERWORLD); + ForgeEntryPoint + .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 getPresets() { + return PRESETS; + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/util/ProtoPlatformBiome.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/util/ProtoPlatformBiome.java new file mode 100644 index 000000000..b67932f0b --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/util/ProtoPlatformBiome.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ + +package com.dfsek.terra.forge.util; + +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.RegistryKey; +import net.minecraft.world.biome.Biome; + +import java.util.Objects; + +import com.dfsek.terra.api.world.biome.PlatformBiome; + + +public class ProtoPlatformBiome implements PlatformBiome { + private final Identifier identifier; + + private RegistryKey delegate; + + public ProtoPlatformBiome(Identifier identifier) { + this.identifier = identifier; + } + + public RegistryKey get(Registry registry) { + return FabricUtil.getEntry(registry, identifier).orElseThrow().getKey().orElseThrow(); + } + + @Override + public Object getHandle() { + return identifier; + } + + public RegistryKey getDelegate() { + return delegate; + } + + public void setDelegate(RegistryKey delegate) { + this.delegate = Objects.requireNonNull(delegate); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/util/SeedHack.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/util/SeedHack.java new file mode 100644 index 000000000..5eaf38093 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/util/SeedHack.java @@ -0,0 +1,29 @@ +package com.dfsek.terra.forge.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 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); + } +} diff --git a/platforms/forge/src/main/java/com/dfsek/terra/forge/util/TagUtil.java b/platforms/forge/src/main/java/com/dfsek/terra/forge/util/TagUtil.java new file mode 100644 index 000000000..27b33a210 --- /dev/null +++ b/platforms/forge/src/main/java/com/dfsek/terra/forge/util/TagUtil.java @@ -0,0 +1,103 @@ +package com.dfsek.terra.forge.util; + +import com.google.common.collect.ImmutableMap; +import net.minecraft.tag.TagKey; +import net.minecraft.tag.WorldPresetTags; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.RegistryEntry; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.gen.WorldPreset; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +public final class TagUtil { + private static final Logger logger = LoggerFactory.getLogger(TagUtil.class); + + private TagUtil() { + + } + + private static Map, List>> tagsToMutableMap(Registry registry) { + return registry + .streamTagsAndEntries() + .collect(HashMap::new, + (map, pair) -> + map.put(pair.getFirst(), new ArrayList<>(pair.getSecond().stream().toList())), + HashMap::putAll); + } + + public static void registerWorldPresetTags(Registry registry) { + logger.info("Doing preset tag garbage...."); + Map, List>> 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 registry) { + logger.info("Doing biome tag garbage...."); + Map, List>> collect = tagsToMutableMap(registry); + + BiomeUtil + .getTerraBiomeMap() + .forEach((vb, terraBiomes) -> + FabricUtil + .getEntry(registry, vb) + .ifPresentOrElse( + vanilla -> terraBiomes + .forEach(tb -> FabricUtil + .getEntry(registry, tb) + .ifPresentOrElse( + terra -> { + logger.debug( + vanilla.getKey() + .orElseThrow() + .getValue() + + " (vanilla for " + + terra.getKey() + .orElseThrow() + .getValue() + + ": " + + vanilla.streamTags() + .toList()); + + vanilla.streamTags() + .forEach( + tag -> collect + .computeIfAbsent( + tag, + t -> new ArrayList<>()) + .add(terra)); + }, + () -> logger.error( + "No such biome: {}", + tb))), + () -> logger.error("No vanilla biome: {}", vb))); + + registry.clearTags(); + registry.populateTags(ImmutableMap.copyOf(collect)); + + if(logger.isDebugEnabled()) { + registry.streamEntries() + .map(e -> e.registryKey().getValue() + ": " + + e.streamTags().reduce("", (s, t) -> t.id() + ", " + s, String::concat)) + .forEach(logger::debug); + } + } +} diff --git a/platforms/forge/src/main/resources/META-INF/mods.toml b/platforms/forge/src/main/resources/META-INF/mods.toml new file mode 100644 index 000000000..ae314f8d3 --- /dev/null +++ b/platforms/forge/src/main/resources/META-INF/mods.toml @@ -0,0 +1,24 @@ +modLoader = "javafml" +loaderVersion = "[41,)" +license = "GNU General Public License, v3.0" +issueTrackerURL="https://github.com/PolyhedralDev/Terra/issues" + +[[mods]] +modId = "terra" +version = "@VERSION@" +displayName = "Terra" +description = "@DESCRIPTION@" + +[[dependencies.terra]] +modId = "forge" +mandatory = true +versionRange = "[41,)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.terra]] +modId = "minecraft" +mandatory = true +versionRange = "[1.19,)" +ordering = "NONE" +side = "BOTH" \ No newline at end of file diff --git a/platforms/forge/src/main/resources/assets/terra/icon.png b/platforms/forge/src/main/resources/assets/terra/icon.png new file mode 100644 index 000000000..a8f458866 Binary files /dev/null and b/platforms/forge/src/main/resources/assets/terra/icon.png differ diff --git a/platforms/forge/src/main/resources/assets/terra/lang/en_us.json b/platforms/forge/src/main/resources/assets/terra/lang/en_us.json new file mode 100644 index 000000000..116199214 --- /dev/null +++ b/platforms/forge/src/main/resources/assets/terra/lang/en_us.json @@ -0,0 +1,4 @@ +{ + "generator.terra": "Terra" +} + diff --git a/platforms/forge/src/main/resources/terra.mixins.json b/platforms/forge/src/main/resources/terra.mixins.json new file mode 100644 index 000000000..e47e8e6be --- /dev/null +++ b/platforms/forge/src/main/resources/terra.mixins.json @@ -0,0 +1,53 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "com.dfsek.terra.fabric.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "com.dfsek.terra.forge.mixin.access.ChunkRegionAccessor", + "com.dfsek.terra.forge.mixin.access.MobSpawnerLogicAccessor", + "com.dfsek.terra.forge.mixin.access.StateAccessor", + "com.dfsek.terra.forge.mixin.access.StructureAccessorAccessor", + "com.dfsek.terra.forge.mixin.fix.BeeMoveGoalsUnsynchronizedRandomAccessFix", + "com.dfsek.terra.forge.mixin.fix.NetherFossilOptimization", + "com.dfsek.terra.forge.mixin.implementations.compat.GenerationSettingsFloraFeaturesMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.BiomeMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.HandleImplementationMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.block.BlockMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.block.entity.BlockEntityMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.block.entity.LootableContainerBlockEntityMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.block.entity.MobSpawnerBlockEntityMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.block.entity.SignBlockEntityMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.block.state.BlockStateMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.block.state.PropertyMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.chunk.ChunkRegionMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.chunk.WorldChunkMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.chunk.data.ProtoChunkMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.entity.EntityMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.entity.EntityTypeMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.entity.PlayerEntityMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.entity.ServerCommandSourceMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.inventory.LockableContainerBlockEntityMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.inventory.item.ItemMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.inventory.item.ItemStackMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.inventory.meta.EnchantmentMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.inventory.meta.ItemStackDamageableMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.inventory.meta.ItemStackMetaMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.world.ChunkRegionMixin", + "com.dfsek.terra.forge.mixin.implementations.terra.world.ServerWorldMixin", + "com.dfsek.terra.forge.mixin.lifecycle.DataPackContentsMixin", + "com.dfsek.terra.forge.mixin.lifecycle.MinecraftServerMixin", + "com.dfsek.terra.forge.mixin.lifecycle.NoiseConfigMixin", + "com.dfsek.terra.forge.mixin.lifecycle.RegistryMixin" + ], + "client": [ + "com.dfsek.terra.forge.mixin.lifecycle.client.MinecraftClientMixin" + ], + "server": [ + "com.dfsek.terra.forge.mixin.lifecycle.server.ServerMainMixin" + ], + "injectors": { + "defaultRequire": 1 + }, + "refmap": "terra-refmap.json" +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 7a34493bb..cdeaf2853 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -27,6 +27,8 @@ pluginManagement { maven("https://maven.fabricmc.net") { name = "Fabric" } + maven ( "https://maven.architectury.dev/" ) + maven ( "https://files.minecraftforge.net/maven/" ) gradlePluginPortal() } }