diff --git a/README.md b/README.md index 88de773..e2c4955 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,51 @@ -# MultiLoader Template - -This project provides a Gradle project template that can compile mods for multiple modloaders using a common sourceset. This project does not require any third party libraries or dependencies. If you have any questions or want to discuss the project join our [Discord](https://discord.myceliummod.network). - -## Getting Started - -### IntelliJ IDEA -This guide will show how to import the MultiLoader Template into IntelliJ IDEA. The setup process is roughly equivalent to setting up the modloaders independently and should be very familiar to anyone who has worked with their MDKs. - -1. Clone or download this repository to your computer. -2. Configure the project by editing the `group`, `mod_name`, `mod_author`, and `mod_id` properties in the `gradle.properties` file. You will also need to change the `rootProject.name` property in `settings.gradle`, this should match the folder name of your project, or else IDEA may complain. -3. Open the template's root folder as a new project in IDEA. This is the folder that contains this README file and the gradlew executable. -4. If your default JVM/JDK is not Java 21 you will encounter an error when opening the project. This error is fixed by going to `File > Settings > Build, Execution, Deployment > Build Tools > Gradle > Gradle JVM` and changing the value to a valid Java 21 JVM. You will also need to set the Project SDK to Java 21. This can be done by going to `File > Project Structure > Project SDK`. Once both have been set open the Gradle tab in IDEA and click the refresh button to reload the project. -5. Open the Gradle tab in IDEA if it has not already been opened. Navigate to `Your Project > Common > Tasks > vanilla gradle > decompile`. Run this task to decompile Minecraft. -6. Open your Run/Debug Configurations. Under the Application category there should now be options to run NeoForge and Fabric projects. Select one of the client options and try to run it. -7. Assuming you were able to run the game in step 7 your workspace should now be set up. - -### Eclipse -While it is possible to use this template in Eclipse it is not recommended. During the development of this template multiple critical bugs and quirks related to Eclipse were found at nearly every level of the required build tools. While we continue to work with these tools to report and resolve issues support for projects like these are not there yet. For now Eclipse is considered unsupported by this project. The development cycle for build tools is notoriously slow so there are no ETAs available. - -## Development Guide -When using this template the majority of your mod is developed in the Common project. The Common project is compiled against the vanilla game and is used to hold code that is shared between the different loader-specific versions of your mod. The Common project has no knowledge or access to ModLoader specific code, apis, or concepts. Code that requires something from a specific loader must be done through the project that is specific to that loader, such as the NeoForge or Fabric project. - -Loader specific projects such as the NeoForge and Fabric project are used to load the Common project into the game. These projects also define code that is specific to that loader. Loader specific projects can access all of the code in the Common project. It is important to remember that the Common project can not access code from loader specific projects. - -## Removing Platforms and Loaders -While the MultiLoader Template includes support for many platforms and loaders you can easily remove support for the ones you don't need. This can be done by deleting the subproject folder and then removing it from the `settings.gradle` file. For example if you wanted to remove support for Forge you would follow the following steps. - -1. Delete the subproject folder. For example, delete `MultiLoader-Template/forge`. -2. Remove the project from `settings.gradle`. For example, remove `include("forge")`. +# Sparse Structures +A simple and configurable mod that makes all (even datapacks and modded) structures more spread out (or more common!), essentially making them rarer/easier to find. Useful in big modpacks with a lot of structures mod to encourage exploration and make the experience more challenging or balanced. + +> Notice: this does not alter the generation of ores/geodes or other features of this kind, and alters very slightly the terrain and trees (not enough to be an issue to most players). + +## Examples +![Comparison between two world on the same seed, the one on the left showing a witch hut and a mansion (without the mod), the one on the right showing the same terrain but without those structures](media/example1.png) + +### In a modded environment: +![First screenshot of the comparison, showing many (maybe too many) structures at once in the ocean because of the quantity of structure mods installed.](media/example2_1.png) +![Second screenshot of the comparison, showing lot less structures in the same world (seed), at the same coordinates.](media/example2_2.png) +
Structure Mods in the picture : +Tidal towns, Explorify, all Yung's mods, Just Another Structure Pack, MVS, Explorations, Towns and Towers, Dungeons and Taverns... +
+ +## Features +* **Configurable** : you can change the rarity of all individual structures, even from mods and datapacks. +* **MC-177381 fix** : fixes the bug where `/locate` returns an incorrect distance between the player and the structure. +* **Dump Structure Sets** : you can dump all structure sets to a file in the right format for the config using `/dumpstructuresets`. +* **No separation limit** : In vanilla, the separation between structures is limited to 4096 chunks (65k blocks). This mod removes this limit, allowing structures to be even more spread out (if for any reason you need structures to be that rare). + +## Configuration +The config can be found in the `config` folder of your instance, and is named `sparsestructures.json5`. It contains the following options : +* `spreadFactor` : the factor by which the separation and spacing of structures should be multiplied. The default value is `2`, which means that structures will be twice as rare. If you want structures to be more common, you can set this value to `0.5` for example. + * ⚠️ **Warning** : a very low value (like `0.01`) makes the world generation process considerably slower (and your world will become filled with overlapping structures). +* `customSpreadFactors` : a list of custom spread factors for specific structures. The default value makes the mansion twice as rare as an example (which has no effects with a global `spreadFactor` of 2). If you want to change the spread factor of a structure, you can add an entry to this list. Each entry is a JSON object with two fields : `name` (the name of the structure) and `spreadFactor` (the spread factor of the structure). Here's an example : +```json5 +{ + "name": "minecraft:mansion", + "spreadFactor": 2 +} +// More detailed info can be found inside the config file +``` +> Tip: you can dump all structure sets to a file in the right format by using the custom command `/dumpstructuresets` + +For now, you have to restart the game for the config to be reloaded. This behavior may change in the future. + +## Dependencies +This mod doesn't have any (not even fabric-api). + +## Future Updates +* Ban specific structures from generating +* World-specific configs +* Support for frequency-based structure generation +* Support for structure tags in the config +* *Open an issue on this project's repo if you have any suggestion!* +* *~~Add a config~~* (added in v2.0) + +## Credits +Icon made with [Gimp](https://www.gimp.org/) using [Twemoji](https://github.com/twitter/twemoji) and [FiraCode](https://github.com/tonsky/FiraCode) +[Multiloader Template](https://github.com/jaredlll08/MultiLoader-Template) by jaredlll08 \ No newline at end of file diff --git a/common/src/main/java/com/example/examplemod/CommonClass.java b/common/src/main/java/com/example/examplemod/CommonClass.java deleted file mode 100644 index 7cd816f..0000000 --- a/common/src/main/java/com/example/examplemod/CommonClass.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.example.examplemod; - -import com.example.examplemod.platform.Services; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.world.item.Items; - -// This class is part of the common project meaning it is shared between all supported loaders. Code written here can only -// import and access the vanilla codebase, libraries used by vanilla, and optionally third party libraries that provide -// common compatible binaries. This means common code can not directly use loader specific concepts such as Forge events -// however it will be compatible with all supported mod loaders. -public class CommonClass { - - // The loader specific projects are able to import and use any code from the common project. This allows you to - // write the majority of your code here and load it from your loader specific projects. This example has some - // code that gets invoked by the entry point of the loader specific projects. - public static void init() { - - Constants.LOG.info("Hello from Common init on {}! we are currently in a {} environment!", Services.PLATFORM.getPlatformName(), Services.PLATFORM.getEnvironmentName()); - Constants.LOG.info("The ID for diamonds is {}", BuiltInRegistries.ITEM.getKey(Items.DIAMOND)); - - // It is common for all supported loaders to provide a similar feature that can not be used directly in the - // common code. A popular way to get around this is using Java's built-in service loader feature to create - // your own abstraction layer. You can learn more about this in our provided services class. In this example - // we have an interface in the common code and use a loader specific implementation to delegate our call to - // the platform specific approach. - if (Services.PLATFORM.isModLoaded("examplemod")) { - - Constants.LOG.info("Hello to examplemod"); - } - } -} \ No newline at end of file diff --git a/common/src/main/java/com/example/examplemod/Constants.java b/common/src/main/java/com/example/examplemod/Constants.java deleted file mode 100644 index 9d3c196..0000000 --- a/common/src/main/java/com/example/examplemod/Constants.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.example.examplemod; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class Constants { - - public static final String MOD_ID = "examplemod"; - public static final String MOD_NAME = "ExampleMod"; - public static final Logger LOG = LoggerFactory.getLogger(MOD_NAME); -} \ No newline at end of file diff --git a/common/src/main/java/com/example/examplemod/mixin/MixinMinecraft.java b/common/src/main/java/com/example/examplemod/mixin/MixinMinecraft.java deleted file mode 100644 index bd8b355..0000000 --- a/common/src/main/java/com/example/examplemod/mixin/MixinMinecraft.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.example.examplemod.mixin; - -import com.example.examplemod.Constants; -import net.minecraft.client.Minecraft; -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(Minecraft.class) -public class MixinMinecraft { - - @Inject(at = @At("TAIL"), method = "") - private void init(CallbackInfo info) { - - Constants.LOG.info("This line is printed by an example mod common mixin!"); - Constants.LOG.info("MC Version: {}", Minecraft.getInstance().getVersionType()); - } -} \ No newline at end of file diff --git a/common/src/main/java/io/github/maxencedc/sparsestructures/Constants.java b/common/src/main/java/io/github/maxencedc/sparsestructures/Constants.java new file mode 100644 index 0000000..fc30b1d --- /dev/null +++ b/common/src/main/java/io/github/maxencedc/sparsestructures/Constants.java @@ -0,0 +1,17 @@ +package io.github.maxencedc.sparsestructures; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.file.Path; +import java.nio.file.Paths; + +public class Constants { + + public static final String MOD_ID = "sparsestructures"; + public static final String MOD_NAME = "Sparse Structures"; + public static final Logger LOG = LoggerFactory.getLogger(MOD_NAME); + public static final String CONFIG_RESOURCE_NAME = "sparse-structures-default-config.json5"; + public static final String CONFIG_FILENAME = "sparsestructures.json5"; + public static final Path CONFIG_FILE_PATH = Path.of("config", CONFIG_FILENAME); +} \ No newline at end of file diff --git a/common/src/main/java/io/github/maxencedc/sparsestructures/CustomSpreadFactors.java b/common/src/main/java/io/github/maxencedc/sparsestructures/CustomSpreadFactors.java new file mode 100644 index 0000000..af56b1f --- /dev/null +++ b/common/src/main/java/io/github/maxencedc/sparsestructures/CustomSpreadFactors.java @@ -0,0 +1,19 @@ +package io.github.maxencedc.sparsestructures; + +public class CustomSpreadFactors { + public String structure; + public double factor; + + public CustomSpreadFactors(String structure, double factor) { + this.structure = structure; + this.factor = factor; + } + + public String structure() { + return this.structure; + } + + public double factor() { + return this.factor; + } +} \ No newline at end of file diff --git a/common/src/main/java/io/github/maxencedc/sparsestructures/SparseStructuresCommon.java b/common/src/main/java/io/github/maxencedc/sparsestructures/SparseStructuresCommon.java new file mode 100644 index 0000000..610a007 --- /dev/null +++ b/common/src/main/java/io/github/maxencedc/sparsestructures/SparseStructuresCommon.java @@ -0,0 +1,37 @@ +package io.github.maxencedc.sparsestructures; + +import io.github.maxencedc.sparsestructures.platform.Services; + +import java.io.InputStream; +import java.nio.file.StandardCopyOption; +import java.nio.file.Files; + +import com.google.gson.Gson; + +import static io.github.maxencedc.sparsestructures.Constants.CONFIG_FILE_PATH; +import static io.github.maxencedc.sparsestructures.Constants.CONFIG_RESOURCE_NAME; + +public class SparseStructuresCommon { + public static SparseStructuresConfig config; + + public static void init() { + + if (Services.PLATFORM.isModLoaded(Constants.MOD_ID)) { + + if (!CONFIG_FILE_PATH.toFile().exists()) { + try (InputStream in = SparseStructuresCommon.class.getClassLoader().getResourceAsStream(CONFIG_RESOURCE_NAME)) { + if (in == null) throw new IllegalStateException("Failed to load SparseStructure's default config \"" + CONFIG_RESOURCE_NAME +"\""); + Files.createDirectories(CONFIG_FILE_PATH); + Files.copy(in, CONFIG_FILE_PATH, StandardCopyOption.REPLACE_EXISTING); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + try (final InputStream in = Files.newInputStream(CONFIG_FILE_PATH)) { + config = new Gson().fromJson(new java.io.InputStreamReader(in), SparseStructuresConfig.class); + } catch (Exception e) { + throw new RuntimeException("SparseStructure's config file is malformed! If you don't know what's causing this, delete the config file and restart the game."); + } + } + } +} \ No newline at end of file diff --git a/common/src/main/java/io/github/maxencedc/sparsestructures/SparseStructuresConfig.java b/common/src/main/java/io/github/maxencedc/sparsestructures/SparseStructuresConfig.java new file mode 100644 index 0000000..2ef3261 --- /dev/null +++ b/common/src/main/java/io/github/maxencedc/sparsestructures/SparseStructuresConfig.java @@ -0,0 +1,15 @@ +package io.github.maxencedc.sparsestructures; + +import java.util.List; + +public class SparseStructuresConfig { + public double spreadFactor; + public double spreadFactor() { return this.spreadFactor; } + public List customSpreadFactors; + public List customSpreadFactors() { return this.customSpreadFactors; } + public SparseStructuresConfig(double spreadFactor, List customSpreadFactors) + { + this.spreadFactor = spreadFactor; + this.customSpreadFactors = customSpreadFactors; + } +} diff --git a/common/src/main/java/io/github/maxencedc/sparsestructures/StructureSetsSet.java b/common/src/main/java/io/github/maxencedc/sparsestructures/StructureSetsSet.java new file mode 100644 index 0000000..bfcbd5d --- /dev/null +++ b/common/src/main/java/io/github/maxencedc/sparsestructures/StructureSetsSet.java @@ -0,0 +1,12 @@ +package io.github.maxencedc.sparsestructures; + +import java.util.SortedSet; +import java.util.TreeSet; + +public class StructureSetsSet { + public static SortedSet structureSets = new TreeSet<>(); + + public static void addStructureSet(String structureSet) { + structureSets.add(structureSet); + } +} diff --git a/common/src/main/java/io/github/maxencedc/sparsestructures/command/DumpStructureSetsCommand.java b/common/src/main/java/io/github/maxencedc/sparsestructures/command/DumpStructureSetsCommand.java new file mode 100644 index 0000000..4c3366f --- /dev/null +++ b/common/src/main/java/io/github/maxencedc/sparsestructures/command/DumpStructureSetsCommand.java @@ -0,0 +1,49 @@ +package io.github.maxencedc.sparsestructures.command; + +import com.mojang.brigadier.CommandDispatcher; +import io.github.maxencedc.sparsestructures.Constants; +import io.github.maxencedc.sparsestructures.StructureSetsSet; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.network.chat.Component; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.SimpleDateFormat; +import java.util.Date; + +import static net.minecraft.commands.Commands.literal; + + +public class DumpStructureSetsCommand { + + public static void register(CommandDispatcher dispatcher) { + + dispatcher.register( + literal("dumpstructuresets") + .requires(cs -> cs.hasPermission(2)) + .executes(context -> { + context.getSource().sendSuccess(() -> Component.literal("Dumping structure set..."), false); + String fileName = new SimpleDateFormat("'structure_sets_dump_'yy_MM_dd_HH_mm'.txt'").format(new Date()); + try { + dumpStructureSets(fileName); + context.getSource().sendSuccess(() -> Component.literal("Structure sets dumped to: `" + Paths.get(Constants.MOD_ID, fileName) + "`\n(if you're on a server, this is dumped in the server's files)"), false); + return 1; + } catch (IOException e) { + context.getSource().sendSuccess(() -> Component.literal("Failed to dump structure sets, check logs for error"), false); + Constants.LOG.error("Failed to dump structure sets", e); + return 0; + } + }) + ); + } + + private static void dumpStructureSets(String fileName) throws IOException { + Path dumpPath = Path.of(Constants.MOD_ID); + StringBuilder dump = new StringBuilder(); + StructureSetsSet.structureSets.forEach(s -> dump.append("{\n \"structure\": \"").append(s).append("\",\n \"factor\": 1//REPLACE WITH YOUR CUSTOM SPREADING FACTOR HERE\n},\n")); + Files.createDirectories(dumpPath); + Files.writeString(dumpPath.resolve(fileName), dump.toString()); + } +} diff --git a/common/src/main/java/io/github/maxencedc/sparsestructures/mixin/FixLocateDistance.java b/common/src/main/java/io/github/maxencedc/sparsestructures/mixin/FixLocateDistance.java new file mode 100644 index 0000000..5588de8 --- /dev/null +++ b/common/src/main/java/io/github/maxencedc/sparsestructures/mixin/FixLocateDistance.java @@ -0,0 +1,20 @@ +package io.github.maxencedc.sparsestructures.mixin; + +import net.minecraft.server.commands.LocateCommand; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +@Mixin(LocateCommand.class) +public class FixLocateDistance { + + /** + * @author Maxence + * @reason Fix the distance calculation (MC-177381) + */ + @Overwrite + private static float dist(int pos1x, int pos1z, int pos2x, int pos2z) { + double d = pos2x - pos1x; + double e = pos2z - pos1z; + return (float) Math.hypot(d, e); + } +} diff --git a/common/src/main/java/com/example/examplemod/platform/Services.java b/common/src/main/java/io/github/maxencedc/sparsestructures/platform/Services.java similarity index 89% rename from common/src/main/java/com/example/examplemod/platform/Services.java rename to common/src/main/java/io/github/maxencedc/sparsestructures/platform/Services.java index b3a1530..cadcf55 100644 --- a/common/src/main/java/com/example/examplemod/platform/Services.java +++ b/common/src/main/java/io/github/maxencedc/sparsestructures/platform/Services.java @@ -1,7 +1,7 @@ -package com.example.examplemod.platform; +package io.github.maxencedc.sparsestructures.platform; -import com.example.examplemod.Constants; -import com.example.examplemod.platform.services.IPlatformHelper; +import io.github.maxencedc.sparsestructures.Constants; +import io.github.maxencedc.sparsestructures.platform.services.IPlatformHelper; import java.util.ServiceLoader; diff --git a/common/src/main/java/com/example/examplemod/platform/services/IPlatformHelper.java b/common/src/main/java/io/github/maxencedc/sparsestructures/platform/services/IPlatformHelper.java similarity index 93% rename from common/src/main/java/com/example/examplemod/platform/services/IPlatformHelper.java rename to common/src/main/java/io/github/maxencedc/sparsestructures/platform/services/IPlatformHelper.java index 3553cac..e65f375 100644 --- a/common/src/main/java/com/example/examplemod/platform/services/IPlatformHelper.java +++ b/common/src/main/java/io/github/maxencedc/sparsestructures/platform/services/IPlatformHelper.java @@ -1,4 +1,4 @@ -package com.example.examplemod.platform.services; +package io.github.maxencedc.sparsestructures.platform.services; public interface IPlatformHelper { diff --git a/common/src/main/resources/sparse-structures-default-config.json5 b/common/src/main/resources/sparse-structures-default-config.json5 new file mode 100644 index 0000000..1d6272c --- /dev/null +++ b/common/src/main/resources/sparse-structures-default-config.json5 @@ -0,0 +1,31 @@ +// ### THE MOD REQUIRES A RESTART OF THE GAME TO APPLY CHANGES ### +{ + // this is the main spread factor (default is 2) + // + // tips : a spread factor can be a decimal number (such as 1.5) + // a spread factor of 1 means all structure's placement are not modified (useful if you want to use only custom spread factors) + // a spread factor above 1 means all structures are rarer + // a spread factor below 1 means all structure are more common + "spreadFactor": 2, + + // this is a list of custom spread factors + "customSpreadFactors": [ + // example of the mansion being doubled in rarity (the mod's default) + { + "structure": "minecraft:mansion", + "factor": 2 + }, + // add the structures you want to modify in the format: + // { + // "structure": "namespace:structure_name", + // "factor": number + // }, + // where "structure" is a structure_set or the name of a structure + // /!\ if you put the name of a structure, all structures in its set will be modified + // (example : "minecraft:village_plains" will modify all structures in the "villages" set) + // see https://minecraft.wiki/w/Tutorials/Custom_structures#Structure_Set for more info + // + // tip : you can dump all structure sets in a file by running the custom command /dumpstructuresets + // tip : the same spread factors rules apply here + ] +} \ No newline at end of file diff --git a/common/src/main/resources/examplemod.mixins.json b/common/src/main/resources/sparsestructures.mixins.json similarity index 62% rename from common/src/main/resources/examplemod.mixins.json rename to common/src/main/resources/sparsestructures.mixins.json index 0735c74..0ac77ae 100644 --- a/common/src/main/resources/examplemod.mixins.json +++ b/common/src/main/resources/sparsestructures.mixins.json @@ -1,13 +1,13 @@ { "required": true, "minVersion": "0.8", - "package": "com.example.examplemod.mixin", + "package": "io.github.maxencedc.sparsestructures.mixin", "refmap": "${mod_id}.refmap.json", "compatibilityLevel": "JAVA_21", - "mixins": [], - "client": [ - "MixinMinecraft" + "mixins": [ + "FixLocateDistance" ], + "client": [], "server": [], "injectors": { "defaultRequire": 1 diff --git a/common/src/main/resources/sparsestructures.png b/common/src/main/resources/sparsestructures.png new file mode 100644 index 0000000..aaaf23e Binary files /dev/null and b/common/src/main/resources/sparsestructures.png differ diff --git a/fabric/src/main/java/com/example/examplemod/ExampleMod.java b/fabric/src/main/java/com/example/examplemod/ExampleMod.java deleted file mode 100644 index e53f1a7..0000000 --- a/fabric/src/main/java/com/example/examplemod/ExampleMod.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.example.examplemod; - -import net.fabricmc.api.ModInitializer; - -public class ExampleMod implements ModInitializer { - - @Override - public void onInitialize() { - - // This method is invoked by the Fabric mod loader when it is ready - // to load your mod. You can access Fabric and Common code in this - // project. - - // Use Fabric to bootstrap the Common mod. - Constants.LOG.info("Hello Fabric world!"); - CommonClass.init(); - } -} diff --git a/fabric/src/main/java/com/example/examplemod/mixin/MixinTitleScreen.java b/fabric/src/main/java/com/example/examplemod/mixin/MixinTitleScreen.java deleted file mode 100644 index 3680cdb..0000000 --- a/fabric/src/main/java/com/example/examplemod/mixin/MixinTitleScreen.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.example.examplemod.mixin; - -import com.example.examplemod.Constants; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screens.TitleScreen; -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(TitleScreen.class) -public class MixinTitleScreen { - - @Inject(at = @At("HEAD"), method = "init()V") - private void init(CallbackInfo info) { - - Constants.LOG.info("This line is printed by an example mod mixin from Fabric!"); - Constants.LOG.info("MC Version: {}", Minecraft.getInstance().getVersionType()); - } -} \ No newline at end of file diff --git a/fabric/src/main/java/io/github/maxencedc/sparsestructures/SparseStructuresFabric.java b/fabric/src/main/java/io/github/maxencedc/sparsestructures/SparseStructuresFabric.java new file mode 100644 index 0000000..668ba29 --- /dev/null +++ b/fabric/src/main/java/io/github/maxencedc/sparsestructures/SparseStructuresFabric.java @@ -0,0 +1,16 @@ +package io.github.maxencedc.sparsestructures; + +import io.github.maxencedc.sparsestructures.command.DumpStructureSetsCommand; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; + +public class SparseStructuresFabric implements ModInitializer { + + @Override + public void onInitialize() { + SparseStructuresCommon.init(); + CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { + DumpStructureSetsCommand.register(dispatcher); + }); + } +} diff --git a/fabric/src/main/java/io/github/maxencedc/sparsestructures/mixin/MakeStructuresSparse.java b/fabric/src/main/java/io/github/maxencedc/sparsestructures/mixin/MakeStructuresSparse.java new file mode 100644 index 0000000..cf5a9ac --- /dev/null +++ b/fabric/src/main/java/io/github/maxencedc/sparsestructures/mixin/MakeStructuresSparse.java @@ -0,0 +1,56 @@ +package io.github.maxencedc.sparsestructures.mixin; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.mojang.serialization.Decoder; +import io.github.maxencedc.sparsestructures.CustomSpreadFactors; +import io.github.maxencedc.sparsestructures.SparseStructuresCommon; +import io.github.maxencedc.sparsestructures.StructureSetsSet; +import net.minecraft.core.RegistrationInfo; +import net.minecraft.core.WritableRegistry; +import net.minecraft.resources.RegistryDataLoader; +import net.minecraft.resources.RegistryOps; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.packs.resources.Resource; +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 org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.io.Reader; + +@Mixin(RegistryDataLoader.class) +public class MakeStructuresSparse { + + @Inject(at = @At(value = "INVOKE", target = "Lcom/mojang/serialization/Decoder;parse(Lcom/mojang/serialization/DynamicOps;Ljava/lang/Object;)Lcom/mojang/serialization/DataResult;"), method = "loadElementFromResource", locals = LocalCapture.CAPTURE_FAILHARD) + private static void loadElementFromResource(WritableRegistry registry, Decoder decoder, RegistryOps ops, ResourceKey resourceKey, Resource resource, RegistrationInfo registrationInfo, CallbackInfo ci, Reader reader, JsonElement jsonElement) { + String string = registry.key().location().getPath(); + if (!string.equals("worldgen/structure_set")) return; + + JsonObject jsonObject = jsonElement.getAsJsonObject(); + JsonObject placement = jsonObject.getAsJsonObject("placement"); + if (!placement.get("type").getAsString().equals("minecraft:concentric_rings")) { + StructureSetsSet.addStructureSet(resourceKey.location().toString()); + + double factor = SparseStructuresCommon.config.customSpreadFactors().stream().filter(s -> { + if (s == null) return false; + String structure_set = resourceKey.location().toString(); + return structure_set.equals(s.structure()) || jsonObject.getAsJsonArray("structures").asList().stream().anyMatch(p -> p.getAsJsonObject().get("structure").getAsString().equals(s.structure())); + }).findFirst().orElse(new CustomSpreadFactors("", SparseStructuresCommon.config.spreadFactor())).factor(); + + int spacing; + int separation; + + spacing = (placement.get("spacing") == null) ? 1 : (int)(placement.get("spacing").getAsDouble() * factor); + separation = (placement.get("separation") == null) ? 1 : (int)(placement.get("separation").getAsDouble() * factor); + if (separation >= spacing) { + spacing = Math.max(1, spacing); + separation = spacing - 1; + } + + placement.addProperty("spacing", spacing); + placement.addProperty("separation", separation); + } + } +} \ No newline at end of file diff --git a/fabric/src/main/java/io/github/maxencedc/sparsestructures/mixin/PushSpreadLimit.java b/fabric/src/main/java/io/github/maxencedc/sparsestructures/mixin/PushSpreadLimit.java new file mode 100644 index 0000000..2e444cc --- /dev/null +++ b/fabric/src/main/java/io/github/maxencedc/sparsestructures/mixin/PushSpreadLimit.java @@ -0,0 +1,16 @@ +package io.github.maxencedc.sparsestructures.mixin; + +import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyConstant; + +@Mixin(RandomSpreadStructurePlacement.class) +public class PushSpreadLimit { + + // method_40170 is the lambda in the CODEC static field (net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement.lambda.static.0) + @ModifyConstant(method = "method_40170", constant = @Constant(intValue = 4096)) + private static int pushSpreadLimit(int original) { + return Integer.MAX_VALUE; + } +} diff --git a/fabric/src/main/java/com/example/examplemod/platform/FabricPlatformHelper.java b/fabric/src/main/java/io/github/maxencedc/sparsestructures/platform/FabricPlatformHelper.java similarity index 77% rename from fabric/src/main/java/com/example/examplemod/platform/FabricPlatformHelper.java rename to fabric/src/main/java/io/github/maxencedc/sparsestructures/platform/FabricPlatformHelper.java index 1a3371f..71286ef 100644 --- a/fabric/src/main/java/com/example/examplemod/platform/FabricPlatformHelper.java +++ b/fabric/src/main/java/io/github/maxencedc/sparsestructures/platform/FabricPlatformHelper.java @@ -1,6 +1,6 @@ -package com.example.examplemod.platform; +package io.github.maxencedc.sparsestructures.platform; -import com.example.examplemod.platform.services.IPlatformHelper; +import io.github.maxencedc.sparsestructures.platform.services.IPlatformHelper; import net.fabricmc.loader.api.FabricLoader; public class FabricPlatformHelper implements IPlatformHelper { diff --git a/fabric/src/main/resources/META-INF/services/com.example.examplemod.platform.services.IPlatformHelper b/fabric/src/main/resources/META-INF/services/com.example.examplemod.platform.services.IPlatformHelper deleted file mode 100644 index 30c54ee..0000000 --- a/fabric/src/main/resources/META-INF/services/com.example.examplemod.platform.services.IPlatformHelper +++ /dev/null @@ -1 +0,0 @@ -com.example.examplemod.platform.FabricPlatformHelper \ No newline at end of file diff --git a/fabric/src/main/resources/META-INF/services/io.github.maxencedc.sparsestructures.platform.services.IPlatformHelper b/fabric/src/main/resources/META-INF/services/io.github.maxencedc.sparsestructures.platform.services.IPlatformHelper new file mode 100644 index 0000000..55e45aa --- /dev/null +++ b/fabric/src/main/resources/META-INF/services/io.github.maxencedc.sparsestructures.platform.services.IPlatformHelper @@ -0,0 +1 @@ +io.github.maxencedc.sparsestructures.platform.FabricPlatformHelper \ No newline at end of file diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 649d575..b0cbc71 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -8,15 +8,18 @@ "${mod_author}" ], "contact": { - "homepage": "https://fabricmc.net/", - "sources": "https://github.com/FabricMC/fabric-example-mod" + "website": "https://maxencedc.github.io", + "repo": "https://github.com/maxencedc/sparsestructures", + "homepage": "https://modrinth.com/mod/sparsestructures", + "issues": "https://github.com/maxencedc/sparsestructures/issues", + "sources": "https://github.com/maxencedc/sparsestructures" }, "license": "${license}", "icon": "${mod_id}.png", "environment": "*", "entrypoints": { "main": [ - "com.example.examplemod.ExampleMod" + "io.github.maxencedc.sparsestructures.SparseStructuresFabric" ] }, "mixins": [ @@ -24,13 +27,15 @@ "${mod_id}.fabric.mixins.json" ], "depends": { - "fabricloader": ">=${fabric_loader_version}", - "fabric-api": "*", "minecraft": "${minecraft_version}", "java": ">=${java_version}" }, - "suggests": { - "another-mod": "*" + "custom": { + "modmenu": { + "links": { + "modmenu.modrinth": "https://modrinth.com/mod/sparsestructures" + } + } } } \ No newline at end of file diff --git a/fabric/src/main/resources/examplemod.fabric.mixins.json b/fabric/src/main/resources/sparsestructures.fabric.mixins.json similarity index 57% rename from fabric/src/main/resources/examplemod.fabric.mixins.json rename to fabric/src/main/resources/sparsestructures.fabric.mixins.json index 5abf2d7..88ee124 100644 --- a/fabric/src/main/resources/examplemod.fabric.mixins.json +++ b/fabric/src/main/resources/sparsestructures.fabric.mixins.json @@ -1,13 +1,14 @@ { "required": true, "minVersion": "0.8", - "package": "com.example.examplemod.mixin", + "package": "io.github.maxencedc.sparsestructures.mixin", "refmap": "${mod_id}.refmap.json", "compatibilityLevel": "JAVA_21", - "mixins": [], - "client": [ - "MixinTitleScreen" + "mixins": [ + "MakeStructuresSparse", + "PushSpreadLimit" ], + "client": [], "server": [], "injectors": { "defaultRequire": 1 diff --git a/gradle.properties b/gradle.properties index baa459a..f599c23 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,26 +2,26 @@ # Every field you add must be added to the root build.gradle expandProps map. # Project -version=1.0.0 -group=com.example.examplemod +version=2.2.0 +group=io.github.maxencedc.sparsestructures java_version=21 # Common minecraft_version=1.21 -mod_name=ExampleMod -mod_author=Jared -mod_id=examplemod -license=CC0-1.0 -credits= -description=The description of your mod. \\nAccepts multilines (but they must be double-escaped). +mod_name=SparseStructures +mod_author=MaxenceDC +mod_id=sparsestructures +license=MIT +credits=All GitHub contributors, jaredlll08 for the Multiloader template, The modding community, and BisectHosting (use code MAX)! +description=Makes all structures more spread out. Configurable.\\n\\nIf you want to support me, you can use code MAX at BisectHosting for 25% off! minecraft_version_range=[1.21, 1.22) # Fabric -fabric_version=0.100.1+1.21 +fabric_version=0.100.3+1.21 fabric_loader_version=0.15.11 # NeoForge -neoforge_version=21.0.20-beta +neoforge_version=21.0.30-beta neoforge_loader_version_range=[4,) # Gradle diff --git a/media/example1.png b/media/example1.png new file mode 100644 index 0000000..08c93cc Binary files /dev/null and b/media/example1.png differ diff --git a/media/example2_1.png b/media/example2_1.png new file mode 100644 index 0000000..41c7281 Binary files /dev/null and b/media/example2_1.png differ diff --git a/media/example2_2.png b/media/example2_2.png new file mode 100644 index 0000000..393a281 Binary files /dev/null and b/media/example2_2.png differ diff --git a/media/icon_small.png b/media/icon_small.png new file mode 100644 index 0000000..ac66745 Binary files /dev/null and b/media/icon_small.png differ diff --git a/media/icon_small2.png b/media/icon_small2.png new file mode 100644 index 0000000..d38a524 Binary files /dev/null and b/media/icon_small2.png differ diff --git a/neoforge/src/main/java/com/example/examplemod/ExampleMod.java b/neoforge/src/main/java/com/example/examplemod/ExampleMod.java deleted file mode 100644 index fb019fe..0000000 --- a/neoforge/src/main/java/com/example/examplemod/ExampleMod.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.example.examplemod; - - -import net.neoforged.bus.api.IEventBus; -import net.neoforged.fml.common.Mod; - -@Mod(Constants.MOD_ID) -public class ExampleMod { - - public ExampleMod(IEventBus eventBus) { - - // This method is invoked by the NeoForge mod loader when it is ready - // to load your mod. You can access NeoForge and Common code in this - // project. - - // Use NeoForge to bootstrap the Common mod. - Constants.LOG.info("Hello NeoForge world!"); - CommonClass.init(); - - } -} \ No newline at end of file diff --git a/neoforge/src/main/java/com/example/examplemod/mixin/MixinTitleScreen.java b/neoforge/src/main/java/com/example/examplemod/mixin/MixinTitleScreen.java deleted file mode 100644 index 7486ecf..0000000 --- a/neoforge/src/main/java/com/example/examplemod/mixin/MixinTitleScreen.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.example.examplemod.mixin; - -import com.example.examplemod.Constants; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screens.TitleScreen; -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(TitleScreen.class) -public class MixinTitleScreen { - - @Inject(at = @At("HEAD"), method = "init()V") - private void init(CallbackInfo info) { - - Constants.LOG.info("This line is printed by an example mod mixin from NeoForge!"); - Constants.LOG.info("MC Version: {}", Minecraft.getInstance().getVersionType()); - } -} \ No newline at end of file diff --git a/neoforge/src/main/java/io/github/maxencedc/sparsestructures/SparseStructuresNeoForge.java b/neoforge/src/main/java/io/github/maxencedc/sparsestructures/SparseStructuresNeoForge.java new file mode 100644 index 0000000..4835e82 --- /dev/null +++ b/neoforge/src/main/java/io/github/maxencedc/sparsestructures/SparseStructuresNeoForge.java @@ -0,0 +1,22 @@ +package io.github.maxencedc.sparsestructures; + + +import io.github.maxencedc.sparsestructures.command.DumpStructureSetsCommand; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.common.Mod; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.RegisterCommandsEvent; + +@Mod(Constants.MOD_ID) +public class SparseStructuresNeoForge { + + public SparseStructuresNeoForge() { + SparseStructuresCommon.init(); + IEventBus forgeBus = NeoForge.EVENT_BUS; + forgeBus.addListener(this::registerCommands); + } + + private void registerCommands(RegisterCommandsEvent event) { + DumpStructureSetsCommand.register(event.getDispatcher()); + } +} \ No newline at end of file diff --git a/neoforge/src/main/java/io/github/maxencedc/sparsestructures/mixin/MakeStructuresSparse.java b/neoforge/src/main/java/io/github/maxencedc/sparsestructures/mixin/MakeStructuresSparse.java new file mode 100644 index 0000000..66024a6 --- /dev/null +++ b/neoforge/src/main/java/io/github/maxencedc/sparsestructures/mixin/MakeStructuresSparse.java @@ -0,0 +1,56 @@ +package io.github.maxencedc.sparsestructures.mixin; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.mojang.serialization.Decoder; +import io.github.maxencedc.sparsestructures.CustomSpreadFactors; +import io.github.maxencedc.sparsestructures.SparseStructuresCommon; +import io.github.maxencedc.sparsestructures.StructureSetsSet; +import net.minecraft.core.RegistrationInfo; +import net.minecraft.core.WritableRegistry; +import net.minecraft.resources.RegistryDataLoader; +import net.minecraft.resources.RegistryOps; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.packs.resources.Resource; +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 org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.io.Reader; + +@Mixin(RegistryDataLoader.class) +public class MakeStructuresSparse { + + @Inject(at = @At(value = "INVOKE", target = "Lcom/mojang/serialization/Decoder;parse(Lcom/mojang/serialization/DynamicOps;Ljava/lang/Object;)Lcom/mojang/serialization/DataResult;"), method = "loadElementFromResource", locals = LocalCapture.CAPTURE_FAILHARD) + private static void loadElementFromResource(WritableRegistry registry, Decoder p_decoder, RegistryOps ops, ResourceKey resourceKey, Resource resource, RegistrationInfo registrationInfo, CallbackInfo ci, Decoder decoder, Reader reader, JsonElement jsonElement) { + String string = registry.key().location().getPath(); + if (!string.equals("worldgen/structure_set")) return; + + JsonObject jsonObject = jsonElement.getAsJsonObject(); + JsonObject placement = jsonObject.getAsJsonObject("placement"); + if (!placement.get("type").getAsString().equals("minecraft:concentric_rings")) { + StructureSetsSet.addStructureSet(resourceKey.location().toString()); + + double factor = SparseStructuresCommon.config.customSpreadFactors().stream().filter(s -> { + if (s == null) return false; + String structure_set = resourceKey.location().toString(); + return structure_set.equals(s.structure()) || jsonObject.getAsJsonArray("structures").asList().stream().anyMatch(p -> p.getAsJsonObject().get("structure").getAsString().equals(s.structure())); + }).findFirst().orElse(new CustomSpreadFactors("", SparseStructuresCommon.config.spreadFactor())).factor(); + + int spacing; + int separation; + + spacing = (placement.get("spacing") == null) ? 1 : (int)(placement.get("spacing").getAsDouble() * factor); + separation = (placement.get("separation") == null) ? 1 : (int)(placement.get("separation").getAsDouble() * factor); + if (separation >= spacing) { + spacing = Math.max(1, spacing); + separation = spacing - 1; + } + + placement.addProperty("spacing", spacing); + placement.addProperty("separation", separation); + } + } +} diff --git a/neoforge/src/main/java/io/github/maxencedc/sparsestructures/mixin/PushSpreadLimit.java b/neoforge/src/main/java/io/github/maxencedc/sparsestructures/mixin/PushSpreadLimit.java new file mode 100644 index 0000000..d0b2adb --- /dev/null +++ b/neoforge/src/main/java/io/github/maxencedc/sparsestructures/mixin/PushSpreadLimit.java @@ -0,0 +1,16 @@ +package io.github.maxencedc.sparsestructures.mixin; + +import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyConstant; + +@Mixin(RandomSpreadStructurePlacement.class) +public class PushSpreadLimit { + + // lambda$static$0 is the lambda in the CODEC static field (net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement.lambda.static.0) + @ModifyConstant(method = "lambda$static$0", constant = @Constant(intValue = 4096)) + private static int pushSpreadLimit(int original) { + return Integer.MAX_VALUE; + } +} diff --git a/neoforge/src/main/java/com/example/examplemod/platform/NeoForgePlatformHelper.java b/neoforge/src/main/java/io/github/maxencedc/sparsestructures/platform/NeoForgePlatformHelper.java similarity index 77% rename from neoforge/src/main/java/com/example/examplemod/platform/NeoForgePlatformHelper.java rename to neoforge/src/main/java/io/github/maxencedc/sparsestructures/platform/NeoForgePlatformHelper.java index c9ce736..c40c4b9 100644 --- a/neoforge/src/main/java/com/example/examplemod/platform/NeoForgePlatformHelper.java +++ b/neoforge/src/main/java/io/github/maxencedc/sparsestructures/platform/NeoForgePlatformHelper.java @@ -1,6 +1,6 @@ -package com.example.examplemod.platform; +package io.github.maxencedc.sparsestructures.platform; -import com.example.examplemod.platform.services.IPlatformHelper; +import io.github.maxencedc.sparsestructures.platform.services.IPlatformHelper; import net.neoforged.fml.ModList; import net.neoforged.fml.loading.FMLLoader; diff --git a/neoforge/src/main/resources/META-INF/services/com.example.examplemod.platform.services.IPlatformHelper b/neoforge/src/main/resources/META-INF/services/com.example.examplemod.platform.services.IPlatformHelper deleted file mode 100644 index 3ae5a61..0000000 --- a/neoforge/src/main/resources/META-INF/services/com.example.examplemod.platform.services.IPlatformHelper +++ /dev/null @@ -1 +0,0 @@ -com.example.examplemod.platform.NeoForgePlatformHelper \ No newline at end of file diff --git a/neoforge/src/main/resources/META-INF/services/io.github.maxencedc.sparsestructures.platform.services.IPlatformHelper b/neoforge/src/main/resources/META-INF/services/io.github.maxencedc.sparsestructures.platform.services.IPlatformHelper new file mode 100644 index 0000000..f1f4eba --- /dev/null +++ b/neoforge/src/main/resources/META-INF/services/io.github.maxencedc.sparsestructures.platform.services.IPlatformHelper @@ -0,0 +1 @@ +io.github.maxencedc.sparsestructures.platform.NeoForgePlatformHelper \ No newline at end of file diff --git a/neoforge/src/main/resources/examplemod.neoforge.mixins.json b/neoforge/src/main/resources/sparsestructures.neoforge.mixins.json similarity index 51% rename from neoforge/src/main/resources/examplemod.neoforge.mixins.json rename to neoforge/src/main/resources/sparsestructures.neoforge.mixins.json index 1ac6c9b..1ff4286 100644 --- a/neoforge/src/main/resources/examplemod.neoforge.mixins.json +++ b/neoforge/src/main/resources/sparsestructures.neoforge.mixins.json @@ -1,12 +1,13 @@ { "required": true, "minVersion": "0.8", - "package": "com.example.examplemod.mixin", + "package": "io.github.maxencedc.sparsestructures.mixin", "compatibilityLevel": "JAVA_21", - "mixins": [], - "client": [ - "MixinTitleScreen" + "mixins": [ + "MakeStructuresSparse", + "PushSpreadLimit" ], + "client": [], "server": [], "injectors": { "defaultRequire": 1 diff --git a/settings.gradle b/settings.gradle index 774b87b..e6f82e3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -46,7 +46,7 @@ plugins { } // This should match the folder name of the project, or else IDEA may complain (see https://youtrack.jetbrains.com/issue/IDEA-317606) -rootProject.name = 'MultiLoader-Template' +rootProject.name = 'SparseStructuresML' include("common") include("fabric") include("neoforge")