diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..c339cf2f --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,37 @@ +name: Java CI with Gradle +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + version: ["1.7", "1.8.9", "1.9.4", "1.10.2", "1.11.2", "1.12.2", "1.13", "1.14", "1.15", "1.16", "1.17", "1.18", "1.19", "1.19.2", "22w19a", "1.20"] + + name: Build ${{ matrix.version }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Validate Gradle wrapper + uses: gradle/wrapper-validation-action@v1 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Generate ${{ matrix.version }} + uses: gradle/gradle-build-action@v2 + with: + arguments: :${{ matrix.version }}:runServer + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: data-${{ matrix.version }} + path: ${{ matrix.version }}/run/minecraft-data + if-no-files-found: error diff --git a/.gitignore b/.gitignore index fdfd540c..8048ee4b 100644 --- a/.gitignore +++ b/.gitignore @@ -112,9 +112,9 @@ gradle-app.setting **/build/ # Common working directory -run/ +**/run/ # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) !gradle-wrapper.jar -bin/ \ No newline at end of file +bin/ diff --git a/1.10.2/build.gradle b/1.10.2/build.gradle new file mode 100644 index 00000000..057916c6 --- /dev/null +++ b/1.10.2/build.gradle @@ -0,0 +1,63 @@ +plugins { + id 'fabric-loom' +} + +repositories { + maven { + name = "legacy-fabric" + url = "https://maven.legacyfabric.net" + } +} + +loom { + setIntermediaryUrl('https://maven.legacyfabric.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar'); + customMinecraftManifest.set("https://meta.legacyfabric.net/v2/manifest/${minecraft_version}") +} + +dependencies { + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API provides hooks for events, item registration, and more. As most mods will need this, it's included by default. + // If you know for a fact you don't, it's not required and can be safely removed. +// modImplementation ("net.legacyfabric.legacy-fabric-api:legacy-fabric-api:${fabric_version}") { +// exclude module: "legacy-fabric-entity-events-v1" +// } + + if (System.getProperty("os.name").toLowerCase().contains("mac")) { + implementation 'org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209' + implementation 'org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209' + implementation 'org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209' + } +} + +if (System.getProperty("os.name").toLowerCase().contains("mac")) { + configurations.configureEach { + resolutionStrategy { + dependencySubstitution { + substitute module('org.lwjgl.lwjgl:lwjgl_util:2.9.2-nightly-201408222') with module('org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209') + substitute module('org.lwjgl.lwjgl:lwjgl:2.9.2-nightly-201408222') with module('org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209') + } + force 'org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209' + } + } +} + +processResources { + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} diff --git a/1.10.2/gradle.properties b/1.10.2/gradle.properties new file mode 100644 index 00000000..335785fc --- /dev/null +++ b/1.10.2/gradle.properties @@ -0,0 +1,7 @@ +# Fabric Properties +# More versions available at: https://grayray75.github.io/LegacyFabric-Versions/ +minecraft_version=1.10.2 +yarn_mappings=1.10.2+build.202206020145 +loader_version=0.14.3 +# Dependencies +# More versions available at: https://grayray75.github.io/LegacyFabric-Versions/ diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeColors.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeColors.java new file mode 100644 index 00000000..257db1a9 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeColors.java @@ -0,0 +1,47 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.biome.Biome; + +import java.util.Iterator; + +public class BiomeColors { + private static final ColorProvider GRASS_COLOR = (biome, pos) -> biome.getGrassColor(pos); + private static final ColorProvider FOLIAGE_COLOR = (biome, pos) -> biome.getFoliageColor(pos); + private static final ColorProvider WATER_COLOR = (biome, pos) -> biome.getWaterColor(); + + private static int getColor(EmptyBlockView view, BlockPos pos, ColorProvider provider) { + int i = 0; + int j = 0; + int k = 0; + + int l; + for (Iterator iterator = BlockPos.mutableIterate(pos.add(-1, 0, -1), pos.add(1, 0, 1)).iterator(); iterator.hasNext(); k += l & 255) { + BlockPos.Mutable mutable = (BlockPos.Mutable) iterator.next(); + l = provider.getColorAtPos(view.getBiome(mutable), mutable); + i += (l & 16711680) >> 16; + j += (l & '\uff00') >> 8; + } + + return (i / 9 & 255) << 16 | (j / 9 & 255) << 8 | k / 9 & 255; + } + + public static int getGrassColor(EmptyBlockView view, BlockPos pos) { + return getColor(view, pos, GRASS_COLOR); + } + + public static int getFoliageColor(EmptyBlockView view, BlockPos pos) { + return getColor(view, pos, FOLIAGE_COLOR); + } + + public static int getWaterColor(EmptyBlockView view, BlockPos pos) { + return getColor(view, pos, WATER_COLOR); + } + + interface ColorProvider { + int getColorAtPos(Biome biome, BlockPos pos); + } +} + + diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColorable.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColorable.java new file mode 100644 index 00000000..3e3e044c --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColorable.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import org.jetbrains.annotations.Nullable; + +public interface BlockColorable { + int method_12155(BlockState blockState, @Nullable EmptyBlockView blockView, @Nullable BlockPos blockPos, int i); +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColors.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColors.java new file mode 100644 index 00000000..4df7551c --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColors.java @@ -0,0 +1,97 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import net.minecraft.block.*; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.FlowerPotBlockEntity; +import net.minecraft.item.BlockItem; +import net.minecraft.item.Item; +import net.minecraft.util.collection.IdList; +import net.minecraft.util.math.BlockPos; +import org.jetbrains.annotations.Nullable; + +public class BlockColors { + private final IdList BlockColor2Id = new IdList<>(32); + + public BlockColors() { + } + + public static BlockColors create() { + final BlockColors blockColors = new BlockColors(); + blockColors.method_12158((blockState, blockView, blockPos, i) -> { + DoublePlantBlock.DoublePlantType doublePlantType = (DoublePlantBlock.DoublePlantType) blockState.get(DoublePlantBlock.VARIANT); + return blockView == null || blockPos == null || doublePlantType != DoublePlantBlock.DoublePlantType.GRASS && doublePlantType != DoublePlantBlock.DoublePlantType.FERN ? -1 : BiomeColors.getGrassColor(blockView, blockPos); + }, Blocks.DOUBLE_PLANT); + blockColors.method_12158((blockState, blockView, blockPos, i) -> { + if (blockView != null && blockPos != null) { + BlockEntity blockEntity = blockView.getBlockEntity(blockPos); + if (blockEntity instanceof FlowerPotBlockEntity) { + Item item = ((FlowerPotBlockEntity) blockEntity).getItem(); + if (item instanceof BlockItem) { + BlockState blockState2 = Block.getBlockFromItem(item).getDefaultState(); + return blockColors.method_12157(blockState2, blockView, blockPos, i); + } + } + } + + return -1; + }, Blocks.FLOWER_POT); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getGrassColor(blockView, blockPos) : GrassColors.getColor(0.5D, 1.0D), Blocks.GRASS); + blockColors.method_12158((blockState, blockView, blockPos, i) -> { + PlanksBlock.WoodType woodType = (PlanksBlock.WoodType) blockState.get(Leaves1Block.VARIANT); + if (woodType == PlanksBlock.WoodType.SPRUCE) { + return FoliageColors.getSpruceColor(); + } else if (woodType == PlanksBlock.WoodType.BIRCH) { + return FoliageColors.getBirchColor(); + } else { + return blockView != null && blockPos != null ? BiomeColors.getFoliageColor(blockView, blockPos) : FoliageColors.getDefaultColor(); + } + }, Blocks.LEAVES); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getFoliageColor(blockView, blockPos) : FoliageColors.getDefaultColor(), Blocks.LEAVES2); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getWaterColor(blockView, blockPos) : -1, Blocks.WATER, Blocks.FLOWING_WATER); + blockColors.method_12158((blockState, blockView, blockPos, i) -> RedstoneWireBlock.method_8877((Integer) blockState.get(RedstoneWireBlock.POWER)), Blocks.REDSTONE_WIRE); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getGrassColor(blockView, blockPos) : -1, Blocks.SUGARCANE); + blockColors.method_12158((blockState, blockView, blockPos, i) -> { + int j = (Integer) blockState.get(AttachedStemBlock.AGE); + int k = j * 32; + int l = 255 - j * 8; + int m = j * 4; + return k << 16 | l << 8 | m; + }, Blocks.MELON_STEM, Blocks.PUMPKIN_STEM); + blockColors.method_12158((blockState, blockView, blockPos, i) -> { + if (blockView != null && blockPos != null) { + return BiomeColors.getGrassColor(blockView, blockPos); + } else { + return blockState.get(TallPlantBlock.TYPE) == TallPlantBlock.GrassType.DEAD_BUSH ? 16777215 : GrassColors.getColor(0.5D, 1.0D); + } + }, Blocks.TALLGRASS); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getFoliageColor(blockView, blockPos) : FoliageColors.getDefaultColor(), Blocks.VINE); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? 2129968 : 7455580, Blocks.LILY_PAD); + return blockColors; + } + + public int method_13410(BlockState blockState) { + BlockColorable blockColorable = (BlockColorable) this.BlockColor2Id.fromId(Block.getIdByBlock(blockState.getBlock())); + if (blockColorable != null) { + return blockColorable.method_12155(blockState, (EmptyBlockView) null, (BlockPos) null, 0); + } else { + MaterialColor materialColor = blockState.getMaterialColor(); + return materialColor != null ? materialColor.color : -1; + } + } + + public int method_12157(BlockState blockState, @Nullable EmptyBlockView blockView, @Nullable BlockPos blockPos, int i) { + BlockColorable blockColorable = (BlockColorable) this.BlockColor2Id.fromId(Block.getIdByBlock(blockState.getBlock())); + return blockColorable == null ? -1 : blockColorable.method_12155(blockState, blockView, blockPos, i); + } + + public void method_12158(BlockColorable blockColorable, Block... blocks) { + int i = blocks.length; + + for (int j = 0; j < i; ++j) { + Block block = blocks[j]; + this.BlockColor2Id.set(blockColorable, Block.getIdByBlock(block)); + } + + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java new file mode 100644 index 00000000..8c32f48f --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java @@ -0,0 +1,37 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +public class FoliageColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] pixels) { + colorMap = pixels; + } + + public static int getColor(double temperature, double humidity) { + humidity *= temperature; + int i = (int) ((1.0D - temperature) * 255.0D); + int j = (int) ((1.0D - humidity) * 255.0D); + return colorMap[j << 8 | i]; + } + + public static int getSpruceColor() { + return 6396257; + } + + public static int getBirchColor() { + return 8431445; + } + + public static int getDefaultColor() { + return 4764952; + } + + public static int getFoliageColor(Biome biome) { + double d = (double) MathHelper.clamp(biome.getTemperature(), 0.0F, 1.0F); + double e = (double) MathHelper.clamp(biome.getRainfall(), 0.0F, 1.0F); + return FoliageColors.getColor(d, e); + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java new file mode 100644 index 00000000..88687071 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java @@ -0,0 +1,29 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +public class GrassColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] map) { + colorMap = map; + } + + public static int getColor(double temperature, double humidity) { + humidity *= temperature; + int i = (int) ((1.0D - temperature) * 255.0D); + int j = (int) ((1.0D - humidity) * 255.0D); + int k = j << 8 | i; + return k > colorMap.length ? -65281 : colorMap[k]; + } + + public static int getGrassColor(Biome biome) { + double d = (double) MathHelper.clamp(biome.getTemperature(), 0.0F, 1.0F); + double e = (double) MathHelper.clamp(biome.getRainfall(), 0.0F, 1.0F); + return GrassColors.getColor(d, e); + } +} + + + diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java new file mode 100644 index 00000000..1572a082 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java @@ -0,0 +1,28 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; + +public class ServerSideRedstoneWireBlock { + public static int getWireColor(int powerLevel) { + float f = (float) powerLevel / 15.0F; + float g = f * 0.6F + 0.4F; + if (powerLevel == 0) { + g = 0.3F; + } + + float h = f * f * 0.7F - 0.5F; + float j = f * f * 0.6F - 0.7F; + if (h < 0.0F) { + h = 0.0F; + } + + if (j < 0.0F) { + j = 0.0F; + } + + int k = MathHelper.clamp((int) (g * 255.0F), 0, 255); + int l = MathHelper.clamp((int) (h * 255.0F), 0, 255); + int m = MathHelper.clamp((int) (j * 255.0F), 0, 255); + return -16777216 | k << 16 | l << 8 | m; + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/Main.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/Main.java new file mode 100644 index 00000000..d23c5ba2 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/Main.java @@ -0,0 +1,13 @@ +package dev.u9g.minecraftdatagenerator; + +import net.fabricmc.api.ModInitializer; + +import java.util.logging.Logger; + +public class Main implements ModInitializer { + public static final Logger LOGGER = Logger.getLogger("mc-data-gen-serv"); + + @Override + public void onInitialize() { + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java new file mode 100644 index 00000000..1689c8b5 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java @@ -0,0 +1,32 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.LanguageAccessor; +import dev.u9g.minecraftdatagenerator.util.Registries; + +import java.util.Map; + +public class AttributesDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "attributes"; + } + + @Override + public JsonElement generateDataJson() { + Map translations = ((LanguageAccessor) Registries.LANGUAGE).translations(); + JsonArray arr = new JsonArray(); + for (Map.Entry translation : translations.entrySet()) { + String key = translation.getKey(); + if (!key.startsWith("attribute.name.")) continue; + JsonObject obj = new JsonObject(); + key = key.replace("attribute.name.", ""); + obj.addProperty("name", key.split("\\.")[1]); + obj.addProperty("resource", key); + arr.add(obj); + } + return arr; + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java new file mode 100644 index 00000000..79299efe --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java @@ -0,0 +1,192 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.BiomeAccessor; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.SimpleRegistry; +import net.minecraft.world.biome.*; +import org.apache.commons.lang3.StringUtils; + +import java.util.Locale; + +public class BiomesDataGenerator implements IDataGenerator { + + private static String guessBiomeDimensionFromCategory(Biome biome) { + if (biome instanceof NetherBiome) { + return "nether"; + } else if (biome instanceof EndBiome) { + return "end"; + } + return "overworld"; + } + + private static int getBiomeColorFor(String biomeDisplayNamed) { + if (biomeDisplayNamed.equals("Redwood Taiga Hills M")) { + biomeDisplayNamed = "MegaTaigaHills"; + } + String biomeDisplayName = StringUtils.join(biomeDisplayNamed.split(" "), ""); + return switch (biomeDisplayName) { + case "Ocean" -> 112; + case "Plains" -> 9286496; + case "Desert" -> 16421912; + case "ExtremeHills", "Extreme Hills" -> 6316128; + case "Forest" -> 353825; + case "Taiga" -> 747097; + case "Swampland" -> 522674; + case "River" -> 255; + case "Hell" -> 16711680; + case "TheEnd", "The End" -> 8421631; + case "FrozenOcean", "Frozen Ocean" -> 7368918; + case "FrozenRiver", "Frozen River" -> 10526975; + case "IcePlains", "Ice Plains" -> 16777215; + case "IceMountains", "Ice Mountains" -> 10526880; + case "MushroomIsland", "Mushroom Island" -> 16711935; + case "MushroomIslandShore", "Mushroom Island Shore" -> 10486015; + case "Beach" -> 16440917; + case "DesertHills", "Desert Hills" -> 13786898; + case "ForestHills", "Forest Hills" -> 2250012; + case "TaigaHills", "Taiga Hills" -> 1456435; + case "ExtremeHillsEdge", "Extreme Hills Edge" -> 7501978; + case "Jungle" -> 5470985; + case "JungleHills", "Jungle Hills" -> 2900485; + case "JungleEdge", "Jungle Edge" -> 6458135; + case "DeepOcean", "Deep Ocean" -> 48; + case "StoneBeach", "Stone Beach" -> 10658436; + case "ColdBeach", "Cold Beach" -> 16445632; + case "BirchForest", "Birch Forest" -> 3175492; + case "BirchForestHills", "Birch Forest Hills" -> 2055986; + case "RoofedForest", "Roofed Forest" -> 4215066; + case "ColdTaiga", "Cold Taiga" -> 3233098; + case "ColdTaigaHills", "Cold Taiga Hills" -> 2375478; + case "MegaTaiga", "Mega Taiga" -> 5858897; + case "MegaTaigaHills", "Mega Taiga Hills" -> 4542270; + case "ExtremeHills+", "Extreme Hills+" -> 5271632; + case "Savanna" -> 12431967; + case "SavannaPlateau", "Savanna Plateau" -> 10984804; + case "Mesa" -> 14238997; + case "MesaPlateauF", "Mesa Plateau F" -> 11573093; + case "MesaPlateau", "Mesa Plateau" -> 13274213; + case "TheEnd-Floatingislands", "The End - Floating islands" -> 8421631; + case "TheEnd-Mediumisland", "The End - Medium island" -> 8421631; + case "TheEnd-Highisland", "The End - High island" -> 8421631; + case "TheEnd-Barrenisland", "The End - Barren island" -> 8421631; + case "WarmOcean", "Warm Ocean" -> 172; + case "LukewarmOcean", "Lukewarm Ocean" -> 144; + case "ColdOcean", "Cold Ocean" -> 2105456; + case "WarmDeepOcean", "Warm Deep Ocean" -> 80; + case "LukewarmDeepOcean", "Lukewarm Deep Ocean" -> 64; + case "ColdDeepOcean", "Cold Deep Ocean" -> 2105400; + case "FrozenDeepOcean", "Frozen Deep Ocean" -> 4210832; + case "TheVoid", "The Void" -> 0; + case "SunflowerPlains", "Sunflower Plains" -> 11918216; + case "DesertM", "Desert M" -> 16759872; + case "ExtremeHillsM", "Extreme Hills M" -> 8947848; + case "FlowerForest", "Flower Forest" -> 2985545; + case "TaigaM", "Taiga M" -> 3378817; + case "SwamplandM", "Swampland M" -> 3145690; + case "IcePlainsSpikes", "Ice Plains Spikes" -> 11853020; + case "JungleM", "Jungle M" -> 8102705; + case "JungleEdgeM", "Jungle Edge M" -> 9089855; + case "BirchForestM", "Birch Forest M" -> 5807212; + case "BirchForestHillsM", "Birch Forest Hills M" -> 4687706; + case "RoofedForestM", "Roofed Forest M" -> 6846786; + case "ColdTaigaM", "Cold Taiga M" -> 5864818; + case "MegaSpruceTaiga", "Mega Spruce Taiga" -> 8490617; + case "MegaSpruceTaiga(Hills)", "Mega Spruce Taiga (Hills)" -> 7173990; + case "ExtremeHills+M", "Extreme Hills+ M" -> 7903352; + case "SavannaM", "Savanna M" -> 15063687; + case "SavannaPlateauM", "Savanna Plateau M" -> 13616524; + case "Mesa(Bryce)", "Mesa (Bryce)" -> 16739645; + case "MesaPlateauFM", "Mesa Plateau F M" -> 14204813; + case "MesaPlateauM", "Mesa Plateau M" -> 15905933; + case "BambooJungle", "Bamboo Jungle" -> 7769620; + case "BambooJungleHills", "Bamboo Jungle Hills" -> 3884810; + default -> throw new Error("Unexpected biome, with name: '" + biomeDisplayName + "'"); + }; + } + + public static JsonObject generateBiomeInfo(SimpleRegistry registry, Biome biome) { + JsonObject biomeDesc = new JsonObject(); + Identifier registryKey = registry.getIdentifier(biome); + + biomeDesc.addProperty("id", registry.getRawId(biome)); + biomeDesc.addProperty("name", String.join("_", ((BiomeAccessor) biome).name().toLowerCase(Locale.ENGLISH).split(" "))); + biomeDesc.addProperty("category", category(biome)); + biomeDesc.addProperty("temperature", biome.getTemperature()); + biomeDesc.addProperty("precipitation", precipitation(biome)); + biomeDesc.addProperty("depth", biome.getDepth()); + biomeDesc.addProperty("dimension", guessBiomeDimensionFromCategory(biome)); + biomeDesc.addProperty("displayName", ((BiomeAccessor) biome).name()); + biomeDesc.addProperty("color", getBiomeColorFor(((BiomeAccessor) biome).name())); + biomeDesc.addProperty("rainfall", biome.getRainfall()); + + return biomeDesc; + } + + private static String category(Biome biome) { + if (biome instanceof ForestBiome) { + return "forest"; + } else if (biome instanceof OceanBiome) { + return "ocean"; + } else if (biome instanceof PlainsBiome) { + return "plains"; + } else if (biome instanceof DesertBiome) { + return "desert"; + } else if (biome instanceof ExtremeHillsBiome) { + return "extreme_hills"; + } else if (biome instanceof TaigaBiome) { + return "taiga"; + } else if (biome instanceof SwampBiome) { + return "swamp"; + } else if (biome instanceof RiverBiome) { + return "river"; + } else if (biome instanceof NetherBiome) { + return "nether"; + } else if (biome instanceof EndBiome) { + return "the_end"; + } else if (biome instanceof IceBiome) { + return "icy"; + } else if (biome instanceof MushroomBiome) { + return "mushroom"; + } else if (biome instanceof BeachBiome) { + return "beach"; + } else if (biome instanceof JungleBiome) { + return "jungle"; + } else if (biome instanceof SavannaBiome) { + return "savanna"; + } else if (biome instanceof MesaBiome) { + return "mesa"; + } else if (biome instanceof StoneBeachBiome || biome instanceof VoidBiome) { + return "none"; // Should StoneBeachBiome be beach too? this is how it is now in mcdata + } + throw new Error("Unable to find biome category for " + biome.getClass().getName()); + } + + private static String precipitation(Biome biome) { + float rainfall = biome.getRainfall(); + float temperature = biome.getTemperature(); + if (rainfall == 0) { + return "none"; + } else if (temperature < 0.2f) { + return "snow"; + } + return "rain"; + } + + @Override + public String getDataName() { + return "biomes"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray biomesArray = new JsonArray(); + SimpleRegistry biomeRegistry = Biome.REGISTRY; + + for (Biome biome : biomeRegistry) { + biomesArray.add(generateBiomeInfo(biomeRegistry, biome)); + } + return biomesArray; + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java new file mode 100644 index 00000000..b2789fd0 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java @@ -0,0 +1,126 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class BlockCollisionShapesDataGenerator implements IDataGenerator { + private static final Box ENTITY_BOX = new Box(0.0D, 0.0D, 0.0D, 1.0D, 2.0D, 1.0D); + + private static String nameOf(Block block) { + return Objects.requireNonNull(Registries.BLOCKS.getIdentifier(block)).getPath(); + } + + private static JsonArray jsonOf(Box box) { + JsonArray arr = new JsonArray(); + if (box == null) return arr; + arr.add(new JsonPrimitive(box.minX)); + arr.add(new JsonPrimitive(box.minY)); + arr.add(new JsonPrimitive(box.minZ)); + arr.add(new JsonPrimitive(box.maxX)); + arr.add(new JsonPrimitive(box.maxY)); + arr.add(new JsonPrimitive(box.maxZ)); + return arr; + } + + @Override + public String getDataName() { + return "blockCollisionShapes"; + } + + @Override + public JsonObject generateDataJson() { + ShapeCache shapeCache = new ShapeCache(); + JsonObject blocksObject = new JsonObject(); + for (Block block : Registries.BLOCKS) { + Object val = shapeCache.addShapesFrom(block); + if (val instanceof JsonArray) { + blocksObject.add(nameOf(block), (JsonElement) val); + } else { + blocksObject.addProperty(nameOf(block), (Integer) val); + } + } + JsonObject resultObject = new JsonObject(); + resultObject.add("blocks", blocksObject); + resultObject.add("shapes", shapeCache.toJSON()); + return resultObject; + } + + public static class ShapeCache { + private final ArrayList shapesCache = new ArrayList<>(); + + public Object addShapesFrom(Block block) { + List indexesOfBoxesInTheShapesCache = new ArrayList<>(); + for (BlockState state : block.getStateManager().getBlockStates().reverse()) { + List boxes = new ArrayList<>(); + try { + state.addCollisionBoxesToList(DGU.getWorld(), BlockPos.ORIGIN, ENTITY_BOX, boxes, null); + } catch (Exception e) { + e.printStackTrace(); + } + Shapes thisBlockStateShapes = new Shapes(boxes); + int indexOfThisBlockStatesShapes = shapesCache.indexOf(thisBlockStateShapes); + if (indexOfThisBlockStatesShapes != -1) { + indexesOfBoxesInTheShapesCache.add(indexOfThisBlockStatesShapes); + } else { + shapesCache.add(thisBlockStateShapes); + indexesOfBoxesInTheShapesCache.add(shapesCache.size() - 1); + } + } + if (indexesOfBoxesInTheShapesCache.stream().distinct().count() < 2) { + return indexesOfBoxesInTheShapesCache.get(0); + } else { + JsonArray shapeIndexes = new JsonArray(); + indexesOfBoxesInTheShapesCache.forEach(shapeIndex -> shapeIndexes.add(new JsonPrimitive(shapeIndex))); + return shapeIndexes; + } + } + + public JsonObject toJSON() { + JsonObject shapes = new JsonObject(); + int i = 0; + for (Shapes s : shapesCache) { + shapes.add(String.valueOf(i++), s.toJSON()); + } + return shapes; + } + + private static class Shapes { + final List boxes; + + public Shapes(List boxes) { + this.boxes = boxes; + } + + public JsonArray toJSON() { + JsonArray arr = new JsonArray(); + boxes.forEach(box -> arr.add(jsonOf(box))); + return arr; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Shapes shapes = (Shapes) o; + return Objects.equals(boxes, shapes.boxes); + } + + @Override + public int hashCode() { + return boxes != null ? boxes.hashCode() : 0; + } + } + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java new file mode 100644 index 00000000..284f566f --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java @@ -0,0 +1,152 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.base.CaseFormat; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.mixin.BlockAccessor; +import dev.u9g.minecraftdatagenerator.mixin.MiningToolItemAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.block.AirBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.TransparentBlock; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ToolItem; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.state.property.EnumProperty; +import net.minecraft.state.property.IntProperty; +import net.minecraft.state.property.Property; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.logging.Logger; + +public class BlocksDataGenerator implements IDataGenerator { + + private static final Logger logger = Main.LOGGER; + + private static List getItemsEffectiveForBlock(Block block) { + List items = new ArrayList<>(); + for (Item item : Registries.ITEMS) { + if (item instanceof ToolItem && ((MiningToolItemAccessor) item).getEffectiveBlocks().contains(block)) { + items.add(item); + } + } + return items; + } + + private static List populateDropsIfPossible(BlockState blockState, Item firstToolItem) { + return new ArrayList<>(); + } + + private static String getPropertyTypeName(Property property) { + //Explicitly handle default minecraft properties + if (property instanceof BooleanProperty) { + return "bool"; + } + if (property instanceof IntProperty) { + return "int"; + } + if (property instanceof EnumProperty) { + return "enum"; + } + + //Use simple class name as fallback, this code will give something like + //example_type for ExampleTypeProperty class name + String rawPropertyName = property.getClass().getSimpleName().replace("Property", ""); + return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, rawPropertyName); + } + + private static > JsonObject generateStateProperty(Property property) { + JsonObject propertyObject = new JsonObject(); + Collection propertyValues = property.getValues(); + + propertyObject.addProperty("name", property.getName()); + propertyObject.addProperty("type", getPropertyTypeName(property)); + propertyObject.addProperty("num_values", propertyValues.size()); + + //Do not add values for vanilla boolean properties, they are known by default + if (!(property instanceof BooleanProperty)) { + JsonArray propertyValuesArray = new JsonArray(); + for (T propertyValue : propertyValues) { + propertyValuesArray.add(new JsonPrimitive(property.name(propertyValue))); + } + propertyObject.add("values", propertyValuesArray); + } + return propertyObject; + } + + public static JsonObject generateBlock(Block block) { + JsonObject blockDesc = new JsonObject(); + + List blockStates = block.getStateManager().getBlockStates(); + BlockState defaultState = block.getDefaultState(); + Identifier registryKey = Registries.BLOCKS.getIdentifier(block); + List effectiveTools = getItemsEffectiveForBlock(block); + + blockDesc.addProperty("id", Registries.BLOCKS.getRawId(block)); + blockDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + if (!block.getTranslatedName().startsWith("tile.")) { + blockDesc.addProperty("displayName", block.getTranslatedName()); + } + + float hardness = block.getDefaultState().getHardness(null, null); + + blockDesc.addProperty("hardness", hardness); + blockDesc.addProperty("resistance", ((BlockAccessor) block).getBlastResistance()); + blockDesc.addProperty("diggable", hardness != -1.0f && !(block instanceof AirBlock)); + JsonObject effTools = new JsonObject(); + effectiveTools.forEach(item -> effTools.addProperty( + String.valueOf(Registries.ITEMS.getRawId(item)), // key + item.getBlockBreakingSpeed(DGU.stackFor(item), defaultState) // value + )); + blockDesc.add("effectiveTools", effTools); + blockDesc.addProperty("transparent", block instanceof TransparentBlock); + blockDesc.addProperty("emitLight", defaultState.getLuminance()); + blockDesc.addProperty("filterLight", block.getDefaultState().getOpacity()); + + JsonArray stateProperties = new JsonArray(); + for (Property property : block.getStateManager().getProperties()) { + stateProperties.add(generateStateProperty(property)); + } + blockDesc.add("states", stateProperties); + blockDesc.add("drops", new JsonArray()); + blockDesc.addProperty("boundingBox", boundingBox(block, defaultState)); + + return blockDesc; + } + + private static String boundingBox(Block block, BlockState state) { + if (block.getDefaultState().getCollisionBox(EmptyBlockView.INSTANCE, BlockPos.ORIGIN) == null) { + return "empty"; + } + return "block"; + } + + private static Item getItemFromBlock(Block block) { + return Registries.ITEMS.get(Registries.BLOCKS.getIdentifier(block)); + } + + @Override + public String getDataName() { + return "blocks"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultBlocksArray = new JsonArray(); + for (Block block : Registries.BLOCKS) { + resultBlocksArray.add(generateBlock(block)); + } + return resultBlocksArray; + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java new file mode 100644 index 00000000..273d67ca --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java @@ -0,0 +1,80 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonWriter; +import dev.u9g.minecraftdatagenerator.Main; + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class DataGenerators { + + private static final List GENERATORS = new ArrayList<>(); + private static final Logger logger = Main.LOGGER; + + static { + register(new BiomesDataGenerator()); + register(new BlockCollisionShapesDataGenerator()); + register(new BlocksDataGenerator()); + register(new EffectsDataGenerator()); + register(new EnchantmentsDataGenerator()); + register(new EntitiesDataGenerator()); + register(new FoodsDataGenerator()); + register(new ItemsDataGenerator()); + register(new ParticlesDataGenerator()); + register(new TintsDataGenerator()); + register(new LanguageDataGenerator()); + register(new InstrumentsDataGenerator()); + register(new AttributesDataGenerator()); + } + + public static void register(IDataGenerator generator) { + GENERATORS.add(generator); + } + + public static boolean runDataGenerators(Path outputDirectory) { + try { + Files.createDirectories(outputDirectory); + } catch (IOException exception) { + logger.log(Level.INFO, "Failed to create data generator output directory at " + outputDirectory); + exception.printStackTrace(); + return false; + } + + int generatorsFailed = 0; + logger.log(Level.INFO, MessageFormat.format("Running minecraft data generators, output at {0}", outputDirectory)); + + for (IDataGenerator dataGenerator : GENERATORS) { + logger.log(Level.INFO, MessageFormat.format("Running generator {0}", dataGenerator.getDataName())); + try { + String outputFileName = String.format("%s.json", dataGenerator.getDataName()); + JsonElement outputElement = dataGenerator.generateDataJson(); + Path outputFilePath = outputDirectory.resolve(outputFileName); + + try (Writer writer = Files.newBufferedWriter(outputFilePath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + JsonWriter jsonWriter = new JsonWriter(writer); + jsonWriter.setIndent(" "); + Streams.write(outputElement, jsonWriter); + } + logger.log(Level.INFO, MessageFormat.format("Generator: {0} -> {1}", dataGenerator.getDataName(), outputFileName)); + + } catch (Throwable exception) { + logger.log(Level.INFO, MessageFormat.format("Failed to run data generator {0}", dataGenerator.getDataName())); + exception.printStackTrace(); + generatorsFailed++; + } + } + + logger.log(Level.INFO, "Finishing running data generators"); + return generatorsFailed == 0; + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java new file mode 100644 index 00000000..6aba51e0 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java @@ -0,0 +1,50 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.StatusEffectAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.util.Identifier; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.Collectors; + +public class EffectsDataGenerator implements IDataGenerator { + + public static JsonObject generateEffect(StatusEffect statusEffect) { + JsonObject effectDesc = new JsonObject(); + @NotNull Identifier registryKey = Objects.requireNonNull(Registries.STATUS_EFFECTS.getIdentifier(statusEffect)); + + effectDesc.addProperty("id", Registries.STATUS_EFFECTS.getRawId(statusEffect)); + if (statusEffect == StatusEffects.UNLUCK) { + effectDesc.addProperty("name", "BadLuck"); + effectDesc.addProperty("displayName", "Bad Luck"); + } else { + effectDesc.addProperty("name", Arrays.stream(registryKey.getPath().split("_")).map(StringUtils::capitalize).collect(Collectors.joining())); + effectDesc.addProperty("displayName", DGU.translateText(statusEffect.getTranslationKey())); + } + + effectDesc.addProperty("type", !((StatusEffectAccessor) statusEffect).negative() ? "good" : "bad"); + return effectDesc; + } + + @Override + public String getDataName() { + return "effects"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + for (StatusEffect effect : Registries.STATUS_EFFECTS) { + resultsArray.add(generateEffect(effect)); + } + return resultsArray; + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java new file mode 100644 index 00000000..1f1685d8 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java @@ -0,0 +1,108 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.util.Identifier; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +public class EnchantmentsDataGenerator implements IDataGenerator { + + private static final ImmutableMap ENCHANTMENT_TARGET_NAMES = ImmutableMap.builder() + .put(EnchantmentTarget.ALL_ARMOR, "armor") + .put(EnchantmentTarget.FEET, "armor_feet") + .put(EnchantmentTarget.LEGS, "armor_legs") + .put(EnchantmentTarget.ARMOR_CHEST, "armor_chest") + .put(EnchantmentTarget.HEAD, "armor_head") + .put(EnchantmentTarget.WEAPON, "weapon") + .put(EnchantmentTarget.DIGGER, "digger") + .put(EnchantmentTarget.FISHING_ROD, "fishing_rod") + .put(EnchantmentTarget.BREAKABLE, "breakable") + .put(EnchantmentTarget.BOW, "bow") + .build(); + + public static String getEnchantmentTargetName(EnchantmentTarget target) { + return ENCHANTMENT_TARGET_NAMES.getOrDefault(target, target.name().toLowerCase(Locale.ROOT)); + } + + // Equation enchantment costs follow is a * level + b, so we can easily retrieve a and b by passing zero level + private static JsonObject generateEnchantmentMinPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMinimumPower(0); + int a = enchantment.getMinimumPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + private static JsonObject generateEnchantmentMaxPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMaximumPower(0); + int a = enchantment.getMaximumPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + public static JsonObject generateEnchantment(Enchantment enchantment) { + JsonObject enchantmentDesc = new JsonObject(); + Identifier registryKey = Registries.ENCHANTMENTS.getIdentifier(enchantment); + + enchantmentDesc.addProperty("id", Registries.ENCHANTMENTS.getRawId(enchantment)); + enchantmentDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + enchantmentDesc.addProperty("displayName", DGU.translateText(enchantment.getTranslationKey())); + + enchantmentDesc.addProperty("maxLevel", enchantment.getMaximumLevel()); + enchantmentDesc.add("minCost", generateEnchantmentMinPowerCoefficients(enchantment)); + enchantmentDesc.add("maxCost", generateEnchantmentMaxPowerCoefficients(enchantment)); + + enchantmentDesc.addProperty("treasureOnly", enchantment.isTreasure()); + enchantmentDesc.addProperty("curse", false); // 1.10 added curse enchants + + List incompatibleEnchantments = new ArrayList<>(); + for (Enchantment other : Registries.ENCHANTMENTS) { + if (!enchantment.differs(other) && !other.differs(enchantment) && other != enchantment) { + incompatibleEnchantments.add(other); + } + } + + JsonArray excludes = new JsonArray(); + for (Enchantment excludedEnchantment : incompatibleEnchantments) { + Identifier otherKey = Registries.ENCHANTMENTS.getIdentifier(excludedEnchantment); + excludes.add(new JsonPrimitive(Objects.requireNonNull(otherKey).getPath())); + } + enchantmentDesc.add("exclude", excludes); + + enchantmentDesc.addProperty("category", getEnchantmentTargetName(enchantment.target)); + enchantmentDesc.addProperty("weight", enchantment.getRarity().getChance()); + enchantmentDesc.addProperty("tradeable", true); // the first non-tradeable enchant came in 1.16, soul speed + enchantmentDesc.addProperty("discoverable", true); // the first non-enchantable enchant came in 1.16, soul speed + + return enchantmentDesc; + } + + @Override + public String getDataName() { + return "enchantments"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + for (Enchantment enchantment : Registries.ENCHANTMENTS) { + resultsArray.add(generateEnchantment(enchantment)); + } + return resultsArray; + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java new file mode 100644 index 00000000..5828f9e9 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java @@ -0,0 +1,138 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.EntityTypeAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.entity.*; +import net.minecraft.entity.mob.AmbientEntity; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.mob.WaterCreatureEntity; +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.projectile.FishingBobberEntity; +import net.minecraft.entity.projectile.Projectile; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +public class EntitiesDataGenerator implements IDataGenerator { + + public static JsonObject generateEntity(Class entityClass) { + JsonObject entityDesc = new JsonObject(); + Identifier registryKey = Registries.ENTITY_TYPES.getIdentifier(entityClass); + int entityRawId = Registries.ENTITY_TYPES.getRawId(entityClass); + @Nullable Entity entity = makeEntity(entityClass); + // FIXME: ENTITY ID IS WRONG + int id = entityId(entity); + entityDesc.addProperty("id", id); + entityDesc.addProperty("internalId", id); + entityDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + String displayName = entity != null ? DGU.translateText(entity.getTranslationKey()) : null; + if (displayName != null && !displayName.startsWith("entity.")) { + entityDesc.addProperty("displayName", displayName); + } + entityDesc.addProperty("width", entity == null ? 0 : entity.width); + entityDesc.addProperty("height", entity == null ? 0 : entity.height); + + String entityTypeString = "UNKNOWN"; + entityTypeString = getEntityTypeForClass(entityClass); + entityDesc.addProperty("type", entityTypeString); + entityDesc.addProperty("category", getCategoryFrom(entityClass)); + + return entityDesc; + } + + private static Entity makeEntity(Class type) { + String name = EntityTypeAccessor.CLASS_NAME_MAP().get(type); + return EntityType.createInstanceFromName(name, DGU.getWorld()); + } + + private static String getCategoryFrom(@NotNull Class entityClass) { + if (entityClass == PlayerEntity.class) return "other"; // fail early for player entities + String packageName = entityClass.getPackage().getName(); + String category = switch (packageName) { + case "net.minecraft.entity.decoration", "net.minecraft.entity.decoration.painting" -> "Immobile"; + case "net.minecraft.entity.boss", "net.minecraft.entity.mob", "net.minecraft.entity.boss.dragon" -> + "Hostile mobs"; + case "net.minecraft.entity.projectile", "net.minecraft.entity.thrown" -> "Projectiles"; + case "net.minecraft.entity.passive" -> "Passive mobs"; + case "net.minecraft.entity.vehicle" -> "Vehicles"; + case "net.minecraft.entity" -> "other"; + default -> throw new Error("Unexpected entity type: " + packageName); + }; + return category; + } + + //Honestly, both "type" and "category" fields in the schema and examples do not contain any useful information + //Since category is optional, I will just leave it out, and for type I will assume general entity classification + //by the Entity class hierarchy (which has some weirdness too by the way) + private static String getEntityTypeForClass(Class entityClass) { + //Top-level classifications + if (WaterCreatureEntity.class.isAssignableFrom(entityClass)) { + return "water_creature"; + } + if (AnimalEntity.class.isAssignableFrom(entityClass)) { + return "animal"; + } + if (HostileEntity.class.isAssignableFrom(entityClass)) { + return "hostile"; + } + if (AmbientEntity.class.isAssignableFrom(entityClass)) { + return "ambient"; + } + + //Second level classifications. PathAwareEntity is not included because it + //doesn't really make much sense to categorize by it + if (PathAwareEntity.class.isAssignableFrom(entityClass)) { + return "passive"; + } + if (MobEntity.class.isAssignableFrom(entityClass)) { + return "mob"; + } + + //Other classifications only include living entities and projectiles. everything else is categorized as other + if (LivingEntity.class.isAssignableFrom(entityClass)) { + return "living"; + } + if (Projectile.class.isAssignableFrom(entityClass)) { + return "projectile"; + } + return "other"; + } + + private static int entityId(Entity entity) { + if (!DGU.getCurrentlyRunningServer().getVersion().equals("1.10.2")) { + throw new Error("These ids were gotten manually for 1.10.2, remake for " + DGU.getCurrentlyRunningServer().getVersion()); + } + int rawId = Registries.ENTITY_TYPES.getRawId(entity.getClass()); + if (rawId == -1) { // see TrackedEntityInstance + if (entity instanceof ItemEntity) { + return 2; + } else if (entity instanceof FishingBobberEntity) { + return 90; + } else { + throw new Error("unable to find rawId for entity: " + entity.getEntityName()); + } + } + return rawId; + } + + @Override + public String getDataName() { + return "entities"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + for (Class entityType : Registries.ENTITY_TYPES) { + resultArray.add(generateEntity(entityType)); + } + return resultArray; + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java new file mode 100644 index 00000000..fd68583a --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java @@ -0,0 +1,50 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.item.FoodItem; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; + +import java.util.Objects; + +public class FoodsDataGenerator implements IDataGenerator { + + public static JsonObject generateFoodDescriptor(FoodItem foodItem) { + JsonObject foodDesc = new JsonObject(); + Identifier registryKey = Registries.ITEMS.getIdentifier(foodItem); + + foodDesc.addProperty("id", Registries.ITEMS.getRawId(foodItem)); + foodDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + foodDesc.addProperty("stackSize", foodItem.getMaxCount()); + foodDesc.addProperty("displayName", foodItem.getDisplayName(DGU.stackFor(foodItem))); + float foodPoints = foodItem.getHungerPoints(DGU.stackFor(foodItem)); + float saturationRatio = foodItem.getSaturation(DGU.stackFor(foodItem)) * 2.0F; + float saturation = foodPoints * saturationRatio; + + foodDesc.addProperty("foodPoints", foodPoints); + foodDesc.addProperty("saturation", saturation); + + foodDesc.addProperty("effectiveQuality", foodPoints + saturation); + foodDesc.addProperty("saturationRatio", saturationRatio); + return foodDesc; + } + + @Override + public String getDataName() { + return "foods"; + } + + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + for (Item item : Registries.ITEMS) { + if (item instanceof FoodItem) { + resultsArray.add(generateFoodDescriptor((FoodItem) item)); + } + } + return resultsArray; + } +} diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java similarity index 100% rename from src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java rename to 1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java new file mode 100644 index 00000000..3aa51c41 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java @@ -0,0 +1,30 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.NoteBlockAccessor; +import dev.u9g.minecraftdatagenerator.mixin.SoundAccessor; +import net.minecraft.client.sound.SoundEvent; + +import java.util.Objects; + +public class InstrumentsDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "instruments"; + } + + @Override + public JsonElement generateDataJson() { + JsonArray array = new JsonArray(); + int i = 0; + for (SoundEvent sound : Objects.requireNonNull(NoteBlockAccessor.TUNES())) { + JsonObject object = new JsonObject(); + object.addProperty("id", i++); + object.addProperty("name", ((SoundAccessor) sound).id().getPath().split("\\.")[2]); + array.add(object); + } + return array; + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java new file mode 100644 index 00000000..8f8ac6f5 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java @@ -0,0 +1,88 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class ItemsDataGenerator implements IDataGenerator { + + private static List calculateItemsToRepairWith(Item sourceItem) { + List items = new ArrayList<>(); + for (Item otherItem : Registries.ITEMS) { + if (sourceItem.canRepair(DGU.stackFor(sourceItem), DGU.stackFor(otherItem))) { + items.add(otherItem); + } + } + return items; + } + + private static List getApplicableEnchantmentTargets(Item sourceItem) { + List targets = new ArrayList<>(); + for (EnchantmentTarget target : EnchantmentTarget.values()) { + if (!target.isCompatible(sourceItem)) continue; + targets.add(target); + } + return targets; + } + + public static JsonObject generateItem(Item item) { + JsonObject itemDesc = new JsonObject(); + Identifier registryKey = Registries.ITEMS.getIdentifier(item); + + itemDesc.addProperty("id", Registries.ITEMS.getRawId(item)); + itemDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + itemDesc.addProperty("displayName", item.getDisplayName(DGU.stackFor(item))); + itemDesc.addProperty("stackSize", item.getMaxCount()); + + List enchantmentTargets = getApplicableEnchantmentTargets(item); + + JsonArray enchantCategoriesArray = new JsonArray(); + for (EnchantmentTarget target : enchantmentTargets) { + enchantCategoriesArray.add(new JsonPrimitive(EnchantmentsDataGenerator.getEnchantmentTargetName(target))); + } + if (enchantCategoriesArray.size() > 0) { + itemDesc.add("enchantCategories", enchantCategoriesArray); + } + + if (item.isDamageable()) { + List repairWithItems = calculateItemsToRepairWith(item); + + JsonArray fixedWithArray = new JsonArray(); + for (Item repairWithItem : repairWithItems) { + Identifier repairWithName = Registries.ITEMS.getIdentifier(repairWithItem); + fixedWithArray.add(new JsonPrimitive(Objects.requireNonNull(repairWithName).getPath())); + } + if (fixedWithArray.size() > 0) { + itemDesc.add("repairWith", fixedWithArray); + } + + int maxDurability = item.getMaxDamage(); + itemDesc.addProperty("maxDurability", maxDurability); + } + return itemDesc; + } + + @Override + public String getDataName() { + return "items"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + for (Item item : Registries.ITEMS) { + resultArray.add(generateItem(item)); + } + return resultArray; + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java new file mode 100644 index 00000000..88e8d2fb --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java @@ -0,0 +1,29 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.LanguageAccessor; +import dev.u9g.minecraftdatagenerator.util.Registries; + +import java.util.Map; + +public class LanguageDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "language"; + } + + @Override + public JsonElement generateDataJson() { + try { + JsonObject obj = new JsonObject(); + Map translations = ((LanguageAccessor) Registries.LANGUAGE).translations(); + for (Map.Entry entry : translations.entrySet()) { + obj.addProperty(entry.getKey(), entry.getValue()); + } + return obj; + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to generate language file"); + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java new file mode 100644 index 00000000..2b1a5085 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java @@ -0,0 +1,31 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.minecraft.client.particle.ParticleType; + +public class ParticlesDataGenerator implements IDataGenerator { + + public static JsonObject generateParticleType(int id, ParticleType particleType) { + JsonObject effectDesc = new JsonObject(); + + effectDesc.addProperty("id", id); + effectDesc.addProperty("name", particleType.getName()); + return effectDesc; + } + + @Override + public String getDataName() { + return "particles"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + int i = 0; + for (ParticleType particleType : ParticleType.values()) { + resultsArray.add(generateParticleType(i++, particleType)); + } + return resultsArray; + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java new file mode 100644 index 00000000..32498dfa --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java @@ -0,0 +1,119 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; + +public class RecipeDataGenerator implements IDataGenerator { + + @Override + public String getDataName() { + return "recipes"; + } + + @Override + public JsonElement generateDataJson() { +// JsonObject finalObj = new JsonObject(); +// Multimap recipes = ArrayListMultimap.create(); +// for (Recipe recipe : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) { +// if (recipe instanceof ShapedRecipe sr) { +// var ingredients = sr.getIngredients(); +// List ingr = new ArrayList<>(); +// for (int i = 0; i < 9; i++) { +// if (i >= ingredients.size()) { +// ingr.add(-1); +// continue; +// } +// var stacks = ingredients.get(i); +//// var matching = stacks.getMatchingStacks(); +//// if (matching.length == 0) { +//// ingr.add(-1); +//// } else { +//// ingr.add(getRawIdFor(matching[0].getItem())); +//// } +// } +// Lists.reverse(ingr); +// +// JsonArray inShape = new JsonArray(); +// +// var iter = ingr.iterator(); +// for (int y = 0; y < 3; y++) { +// var jsonRow = new JsonArray(); +// int one = iter.next(); +// int two = iter.next(); +// int three = iter.next(); +// if (y > 0 && one == -1 && two == -1 && three == -1) continue; +// jsonRow.add(one); +// jsonRow.add(two); +// jsonRow.add(three); +// inShape.add(jsonRow); +// } +// +// JsonObject finalRecipe = new JsonObject(); +// finalRecipe.add("inShape", inShape); +// +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +// resultObject.addProperty("count", sr.getOutput().getCount()); +// finalRecipe.add("result", resultObject); +// +// String id = ((Integer) getRawIdFor(sr.getOutput().getItem())).toString(); +// +// if (!finalObj.has(id)) { +// finalObj.add(id, new JsonArray()); +// } +// finalObj.get(id).getAsJsonArray().add(finalRecipe); +//// var input = new JsonArray(); +//// var ingredients = sr.getIngredients().stream().toList(); +//// for (int y = 0; y < sr.getHeight(); y++) { +//// var arr = new JsonArray(); +//// for (int x = 0; x < sr.getWidth(); x++) { +//// if ((y*3)+x >= ingredients.size()) { +//// arr.add(JsonNull.INSTANCE); +//// continue; +//// } +//// var ingredient = ingredients.get((y*3)+x).getMatchingStacks(); // FIXME: fix when there are more than one matching stack +//// if (ingredient.length == 0) { +//// arr.add(JsonNull.INSTANCE); +//// } else { +//// arr.add(getRawIdFor(ingredient[0].getItem())); +//// } +//// } +//// input.add(arr); +//// } +//// var rootRecipeObject = new JsonObject(); +//// rootRecipeObject.add("inShape", input); +//// var resultObject = new JsonObject(); +//// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +//// resultObject.addProperty("count", sr.getOutput().getCount()); +//// rootRecipeObject.add("result", resultObject); +//// recipes.put(getRawIdFor(sr.getOutput().getItem()), rootRecipeObject); +// } else if (recipe instanceof ShapelessRecipe sl) { + +// var ingredients = new JsonArray(); +// for (Ingredient ingredient : sl.getIngredients()) { +// if (ingredient.isEmpty()) continue; +//// ingredients.add(getRawIdFor(ingredient.getMatchingStacks()[0].getItem())); +// } +// var rootRecipeObject = new JsonObject(); +// rootRecipeObject.add("ingredients", ingredients); +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sl.getOutput().getItem())); +// resultObject.addProperty("count", sl.getOutput().getCount()); +// rootRecipeObject.add("result", resultObject); +// recipes.put(getRawIdFor(sl.getOutput().getItem()), rootRecipeObject); +// } +// } +// recipes.forEach((a, b) -> { +// if (!finalObj.has(a.toString())) { +// finalObj.add(a.toString(), new JsonArray()); +// } +// finalObj.get(a.toString()).getAsJsonArray().add(b); +// }); +// return finalObj; + return JsonNull.INSTANCE; + } +// +// private static int getRawIdFor (Item item) { +// return Registry.ITEM.getRawId(item); +// } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java new file mode 100644 index 00000000..a4f970c9 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java @@ -0,0 +1,165 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.BlockColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.FoliageColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.GrassColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.ServerSideRedstoneWireBlock; +import dev.u9g.minecraftdatagenerator.mixin.BiomeAccessor; +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.block.RedstoneWireBlock; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.biome.Biome; + +import java.util.*; + +public class TintsDataGenerator implements IDataGenerator { + + private static final BlockColors blockColors = BlockColors.create(); + + public static BiomeTintColors generateBiomeTintColors() { + BiomeTintColors colors = new BiomeTintColors(); + + for (Biome biome : Registries.BIOMES) { + EmptyBlockView bv = new EmptyBlockView() { + @Override + public Biome getBiome(BlockPos pos) { + return biome; + } + }; + int biomeGrassColor = GrassColors.getGrassColor(bv.getBiome(BlockPos.ORIGIN)); + int biomeFoliageColor = FoliageColors.getFoliageColor(bv.getBiome(BlockPos.ORIGIN)); + int biomeWaterColor = ((BiomeAccessor) biome).waterColor(); + + colors.grassColoursMap.computeIfAbsent(biomeGrassColor, k -> new ArrayList<>()).add(biome); + colors.foliageColoursMap.computeIfAbsent(biomeFoliageColor, k -> new ArrayList<>()).add(biome); + colors.waterColourMap.computeIfAbsent(biomeWaterColor, k -> new ArrayList<>()).add(biome); + } + return colors; + } + + public static Map generateRedstoneTintColors() { + Map resultColors = new HashMap<>(); + + for (int redstoneLevel : RedstoneWireBlock.POWER.getValues()) { + int color = ServerSideRedstoneWireBlock.getWireColor(redstoneLevel); + resultColors.put(redstoneLevel, color); + } + return resultColors; + } + + private static int getBlockColor(Block block) { + return blockColors.method_13410(block.getDefaultState()); + } + + public static Map generateConstantTintColors() { + Map resultColors = new HashMap<>(); + BlockColors blockColors = BlockColors.create(); + // FIXME: ? + // resultColors.put(Blocks.BIRCH_LEAVES, FoliageColors.getBirchColor()); + // resultColors.put(Blocks.SPRUCE_LEAVES, FoliageColors.getSpruceColor()); + + resultColors.put(Blocks.LILY_PAD, getBlockColor(Blocks.LILY_PAD)); + // FIXME: ? + // resultColors.put(Blocks.ATTACHED_MELON_STEM, getBlockColor(Blocks.ATTACHED_MELON_STEM, blockColors)); + // resultColors.put(Blocks.ATTACHED_PUMPKIN_STEM, getBlockColor(Blocks.ATTACHED_PUMPKIN_STEM, blockColors)); + + //not really constant, depend on the block age, but kinda have to be handled since textures are literally white without them + resultColors.put(Blocks.MELON_STEM, getBlockColor(Blocks.MELON_STEM)); + resultColors.put(Blocks.PUMPKIN_STEM, getBlockColor(Blocks.PUMPKIN_STEM)); + + return resultColors; + } + + private static JsonObject encodeBiomeColorMap(Map> colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry> entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + for (Biome biome : entry.getValue()) { + Identifier registryKey = Registries.BIOMES.getIdentifier(biome); + keysArray.add(new JsonPrimitive(Objects.requireNonNull(registryKey).getPath())); + } + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getKey()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeRedstoneColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + keysArray.add(new JsonPrimitive(entry.getKey())); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeBlocksColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + JsonArray keysArray = new JsonArray(); + Identifier registryKey = Registries.BLOCKS.getIdentifier(entry.getKey()); + keysArray.add(new JsonPrimitive(Objects.requireNonNull(registryKey).getPath())); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + @Override + public String getDataName() { + return "tints"; + } + + @Override + public JsonObject generateDataJson() { + BiomeTintColors biomeTintColors = generateBiomeTintColors(); + Map redstoneColors = generateRedstoneTintColors(); + Map constantTintColors = generateConstantTintColors(); + + JsonObject resultObject = new JsonObject(); + + resultObject.add("grass", encodeBiomeColorMap(biomeTintColors.grassColoursMap)); + resultObject.add("foliage", encodeBiomeColorMap(biomeTintColors.foliageColoursMap)); + resultObject.add("water", encodeBiomeColorMap(biomeTintColors.waterColourMap)); + + resultObject.add("redstone", encodeRedstoneColorMap(redstoneColors)); + resultObject.add("constant", encodeBlocksColorMap(constantTintColors)); + + return resultObject; + } + + public static class BiomeTintColors { + final Map> grassColoursMap = new HashMap<>(); + final Map> foliageColoursMap = new HashMap<>(); + final Map> waterColourMap = new HashMap<>(); + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BiomeAccessor.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BiomeAccessor.java new file mode 100644 index 00000000..1feed6b7 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BiomeAccessor.java @@ -0,0 +1,14 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.world.biome.Biome; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Biome.class) +public interface BiomeAccessor { + @Accessor("waterColor") + int waterColor(); + + @Accessor("name") + String name(); +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BlockAccessor.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BlockAccessor.java new file mode 100644 index 00000000..1c25c003 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BlockAccessor.java @@ -0,0 +1,11 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Block; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Block.class) +public interface BlockAccessor { + @Accessor("blastResistance") + float getBlastResistance(); +} diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java similarity index 97% rename from src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java rename to 1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java index ac88bbd8..0d297e7a 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java @@ -1,15 +1,15 @@ -package dev.u9g.minecraftdatagenerator.mixin; - -import net.minecraft.server.dedicated.EulaReader; -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; - -@Mixin(EulaReader.class) -public class EULAMixin { - @Inject(method = "isEulaAgreedTo()Z", at = @At("TAIL"), cancellable = true) - public void init(CallbackInfoReturnable cir) { - cir.setReturnValue(true); - } -} +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.server.dedicated.EulaReader; +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; + +@Mixin(EulaReader.class) +public class EULAMixin { + @Inject(method = "isEulaAgreedTo()Z", at = @At("TAIL"), cancellable = true) + public void init(CallbackInfoReturnable cir) { + cir.setReturnValue(true); + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockAccessor.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockAccessor.java new file mode 100644 index 00000000..5bec8213 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockAccessor.java @@ -0,0 +1,19 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.EndPortalFrameBlock; +import net.minecraft.util.math.Box; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(EndPortalFrameBlock.class) +public interface EndPortalFrameBlockAccessor { + @Accessor("PORTAL_FRAME") + static Box portalFrame() { + throw new IllegalStateException(); + } + + @Accessor("PORTAL_EYE") + static Box eye() { + throw new IllegalStateException(); + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockOverwrite.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockOverwrite.java new file mode 100644 index 00000000..4da63091 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockOverwrite.java @@ -0,0 +1,26 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.BlockState; +import net.minecraft.block.EndPortalFrameBlock; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +import java.util.List; + +@Mixin(EndPortalFrameBlock.class) +public class EndPortalFrameBlockOverwrite { + /** + * @author a + * @reason a + */ + @Overwrite() + public void appendCollisionBoxes(BlockState state, World world, BlockPos pos, Box entityBox, List boxes, @Nullable Entity entity) { + boxes.add(EndPortalFrameBlockAccessor.portalFrame()); + boxes.add(EndPortalFrameBlockAccessor.eye()); + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java new file mode 100644 index 00000000..60386ba8 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java @@ -0,0 +1,36 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(EntityType.class) +public interface EntityTypeAccessor { + @Accessor("NAME_CLASS_MAP") + static Map> NAME_CLASS_MAP() { + throw new Error(); + } + + @Accessor("CLASS_NAME_MAP") + static Map, String> CLASS_NAME_MAP() { + throw new Error(); + } + + @Accessor("ID_CLASS_MAP") + static Map> ID_CLASS_MAP() { + throw new Error(); + } + + @Accessor("CLASS_ID_MAP") + static Map, Integer> CLASS_ID_MAP() { + throw new Error(); + } + + @Accessor("NAME_ID_MAP") + static Map NAME_ID_MAP() { + throw new Error(); + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ItemEntityOverwrite.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ItemEntityOverwrite.java new file mode 100644 index 00000000..4b29afb3 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ItemEntityOverwrite.java @@ -0,0 +1,24 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +@Mixin(ItemEntity.class) +public abstract class ItemEntityOverwrite extends Entity { + + public ItemEntityOverwrite(World world) { + super(world); + } + + /** + * @author a + * @reason a + */ + @Overwrite + public String getTranslationKey() { + return super.getTranslationKey(); + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/LanguageAccessor.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/LanguageAccessor.java new file mode 100644 index 00000000..7bfd95fc --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/LanguageAccessor.java @@ -0,0 +1,13 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.util.Language; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(Language.class) +public interface LanguageAccessor { + @Accessor("translations") + Map translations(); +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java new file mode 100644 index 00000000..53b8cdb5 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Block; +import net.minecraft.item.ToolItem; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Set; + +@Mixin(ToolItem.class) +public interface MiningToolItemAccessor { + + @Accessor + Set getEffectiveBlocks(); +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/NoteBlockAccessor.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/NoteBlockAccessor.java new file mode 100644 index 00000000..4bc3bcb9 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/NoteBlockAccessor.java @@ -0,0 +1,16 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.NoteBlock; +import net.minecraft.client.sound.SoundEvent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + +@Mixin(NoteBlock.class) +public interface NoteBlockAccessor { + @Accessor("TUNES") + static List TUNES() { + return null; + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java new file mode 100644 index 00000000..32871cc3 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.generators.DataGenerators; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; +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.nio.file.Path; +import java.util.logging.Level; + +@Mixin(MinecraftDedicatedServer.class) +public class ReadyMixin { + @Inject(method = "setupServer()Z", at = @At("TAIL")) + private void init(CallbackInfoReturnable cir) { + Main.LOGGER.log(Level.INFO, "Starting data generation!"); + String versionName = DGU.getCurrentlyRunningServer().getVersion(); + Path serverRootDirectory = DGU.getCurrentlyRunningServer().getRunDirectory().toPath().toAbsolutePath(); + Path dataDumpDirectory = serverRootDirectory.resolve("minecraft-data").resolve(versionName); + DataGenerators.runDataGenerators(dataDumpDirectory); + Main.LOGGER.log(Level.INFO, "Done data generation!"); + System.exit(0); + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/SoundAccessor.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/SoundAccessor.java new file mode 100644 index 00000000..fe505910 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/SoundAccessor.java @@ -0,0 +1,12 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.client.sound.SoundEvent; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(SoundEvent.class) +public interface SoundAccessor { + @Accessor("id") + Identifier id(); +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java new file mode 100644 index 00000000..33d4cfcd --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java @@ -0,0 +1,11 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.entity.effect.StatusEffect; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(StatusEffect.class) +public interface StatusEffectAccessor { + @Accessor("negative") + boolean negative(); +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java new file mode 100644 index 00000000..bf7137e6 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java @@ -0,0 +1,90 @@ +package dev.u9g.minecraftdatagenerator.util; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapter; +import com.google.gson.internal.bind.TypeAdapters; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.World; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; + +public class DGU { + + public static Gson gson = new GsonBuilder().registerTypeAdapterFactory(TypeAdapters.newFactory(double.class, Double.class, new TypeAdapter() { + @Override + public Number read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + return in.nextDouble(); + } + + @Override + public void write(JsonWriter out, Number value) throws IOException { + out.value(value); + } + })).create(); + + @Environment(EnvType.CLIENT) + private static MinecraftServer getCurrentlyRunningServerClient() { + return MinecraftClient.getInstance().getServer(); + } + + @SuppressWarnings("deprecation") + private static MinecraftServer getCurrentlyRunningServerDedicated() { + return (MinecraftServer) FabricLoader.getInstance().getGameInstance(); + } + + public static MinecraftServer getCurrentlyRunningServer() { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return getCurrentlyRunningServerClient(); + } else if (environmentType == EnvType.SERVER) { + return getCurrentlyRunningServerDedicated(); + } + throw new UnsupportedOperationException(); + } + + @Environment(EnvType.CLIENT) + private static String translateTextClient(String translationKey) { + return I18n.translate(translationKey); + } + + private static String translateTextFallback(String translationKey) { + try { + return Registries.LANGUAGE.translate(translationKey); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to translate: '" + translationKey + "'"); + } + + public static String translateText(String translationKey) { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return translateTextClient(translationKey); + } + return translateTextFallback(translationKey); + } + + @NotNull + public static World getWorld() { + return getCurrentlyRunningServer().getWorld(); + } + + public static ItemStack stackFor(Item ic) { + return new ItemStack(ic); + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java new file mode 100644 index 00000000..41fa9be3 --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java @@ -0,0 +1,49 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.BlockView; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.level.LevelGeneratorType; +import org.jetbrains.annotations.Nullable; + +public class EmptyBlockView implements BlockView { + public static final EmptyBlockView INSTANCE = new EmptyBlockView(); + + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + return null; + } + + @Override + public int getLight(BlockPos pos, int minBlockLight) { + return 0; + } + + public BlockState getBlockState(BlockPos pos) { + return Blocks.AIR.getDefaultState(); + } + + @Override + public boolean isAir(BlockPos pos) { + return false; + } + + @Override + public Biome getBiome(BlockPos pos) { + return null; + } + + @Override + public int getStrongRedstonePower(BlockPos pos, Direction direction) { + return 0; + } + + @Override + public LevelGeneratorType getGeneratorType() { + return null; + } +} diff --git a/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/util/Registries.java b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/util/Registries.java new file mode 100644 index 00000000..7f5b00ae --- /dev/null +++ b/1.10.2/src/main/java/dev/u9g/minecraftdatagenerator/util/Registries.java @@ -0,0 +1,34 @@ +package dev.u9g.minecraftdatagenerator.util; + +import dev.u9g.minecraftdatagenerator.mixin.EntityTypeAccessor; +import net.minecraft.block.Block; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.entity.Entity; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; +import net.minecraft.util.Language; +import net.minecraft.util.registry.SimpleRegistry; +import net.minecraft.world.biome.Biome; + +import java.util.Map; + +public class Registries { + public static final SimpleRegistry BIOMES = Biome.REGISTRY; + public static final SimpleRegistry BLOCKS = Block.REGISTRY; + public static final SimpleRegistry ITEMS = Item.REGISTRY; + public static final SimpleRegistry STATUS_EFFECTS = StatusEffect.REGISTRY; + public static final SimpleRegistry ENCHANTMENTS = Enchantment.REGISTRY; + public static final SimpleRegistry> ENTITY_TYPES = new SimpleRegistry<>(); + public static final Language LANGUAGE = new Language(); + + static { + for (Map.Entry> entry : EntityTypeAccessor.ID_CLASS_MAP().entrySet()) { + String name = EntityTypeAccessor.CLASS_NAME_MAP().get(entry.getValue()); + if (name.equals("Mob") || name.equals("Monster")) { + continue; + } + ENTITY_TYPES.add(entry.getKey(), new Identifier(name), entry.getValue()); + } + } +} diff --git a/1.10.2/src/main/resources/fabric.mod.json b/1.10.2/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..a73252d3 --- /dev/null +++ b/1.10.2/src/main/resources/fabric.mod.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": 1, + "id": "minecraft-data-generator", + "version": "${version}", + "name": "Minecraft Data Generator", + "description": "", + "authors": [ + "Archengius", + "U9G" + ], + "contact": {}, + "license": "MIT", + "environment": "server", + "entrypoints": { + "main": [ + "dev.u9g.minecraftdatagenerator.Main" + ] + }, + "mixins": [ + "minecraft-data-generator.mixins.json" + ], + "depends": { + "fabricloader": ">=0.13.3", + "minecraft": ">=1.10" + } +} diff --git a/1.10.2/src/main/resources/minecraft-data-generator.mixins.json b/1.10.2/src/main/resources/minecraft-data-generator.mixins.json new file mode 100644 index 00000000..4dd13158 --- /dev/null +++ b/1.10.2/src/main/resources/minecraft-data-generator.mixins.json @@ -0,0 +1,28 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "dev.u9g.minecraftdatagenerator.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "BiomeAccessor", + "BlockAccessor", + "EndPortalFrameBlockAccessor", + "EndPortalFrameBlockOverwrite", + "EntityTypeAccessor", + "ItemEntityOverwrite", + "LanguageAccessor", + "MiningToolItemAccessor", + "NoteBlockAccessor", + "ReadyMixin", + "SoundAccessor", + "StatusEffectAccessor" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "server": [ + "EULAMixin" + ] +} diff --git a/1.11.2/build.gradle b/1.11.2/build.gradle new file mode 100644 index 00000000..057916c6 --- /dev/null +++ b/1.11.2/build.gradle @@ -0,0 +1,63 @@ +plugins { + id 'fabric-loom' +} + +repositories { + maven { + name = "legacy-fabric" + url = "https://maven.legacyfabric.net" + } +} + +loom { + setIntermediaryUrl('https://maven.legacyfabric.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar'); + customMinecraftManifest.set("https://meta.legacyfabric.net/v2/manifest/${minecraft_version}") +} + +dependencies { + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API provides hooks for events, item registration, and more. As most mods will need this, it's included by default. + // If you know for a fact you don't, it's not required and can be safely removed. +// modImplementation ("net.legacyfabric.legacy-fabric-api:legacy-fabric-api:${fabric_version}") { +// exclude module: "legacy-fabric-entity-events-v1" +// } + + if (System.getProperty("os.name").toLowerCase().contains("mac")) { + implementation 'org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209' + implementation 'org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209' + implementation 'org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209' + } +} + +if (System.getProperty("os.name").toLowerCase().contains("mac")) { + configurations.configureEach { + resolutionStrategy { + dependencySubstitution { + substitute module('org.lwjgl.lwjgl:lwjgl_util:2.9.2-nightly-201408222') with module('org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209') + substitute module('org.lwjgl.lwjgl:lwjgl:2.9.2-nightly-201408222') with module('org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209') + } + force 'org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209' + } + } +} + +processResources { + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} diff --git a/1.11.2/gradle.properties b/1.11.2/gradle.properties new file mode 100644 index 00000000..ff8f56c6 --- /dev/null +++ b/1.11.2/gradle.properties @@ -0,0 +1,7 @@ +# Fabric Properties +# More versions available at: https://grayray75.github.io/LegacyFabric-Versions/ +minecraft_version=1.11.2 +yarn_mappings=1.11.2+build.202206020145 +loader_version=0.14.3 +# Dependencies +# More versions available at: https://grayray75.github.io/LegacyFabric-Versions/ diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeColors.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeColors.java new file mode 100644 index 00000000..9bee1854 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeColors.java @@ -0,0 +1,55 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +import java.util.Iterator; + +public class BiomeColors { + private static final ColorProvider GRASS_COLOR = (biome, pos) -> { + double d = (double) MathHelper.clamp(biome.getTemperature(pos), 0.0F, 1.0F); + double e = (double) MathHelper.clamp(biome.getRainfall(), 0.0F, 1.0F); + return GrassColors.getColor(d, e); + }; + private static final ColorProvider FOLIAGE_COLOR = (biome, pos) -> { + double d = (double) MathHelper.clamp(biome.getTemperature(pos), 0.0F, 1.0F); + double e = (double) MathHelper.clamp(biome.getRainfall(), 0.0F, 1.0F); + return FoliageColors.getColor(d, e); + }; + private static final ColorProvider WATER_COLOR = (biome, pos) -> biome.getWaterColor(); + + private static int getColor(EmptyBlockView view, BlockPos pos, ColorProvider provider) { + int i = 0; + int j = 0; + int k = 0; + + int l; + for (Iterator var6 = BlockPos.mutableIterate(pos.add(-1, 0, -1), pos.add(1, 0, 1)).iterator(); var6.hasNext(); k += l & 255) { + BlockPos.Mutable mutable = (BlockPos.Mutable) var6.next(); + l = provider.getColorAtPos(view.getBiome(mutable), mutable); + i += (l & 16711680) >> 16; + j += (l & '\uff00') >> 8; + } + + return (i / 9 & 255) << 16 | (j / 9 & 255) << 8 | k / 9 & 255; + } + + public static int getGrassColor(EmptyBlockView view, BlockPos pos) { + return getColor(view, pos, GRASS_COLOR); + } + + public static int getFoliageColor(EmptyBlockView view, BlockPos pos) { + return getColor(view, pos, FOLIAGE_COLOR); + } + + public static int getWaterColor(EmptyBlockView view, BlockPos pos) { + return getColor(view, pos, WATER_COLOR); + } + + interface ColorProvider { + int getColorAtPos(Biome biome, BlockPos pos); + } +} + diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColorable.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColorable.java new file mode 100644 index 00000000..3e3e044c --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColorable.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import org.jetbrains.annotations.Nullable; + +public interface BlockColorable { + int method_12155(BlockState blockState, @Nullable EmptyBlockView blockView, @Nullable BlockPos blockPos, int i); +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColors.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColors.java new file mode 100644 index 00000000..773f95fe --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColors.java @@ -0,0 +1,96 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import net.minecraft.block.*; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.FlowerPotBlockEntity; +import net.minecraft.item.Item; +import net.minecraft.util.collection.IdList; +import net.minecraft.util.math.BlockPos; +import org.jetbrains.annotations.Nullable; + +public class BlockColors { + private final IdList BlockColor2Id = new IdList<>(32); + + public BlockColors() { + } + + public static BlockColors create() { + final BlockColors blockColors = new BlockColors(); + blockColors.method_12158((blockState, blockView, blockPos, i) -> { + DoublePlantBlock.DoublePlantType doublePlantType = (DoublePlantBlock.DoublePlantType) blockState.get(DoublePlantBlock.VARIANT); + return blockView != null && blockPos != null && (doublePlantType == DoublePlantBlock.DoublePlantType.GRASS || doublePlantType == DoublePlantBlock.DoublePlantType.FERN) ? BiomeColors.getGrassColor(blockView, blockState.get(DoublePlantBlock.HALF) == DoublePlantBlock.HalfType.UPPER ? blockPos.down() : blockPos) : -1; + }, Blocks.DOUBLE_PLANT); + blockColors.method_12158((blockState, blockView, blockPos, i) -> { + if (blockView != null && blockPos != null) { + BlockEntity blockEntity = blockView.getBlockEntity(blockPos); + if (blockEntity instanceof FlowerPotBlockEntity) { + Item item = ((FlowerPotBlockEntity) blockEntity).getItem(); + BlockState blockState2 = Block.getBlockFromItem(item).getDefaultState(); + return blockColors.method_12157(blockState2, blockView, blockPos, i); + } else { + return -1; + } + } else { + return -1; + } + }, Blocks.FLOWER_POT); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getGrassColor(blockView, blockPos) : GrassColors.getColor(0.5D, 1.0D), Blocks.GRASS); + blockColors.method_12158((blockState, blockView, blockPos, i) -> { + PlanksBlock.WoodType woodType = (PlanksBlock.WoodType) blockState.get(Leaves1Block.VARIANT); + if (woodType == PlanksBlock.WoodType.SPRUCE) { + return net.minecraft.client.color.world.FoliageColors.getSpruceColor(); + } else if (woodType == PlanksBlock.WoodType.BIRCH) { + return net.minecraft.client.color.world.FoliageColors.getBirchColor(); + } else { + return blockView != null && blockPos != null ? BiomeColors.getFoliageColor(blockView, blockPos) : FoliageColors.getDefaultColor(); + } + }, Blocks.LEAVES); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getFoliageColor(blockView, blockPos) : FoliageColors.getDefaultColor(), Blocks.LEAVES2); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getWaterColor(blockView, blockPos) : -1, Blocks.WATER, Blocks.FLOWING_WATER); + blockColors.method_12158((blockState, blockView, blockPos, i) -> RedstoneWireBlock.method_8877((Integer) blockState.get(RedstoneWireBlock.POWER)), Blocks.REDSTONE_WIRE); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getGrassColor(blockView, blockPos) : -1, Blocks.SUGARCANE); + blockColors.method_12158((blockState, blockView, blockPos, i) -> { + int j = (Integer) blockState.get(AttachedStemBlock.AGE); + int k = j * 32; + int l = 255 - j * 8; + int m = j * 4; + return k << 16 | l << 8 | m; + }, Blocks.MELON_STEM, Blocks.PUMPKIN_STEM); + blockColors.method_12158((blockState, blockView, blockPos, i) -> { + if (blockView != null && blockPos != null) { + return BiomeColors.getGrassColor(blockView, blockPos); + } else { + return blockState.get(TallPlantBlock.TYPE) == TallPlantBlock.GrassType.DEAD_BUSH ? 16777215 : GrassColors.getColor(0.5D, 1.0D); + } + }, Blocks.TALLGRASS); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getFoliageColor(blockView, blockPos) : FoliageColors.getDefaultColor(), Blocks.VINE); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? 2129968 : 7455580, Blocks.LILY_PAD); + return blockColors; + } + + public int method_13410(BlockState blockState) { + BlockColorable blockColorable = (BlockColorable) this.BlockColor2Id.fromId(Block.getIdByBlock(blockState.getBlock())); + if (blockColorable != null) { + return blockColorable.method_12155(blockState, null, (BlockPos) null, 0); + } else { + MaterialColor materialColor = blockState.getMaterialColor(); + return materialColor != null ? materialColor.color : -1; + } + } + + public int method_12157(BlockState blockState, @Nullable EmptyBlockView blockView, @Nullable BlockPos blockPos, int i) { + BlockColorable blockColorable = (BlockColorable) this.BlockColor2Id.fromId(Block.getIdByBlock(blockState.getBlock())); + return blockColorable == null ? -1 : blockColorable.method_12155(blockState, blockView, blockPos, i); + } + + public void method_12158(BlockColorable blockColorable, Block... blocks) { + int var4 = blocks.length; + + for (int var5 = 0; var5 < var4; ++var5) { + Block block = blocks[var5]; + this.BlockColor2Id.set(blockColorable, Block.getIdByBlock(block)); + } + + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java new file mode 100644 index 00000000..d728f58f --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java @@ -0,0 +1,28 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +public class FoliageColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] pixels) { + colorMap = pixels; + } + + public static int getColor(double temperature, double humidity) { + humidity *= temperature; + int i = (int) ((1.0D - temperature) * 255.0D); + int j = (int) ((1.0D - humidity) * 255.0D); + return colorMap[j << 8 | i]; + } + + public static int getSpruceColor() { + return 6396257; + } + + public static int getBirchColor() { + return 8431445; + } + + public static int getDefaultColor() { + return 4764952; + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java new file mode 100644 index 00000000..2e828e1c --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java @@ -0,0 +1,19 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +public class GrassColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] map) { + colorMap = map; + } + + public static int getColor(double temperature, double humidity) { + humidity *= temperature; + int i = (int) ((1.0D - temperature) * 255.0D); + int j = (int) ((1.0D - humidity) * 255.0D); + int k = j << 8 | i; + return k > colorMap.length ? -65281 : colorMap[k]; + } +} + + diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java new file mode 100644 index 00000000..1572a082 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java @@ -0,0 +1,28 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; + +public class ServerSideRedstoneWireBlock { + public static int getWireColor(int powerLevel) { + float f = (float) powerLevel / 15.0F; + float g = f * 0.6F + 0.4F; + if (powerLevel == 0) { + g = 0.3F; + } + + float h = f * f * 0.7F - 0.5F; + float j = f * f * 0.6F - 0.7F; + if (h < 0.0F) { + h = 0.0F; + } + + if (j < 0.0F) { + j = 0.0F; + } + + int k = MathHelper.clamp((int) (g * 255.0F), 0, 255); + int l = MathHelper.clamp((int) (h * 255.0F), 0, 255); + int m = MathHelper.clamp((int) (j * 255.0F), 0, 255); + return -16777216 | k << 16 | l << 8 | m; + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/SkyColor.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/SkyColor.java new file mode 100644 index 00000000..033a11ca --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/SkyColor.java @@ -0,0 +1,74 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +public class SkyColor { + public static int getSkyColor(Biome biome) { + return realSkyColor(biome.getTemperature()); + } + + private static int realSkyColor(float temperature) { + temperature /= 3.0F; + temperature = MathHelper.clamp(temperature, -1.0F, 1.0F); + return hsvToRgb(0.62222224F - temperature * 0.05F, 0.5F + temperature * 0.1F, 1.0F); + } + + public static int hsvToRgb(float hue, float saturation, float value) { + int i = (int) (hue * 6.0F) % 6; + float f = hue * 6.0F - (float) i; + float g = value * (1.0F - saturation); + float h = value * (1.0F - f * saturation); + float j = value * (1.0F - (1.0F - f) * saturation); + float k; + float l; + float m; + switch (i) { + case 0: + k = value; + l = j; + m = g; + break; + case 1: + k = h; + l = value; + m = g; + break; + case 2: + k = g; + l = value; + m = j; + break; + case 3: + k = g; + l = h; + m = value; + break; + case 4: + k = j; + l = g; + m = value; + break; + case 5: + k = value; + l = g; + m = h; + break; + default: + throw new RuntimeException("Something went wrong when converting from HSV to RGB. Input was " + hue + ", " + saturation + ", " + value); + } + + int n = clamp((int) (k * 255.0F), 0, 255); + int o = clamp((int) (l * 255.0F), 0, 255); + int p = clamp((int) (m * 255.0F), 0, 255); + return n << 16 | o << 8 | p; + } + + public static int clamp(int value, int min, int max) { + if (value < min) { + return min; + } else { + return Math.min(value, max); + } + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/Main.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/Main.java new file mode 100644 index 00000000..d23c5ba2 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/Main.java @@ -0,0 +1,13 @@ +package dev.u9g.minecraftdatagenerator; + +import net.fabricmc.api.ModInitializer; + +import java.util.logging.Logger; + +public class Main implements ModInitializer { + public static final Logger LOGGER = Logger.getLogger("mc-data-gen-serv"); + + @Override + public void onInitialize() { + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java new file mode 100644 index 00000000..1689c8b5 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java @@ -0,0 +1,32 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.LanguageAccessor; +import dev.u9g.minecraftdatagenerator.util.Registries; + +import java.util.Map; + +public class AttributesDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "attributes"; + } + + @Override + public JsonElement generateDataJson() { + Map translations = ((LanguageAccessor) Registries.LANGUAGE).translations(); + JsonArray arr = new JsonArray(); + for (Map.Entry translation : translations.entrySet()) { + String key = translation.getKey(); + if (!key.startsWith("attribute.name.")) continue; + JsonObject obj = new JsonObject(); + key = key.replace("attribute.name.", ""); + obj.addProperty("name", key.split("\\.")[1]); + obj.addProperty("resource", key); + arr.add(obj); + } + return arr; + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java new file mode 100644 index 00000000..893c26c5 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java @@ -0,0 +1,107 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.SkyColor; +import dev.u9g.minecraftdatagenerator.mixin.BiomeAccessor; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.SimpleRegistry; +import net.minecraft.world.biome.*; + +import java.util.Locale; + +public class BiomesDataGenerator implements IDataGenerator { + + private static String guessBiomeDimensionFromCategory(Biome biome) { + if (biome instanceof NetherBiome) { + return "nether"; + } else if (biome instanceof EndBiome) { + return "end"; + } + return "overworld"; + } + + public static JsonObject generateBiomeInfo(SimpleRegistry registry, Biome biome) { + JsonObject biomeDesc = new JsonObject(); + Identifier registryKey = registry.getIdentifier(biome); + + biomeDesc.addProperty("id", registry.getRawId(biome)); + biomeDesc.addProperty("name", String.join("_", ((BiomeAccessor) biome).name().toLowerCase(Locale.ENGLISH).split(" "))); + biomeDesc.addProperty("category", category(biome)); + biomeDesc.addProperty("temperature", biome.getTemperature()); + biomeDesc.addProperty("precipitation", precipitation(biome)); + biomeDesc.addProperty("depth", biome.getDepth()); + biomeDesc.addProperty("dimension", guessBiomeDimensionFromCategory(biome)); + biomeDesc.addProperty("displayName", ((BiomeAccessor) biome).name()); + biomeDesc.addProperty("color", SkyColor.getSkyColor(biome)); + biomeDesc.addProperty("rainfall", biome.getRainfall()); + + return biomeDesc; + } + + private static String category(Biome biome) { + if (biome instanceof ForestBiome) { + return "forest"; + } else if (biome instanceof OceanBiome) { + return "ocean"; + } else if (biome instanceof PlainsBiome) { + return "plains"; + } else if (biome instanceof DesertBiome) { + return "desert"; + } else if (biome instanceof ExtremeHillsBiome) { + return "extreme_hills"; + } else if (biome instanceof TaigaBiome) { + return "taiga"; + } else if (biome instanceof SwampBiome) { + return "swamp"; + } else if (biome instanceof RiverBiome) { + return "river"; + } else if (biome instanceof NetherBiome) { + return "nether"; + } else if (biome instanceof EndBiome) { + return "the_end"; + } else if (biome instanceof IceBiome) { + return "icy"; + } else if (biome instanceof MushroomBiome) { + return "mushroom"; + } else if (biome instanceof BeachBiome) { + return "beach"; + } else if (biome instanceof JungleBiome) { + return "jungle"; + } else if (biome instanceof SavannaBiome) { + return "savanna"; + } else if (biome instanceof MesaBiome) { + return "mesa"; + } else if (biome instanceof StoneBeachBiome || biome instanceof VoidBiome) { + return "none"; // Should StoneBeachBiome be beach too? this is how it is now in mcdata + } + throw new Error("Unable to find biome category for " + biome.getClass().getName()); + } + + private static String precipitation(Biome biome) { + float rainfall = biome.getRainfall(); + float temperature = biome.getTemperature(); + if (rainfall == 0) { + return "none"; + } else if (temperature < 0.2f) { + return "snow"; + } + return "rain"; + } + + @Override + public String getDataName() { + return "biomes"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray biomesArray = new JsonArray(); + SimpleRegistry biomeRegistry = Biome.REGISTRY; + + for (Biome biome : biomeRegistry) { + biomesArray.add(generateBiomeInfo(biomeRegistry, biome)); + } + return biomesArray; + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java new file mode 100644 index 00000000..fe814585 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java @@ -0,0 +1,123 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.block.*; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; + +import java.util.*; + +public class BlockCollisionShapesDataGenerator implements IDataGenerator { + private static final Box ENTITY_BOX = new Box(0.0D, 0.0D, 0.0D, 1.0D, 2.0D, 1.0D); + + private static String nameOf(Block block) { + return Objects.requireNonNull(Registries.BLOCKS.getIdentifier(block)).getPath(); + } + + private static JsonArray jsonOf(Box box) { + JsonArray arr = new JsonArray(); + if (box == null) return arr; + arr.add(new JsonPrimitive(box.minX)); + arr.add(new JsonPrimitive(box.minY)); + arr.add(new JsonPrimitive(box.minZ)); + arr.add(new JsonPrimitive(box.maxX)); + arr.add(new JsonPrimitive(box.maxY)); + arr.add(new JsonPrimitive(box.maxZ)); + return arr; + } + + @Override + public String getDataName() { + return "blockCollisionShapes"; + } + + @Override + public JsonObject generateDataJson() { + ShapeCache shapeCache = new ShapeCache(); + JsonObject blocksObject = new JsonObject(); + for (Block block : Registries.BLOCKS) { + Object val = shapeCache.addShapesFrom(block); + if (val instanceof JsonArray) { + blocksObject.add(nameOf(block), (JsonElement) val); + } else { + blocksObject.addProperty(nameOf(block), (Integer) val); + } + } + JsonObject resultObject = new JsonObject(); + resultObject.add("blocks", blocksObject); + resultObject.add("shapes", shapeCache.toJSON()); + return resultObject; + } + + public static class ShapeCache { + private final ArrayList shapesCache = new ArrayList<>(); + + public Object addShapesFrom(Block block) { + List indexesOfBoxesInTheShapesCache = new ArrayList<>(); + for (BlockState state : block.getStateManager().getBlockStates().reverse()) { + List boxes = new ArrayList<>(); + try { + state.appendCollisionBoxes(DGU.getWorld(), BlockPos.ORIGIN, ENTITY_BOX, boxes, null, true); + } catch (Exception e) { + e.printStackTrace(); + } + Shapes thisBlockStateShapes = new Shapes(boxes); + int indexOfThisBlockStatesShapes = shapesCache.indexOf(thisBlockStateShapes); + if (indexOfThisBlockStatesShapes != -1) { + indexesOfBoxesInTheShapesCache.add(indexOfThisBlockStatesShapes); + } else { + shapesCache.add(thisBlockStateShapes); + indexesOfBoxesInTheShapesCache.add(shapesCache.size() - 1); + } + } + if (indexesOfBoxesInTheShapesCache.stream().distinct().count() < 2) { + return indexesOfBoxesInTheShapesCache.get(0); + } else { + JsonArray shapeIndexes = new JsonArray(); + indexesOfBoxesInTheShapesCache.forEach(shapeIndex -> shapeIndexes.add(new JsonPrimitive(shapeIndex))); + return shapeIndexes; + } + } + + public JsonObject toJSON() { + JsonObject shapes = new JsonObject(); + int i = 0; + for (Shapes s : shapesCache) { + shapes.add(String.valueOf(i++), s.toJSON()); + } + return shapes; + } + + private static class Shapes { + final List boxes; + + public Shapes(List boxes) { + this.boxes = boxes; + } + + public JsonArray toJSON() { + JsonArray arr = new JsonArray(); + boxes.forEach(box -> arr.add(jsonOf(box))); + return arr; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Shapes shapes = (Shapes) o; + return Objects.equals(boxes, shapes.boxes); + } + + @Override + public int hashCode() { + return boxes != null ? boxes.hashCode() : 0; + } + } + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java new file mode 100644 index 00000000..17c00e52 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java @@ -0,0 +1,147 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.base.CaseFormat; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.mixin.BlockAccessor; +import dev.u9g.minecraftdatagenerator.mixin.MiningToolItemAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.block.AirBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.TransparentBlock; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ToolItem; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.state.property.EnumProperty; +import net.minecraft.state.property.IntProperty; +import net.minecraft.state.property.Property; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.logging.Logger; + +public class BlocksDataGenerator implements IDataGenerator { + + private static final Logger logger = Main.LOGGER; + + private static List getItemsEffectiveForBlock(Block block) { + List items = new ArrayList<>(); + for (Item item : Registries.ITEMS) { + if (item instanceof ToolItem && ((MiningToolItemAccessor) item).getEffectiveBlocks().contains(block)) { + items.add(item); + } + } + return items; + } + + private static List populateDropsIfPossible(BlockState blockState, Item firstToolItem) { + return new ArrayList<>(); + } + + private static String getPropertyTypeName(Property property) { + //Explicitly handle default minecraft properties + if (property instanceof BooleanProperty) { + return "bool"; + } + if (property instanceof IntProperty) { + return "int"; + } + if (property instanceof EnumProperty) { + return "enum"; + } + + //Use simple class name as fallback, this code will give something like + //example_type for ExampleTypeProperty class name + String rawPropertyName = property.getClass().getSimpleName().replace("Property", ""); + return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, rawPropertyName); + } + + private static > JsonObject generateStateProperty(Property property) { + JsonObject propertyObject = new JsonObject(); + Collection propertyValues = property.getValues(); + + propertyObject.addProperty("name", property.getName()); + propertyObject.addProperty("type", getPropertyTypeName(property)); + propertyObject.addProperty("num_values", propertyValues.size()); + + //Do not add values for vanilla boolean properties, they are known by default + if (!(property instanceof BooleanProperty)) { + JsonArray propertyValuesArray = new JsonArray(); + for (T propertyValue : propertyValues) { + propertyValuesArray.add(new JsonPrimitive(property.name(propertyValue))); + } + propertyObject.add("values", propertyValuesArray); + } + return propertyObject; + } + + public static JsonObject generateBlock(Block block) { + JsonObject blockDesc = new JsonObject(); + + List blockStates = block.getStateManager().getBlockStates(); + BlockState defaultState = block.getDefaultState(); + Identifier registryKey = Registries.BLOCKS.getIdentifier(block); + List effectiveTools = getItemsEffectiveForBlock(block); + + blockDesc.addProperty("id", Registries.BLOCKS.getRawId(block)); + blockDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + blockDesc.addProperty("displayName", block.getTranslatedName()); + + float hardness = block.getDefaultState().getHardness(null, null); + + blockDesc.addProperty("hardness", hardness); + blockDesc.addProperty("resistance", ((BlockAccessor) block).getBlastResistance()); + blockDesc.addProperty("stackSize", Item.fromBlock(block).getMaxCount()); + blockDesc.addProperty("diggable", hardness != -1.0f && !(block instanceof AirBlock)); + JsonObject effTools = new JsonObject(); + effectiveTools.forEach(item -> effTools.addProperty( + String.valueOf(Registries.ITEMS.getRawId(item)), // key + item.getBlockBreakingSpeed(DGU.stackFor(item), defaultState) // value + )); + blockDesc.add("effectiveTools", effTools); + blockDesc.addProperty("transparent", block instanceof TransparentBlock); + blockDesc.addProperty("emitLight", defaultState.getLuminance()); + blockDesc.addProperty("filterLight", block.getDefaultState().getOpacity()); + + JsonArray stateProperties = new JsonArray(); + for (Property property : block.getStateManager().getProperties()) { + stateProperties.add(generateStateProperty(property)); + } + blockDesc.add("states", stateProperties); + blockDesc.add("drops", new JsonArray()); + blockDesc.addProperty("boundingBox", boundingBox(block, defaultState)); + + return blockDesc; + } + + private static String boundingBox(Block block, BlockState state) { + if (block.getCollisionBox(state, EmptyBlockView.INSTANCE, BlockPos.ORIGIN) == null) { + return "empty"; + } + return "block"; + } + + @Override + public String getDataName() { + return "blocks"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultBlocksArray = new JsonArray(); + for (Block block : Registries.BLOCKS) { + resultBlocksArray.add(generateBlock(block)); + } + return resultBlocksArray; + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java new file mode 100644 index 00000000..273d67ca --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java @@ -0,0 +1,80 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonWriter; +import dev.u9g.minecraftdatagenerator.Main; + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class DataGenerators { + + private static final List GENERATORS = new ArrayList<>(); + private static final Logger logger = Main.LOGGER; + + static { + register(new BiomesDataGenerator()); + register(new BlockCollisionShapesDataGenerator()); + register(new BlocksDataGenerator()); + register(new EffectsDataGenerator()); + register(new EnchantmentsDataGenerator()); + register(new EntitiesDataGenerator()); + register(new FoodsDataGenerator()); + register(new ItemsDataGenerator()); + register(new ParticlesDataGenerator()); + register(new TintsDataGenerator()); + register(new LanguageDataGenerator()); + register(new InstrumentsDataGenerator()); + register(new AttributesDataGenerator()); + } + + public static void register(IDataGenerator generator) { + GENERATORS.add(generator); + } + + public static boolean runDataGenerators(Path outputDirectory) { + try { + Files.createDirectories(outputDirectory); + } catch (IOException exception) { + logger.log(Level.INFO, "Failed to create data generator output directory at " + outputDirectory); + exception.printStackTrace(); + return false; + } + + int generatorsFailed = 0; + logger.log(Level.INFO, MessageFormat.format("Running minecraft data generators, output at {0}", outputDirectory)); + + for (IDataGenerator dataGenerator : GENERATORS) { + logger.log(Level.INFO, MessageFormat.format("Running generator {0}", dataGenerator.getDataName())); + try { + String outputFileName = String.format("%s.json", dataGenerator.getDataName()); + JsonElement outputElement = dataGenerator.generateDataJson(); + Path outputFilePath = outputDirectory.resolve(outputFileName); + + try (Writer writer = Files.newBufferedWriter(outputFilePath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + JsonWriter jsonWriter = new JsonWriter(writer); + jsonWriter.setIndent(" "); + Streams.write(outputElement, jsonWriter); + } + logger.log(Level.INFO, MessageFormat.format("Generator: {0} -> {1}", dataGenerator.getDataName(), outputFileName)); + + } catch (Throwable exception) { + logger.log(Level.INFO, MessageFormat.format("Failed to run data generator {0}", dataGenerator.getDataName())); + exception.printStackTrace(); + generatorsFailed++; + } + } + + logger.log(Level.INFO, "Finishing running data generators"); + return generatorsFailed == 0; + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java new file mode 100644 index 00000000..6aba51e0 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java @@ -0,0 +1,50 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.StatusEffectAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.util.Identifier; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.Collectors; + +public class EffectsDataGenerator implements IDataGenerator { + + public static JsonObject generateEffect(StatusEffect statusEffect) { + JsonObject effectDesc = new JsonObject(); + @NotNull Identifier registryKey = Objects.requireNonNull(Registries.STATUS_EFFECTS.getIdentifier(statusEffect)); + + effectDesc.addProperty("id", Registries.STATUS_EFFECTS.getRawId(statusEffect)); + if (statusEffect == StatusEffects.UNLUCK) { + effectDesc.addProperty("name", "BadLuck"); + effectDesc.addProperty("displayName", "Bad Luck"); + } else { + effectDesc.addProperty("name", Arrays.stream(registryKey.getPath().split("_")).map(StringUtils::capitalize).collect(Collectors.joining())); + effectDesc.addProperty("displayName", DGU.translateText(statusEffect.getTranslationKey())); + } + + effectDesc.addProperty("type", !((StatusEffectAccessor) statusEffect).negative() ? "good" : "bad"); + return effectDesc; + } + + @Override + public String getDataName() { + return "effects"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + for (StatusEffect effect : Registries.STATUS_EFFECTS) { + resultsArray.add(generateEffect(effect)); + } + return resultsArray; + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java new file mode 100644 index 00000000..cbfff2db --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java @@ -0,0 +1,106 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.enchantment.*; +import net.minecraft.util.Identifier; + +import java.util.*; + +public class EnchantmentsDataGenerator implements IDataGenerator { + + private static final ImmutableMap ENCHANTMENT_TARGET_NAMES = ImmutableMap.builder() + .put(EnchantmentTarget.ALL_ARMOR, "armor") + .put(EnchantmentTarget.FEET, "armor_feet") + .put(EnchantmentTarget.LEGS, "armor_legs") + .put(EnchantmentTarget.ARMOR_CHEST, "armor_chest") + .put(EnchantmentTarget.HEAD, "armor_head") + .put(EnchantmentTarget.WEAPON, "weapon") + .put(EnchantmentTarget.DIGGER, "digger") + .put(EnchantmentTarget.FISHING_ROD, "fishing_rod") + .put(EnchantmentTarget.BREAKABLE, "breakable") + .put(EnchantmentTarget.BOW, "bow") + .put(EnchantmentTarget.WEARABLE, "wearable") + .put(EnchantmentTarget.ALL, "vanishable") // according to VanishingCurseEnchantment + .build(); + + public static String getEnchantmentTargetName(EnchantmentTarget target) { + return ENCHANTMENT_TARGET_NAMES.getOrDefault(target, target.name().toLowerCase(Locale.ROOT)); + } + + // Equation enchantment costs follow is a * level + b, so we can easily retrieve a and b by passing zero level + private static JsonObject generateEnchantmentMinPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMinimumPower(0); + int a = enchantment.getMinimumPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + private static JsonObject generateEnchantmentMaxPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMaximumPower(0); + int a = enchantment.getMaximumPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + public static JsonObject generateEnchantment(Enchantment enchantment) { + JsonObject enchantmentDesc = new JsonObject(); + Identifier registryKey = Registries.ENCHANTMENTS.getIdentifier(enchantment); + + enchantmentDesc.addProperty("id", Registries.ENCHANTMENTS.getRawId(enchantment)); + enchantmentDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + enchantmentDesc.addProperty("displayName", DGU.translateText(enchantment.getTranslationKey())); + + enchantmentDesc.addProperty("maxLevel", enchantment.getMaximumLevel()); + enchantmentDesc.add("minCost", generateEnchantmentMinPowerCoefficients(enchantment)); + enchantmentDesc.add("maxCost", generateEnchantmentMaxPowerCoefficients(enchantment)); + + enchantmentDesc.addProperty("treasureOnly", enchantment.isTreasure()); + enchantmentDesc.addProperty("curse", enchantment.isCursed()); + + List incompatibleEnchantments = new ArrayList<>(); + for (Enchantment other : Registries.ENCHANTMENTS) { + if (!enchantment.isDifferent(other) && other != enchantment) { + incompatibleEnchantments.add(other); + } + } + + JsonArray excludes = new JsonArray(); + for (Enchantment excludedEnchantment : incompatibleEnchantments) { + Identifier otherKey = Registries.ENCHANTMENTS.getIdentifier(excludedEnchantment); + excludes.add(new JsonPrimitive(Objects.requireNonNull(otherKey).getPath())); + } + enchantmentDesc.add("exclude", excludes); + + enchantmentDesc.addProperty("category", getEnchantmentTargetName(enchantment.target)); + enchantmentDesc.addProperty("weight", enchantment.getRarity().getChance()); + enchantmentDesc.addProperty("tradeable", true); // the first non-tradeable enchant came in 1.16, soul speed + enchantmentDesc.addProperty("discoverable", true); // the first non-enchantable enchant came in 1.16, soul speed + + return enchantmentDesc; + } + + @Override + public String getDataName() { + return "enchantments"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + for (Enchantment enchantment : Registries.ENCHANTMENTS) { + resultsArray.add(generateEnchantment(enchantment)); + } + return resultsArray; + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java new file mode 100644 index 00000000..cb4b58b6 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java @@ -0,0 +1,132 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.entity.*; +import net.minecraft.entity.mob.*; +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.passive.PassiveEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.projectile.FishingBobberEntity; +import net.minecraft.entity.projectile.Projectile; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +public class EntitiesDataGenerator implements IDataGenerator { + + public static JsonObject generateEntity(Class entityClass) { + JsonObject entityDesc = new JsonObject(); + Identifier registryKey = Registries.ENTITY_TYPES.getIdentifier(entityClass); + int entityRawId = Registries.ENTITY_TYPES.getRawId(entityClass); + @Nullable Entity entity = makeEntity(entityClass); + // FIXME: ENTITY ID IS WRONG + int id = entityId(entity); + entityDesc.addProperty("id", id); + entityDesc.addProperty("internalId", id); + entityDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + if (entity != null) entityDesc.addProperty("displayName", DGU.translateText(entity.getTranslationKey())); + entityDesc.addProperty("width", entity == null ? 0 : entity.width); + entityDesc.addProperty("height", entity == null ? 0 : entity.height); + + String entityTypeString = "UNKNOWN"; + entityTypeString = getEntityTypeForClass(entityClass); + entityDesc.addProperty("type", entityTypeString); + entityDesc.addProperty("category", getCategoryFrom(entityClass)); + + return entityDesc; + } + + private static Entity makeEntity(Class type) { + return EntityType.createInstanceFromClass(type, DGU.getWorld()); + } + + private static String getCategoryFrom(@NotNull Class entityClass) { + if (entityClass == PlayerEntity.class) return "other"; // fail early for player entities + String packageName = entityClass.getPackage().getName(); + String category = switch (packageName) { + case "net.minecraft.entity.decoration", "net.minecraft.entity.decoration.painting" -> "Immobile"; + case "net.minecraft.entity.boss", "net.minecraft.entity.mob", "net.minecraft.entity.boss.dragon" -> + "Hostile mobs"; + case "net.minecraft.entity.projectile", "net.minecraft.entity.thrown" -> "Projectiles"; + case "net.minecraft.entity.passive" -> "Passive mobs"; + case "net.minecraft.entity.vehicle" -> "Vehicles"; + case "net.minecraft.entity" -> "other"; + default -> throw new Error("Unexpected entity type: " + packageName); + }; + return category; + } + + //Honestly, both "type" and "category" fields in the schema and examples do not contain any useful information + //Since category is optional, I will just leave it out, and for type I will assume general entity classification + //by the Entity class hierarchy (which has some weirdness too by the way) + private static String getEntityTypeForClass(Class entityClass) { + //Top-level classifications + if (WaterCreatureEntity.class.isAssignableFrom(entityClass)) { + return "water_creature"; + } + if (AnimalEntity.class.isAssignableFrom(entityClass)) { + return "animal"; + } + if (HostileEntity.class.isAssignableFrom(entityClass)) { + return "hostile"; + } + if (AmbientEntity.class.isAssignableFrom(entityClass)) { + return "ambient"; + } + + //Second level classifications. PathAwareEntity is not included because it + //doesn't really make much sense to categorize by it + if (PassiveEntity.class.isAssignableFrom(entityClass)) { + return "passive"; + } + if (MobEntity.class.isAssignableFrom(entityClass)) { + return "mob"; + } + + //Other classifications only include living entities and projectiles. everything else is categorized as other + if (LivingEntity.class.isAssignableFrom(entityClass)) { + return "living"; + } + if (Projectile.class.isAssignableFrom(entityClass)) { + return "projectile"; + } + return "other"; + } + + private static int entityId(Entity entity) { + if (!DGU.getCurrentlyRunningServer().getVersion().equals("1.11.2")) { + throw new Error("These ids were gotten manually for 1.11.2, remake for " + DGU.getCurrentlyRunningServer().getVersion()); + } + int rawId = Registries.ENTITY_TYPES.getRawId(entity.getClass()); + if (rawId == -1) { // see TrackedEntityInstance + if (entity instanceof ItemEntity) { + return 1; + } else if (entity instanceof FishingBobberEntity) { + return 90; + } else { + throw new Error("unable to find rawId for entity: " + entity.getEntityName()); + } + } + return rawId; + } + + @Override + public String getDataName() { + return "entities"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + for (Class entityType : Registries.ENTITY_TYPES) { + resultArray.add(generateEntity(entityType)); + } + return resultArray; + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java new file mode 100644 index 00000000..66c53790 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java @@ -0,0 +1,50 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.item.FoodItem; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; + +import java.util.Objects; + +public class FoodsDataGenerator implements IDataGenerator { + + public static JsonObject generateFoodDescriptor(FoodItem foodItem) { + JsonObject foodDesc = new JsonObject(); + Identifier registryKey = Registries.ITEMS.getIdentifier(foodItem); + + foodDesc.addProperty("id", Registries.ITEMS.getRawId(foodItem)); + foodDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + foodDesc.addProperty("stackSize", foodItem.getMaxCount()); + foodDesc.addProperty("displayName", DGU.translateText(foodItem.getTranslationKey())); + float foodPoints = foodItem.getHungerPoints(DGU.stackFor(foodItem)); + float saturationRatio = foodItem.getSaturation(DGU.stackFor(foodItem)) * 2.0F; + float saturation = foodPoints * saturationRatio; + + foodDesc.addProperty("foodPoints", foodPoints); + foodDesc.addProperty("saturation", saturation); + + foodDesc.addProperty("effectiveQuality", foodPoints + saturation); + foodDesc.addProperty("saturationRatio", saturationRatio); + return foodDesc; + } + + @Override + public String getDataName() { + return "foods"; + } + + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + for (Item item : Registries.ITEMS) { + if (item instanceof FoodItem) { + resultsArray.add(generateFoodDescriptor((FoodItem) item)); + } + } + return resultsArray; + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java new file mode 100644 index 00000000..81fe4e20 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; + +public interface IDataGenerator { + + String getDataName(); + + JsonElement generateDataJson(); +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java new file mode 100644 index 00000000..3aa51c41 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java @@ -0,0 +1,30 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.NoteBlockAccessor; +import dev.u9g.minecraftdatagenerator.mixin.SoundAccessor; +import net.minecraft.client.sound.SoundEvent; + +import java.util.Objects; + +public class InstrumentsDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "instruments"; + } + + @Override + public JsonElement generateDataJson() { + JsonArray array = new JsonArray(); + int i = 0; + for (SoundEvent sound : Objects.requireNonNull(NoteBlockAccessor.TUNES())) { + JsonObject object = new JsonObject(); + object.addProperty("id", i++); + object.addProperty("name", ((SoundAccessor) sound).id().getPath().split("\\.")[2]); + array.add(object); + } + return array; + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java new file mode 100644 index 00000000..4731a2fb --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java @@ -0,0 +1,88 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class ItemsDataGenerator implements IDataGenerator { + + private static List calculateItemsToRepairWith(Item sourceItem) { + List items = new ArrayList<>(); + for (Item otherItem : Registries.ITEMS) { + if (sourceItem.canRepair(DGU.stackFor(sourceItem), DGU.stackFor(otherItem))) { + items.add(otherItem); + } + } + return items; + } + + private static List getApplicableEnchantmentTargets(Item sourceItem) { + List targets = new ArrayList<>(); + for (EnchantmentTarget target : EnchantmentTarget.values()) { + if (!target.isCompatible(sourceItem)) continue; + targets.add(target); + } + return targets; + } + + public static JsonObject generateItem(Item item) { + JsonObject itemDesc = new JsonObject(); + Identifier registryKey = Registries.ITEMS.getIdentifier(item); + + itemDesc.addProperty("id", Registries.ITEMS.getRawId(item)); + itemDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + itemDesc.addProperty("displayName", DGU.translateText(item.getTranslationKey())); + itemDesc.addProperty("stackSize", item.getMaxCount()); + + List enchantmentTargets = getApplicableEnchantmentTargets(item); + + JsonArray enchantCategoriesArray = new JsonArray(); + for (EnchantmentTarget target : enchantmentTargets) { + enchantCategoriesArray.add(new JsonPrimitive(EnchantmentsDataGenerator.getEnchantmentTargetName(target))); + } + if (enchantCategoriesArray.size() > 0) { + itemDesc.add("enchantCategories", enchantCategoriesArray); + } + + if (item.isDamageable()) { + List repairWithItems = calculateItemsToRepairWith(item); + + JsonArray fixedWithArray = new JsonArray(); + for (Item repairWithItem : repairWithItems) { + Identifier repairWithName = Registries.ITEMS.getIdentifier(repairWithItem); + fixedWithArray.add(new JsonPrimitive(Objects.requireNonNull(repairWithName).getPath())); + } + if (fixedWithArray.size() > 0) { + itemDesc.add("repairWith", fixedWithArray); + } + + int maxDurability = item.getMaxDamage(); + itemDesc.addProperty("maxDurability", maxDurability); + } + return itemDesc; + } + + @Override + public String getDataName() { + return "items"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + for (Item item : Registries.ITEMS) { + resultArray.add(generateItem(item)); + } + return resultArray; + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java new file mode 100644 index 00000000..88e8d2fb --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java @@ -0,0 +1,29 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.LanguageAccessor; +import dev.u9g.minecraftdatagenerator.util.Registries; + +import java.util.Map; + +public class LanguageDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "language"; + } + + @Override + public JsonElement generateDataJson() { + try { + JsonObject obj = new JsonObject(); + Map translations = ((LanguageAccessor) Registries.LANGUAGE).translations(); + for (Map.Entry entry : translations.entrySet()) { + obj.addProperty(entry.getKey(), entry.getValue()); + } + return obj; + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to generate language file"); + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java new file mode 100644 index 00000000..2b1a5085 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java @@ -0,0 +1,31 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.minecraft.client.particle.ParticleType; + +public class ParticlesDataGenerator implements IDataGenerator { + + public static JsonObject generateParticleType(int id, ParticleType particleType) { + JsonObject effectDesc = new JsonObject(); + + effectDesc.addProperty("id", id); + effectDesc.addProperty("name", particleType.getName()); + return effectDesc; + } + + @Override + public String getDataName() { + return "particles"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + int i = 0; + for (ParticleType particleType : ParticleType.values()) { + resultsArray.add(generateParticleType(i++, particleType)); + } + return resultsArray; + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java new file mode 100644 index 00000000..32498dfa --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java @@ -0,0 +1,119 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; + +public class RecipeDataGenerator implements IDataGenerator { + + @Override + public String getDataName() { + return "recipes"; + } + + @Override + public JsonElement generateDataJson() { +// JsonObject finalObj = new JsonObject(); +// Multimap recipes = ArrayListMultimap.create(); +// for (Recipe recipe : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) { +// if (recipe instanceof ShapedRecipe sr) { +// var ingredients = sr.getIngredients(); +// List ingr = new ArrayList<>(); +// for (int i = 0; i < 9; i++) { +// if (i >= ingredients.size()) { +// ingr.add(-1); +// continue; +// } +// var stacks = ingredients.get(i); +//// var matching = stacks.getMatchingStacks(); +//// if (matching.length == 0) { +//// ingr.add(-1); +//// } else { +//// ingr.add(getRawIdFor(matching[0].getItem())); +//// } +// } +// Lists.reverse(ingr); +// +// JsonArray inShape = new JsonArray(); +// +// var iter = ingr.iterator(); +// for (int y = 0; y < 3; y++) { +// var jsonRow = new JsonArray(); +// int one = iter.next(); +// int two = iter.next(); +// int three = iter.next(); +// if (y > 0 && one == -1 && two == -1 && three == -1) continue; +// jsonRow.add(one); +// jsonRow.add(two); +// jsonRow.add(three); +// inShape.add(jsonRow); +// } +// +// JsonObject finalRecipe = new JsonObject(); +// finalRecipe.add("inShape", inShape); +// +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +// resultObject.addProperty("count", sr.getOutput().getCount()); +// finalRecipe.add("result", resultObject); +// +// String id = ((Integer) getRawIdFor(sr.getOutput().getItem())).toString(); +// +// if (!finalObj.has(id)) { +// finalObj.add(id, new JsonArray()); +// } +// finalObj.get(id).getAsJsonArray().add(finalRecipe); +//// var input = new JsonArray(); +//// var ingredients = sr.getIngredients().stream().toList(); +//// for (int y = 0; y < sr.getHeight(); y++) { +//// var arr = new JsonArray(); +//// for (int x = 0; x < sr.getWidth(); x++) { +//// if ((y*3)+x >= ingredients.size()) { +//// arr.add(JsonNull.INSTANCE); +//// continue; +//// } +//// var ingredient = ingredients.get((y*3)+x).getMatchingStacks(); // FIXME: fix when there are more than one matching stack +//// if (ingredient.length == 0) { +//// arr.add(JsonNull.INSTANCE); +//// } else { +//// arr.add(getRawIdFor(ingredient[0].getItem())); +//// } +//// } +//// input.add(arr); +//// } +//// var rootRecipeObject = new JsonObject(); +//// rootRecipeObject.add("inShape", input); +//// var resultObject = new JsonObject(); +//// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +//// resultObject.addProperty("count", sr.getOutput().getCount()); +//// rootRecipeObject.add("result", resultObject); +//// recipes.put(getRawIdFor(sr.getOutput().getItem()), rootRecipeObject); +// } else if (recipe instanceof ShapelessRecipe sl) { + +// var ingredients = new JsonArray(); +// for (Ingredient ingredient : sl.getIngredients()) { +// if (ingredient.isEmpty()) continue; +//// ingredients.add(getRawIdFor(ingredient.getMatchingStacks()[0].getItem())); +// } +// var rootRecipeObject = new JsonObject(); +// rootRecipeObject.add("ingredients", ingredients); +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sl.getOutput().getItem())); +// resultObject.addProperty("count", sl.getOutput().getCount()); +// rootRecipeObject.add("result", resultObject); +// recipes.put(getRawIdFor(sl.getOutput().getItem()), rootRecipeObject); +// } +// } +// recipes.forEach((a, b) -> { +// if (!finalObj.has(a.toString())) { +// finalObj.add(a.toString(), new JsonArray()); +// } +// finalObj.get(a.toString()).getAsJsonArray().add(b); +// }); +// return finalObj; + return JsonNull.INSTANCE; + } +// +// private static int getRawIdFor (Item item) { +// return Registry.ITEM.getRawId(item); +// } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java new file mode 100644 index 00000000..ce9c0ee9 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java @@ -0,0 +1,164 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.BiomeColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.BlockColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.ServerSideRedstoneWireBlock; +import dev.u9g.minecraftdatagenerator.mixin.BiomeAccessor; +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.block.RedstoneWireBlock; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.biome.Biome; + +import java.util.*; + +public class TintsDataGenerator implements IDataGenerator { + + private static final BlockColors blockColors = BlockColors.create(); + + public static BiomeTintColors generateBiomeTintColors() { + BiomeTintColors colors = new BiomeTintColors(); + + for (Biome biome : Registries.BIOMES) { + EmptyBlockView bv = new EmptyBlockView() { + @Override + public Biome getBiome(BlockPos pos) { + return biome; + } + }; + int biomeGrassColor = BiomeColors.getGrassColor(bv, BlockPos.ORIGIN); + int biomeFoliageColor = BiomeColors.getFoliageColor(bv, BlockPos.ORIGIN); + int biomeWaterColor = ((BiomeAccessor) biome).waterColor(); + + colors.grassColoursMap.computeIfAbsent(biomeGrassColor, k -> new ArrayList<>()).add(biome); + colors.foliageColoursMap.computeIfAbsent(biomeFoliageColor, k -> new ArrayList<>()).add(biome); + colors.waterColourMap.computeIfAbsent(biomeWaterColor, k -> new ArrayList<>()).add(biome); + } + return colors; + } + + public static Map generateRedstoneTintColors() { + Map resultColors = new HashMap<>(); + + for (int redstoneLevel : RedstoneWireBlock.POWER.getValues()) { + int color = ServerSideRedstoneWireBlock.getWireColor(redstoneLevel); + resultColors.put(redstoneLevel, color); + } + return resultColors; + } + + private static int getBlockColor(Block block) { + return blockColors.method_13410(block.getDefaultState()); + } + + public static Map generateConstantTintColors() { + Map resultColors = new HashMap<>(); + BlockColors blockColors = BlockColors.create(); + // FIXME: ? + // resultColors.put(Blocks.BIRCH_LEAVES, FoliageColors.getBirchColor()); + // resultColors.put(Blocks.SPRUCE_LEAVES, FoliageColors.getSpruceColor()); + + resultColors.put(Blocks.LILY_PAD, getBlockColor(Blocks.LILY_PAD)); + // FIXME: ? + // resultColors.put(Blocks.ATTACHED_MELON_STEM, getBlockColor(Blocks.ATTACHED_MELON_STEM, blockColors)); + // resultColors.put(Blocks.ATTACHED_PUMPKIN_STEM, getBlockColor(Blocks.ATTACHED_PUMPKIN_STEM, blockColors)); + + //not really constant, depend on the block age, but kinda have to be handled since textures are literally white without them + resultColors.put(Blocks.MELON_STEM, getBlockColor(Blocks.MELON_STEM)); + resultColors.put(Blocks.PUMPKIN_STEM, getBlockColor(Blocks.PUMPKIN_STEM)); + + return resultColors; + } + + private static JsonObject encodeBiomeColorMap(Map> colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry> entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + for (Biome biome : entry.getValue()) { + Identifier registryKey = Registries.BIOMES.getIdentifier(biome); + keysArray.add(new JsonPrimitive(Objects.requireNonNull(registryKey).getPath())); + } + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getKey()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeRedstoneColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + keysArray.add(new JsonPrimitive(entry.getKey())); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeBlocksColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + JsonArray keysArray = new JsonArray(); + Identifier registryKey = Registries.BLOCKS.getIdentifier(entry.getKey()); + keysArray.add(new JsonPrimitive(Objects.requireNonNull(registryKey).getPath())); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + @Override + public String getDataName() { + return "tints"; + } + + @Override + public JsonObject generateDataJson() { + BiomeTintColors biomeTintColors = generateBiomeTintColors(); + Map redstoneColors = generateRedstoneTintColors(); + Map constantTintColors = generateConstantTintColors(); + + JsonObject resultObject = new JsonObject(); + + resultObject.add("grass", encodeBiomeColorMap(biomeTintColors.grassColoursMap)); + resultObject.add("foliage", encodeBiomeColorMap(biomeTintColors.foliageColoursMap)); + resultObject.add("water", encodeBiomeColorMap(biomeTintColors.waterColourMap)); + + resultObject.add("redstone", encodeRedstoneColorMap(redstoneColors)); + resultObject.add("constant", encodeBlocksColorMap(constantTintColors)); + + return resultObject; + } + + public static class BiomeTintColors { + final Map> grassColoursMap = new HashMap<>(); + final Map> foliageColoursMap = new HashMap<>(); + final Map> waterColourMap = new HashMap<>(); + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BiomeAccessor.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BiomeAccessor.java new file mode 100644 index 00000000..1feed6b7 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BiomeAccessor.java @@ -0,0 +1,14 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.world.biome.Biome; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Biome.class) +public interface BiomeAccessor { + @Accessor("waterColor") + int waterColor(); + + @Accessor("name") + String name(); +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BlockAccessor.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BlockAccessor.java new file mode 100644 index 00000000..1c25c003 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BlockAccessor.java @@ -0,0 +1,11 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Block; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Block.class) +public interface BlockAccessor { + @Accessor("blastResistance") + float getBlastResistance(); +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java new file mode 100644 index 00000000..0d297e7a --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.server.dedicated.EulaReader; +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; + +@Mixin(EulaReader.class) +public class EULAMixin { + @Inject(method = "isEulaAgreedTo()Z", at = @At("TAIL"), cancellable = true) + public void init(CallbackInfoReturnable cir) { + cir.setReturnValue(true); + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockAccessor.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockAccessor.java new file mode 100644 index 00000000..5bec8213 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockAccessor.java @@ -0,0 +1,19 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.EndPortalFrameBlock; +import net.minecraft.util.math.Box; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(EndPortalFrameBlock.class) +public interface EndPortalFrameBlockAccessor { + @Accessor("PORTAL_FRAME") + static Box portalFrame() { + throw new IllegalStateException(); + } + + @Accessor("PORTAL_EYE") + static Box eye() { + throw new IllegalStateException(); + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockMixin.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockMixin.java new file mode 100644 index 00000000..1de19b27 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockMixin.java @@ -0,0 +1,26 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.BlockState; +import net.minecraft.block.EndPortalFrameBlock; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +import java.util.List; + +@Mixin(EndPortalFrameBlock.class) +public class EndPortalFrameBlockMixin { + /** + * @author a + * @reason a + */ + @Overwrite() + public void appendCollisionBoxes(BlockState state, World world, BlockPos pos, Box entityBox, List boxes, @Nullable Entity entity, boolean isActualState) { + boxes.add(EndPortalFrameBlockAccessor.portalFrame()); + boxes.add(EndPortalFrameBlockAccessor.eye()); + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/LanguageAccessor.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/LanguageAccessor.java new file mode 100644 index 00000000..7bfd95fc --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/LanguageAccessor.java @@ -0,0 +1,13 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.util.Language; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(Language.class) +public interface LanguageAccessor { + @Accessor("translations") + Map translations(); +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java new file mode 100644 index 00000000..53b8cdb5 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Block; +import net.minecraft.item.ToolItem; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Set; + +@Mixin(ToolItem.class) +public interface MiningToolItemAccessor { + + @Accessor + Set getEffectiveBlocks(); +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/NoteBlockAccessor.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/NoteBlockAccessor.java new file mode 100644 index 00000000..4bc3bcb9 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/NoteBlockAccessor.java @@ -0,0 +1,16 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.NoteBlock; +import net.minecraft.client.sound.SoundEvent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + +@Mixin(NoteBlock.class) +public interface NoteBlockAccessor { + @Accessor("TUNES") + static List TUNES() { + return null; + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java new file mode 100644 index 00000000..32871cc3 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.generators.DataGenerators; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; +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.nio.file.Path; +import java.util.logging.Level; + +@Mixin(MinecraftDedicatedServer.class) +public class ReadyMixin { + @Inject(method = "setupServer()Z", at = @At("TAIL")) + private void init(CallbackInfoReturnable cir) { + Main.LOGGER.log(Level.INFO, "Starting data generation!"); + String versionName = DGU.getCurrentlyRunningServer().getVersion(); + Path serverRootDirectory = DGU.getCurrentlyRunningServer().getRunDirectory().toPath().toAbsolutePath(); + Path dataDumpDirectory = serverRootDirectory.resolve("minecraft-data").resolve(versionName); + DataGenerators.runDataGenerators(dataDumpDirectory); + Main.LOGGER.log(Level.INFO, "Done data generation!"); + System.exit(0); + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/SoundAccessor.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/SoundAccessor.java new file mode 100644 index 00000000..fe505910 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/SoundAccessor.java @@ -0,0 +1,12 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.client.sound.SoundEvent; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(SoundEvent.class) +public interface SoundAccessor { + @Accessor("id") + Identifier id(); +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java new file mode 100644 index 00000000..33d4cfcd --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java @@ -0,0 +1,11 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.entity.effect.StatusEffect; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(StatusEffect.class) +public interface StatusEffectAccessor { + @Accessor("negative") + boolean negative(); +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java new file mode 100644 index 00000000..bf7137e6 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java @@ -0,0 +1,90 @@ +package dev.u9g.minecraftdatagenerator.util; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapter; +import com.google.gson.internal.bind.TypeAdapters; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.World; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; + +public class DGU { + + public static Gson gson = new GsonBuilder().registerTypeAdapterFactory(TypeAdapters.newFactory(double.class, Double.class, new TypeAdapter() { + @Override + public Number read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + return in.nextDouble(); + } + + @Override + public void write(JsonWriter out, Number value) throws IOException { + out.value(value); + } + })).create(); + + @Environment(EnvType.CLIENT) + private static MinecraftServer getCurrentlyRunningServerClient() { + return MinecraftClient.getInstance().getServer(); + } + + @SuppressWarnings("deprecation") + private static MinecraftServer getCurrentlyRunningServerDedicated() { + return (MinecraftServer) FabricLoader.getInstance().getGameInstance(); + } + + public static MinecraftServer getCurrentlyRunningServer() { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return getCurrentlyRunningServerClient(); + } else if (environmentType == EnvType.SERVER) { + return getCurrentlyRunningServerDedicated(); + } + throw new UnsupportedOperationException(); + } + + @Environment(EnvType.CLIENT) + private static String translateTextClient(String translationKey) { + return I18n.translate(translationKey); + } + + private static String translateTextFallback(String translationKey) { + try { + return Registries.LANGUAGE.translate(translationKey); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to translate: '" + translationKey + "'"); + } + + public static String translateText(String translationKey) { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return translateTextClient(translationKey); + } + return translateTextFallback(translationKey); + } + + @NotNull + public static World getWorld() { + return getCurrentlyRunningServer().getWorld(); + } + + public static ItemStack stackFor(Item ic) { + return new ItemStack(ic); + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java new file mode 100644 index 00000000..41fa9be3 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java @@ -0,0 +1,49 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.BlockView; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.level.LevelGeneratorType; +import org.jetbrains.annotations.Nullable; + +public class EmptyBlockView implements BlockView { + public static final EmptyBlockView INSTANCE = new EmptyBlockView(); + + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + return null; + } + + @Override + public int getLight(BlockPos pos, int minBlockLight) { + return 0; + } + + public BlockState getBlockState(BlockPos pos) { + return Blocks.AIR.getDefaultState(); + } + + @Override + public boolean isAir(BlockPos pos) { + return false; + } + + @Override + public Biome getBiome(BlockPos pos) { + return null; + } + + @Override + public int getStrongRedstonePower(BlockPos pos, Direction direction) { + return 0; + } + + @Override + public LevelGeneratorType getGeneratorType() { + return null; + } +} diff --git a/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/util/Registries.java b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/util/Registries.java new file mode 100644 index 00000000..adbbaf36 --- /dev/null +++ b/1.11.2/src/main/java/dev/u9g/minecraftdatagenerator/util/Registries.java @@ -0,0 +1,22 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.minecraft.block.Block; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; +import net.minecraft.util.Language; +import net.minecraft.util.registry.SimpleRegistry; +import net.minecraft.world.biome.Biome; + +public class Registries { + public static final SimpleRegistry BIOMES = Biome.REGISTRY; + public static final SimpleRegistry BLOCKS = Block.REGISTRY; + public static final SimpleRegistry ITEMS = Item.REGISTRY; + public static final SimpleRegistry STATUS_EFFECTS = StatusEffect.REGISTRY; + public static final SimpleRegistry ENCHANTMENTS = Enchantment.REGISTRY; + public static final SimpleRegistry> ENTITY_TYPES = EntityType.REGISTRY; + public static final Language LANGUAGE = new Language(); +} diff --git a/1.11.2/src/main/resources/fabric.mod.json b/1.11.2/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..03fbafe0 --- /dev/null +++ b/1.11.2/src/main/resources/fabric.mod.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": 1, + "id": "minecraft-data-generator", + "version": "${version}", + "name": "Minecraft Data Generator", + "description": "", + "authors": [ + "Archengius", + "U9G" + ], + "contact": {}, + "license": "MIT", + "environment": "server", + "entrypoints": { + "main": [ + "dev.u9g.minecraftdatagenerator.Main" + ] + }, + "mixins": [ + "minecraft-data-generator.mixins.json" + ], + "depends": { + "fabricloader": ">=0.13.3", + "minecraft": ">=1.11" + } +} diff --git a/1.11.2/src/main/resources/minecraft-data-generator.mixins.json b/1.11.2/src/main/resources/minecraft-data-generator.mixins.json new file mode 100644 index 00000000..fcdcd1a0 --- /dev/null +++ b/1.11.2/src/main/resources/minecraft-data-generator.mixins.json @@ -0,0 +1,26 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "dev.u9g.minecraftdatagenerator.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "BiomeAccessor", + "BlockAccessor", + "EndPortalFrameBlockAccessor", + "EndPortalFrameBlockMixin", + "LanguageAccessor", + "MiningToolItemAccessor", + "NoteBlockAccessor", + "ReadyMixin", + "SoundAccessor", + "StatusEffectAccessor" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "server": [ + "EULAMixin" + ] +} diff --git a/1.12.2/build.gradle b/1.12.2/build.gradle new file mode 100644 index 00000000..057916c6 --- /dev/null +++ b/1.12.2/build.gradle @@ -0,0 +1,63 @@ +plugins { + id 'fabric-loom' +} + +repositories { + maven { + name = "legacy-fabric" + url = "https://maven.legacyfabric.net" + } +} + +loom { + setIntermediaryUrl('https://maven.legacyfabric.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar'); + customMinecraftManifest.set("https://meta.legacyfabric.net/v2/manifest/${minecraft_version}") +} + +dependencies { + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API provides hooks for events, item registration, and more. As most mods will need this, it's included by default. + // If you know for a fact you don't, it's not required and can be safely removed. +// modImplementation ("net.legacyfabric.legacy-fabric-api:legacy-fabric-api:${fabric_version}") { +// exclude module: "legacy-fabric-entity-events-v1" +// } + + if (System.getProperty("os.name").toLowerCase().contains("mac")) { + implementation 'org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209' + implementation 'org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209' + implementation 'org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209' + } +} + +if (System.getProperty("os.name").toLowerCase().contains("mac")) { + configurations.configureEach { + resolutionStrategy { + dependencySubstitution { + substitute module('org.lwjgl.lwjgl:lwjgl_util:2.9.2-nightly-201408222') with module('org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209') + substitute module('org.lwjgl.lwjgl:lwjgl:2.9.2-nightly-201408222') with module('org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209') + } + force 'org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209' + } + } +} + +processResources { + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} diff --git a/1.12.2/gradle.properties b/1.12.2/gradle.properties new file mode 100644 index 00000000..bf25787b --- /dev/null +++ b/1.12.2/gradle.properties @@ -0,0 +1,7 @@ +# Fabric Properties +# More versions available at: https://grayray75.github.io/LegacyFabric-Versions/ +minecraft_version=1.12.2 +yarn_mappings=1.12.2+build.202206020145 +loader_version=0.14.3 +# Dependencies +# More versions available at: https://grayray75.github.io/LegacyFabric-Versions/ diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeColors.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeColors.java new file mode 100644 index 00000000..e4070ada --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeColors.java @@ -0,0 +1,46 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.BlockView; +import net.minecraft.world.biome.Biome; + +import java.util.Iterator; + +public class BiomeColors { + private static final ColorProvider GRASS_COLOR = (biome, pos) -> biome.getGrassColor(pos); + private static final ColorProvider FOLIAGE_COLOR = (biome, pos) -> biome.getFoliageColor(pos); + private static final ColorProvider WATER_COLOR = (biome, pos) -> biome.getWaterColor(); + + private static int getColor(BlockView view, BlockPos pos, ColorProvider provider) { + int i = 0; + int j = 0; + int k = 0; + + int l; + for (Iterator var6 = BlockPos.mutableIterate(pos.add(-1, 0, -1), pos.add(1, 0, 1)).iterator(); var6.hasNext(); k += l & 255) { + BlockPos.Mutable mutable = (BlockPos.Mutable) var6.next(); + l = provider.getColorAtPos(view.getBiome(mutable), mutable); + i += (l & 16711680) >> 16; + j += (l & '\uff00') >> 8; + } + + return (i / 9 & 255) << 16 | (j / 9 & 255) << 8 | k / 9 & 255; + } + + public static int getGrassColor(BlockView view, BlockPos pos) { + return getColor(view, pos, GRASS_COLOR); + } + + public static int getFoliageColor(BlockView view, BlockPos pos) { + return getColor(view, pos, FOLIAGE_COLOR); + } + + public static int getWaterColor(BlockView view, BlockPos pos) { + return getColor(view, pos, WATER_COLOR); + } + + interface ColorProvider { + int getColorAtPos(Biome biome, BlockPos pos); + } +} + diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColorable.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColorable.java new file mode 100644 index 00000000..d1443853 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColorable.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.BlockView; +import org.jetbrains.annotations.Nullable; + +public interface BlockColorable { + int method_12155(BlockState blockState, @Nullable BlockView blockView, @Nullable BlockPos blockPos, int i); +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColors.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColors.java new file mode 100644 index 00000000..a18847ac --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColors.java @@ -0,0 +1,99 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.block.*; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.FlowerPotBlockEntity; +import net.minecraft.item.Item; +import net.minecraft.util.collection.IdList; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.BlockView; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; + +public class BlockColors { + private final IdList BlockColor2Id = new IdList<>(32); + + public BlockColors() { + } + + public static BlockColors create() { + final BlockColors blockColors = new BlockColors(); + blockColors.method_12158((blockState, blockView, blockPos, i) -> { + DoublePlantBlock.DoublePlantType doublePlantType = blockState.get(DoublePlantBlock.VARIANT); + return blockView != null && blockPos != null && + (doublePlantType == DoublePlantBlock.DoublePlantType.GRASS || doublePlantType == DoublePlantBlock.DoublePlantType.FERN) ? + BiomeColors.getGrassColor(blockView, blockState.get(DoublePlantBlock.HALF) == DoublePlantBlock.HalfType.UPPER ? blockPos.down() : blockPos) : -1; + }, Blocks.DOUBLE_PLANT); + blockColors.method_12158((blockState, blockView, blockPos, i) -> { + if (blockView != null && blockPos != null) { + BlockEntity blockEntity = blockView.getBlockEntity(blockPos); + if (blockEntity instanceof FlowerPotBlockEntity) { + Item item = ((FlowerPotBlockEntity) blockEntity).getItem(); + BlockState blockState2 = Block.getBlockFromItem(item).getDefaultState(); + return blockColors.method_12157(blockState2, blockView, blockPos, i); + } else { + return -1; + } + } else { + return -1; + } + }, Blocks.FLOWER_POT); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getGrassColor(blockView, blockPos) : GrassColors.getColor(0.5D, 1.0D), Blocks.GRASS); + blockColors.method_12158((blockState, blockView, blockPos, i) -> { + PlanksBlock.WoodType woodType = blockState.get(Leaves1Block.VARIANT); + if (woodType == PlanksBlock.WoodType.SPRUCE) { + return FoliageColors.getSpruceColor(); + } else if (woodType == PlanksBlock.WoodType.BIRCH) { + return FoliageColors.getBirchColor(); + } else { + return blockView != null && blockPos != null ? BiomeColors.getFoliageColor(blockView, blockPos) : FoliageColors.getDefaultColor(); + } + }, Blocks.LEAVES); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getFoliageColor(blockView, blockPos) : FoliageColors.getDefaultColor(), Blocks.LEAVES2); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getWaterColor(blockView, blockPos) : -1, Blocks.WATER, Blocks.FLOWING_WATER); + blockColors.method_12158((blockState, blockView, blockPos, i) -> RedstoneWireBlock.method_8877((Integer) blockState.get(RedstoneWireBlock.POWER)), Blocks.REDSTONE_WIRE); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getGrassColor(blockView, blockPos) : -1, Blocks.SUGARCANE); + blockColors.method_12158((blockState, blockView, blockPos, i) -> { + int j = blockState.get(AttachedStemBlock.AGE); + int k = j * 32; + int l = 255 - j * 8; + int m = j * 4; + return k << 16 | l << 8 | m; + }, Blocks.MELON_STEM, Blocks.PUMPKIN_STEM); +// blockColors.method_12158((blockState, blockView, blockPos, i) -> { +// if (blockView != null && blockPos != null) { +// return BiomeColors.getGrassColor(blockView, blockPos); +// } else { +// return blockState.get(TallPlantBlock.TYPE) == TallPlantBlock.GrassType.DEAD_BUSH ? 16777215 : GrassColors.getColor(0.5D, 1.0D); +// } +// }, Blocks.TALLGRASS); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getGrassColor(blockView, blockPos) : blockState.get(TallPlantBlock.TYPE) == TallPlantBlock.GrassType.DEAD_BUSH ? 16777215 : GrassColors.getColor(0.5D, 1.0D), Blocks.TALLGRASS); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getFoliageColor(blockView, blockPos) : FoliageColors.getDefaultColor(), Blocks.VINE); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? 2129968 : 7455580, Blocks.LILY_PAD); + return blockColors; + } + + public int method_13410(BlockState blockState, World world, BlockPos blockPos) { + BlockColorable blockColorable = BlockColor2Id.fromId(Block.getIdByBlock(blockState.getBlock())); + if (blockColorable != null) { + return blockColorable.method_12155(blockState, null, null, 0); + } else { + MaterialColor materialColor = blockState.getMaterialColor(world, blockPos); + return materialColor != null ? materialColor.color : -1; + } + } + + public int method_12157(BlockState blockState, @Nullable BlockView blockView, @Nullable BlockPos blockPos, int i) { + net.minecraft.client.BlockColorable blockColorable = (net.minecraft.client.BlockColorable) this.BlockColor2Id.fromId(Block.getIdByBlock(blockState.getBlock())); + return blockColorable == null ? -1 : blockColorable.method_12155(blockState, blockView, blockPos, i); + } + + public void method_12158(BlockColorable blockColorable, Block... blocks) { + int var4 = blocks.length; + + for (int var5 = 0; var5 < var4; ++var5) { + Block block = blocks[var5]; + this.BlockColor2Id.set(blockColorable, Block.getIdByBlock(block)); + } + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java new file mode 100644 index 00000000..9a7ad297 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java @@ -0,0 +1,36 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +public class FoliageColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] pixels) { + colorMap = pixels; + } + + private static int getColor(double temperature, double humidity) { + int i = (int) ((1.0 - temperature) * 255.0); + int j = (int) ((1.0 - (humidity *= temperature)) * 255.0); + return colorMap[j << 8 | i]; + } + + public static int getSpruceColor() { + return 0x619961; + } + + public static int getBirchColor() { + return 8431445; + } + + public static int getDefaultColor() { + return 4764952; + } + + public static int getFoliageColor(Biome biome) { + double d = MathHelper.clamp(biome.getTemperature(), 0.0f, 1.0f); + double e = MathHelper.clamp(biome.getRainfall(), 0.0f, 1.0f); + return getColor(d, e); + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java new file mode 100644 index 00000000..d6fe5bd7 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java @@ -0,0 +1,29 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +public class GrassColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] map) { + colorMap = map; + } + + public static int getColor(double temperature, double humidity) { + int j = (int) ((1.0 - (humidity *= temperature)) * 255.0); + int i = (int) ((1.0 - temperature) * 255.0); + int k = j << 8 | i; + if (k > colorMap.length) { + return -65281; + } + return colorMap[k]; + } + + public static int getGrassColor(Biome biome) { + double d = MathHelper.clamp(biome.getTemperature(), 0.0f, 1.0f); + double e = MathHelper.clamp(biome.getRainfall(), 0.0f, 1.0f); + return getColor(d, e); + } +} + diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java new file mode 100644 index 00000000..c6b2943e --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java @@ -0,0 +1,25 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; + +public class ServerSideRedstoneWireBlock { + public static int getWireColor(int powerLevel) { + float f = (float) powerLevel / 15.0f; + float g = f * 0.6f + 0.4f; + if (powerLevel == 0) { + g = 0.3f; + } + float h = f * f * 0.7f - 0.5f; + float j = f * f * 0.6f - 0.7f; + if (h < 0.0f) { + h = 0.0f; + } + if (j < 0.0f) { + j = 0.0f; + } + int k = MathHelper.clamp((int) (g * 255.0f), 0, 255); + int l = MathHelper.clamp((int) (h * 255.0f), 0, 255); + int m = MathHelper.clamp((int) (j * 255.0f), 0, 255); + return 0xFF000000 | k << 16 | l << 8 | m; + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/Main.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/Main.java new file mode 100644 index 00000000..d23c5ba2 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/Main.java @@ -0,0 +1,13 @@ +package dev.u9g.minecraftdatagenerator; + +import net.fabricmc.api.ModInitializer; + +import java.util.logging.Logger; + +public class Main implements ModInitializer { + public static final Logger LOGGER = Logger.getLogger("mc-data-gen-serv"); + + @Override + public void onInitialize() { + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java new file mode 100644 index 00000000..1689c8b5 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java @@ -0,0 +1,32 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.LanguageAccessor; +import dev.u9g.minecraftdatagenerator.util.Registries; + +import java.util.Map; + +public class AttributesDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "attributes"; + } + + @Override + public JsonElement generateDataJson() { + Map translations = ((LanguageAccessor) Registries.LANGUAGE).translations(); + JsonArray arr = new JsonArray(); + for (Map.Entry translation : translations.entrySet()) { + String key = translation.getKey(); + if (!key.startsWith("attribute.name.")) continue; + JsonObject obj = new JsonObject(); + key = key.replace("attribute.name.", ""); + obj.addProperty("name", key.split("\\.")[1]); + obj.addProperty("resource", key); + arr.add(obj); + } + return arr; + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java new file mode 100644 index 00000000..8fa1f8d4 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java @@ -0,0 +1,194 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.BiomeAccessor; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.SimpleRegistry; +import net.minecraft.world.biome.*; +import org.apache.commons.lang3.StringUtils; + +import java.util.Locale; + +public class BiomesDataGenerator implements IDataGenerator { + + private static String guessBiomeDimensionFromCategory(Biome biome) { + if (biome instanceof NetherBiome) { + return "nether"; + } else if (biome instanceof EndBiome) { + return "end"; + } + return "overworld"; + } + + private static int getBiomeColorFor(String biomeDisplayNamed) { + if (biomeDisplayNamed.equals("Redwood Taiga Hills M")) { + biomeDisplayNamed = "MegaTaigaHills"; + } + String biomeDisplayName = StringUtils.join(biomeDisplayNamed.split(" "), ""); + return switch (biomeDisplayName) { + case "Ocean" -> 112; + case "Plains" -> 9286496; + case "Desert" -> 16421912; + case "ExtremeHills", "Extreme Hills" -> 6316128; + case "Forest" -> 353825; + case "Taiga" -> 747097; + case "Swampland" -> 522674; + case "River" -> 255; + case "Hell" -> 16711680; + case "TheEnd", "The End" -> 8421631; + case "FrozenOcean", "Frozen Ocean" -> 7368918; + case "FrozenRiver", "Frozen River" -> 10526975; + case "IcePlains", "Ice Plains" -> 16777215; + case "IceMountains", "Ice Mountains" -> 10526880; + case "MushroomIsland", "Mushroom Island" -> 16711935; + case "MushroomIslandShore", "Mushroom Island Shore" -> 10486015; + case "Beach" -> 16440917; + case "DesertHills", "Desert Hills" -> 13786898; + case "ForestHills", "Forest Hills" -> 2250012; + case "TaigaHills", "Taiga Hills" -> 1456435; + case "ExtremeHillsEdge", "Extreme Hills Edge" -> 7501978; + case "Jungle" -> 5470985; + case "JungleHills", "Jungle Hills" -> 2900485; + case "JungleEdge", "Jungle Edge" -> 6458135; + case "DeepOcean", "Deep Ocean" -> 48; + case "StoneBeach", "Stone Beach" -> 10658436; + case "ColdBeach", "Cold Beach" -> 16445632; + case "BirchForest", "Birch Forest" -> 3175492; + case "BirchForestHills", "Birch Forest Hills" -> 2055986; + case "RoofedForest", "Roofed Forest" -> 4215066; + case "ColdTaiga", "Cold Taiga" -> 3233098; + case "ColdTaigaHills", "Cold Taiga Hills" -> 2375478; + case "MegaTaiga", "Mega Taiga" -> 5858897; + case "MegaTaigaHills", "Mega Taiga Hills" -> 4542270; + case "ExtremeHills+", "Extreme Hills+" -> 5271632; + case "Savanna" -> 12431967; + case "SavannaPlateau", "Savanna Plateau" -> 10984804; + case "Mesa" -> 14238997; + case "MesaPlateauF", "Mesa Plateau F" -> 11573093; + case "MesaPlateau", "Mesa Plateau" -> 13274213; + case "TheEnd-Floatingislands", "The End - Floating islands" -> 8421631; + case "TheEnd-Mediumisland", "The End - Medium island" -> 8421631; + case "TheEnd-Highisland", "The End - High island" -> 8421631; + case "TheEnd-Barrenisland", "The End - Barren island" -> 8421631; + case "WarmOcean", "Warm Ocean" -> 172; + case "LukewarmOcean", "Lukewarm Ocean" -> 144; + case "ColdOcean", "Cold Ocean" -> 2105456; + case "WarmDeepOcean", "Warm Deep Ocean" -> 80; + case "LukewarmDeepOcean", "Lukewarm Deep Ocean" -> 64; + case "ColdDeepOcean", "Cold Deep Ocean" -> 2105400; + case "FrozenDeepOcean", "Frozen Deep Ocean" -> 4210832; + case "TheVoid", "The Void" -> 0; + case "SunflowerPlains", "Sunflower Plains" -> 11918216; + case "DesertM", "Desert M" -> 16759872; + case "ExtremeHillsM", "Extreme Hills M" -> 8947848; + case "FlowerForest", "Flower Forest" -> 2985545; + case "TaigaM", "Taiga M" -> 3378817; + case "SwamplandM", "Swampland M" -> 3145690; + case "IcePlainsSpikes", "Ice Plains Spikes" -> 11853020; + case "JungleM", "Jungle M" -> 8102705; + case "JungleEdgeM", "Jungle Edge M" -> 9089855; + case "BirchForestM", "Birch Forest M" -> 5807212; + case "BirchForestHillsM", "Birch Forest Hills M" -> 4687706; + case "RoofedForestM", "Roofed Forest M" -> 6846786; + case "ColdTaigaM", "Cold Taiga M" -> 5864818; + case "MegaSpruceTaiga", "Mega Spruce Taiga" -> 8490617; + case "MegaSpruceTaiga(Hills)", "Mega Spruce Taiga (Hills)" -> 7173990; + case "ExtremeHills+M", "Extreme Hills+ M" -> 7903352; + case "SavannaM", "Savanna M" -> 15063687; + case "SavannaPlateauM", "Savanna Plateau M" -> 13616524; + case "Mesa(Bryce)", "Mesa (Bryce)" -> 16739645; + case "MesaPlateauFM", "Mesa Plateau F M" -> 14204813; + case "MesaPlateauM", "Mesa Plateau M" -> 15905933; + case "BambooJungle", "Bamboo Jungle" -> 7769620; + case "BambooJungleHills", "Bamboo Jungle Hills" -> 3884810; + default -> throw new Error("Unexpected biome, with name: '" + biomeDisplayName + "'"); + }; + } + + public static JsonObject generateBiomeInfo(SimpleRegistry registry, Biome biome) { + JsonObject biomeDesc = new JsonObject(); + Identifier registryKey = registry.getIdentifier(biome); + + String name = String.join("_", ((BiomeAccessor) biome).name().toLowerCase(Locale.ENGLISH).split(" ")); + String displayName = ((BiomeAccessor) biome).name(); + biomeDesc.addProperty("id", registry.getRawId(biome)); + biomeDesc.addProperty("name", name); + biomeDesc.addProperty("category", category(biome)); + biomeDesc.addProperty("temperature", biome.getTemperature()); + biomeDesc.addProperty("precipitation", precipitation(biome)); + biomeDesc.addProperty("depth", biome.getDepth()); + biomeDesc.addProperty("dimension", guessBiomeDimensionFromCategory(biome)); + biomeDesc.addProperty("displayName", displayName); + biomeDesc.addProperty("color", getBiomeColorFor(displayName)); + biomeDesc.addProperty("rainfall", biome.getRainfall()); + + return biomeDesc; + } + + private static String category(Biome biome) { + if (biome instanceof ForestBiome) { + return "forest"; + } else if (biome instanceof OceanBiome) { + return "ocean"; + } else if (biome instanceof PlainsBiome) { + return "plains"; + } else if (biome instanceof DesertBiome) { + return "desert"; + } else if (biome instanceof ExtremeHillsBiome) { + return "extreme_hills"; + } else if (biome instanceof TaigaBiome) { + return "taiga"; + } else if (biome instanceof SwampBiome) { + return "swamp"; + } else if (biome instanceof RiverBiome) { + return "river"; + } else if (biome instanceof NetherBiome) { + return "nether"; + } else if (biome instanceof EndBiome) { + return "the_end"; + } else if (biome instanceof IceBiome) { + return "icy"; + } else if (biome instanceof MushroomBiome) { + return "mushroom"; + } else if (biome instanceof BeachBiome) { + return "beach"; + } else if (biome instanceof JungleBiome) { + return "jungle"; + } else if (biome instanceof SavannaBiome) { + return "savanna"; + } else if (biome instanceof MesaBiome) { + return "mesa"; + } else if (biome instanceof StoneBeachBiome || biome instanceof VoidBiome) { + return "none"; // Should StoneBeachBiome be beach too? this is how it is now in mcdata + } + throw new Error("Unable to find biome category for " + biome.getClass().getName()); + } + + private static String precipitation(Biome biome) { + float rainfall = biome.getRainfall(); + float temperature = biome.getTemperature(); + if (rainfall == 0) { + return "none"; + } else if (temperature < 0.2f) { + return "snow"; + } + return "rain"; + } + + @Override + public String getDataName() { + return "biomes"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray biomesArray = new JsonArray(); + SimpleRegistry biomeRegistry = Biome.REGISTRY; + + for (Biome biome : biomeRegistry) { + biomesArray.add(generateBiomeInfo(biomeRegistry, biome)); + } + return biomesArray; + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java new file mode 100644 index 00000000..1036d62c --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java @@ -0,0 +1,125 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class BlockCollisionShapesDataGenerator implements IDataGenerator { + private static final Box ENTITY_BOX = new Box(0.0D, 0.0D, 0.0D, 1.0D, 2.0D, 1.0D); + + private static String nameOf(Block block) { + return Objects.requireNonNull(Registries.BLOCKS.getIdentifier(block)).getPath(); + } + + private static JsonArray jsonOf(Box box) { + JsonArray arr = new JsonArray(); + if (box == null) return arr; + arr.add(box.minX); + arr.add(box.minY); + arr.add(box.minZ); + arr.add(box.maxX); + arr.add(box.maxY); + arr.add(box.maxZ); + return arr; + } + + @Override + public String getDataName() { + return "blockCollisionShapes"; + } + + @Override + public JsonObject generateDataJson() { + ShapeCache shapeCache = new ShapeCache(); + JsonObject blocksObject = new JsonObject(); + for (Block block : Registries.BLOCKS) { + Object val = shapeCache.addShapesFrom(block); + if (val instanceof JsonArray) { + blocksObject.add(nameOf(block), (JsonElement) val); + } else { + blocksObject.addProperty(nameOf(block), (Integer) val); + } + } + JsonObject resultObject = new JsonObject(); + resultObject.add("blocks", blocksObject); + resultObject.add("shapes", shapeCache.toJSON()); + return resultObject; + } + + public static class ShapeCache { + private final ArrayList shapesCache = new ArrayList<>(); + + public Object addShapesFrom(Block block) { + List indexesOfBoxesInTheShapesCache = new ArrayList<>(); + for (BlockState state : block.getStateManager().getBlockStates().reverse()) { + List boxes = new ArrayList<>(); + try { + state.appendCollisionBoxes(DGU.getWorld(), BlockPos.ORIGIN, ENTITY_BOX, boxes, null, true); + } catch (Exception e) { + e.printStackTrace(); + } + Shapes thisBlockStateShapes = new Shapes(boxes); + int indexOfThisBlockStatesShapes = shapesCache.indexOf(thisBlockStateShapes); + if (indexOfThisBlockStatesShapes != -1) { + indexesOfBoxesInTheShapesCache.add(indexOfThisBlockStatesShapes); + } else { + shapesCache.add(thisBlockStateShapes); + indexesOfBoxesInTheShapesCache.add(shapesCache.size() - 1); + } + } + if (indexesOfBoxesInTheShapesCache.stream().distinct().count() < 2) { + return indexesOfBoxesInTheShapesCache.get(0); + } else { + JsonArray shapeIndexes = new JsonArray(); + indexesOfBoxesInTheShapesCache.forEach(shapeIndexes::add); + return shapeIndexes; + } + } + + public JsonObject toJSON() { + JsonObject shapes = new JsonObject(); + int i = 0; + for (Shapes s : shapesCache) { + shapes.add(String.valueOf(i++), s.toJSON()); + } + return shapes; + } + + private static class Shapes { + final List boxes; + + public Shapes(List boxes) { + this.boxes = boxes; + } + + public JsonArray toJSON() { + JsonArray arr = new JsonArray(); + boxes.forEach(box -> arr.add(jsonOf(box))); + return arr; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Shapes shapes = (Shapes) o; + return Objects.equals(boxes, shapes.boxes); + } + + @Override + public int hashCode() { + return boxes != null ? boxes.hashCode() : 0; + } + } + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java new file mode 100644 index 00000000..cdc6711d --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java @@ -0,0 +1,146 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.base.CaseFormat; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.mixin.BlockAccessor; +import dev.u9g.minecraftdatagenerator.mixin.MiningToolItemAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.block.AirBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.TransparentBlock; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ToolItem; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.state.property.EnumProperty; +import net.minecraft.state.property.IntProperty; +import net.minecraft.state.property.Property; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.logging.Logger; + +public class BlocksDataGenerator implements IDataGenerator { + + private static final Logger logger = Main.LOGGER; + + private static List getItemsEffectiveForBlock(Block block) { + List items = new ArrayList<>(); + for (Item item : Registries.ITEMS) { + if (item instanceof ToolItem && ((MiningToolItemAccessor) item).getEffectiveBlocks().contains(block)) { + items.add(item); + } + } + return items; + } + + private static List populateDropsIfPossible(BlockState blockState, Item firstToolItem) { + return new ArrayList<>(); + } + + private static String getPropertyTypeName(Property property) { + //Explicitly handle default minecraft properties + if (property instanceof BooleanProperty) { + return "bool"; + } + if (property instanceof IntProperty) { + return "int"; + } + if (property instanceof EnumProperty) { + return "enum"; + } + + //Use simple class name as fallback, this code will give something like + //example_type for ExampleTypeProperty class name + String rawPropertyName = property.getClass().getSimpleName().replace("Property", ""); + return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, rawPropertyName); + } + + private static > JsonObject generateStateProperty(Property property) { + JsonObject propertyObject = new JsonObject(); + Collection propertyValues = property.getValues(); + + propertyObject.addProperty("name", property.getName()); + propertyObject.addProperty("type", getPropertyTypeName(property)); + propertyObject.addProperty("num_values", propertyValues.size()); + + //Do not add values for vanilla boolean properties, they are known by default + if (!(property instanceof BooleanProperty)) { + JsonArray propertyValuesArray = new JsonArray(); + for (T propertyValue : propertyValues) { + propertyValuesArray.add(property.name(propertyValue)); + } + propertyObject.add("values", propertyValuesArray); + } + return propertyObject; + } + + public static JsonObject generateBlock(Block block) { + JsonObject blockDesc = new JsonObject(); + + List blockStates = block.getStateManager().getBlockStates(); + BlockState defaultState = block.getDefaultState(); + Identifier registryKey = Registries.BLOCKS.getIdentifier(block); + List effectiveTools = getItemsEffectiveForBlock(block); + + blockDesc.addProperty("id", Registries.BLOCKS.getRawId(block)); + blockDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + blockDesc.addProperty("displayName", block.getTranslatedName()); + + float hardness = block.getDefaultState().getHardness(null, null); + + blockDesc.addProperty("hardness", hardness); + blockDesc.addProperty("resistance", ((BlockAccessor) block).getBlastResistance()); + blockDesc.addProperty("stackSize", Item.fromBlock(block).getMaxCount()); + blockDesc.addProperty("diggable", hardness != -1.0f && !(block instanceof AirBlock)); + JsonObject effTools = new JsonObject(); + effectiveTools.forEach(item -> effTools.addProperty( + String.valueOf(Registries.ITEMS.getRawId(item)), // key + item.getBlockBreakingSpeed(DGU.stackFor(item), defaultState) // value + )); + blockDesc.add("effectiveTools", effTools); + blockDesc.addProperty("transparent", block instanceof TransparentBlock); + blockDesc.addProperty("emitLight", defaultState.getLuminance()); + blockDesc.addProperty("filterLight", block.getDefaultState().getOpacity()); + + JsonArray stateProperties = new JsonArray(); + for (Property property : block.getStateManager().getProperties()) { + stateProperties.add(generateStateProperty(property)); + } + blockDesc.add("states", stateProperties); + blockDesc.add("drops", new JsonArray()); + blockDesc.addProperty("boundingBox", boundingBox(block, defaultState)); + + return blockDesc; + } + + private static String boundingBox(Block block, BlockState state) { + if (block.getCollisionBox(state, EmptyBlockView.INSTANCE, BlockPos.ORIGIN) == null) { + return "empty"; + } + return "block"; + } + + @Override + public String getDataName() { + return "blocks"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultBlocksArray = new JsonArray(); + for (Block block : Registries.BLOCKS) { + resultBlocksArray.add(generateBlock(block)); + } + return resultBlocksArray; + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java new file mode 100644 index 00000000..273d67ca --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java @@ -0,0 +1,80 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonWriter; +import dev.u9g.minecraftdatagenerator.Main; + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class DataGenerators { + + private static final List GENERATORS = new ArrayList<>(); + private static final Logger logger = Main.LOGGER; + + static { + register(new BiomesDataGenerator()); + register(new BlockCollisionShapesDataGenerator()); + register(new BlocksDataGenerator()); + register(new EffectsDataGenerator()); + register(new EnchantmentsDataGenerator()); + register(new EntitiesDataGenerator()); + register(new FoodsDataGenerator()); + register(new ItemsDataGenerator()); + register(new ParticlesDataGenerator()); + register(new TintsDataGenerator()); + register(new LanguageDataGenerator()); + register(new InstrumentsDataGenerator()); + register(new AttributesDataGenerator()); + } + + public static void register(IDataGenerator generator) { + GENERATORS.add(generator); + } + + public static boolean runDataGenerators(Path outputDirectory) { + try { + Files.createDirectories(outputDirectory); + } catch (IOException exception) { + logger.log(Level.INFO, "Failed to create data generator output directory at " + outputDirectory); + exception.printStackTrace(); + return false; + } + + int generatorsFailed = 0; + logger.log(Level.INFO, MessageFormat.format("Running minecraft data generators, output at {0}", outputDirectory)); + + for (IDataGenerator dataGenerator : GENERATORS) { + logger.log(Level.INFO, MessageFormat.format("Running generator {0}", dataGenerator.getDataName())); + try { + String outputFileName = String.format("%s.json", dataGenerator.getDataName()); + JsonElement outputElement = dataGenerator.generateDataJson(); + Path outputFilePath = outputDirectory.resolve(outputFileName); + + try (Writer writer = Files.newBufferedWriter(outputFilePath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + JsonWriter jsonWriter = new JsonWriter(writer); + jsonWriter.setIndent(" "); + Streams.write(outputElement, jsonWriter); + } + logger.log(Level.INFO, MessageFormat.format("Generator: {0} -> {1}", dataGenerator.getDataName(), outputFileName)); + + } catch (Throwable exception) { + logger.log(Level.INFO, MessageFormat.format("Failed to run data generator {0}", dataGenerator.getDataName())); + exception.printStackTrace(); + generatorsFailed++; + } + } + + logger.log(Level.INFO, "Finishing running data generators"); + return generatorsFailed == 0; + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java new file mode 100644 index 00000000..6aba51e0 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java @@ -0,0 +1,50 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.StatusEffectAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.util.Identifier; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.Collectors; + +public class EffectsDataGenerator implements IDataGenerator { + + public static JsonObject generateEffect(StatusEffect statusEffect) { + JsonObject effectDesc = new JsonObject(); + @NotNull Identifier registryKey = Objects.requireNonNull(Registries.STATUS_EFFECTS.getIdentifier(statusEffect)); + + effectDesc.addProperty("id", Registries.STATUS_EFFECTS.getRawId(statusEffect)); + if (statusEffect == StatusEffects.UNLUCK) { + effectDesc.addProperty("name", "BadLuck"); + effectDesc.addProperty("displayName", "Bad Luck"); + } else { + effectDesc.addProperty("name", Arrays.stream(registryKey.getPath().split("_")).map(StringUtils::capitalize).collect(Collectors.joining())); + effectDesc.addProperty("displayName", DGU.translateText(statusEffect.getTranslationKey())); + } + + effectDesc.addProperty("type", !((StatusEffectAccessor) statusEffect).negative() ? "good" : "bad"); + return effectDesc; + } + + @Override + public String getDataName() { + return "effects"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + for (StatusEffect effect : Registries.STATUS_EFFECTS) { + resultsArray.add(generateEffect(effect)); + } + return resultsArray; + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java new file mode 100644 index 00000000..ff40e321 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java @@ -0,0 +1,109 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.util.Identifier; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +public class EnchantmentsDataGenerator implements IDataGenerator { + + private static final ImmutableMap ENCHANTMENT_TARGET_NAMES = ImmutableMap.builder() + .put(EnchantmentTarget.ALL_ARMOR, "armor") + .put(EnchantmentTarget.FEET, "armor_feet") + .put(EnchantmentTarget.LEGS, "armor_legs") + .put(EnchantmentTarget.ARMOR_CHEST, "armor_chest") + .put(EnchantmentTarget.HEAD, "armor_head") + .put(EnchantmentTarget.WEAPON, "weapon") + .put(EnchantmentTarget.DIGGER, "digger") + .put(EnchantmentTarget.FISHING_ROD, "fishing_rod") + .put(EnchantmentTarget.BREAKABLE, "breakable") + .put(EnchantmentTarget.BOW, "bow") + .put(EnchantmentTarget.WEARABLE, "wearable") + .put(EnchantmentTarget.ALL, "vanishable") // according to VanishingCurseEnchantment + .build(); + + public static String getEnchantmentTargetName(EnchantmentTarget target) { + return ENCHANTMENT_TARGET_NAMES.getOrDefault(target, target.name().toLowerCase(Locale.ROOT)); + } + + // Equation enchantment costs follow is a * level + b, so we can easily retrieve a and b by passing zero level + private static JsonObject generateEnchantmentMinPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMinimumPower(0); + int a = enchantment.getMinimumPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + private static JsonObject generateEnchantmentMaxPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMaximumPower(0); + int a = enchantment.getMaximumPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + public static JsonObject generateEnchantment(Enchantment enchantment) { + JsonObject enchantmentDesc = new JsonObject(); + Identifier registryKey = Registries.ENCHANTMENTS.getIdentifier(enchantment); + + enchantmentDesc.addProperty("id", Registries.ENCHANTMENTS.getRawId(enchantment)); + enchantmentDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + enchantmentDesc.addProperty("displayName", DGU.translateText(enchantment.getTranslationKey())); + + enchantmentDesc.addProperty("maxLevel", enchantment.getMaximumLevel()); + enchantmentDesc.add("minCost", generateEnchantmentMinPowerCoefficients(enchantment)); + enchantmentDesc.add("maxCost", generateEnchantmentMaxPowerCoefficients(enchantment)); + + enchantmentDesc.addProperty("treasureOnly", enchantment.isTreasure()); + enchantmentDesc.addProperty("curse", enchantment.isCursed()); + + List incompatibleEnchantments = new ArrayList<>(); + for (Enchantment other : Registries.ENCHANTMENTS) { + if (!enchantment.isDifferent(other) && other != enchantment) { + incompatibleEnchantments.add(other); + } + } + + JsonArray excludes = new JsonArray(); + for (Enchantment excludedEnchantment : incompatibleEnchantments) { + Identifier otherKey = Registries.ENCHANTMENTS.getIdentifier(excludedEnchantment); + excludes.add(Objects.requireNonNull(otherKey).getPath()); + } + enchantmentDesc.add("exclude", excludes); + + enchantmentDesc.addProperty("category", getEnchantmentTargetName(enchantment.target)); + enchantmentDesc.addProperty("weight", enchantment.getRarity().getChance()); + enchantmentDesc.addProperty("tradeable", true); // the first non-tradeable enchant came in 1.16, soul speed + enchantmentDesc.addProperty("discoverable", true); // the first non-enchantable enchant came in 1.16, soul speed + + return enchantmentDesc; + } + + @Override + public String getDataName() { + return "enchantments"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + for (Enchantment enchantment : Registries.ENCHANTMENTS) { + resultsArray.add(generateEnchantment(enchantment)); + } + return resultsArray; + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java new file mode 100644 index 00000000..0b375d55 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java @@ -0,0 +1,138 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.mob.AmbientEntity; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.mob.WaterCreatureEntity; +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.passive.PassiveEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.projectile.FishingBobberEntity; +import net.minecraft.entity.projectile.Projectile; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +public class EntitiesDataGenerator implements IDataGenerator { + + public static JsonObject generateEntity(Class entityClass) { + JsonObject entityDesc = new JsonObject(); + Identifier registryKey = Registries.ENTITY_TYPES.getIdentifier(entityClass); + int entityRawId = Registries.ENTITY_TYPES.getRawId(entityClass); + @Nullable Entity entity = makeEntity(entityClass); + // FIXME: ENTITY ID IS WRONG + int id = entityId(entity); + entityDesc.addProperty("id", id); + entityDesc.addProperty("internalId", id); + entityDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + if (entity != null) entityDesc.addProperty("displayName", DGU.translateText(entity.getTranslationKey())); + entityDesc.addProperty("width", entity == null ? 0 : entity.width); + entityDesc.addProperty("height", entity == null ? 0 : entity.height); + + String entityTypeString = "UNKNOWN"; + entityTypeString = getEntityTypeForClass(entityClass); + entityDesc.addProperty("type", entityTypeString); + entityDesc.addProperty("category", getCategoryFrom(entityClass)); + + return entityDesc; + } + + private static Entity makeEntity(Class type) { + return EntityType.createInstanceFromClass(type, DGU.getWorld()); + } + + private static String getCategoryFrom(@NotNull Class entityClass) { + if (entityClass == PlayerEntity.class) return "other"; // fail early for player entities + String packageName = entityClass.getPackage().getName(); + String category = switch (packageName) { + case "net.minecraft.entity.decoration", "net.minecraft.entity.decoration.painting" -> "Immobile"; + case "net.minecraft.entity.boss", "net.minecraft.entity.mob", "net.minecraft.entity.boss.dragon" -> + "Hostile mobs"; + case "net.minecraft.entity.projectile", "net.minecraft.entity.thrown" -> "Projectiles"; + case "net.minecraft.entity.passive" -> "Passive mobs"; + case "net.minecraft.entity.vehicle" -> "Vehicles"; + case "net.minecraft.entity" -> "other"; + default -> throw new Error("Unexpected entity type: " + packageName); + }; + return category; + } + + //Honestly, both "type" and "category" fields in the schema and examples do not contain any useful information + //Since category is optional, I will just leave it out, and for type I will assume general entity classification + //by the Entity class hierarchy (which has some weirdness too by the way) + private static String getEntityTypeForClass(Class entityClass) { + //Top-level classifications + if (WaterCreatureEntity.class.isAssignableFrom(entityClass)) { + return "water_creature"; + } + if (AnimalEntity.class.isAssignableFrom(entityClass)) { + return "animal"; + } + if (HostileEntity.class.isAssignableFrom(entityClass)) { + return "hostile"; + } + if (AmbientEntity.class.isAssignableFrom(entityClass)) { + return "ambient"; + } + + //Second level classifications. PathAwareEntity is not included because it + //doesn't really make much sense to categorize by it + if (PassiveEntity.class.isAssignableFrom(entityClass)) { + return "passive"; + } + if (MobEntity.class.isAssignableFrom(entityClass)) { + return "mob"; + } + + //Other classifications only include living entities and projectiles. everything else is categorized as other + if (LivingEntity.class.isAssignableFrom(entityClass)) { + return "living"; + } + if (Projectile.class.isAssignableFrom(entityClass)) { + return "projectile"; + } + return "other"; + } + + private static int entityId(Entity entity) { + if (!DGU.getCurrentlyRunningServer().getVersion().equals("1.12.2")) { + throw new Error("These ids were gotten manually for 1.12.2, remake for " + DGU.getCurrentlyRunningServer().getVersion()); + } + int rawId = Registries.ENTITY_TYPES.getRawId(entity.getClass()); + if (rawId == -1) { // see TrackedEntityInstance + if (entity instanceof ItemEntity) { + return 1; + } else if (entity instanceof FishingBobberEntity) { + return 90; + } else { + throw new Error("unable to find rawId for entity: " + entity.getEntityName()); + } + } + return rawId; + } + + @Override + public String getDataName() { + return "entities"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + for (Class entityType : Registries.ENTITY_TYPES) { + resultArray.add(generateEntity(entityType)); + } + return resultArray; + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java new file mode 100644 index 00000000..66c53790 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java @@ -0,0 +1,50 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.item.FoodItem; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; + +import java.util.Objects; + +public class FoodsDataGenerator implements IDataGenerator { + + public static JsonObject generateFoodDescriptor(FoodItem foodItem) { + JsonObject foodDesc = new JsonObject(); + Identifier registryKey = Registries.ITEMS.getIdentifier(foodItem); + + foodDesc.addProperty("id", Registries.ITEMS.getRawId(foodItem)); + foodDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + foodDesc.addProperty("stackSize", foodItem.getMaxCount()); + foodDesc.addProperty("displayName", DGU.translateText(foodItem.getTranslationKey())); + float foodPoints = foodItem.getHungerPoints(DGU.stackFor(foodItem)); + float saturationRatio = foodItem.getSaturation(DGU.stackFor(foodItem)) * 2.0F; + float saturation = foodPoints * saturationRatio; + + foodDesc.addProperty("foodPoints", foodPoints); + foodDesc.addProperty("saturation", saturation); + + foodDesc.addProperty("effectiveQuality", foodPoints + saturation); + foodDesc.addProperty("saturationRatio", saturationRatio); + return foodDesc; + } + + @Override + public String getDataName() { + return "foods"; + } + + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + for (Item item : Registries.ITEMS) { + if (item instanceof FoodItem) { + resultsArray.add(generateFoodDescriptor((FoodItem) item)); + } + } + return resultsArray; + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java new file mode 100644 index 00000000..81fe4e20 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; + +public interface IDataGenerator { + + String getDataName(); + + JsonElement generateDataJson(); +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java new file mode 100644 index 00000000..3aa51c41 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java @@ -0,0 +1,30 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.NoteBlockAccessor; +import dev.u9g.minecraftdatagenerator.mixin.SoundAccessor; +import net.minecraft.client.sound.SoundEvent; + +import java.util.Objects; + +public class InstrumentsDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "instruments"; + } + + @Override + public JsonElement generateDataJson() { + JsonArray array = new JsonArray(); + int i = 0; + for (SoundEvent sound : Objects.requireNonNull(NoteBlockAccessor.TUNES())) { + JsonObject object = new JsonObject(); + object.addProperty("id", i++); + object.addProperty("name", ((SoundAccessor) sound).id().getPath().split("\\.")[2]); + array.add(object); + } + return array; + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java new file mode 100644 index 00000000..069a6f83 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java @@ -0,0 +1,87 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class ItemsDataGenerator implements IDataGenerator { + + private static List calculateItemsToRepairWith(Item sourceItem) { + List items = new ArrayList<>(); + for (Item otherItem : Registries.ITEMS) { + if (sourceItem.canRepair(DGU.stackFor(sourceItem), DGU.stackFor(otherItem))) { + items.add(otherItem); + } + } + return items; + } + + private static List getApplicableEnchantmentTargets(Item sourceItem) { + List targets = new ArrayList<>(); + for (EnchantmentTarget target : EnchantmentTarget.values()) { + if (!target.isCompatible(sourceItem)) continue; + targets.add(target); + } + return targets; + } + + public static JsonObject generateItem(Item item) { + JsonObject itemDesc = new JsonObject(); + Identifier registryKey = Registries.ITEMS.getIdentifier(item); + + itemDesc.addProperty("id", Registries.ITEMS.getRawId(item)); + itemDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + itemDesc.addProperty("displayName", DGU.translateText(item.getTranslationKey())); + itemDesc.addProperty("stackSize", item.getMaxCount()); + + List enchantmentTargets = getApplicableEnchantmentTargets(item); + + JsonArray enchantCategoriesArray = new JsonArray(); + for (EnchantmentTarget target : enchantmentTargets) { + enchantCategoriesArray.add(EnchantmentsDataGenerator.getEnchantmentTargetName(target)); + } + if (enchantCategoriesArray.size() > 0) { + itemDesc.add("enchantCategories", enchantCategoriesArray); + } + + if (item.isDamageable()) { + List repairWithItems = calculateItemsToRepairWith(item); + + JsonArray fixedWithArray = new JsonArray(); + for (Item repairWithItem : repairWithItems) { + Identifier repairWithName = Registries.ITEMS.getIdentifier(repairWithItem); + fixedWithArray.add(Objects.requireNonNull(repairWithName).getPath()); + } + if (fixedWithArray.size() > 0) { + itemDesc.add("repairWith", fixedWithArray); + } + + int maxDurability = item.getMaxDamage(); + itemDesc.addProperty("maxDurability", maxDurability); + } + return itemDesc; + } + + @Override + public String getDataName() { + return "items"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + for (Item item : Registries.ITEMS) { + resultArray.add(generateItem(item)); + } + return resultArray; + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java new file mode 100644 index 00000000..88e8d2fb --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java @@ -0,0 +1,29 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.LanguageAccessor; +import dev.u9g.minecraftdatagenerator.util.Registries; + +import java.util.Map; + +public class LanguageDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "language"; + } + + @Override + public JsonElement generateDataJson() { + try { + JsonObject obj = new JsonObject(); + Map translations = ((LanguageAccessor) Registries.LANGUAGE).translations(); + for (Map.Entry entry : translations.entrySet()) { + obj.addProperty(entry.getKey(), entry.getValue()); + } + return obj; + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to generate language file"); + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java new file mode 100644 index 00000000..2b1a5085 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java @@ -0,0 +1,31 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.minecraft.client.particle.ParticleType; + +public class ParticlesDataGenerator implements IDataGenerator { + + public static JsonObject generateParticleType(int id, ParticleType particleType) { + JsonObject effectDesc = new JsonObject(); + + effectDesc.addProperty("id", id); + effectDesc.addProperty("name", particleType.getName()); + return effectDesc; + } + + @Override + public String getDataName() { + return "particles"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + int i = 0; + for (ParticleType particleType : ParticleType.values()) { + resultsArray.add(generateParticleType(i++, particleType)); + } + return resultsArray; + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java new file mode 100644 index 00000000..32498dfa --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java @@ -0,0 +1,119 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; + +public class RecipeDataGenerator implements IDataGenerator { + + @Override + public String getDataName() { + return "recipes"; + } + + @Override + public JsonElement generateDataJson() { +// JsonObject finalObj = new JsonObject(); +// Multimap recipes = ArrayListMultimap.create(); +// for (Recipe recipe : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) { +// if (recipe instanceof ShapedRecipe sr) { +// var ingredients = sr.getIngredients(); +// List ingr = new ArrayList<>(); +// for (int i = 0; i < 9; i++) { +// if (i >= ingredients.size()) { +// ingr.add(-1); +// continue; +// } +// var stacks = ingredients.get(i); +//// var matching = stacks.getMatchingStacks(); +//// if (matching.length == 0) { +//// ingr.add(-1); +//// } else { +//// ingr.add(getRawIdFor(matching[0].getItem())); +//// } +// } +// Lists.reverse(ingr); +// +// JsonArray inShape = new JsonArray(); +// +// var iter = ingr.iterator(); +// for (int y = 0; y < 3; y++) { +// var jsonRow = new JsonArray(); +// int one = iter.next(); +// int two = iter.next(); +// int three = iter.next(); +// if (y > 0 && one == -1 && two == -1 && three == -1) continue; +// jsonRow.add(one); +// jsonRow.add(two); +// jsonRow.add(three); +// inShape.add(jsonRow); +// } +// +// JsonObject finalRecipe = new JsonObject(); +// finalRecipe.add("inShape", inShape); +// +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +// resultObject.addProperty("count", sr.getOutput().getCount()); +// finalRecipe.add("result", resultObject); +// +// String id = ((Integer) getRawIdFor(sr.getOutput().getItem())).toString(); +// +// if (!finalObj.has(id)) { +// finalObj.add(id, new JsonArray()); +// } +// finalObj.get(id).getAsJsonArray().add(finalRecipe); +//// var input = new JsonArray(); +//// var ingredients = sr.getIngredients().stream().toList(); +//// for (int y = 0; y < sr.getHeight(); y++) { +//// var arr = new JsonArray(); +//// for (int x = 0; x < sr.getWidth(); x++) { +//// if ((y*3)+x >= ingredients.size()) { +//// arr.add(JsonNull.INSTANCE); +//// continue; +//// } +//// var ingredient = ingredients.get((y*3)+x).getMatchingStacks(); // FIXME: fix when there are more than one matching stack +//// if (ingredient.length == 0) { +//// arr.add(JsonNull.INSTANCE); +//// } else { +//// arr.add(getRawIdFor(ingredient[0].getItem())); +//// } +//// } +//// input.add(arr); +//// } +//// var rootRecipeObject = new JsonObject(); +//// rootRecipeObject.add("inShape", input); +//// var resultObject = new JsonObject(); +//// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +//// resultObject.addProperty("count", sr.getOutput().getCount()); +//// rootRecipeObject.add("result", resultObject); +//// recipes.put(getRawIdFor(sr.getOutput().getItem()), rootRecipeObject); +// } else if (recipe instanceof ShapelessRecipe sl) { + +// var ingredients = new JsonArray(); +// for (Ingredient ingredient : sl.getIngredients()) { +// if (ingredient.isEmpty()) continue; +//// ingredients.add(getRawIdFor(ingredient.getMatchingStacks()[0].getItem())); +// } +// var rootRecipeObject = new JsonObject(); +// rootRecipeObject.add("ingredients", ingredients); +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sl.getOutput().getItem())); +// resultObject.addProperty("count", sl.getOutput().getCount()); +// rootRecipeObject.add("result", resultObject); +// recipes.put(getRawIdFor(sl.getOutput().getItem()), rootRecipeObject); +// } +// } +// recipes.forEach((a, b) -> { +// if (!finalObj.has(a.toString())) { +// finalObj.add(a.toString(), new JsonArray()); +// } +// finalObj.get(a.toString()).getAsJsonArray().add(b); +// }); +// return finalObj; + return JsonNull.INSTANCE; + } +// +// private static int getRawIdFor (Item item) { +// return Registry.ITEM.getRawId(item); +// } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java new file mode 100644 index 00000000..146afba4 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java @@ -0,0 +1,159 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.BlockColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.FoliageColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.GrassColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.ServerSideRedstoneWireBlock; +import dev.u9g.minecraftdatagenerator.mixin.BiomeAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.block.RedstoneWireBlock; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.biome.Biome; + +import java.util.*; + +public class TintsDataGenerator implements IDataGenerator { + + private static final BlockColors blockColors = BlockColors.create(); + + public static BiomeTintColors generateBiomeTintColors() { + BiomeTintColors colors = new BiomeTintColors(); + + for (Biome biome : Registries.BIOMES) { + int biomeGrassColor = GrassColors.getGrassColor(biome); + int biomeFoliageColor = FoliageColors.getFoliageColor(biome); + int biomeWaterColor = ((BiomeAccessor) biome).waterColor(); + + colors.grassColoursMap.computeIfAbsent(biomeGrassColor, k -> new ArrayList<>()).add(biome); + colors.foliageColoursMap.computeIfAbsent(biomeFoliageColor, k -> new ArrayList<>()).add(biome); + colors.waterColourMap.computeIfAbsent(biomeWaterColor, k -> new ArrayList<>()).add(biome); + } + return colors; + } + + public static Map generateRedstoneTintColors() { + Map resultColors = new HashMap<>(); + + for (int redstoneLevel : RedstoneWireBlock.POWER.getValues()) { + int color = ServerSideRedstoneWireBlock.getWireColor(redstoneLevel); + resultColors.put(redstoneLevel, color); + } + return resultColors; + } + + private static int getBlockColor(Block block) { + return blockColors.method_13410(block.getDefaultState(), DGU.getWorld(), BlockPos.ORIGIN); + } + + public static Map generateConstantTintColors() { + Map resultColors = new HashMap<>(); + BlockColors blockColors = BlockColors.create(); + // FIXME: ? + // resultColors.put(Blocks.BIRCH_LEAVES, FoliageColors.getBirchColor()); + // resultColors.put(Blocks.SPRUCE_LEAVES, FoliageColors.getSpruceColor()); + + resultColors.put(Blocks.LILY_PAD, getBlockColor(Blocks.LILY_PAD)); + // FIXME: ? + // resultColors.put(Blocks.ATTACHED_MELON_STEM, getBlockColor(Blocks.ATTACHED_MELON_STEM, blockColors)); + // resultColors.put(Blocks.ATTACHED_PUMPKIN_STEM, getBlockColor(Blocks.ATTACHED_PUMPKIN_STEM, blockColors)); + + //not really constant, depend on the block age, but kinda have to be handled since textures are literally white without them + resultColors.put(Blocks.MELON_STEM, getBlockColor(Blocks.MELON_STEM)); + resultColors.put(Blocks.PUMPKIN_STEM, getBlockColor(Blocks.PUMPKIN_STEM)); + + return resultColors; + } + + private static JsonObject encodeBiomeColorMap(Map> colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry> entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + for (Biome biome : entry.getValue()) { + Identifier registryKey = Registries.BIOMES.getIdentifier(biome); + keysArray.add(Objects.requireNonNull(registryKey).getPath()); + } + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getKey()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeRedstoneColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + keysArray.add(entry.getKey()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeBlocksColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + Identifier registryKey = Registries.BLOCKS.getIdentifier(entry.getKey()); + keysArray.add(Objects.requireNonNull(registryKey).getPath()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + @Override + public String getDataName() { + return "tints"; + } + + @Override + public JsonObject generateDataJson() { + BiomeTintColors biomeTintColors = generateBiomeTintColors(); + Map redstoneColors = generateRedstoneTintColors(); + Map constantTintColors = generateConstantTintColors(); + + JsonObject resultObject = new JsonObject(); + + resultObject.add("grass", encodeBiomeColorMap(biomeTintColors.grassColoursMap)); + resultObject.add("foliage", encodeBiomeColorMap(biomeTintColors.foliageColoursMap)); + resultObject.add("water", encodeBiomeColorMap(biomeTintColors.waterColourMap)); + + resultObject.add("redstone", encodeRedstoneColorMap(redstoneColors)); + resultObject.add("constant", encodeBlocksColorMap(constantTintColors)); + + return resultObject; + } + + public static class BiomeTintColors { + final Map> grassColoursMap = new HashMap<>(); + final Map> foliageColoursMap = new HashMap<>(); + final Map> waterColourMap = new HashMap<>(); + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BiomeAccessor.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BiomeAccessor.java new file mode 100644 index 00000000..1feed6b7 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BiomeAccessor.java @@ -0,0 +1,14 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.world.biome.Biome; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Biome.class) +public interface BiomeAccessor { + @Accessor("waterColor") + int waterColor(); + + @Accessor("name") + String name(); +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BlockAccessor.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BlockAccessor.java new file mode 100644 index 00000000..1c25c003 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BlockAccessor.java @@ -0,0 +1,11 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Block; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Block.class) +public interface BlockAccessor { + @Accessor("blastResistance") + float getBlastResistance(); +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java new file mode 100644 index 00000000..0d297e7a --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.server.dedicated.EulaReader; +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; + +@Mixin(EulaReader.class) +public class EULAMixin { + @Inject(method = "isEulaAgreedTo()Z", at = @At("TAIL"), cancellable = true) + public void init(CallbackInfoReturnable cir) { + cir.setReturnValue(true); + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockAccessor.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockAccessor.java new file mode 100644 index 00000000..5bec8213 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockAccessor.java @@ -0,0 +1,19 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.EndPortalFrameBlock; +import net.minecraft.util.math.Box; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(EndPortalFrameBlock.class) +public interface EndPortalFrameBlockAccessor { + @Accessor("PORTAL_FRAME") + static Box portalFrame() { + throw new IllegalStateException(); + } + + @Accessor("PORTAL_EYE") + static Box eye() { + throw new IllegalStateException(); + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockMixin.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockMixin.java new file mode 100644 index 00000000..1de19b27 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockMixin.java @@ -0,0 +1,26 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.BlockState; +import net.minecraft.block.EndPortalFrameBlock; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +import java.util.List; + +@Mixin(EndPortalFrameBlock.class) +public class EndPortalFrameBlockMixin { + /** + * @author a + * @reason a + */ + @Overwrite() + public void appendCollisionBoxes(BlockState state, World world, BlockPos pos, Box entityBox, List boxes, @Nullable Entity entity, boolean isActualState) { + boxes.add(EndPortalFrameBlockAccessor.portalFrame()); + boxes.add(EndPortalFrameBlockAccessor.eye()); + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/LanguageAccessor.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/LanguageAccessor.java new file mode 100644 index 00000000..7bfd95fc --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/LanguageAccessor.java @@ -0,0 +1,13 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.util.Language; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(Language.class) +public interface LanguageAccessor { + @Accessor("translations") + Map translations(); +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java new file mode 100644 index 00000000..53b8cdb5 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Block; +import net.minecraft.item.ToolItem; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Set; + +@Mixin(ToolItem.class) +public interface MiningToolItemAccessor { + + @Accessor + Set getEffectiveBlocks(); +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/NoteBlockAccessor.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/NoteBlockAccessor.java new file mode 100644 index 00000000..4bc3bcb9 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/NoteBlockAccessor.java @@ -0,0 +1,16 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.NoteBlock; +import net.minecraft.client.sound.SoundEvent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + +@Mixin(NoteBlock.class) +public interface NoteBlockAccessor { + @Accessor("TUNES") + static List TUNES() { + return null; + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java new file mode 100644 index 00000000..5e22dfed --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java @@ -0,0 +1,28 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.generators.DataGenerators; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; +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.nio.file.Path; +import java.util.logging.Level; + +@Mixin(MinecraftDedicatedServer.class) +public class ReadyMixin { + @Inject(method = "setupServer()Z", at = @At("TAIL")) + private void init(CallbackInfoReturnable cir) { + Main.LOGGER.log(Level.INFO, "Starting data generation!"); + String versionName = DGU.getCurrentlyRunningServer().getVersion(); + Path serverRootDirectory = DGU.getCurrentlyRunningServer().getRunDirectory().toPath().toAbsolutePath(); + Path dataDumpDirectory = serverRootDirectory.resolve("minecraft-data").resolve(versionName); + DataGenerators.runDataGenerators(dataDumpDirectory); + Main.LOGGER.log(Level.INFO, "Done data generation!"); + DGU.getCurrentlyRunningServer().stopServer(); + System.exit(0); + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/SoundAccessor.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/SoundAccessor.java new file mode 100644 index 00000000..fe505910 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/SoundAccessor.java @@ -0,0 +1,12 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.client.sound.SoundEvent; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(SoundEvent.class) +public interface SoundAccessor { + @Accessor("id") + Identifier id(); +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java new file mode 100644 index 00000000..33d4cfcd --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java @@ -0,0 +1,11 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.entity.effect.StatusEffect; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(StatusEffect.class) +public interface StatusEffectAccessor { + @Accessor("negative") + boolean negative(); +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java new file mode 100644 index 00000000..bf7137e6 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java @@ -0,0 +1,90 @@ +package dev.u9g.minecraftdatagenerator.util; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapter; +import com.google.gson.internal.bind.TypeAdapters; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.World; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; + +public class DGU { + + public static Gson gson = new GsonBuilder().registerTypeAdapterFactory(TypeAdapters.newFactory(double.class, Double.class, new TypeAdapter() { + @Override + public Number read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + return in.nextDouble(); + } + + @Override + public void write(JsonWriter out, Number value) throws IOException { + out.value(value); + } + })).create(); + + @Environment(EnvType.CLIENT) + private static MinecraftServer getCurrentlyRunningServerClient() { + return MinecraftClient.getInstance().getServer(); + } + + @SuppressWarnings("deprecation") + private static MinecraftServer getCurrentlyRunningServerDedicated() { + return (MinecraftServer) FabricLoader.getInstance().getGameInstance(); + } + + public static MinecraftServer getCurrentlyRunningServer() { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return getCurrentlyRunningServerClient(); + } else if (environmentType == EnvType.SERVER) { + return getCurrentlyRunningServerDedicated(); + } + throw new UnsupportedOperationException(); + } + + @Environment(EnvType.CLIENT) + private static String translateTextClient(String translationKey) { + return I18n.translate(translationKey); + } + + private static String translateTextFallback(String translationKey) { + try { + return Registries.LANGUAGE.translate(translationKey); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to translate: '" + translationKey + "'"); + } + + public static String translateText(String translationKey) { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return translateTextClient(translationKey); + } + return translateTextFallback(translationKey); + } + + @NotNull + public static World getWorld() { + return getCurrentlyRunningServer().getWorld(); + } + + public static ItemStack stackFor(Item ic) { + return new ItemStack(ic); + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java new file mode 100644 index 00000000..449403c7 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java @@ -0,0 +1,49 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.BlockView; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.level.LevelGeneratorType; +import org.jetbrains.annotations.Nullable; + +public enum EmptyBlockView implements BlockView { + INSTANCE; + + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + return null; + } + + @Override + public int getLight(BlockPos pos, int minBlockLight) { + return 0; + } + + public BlockState getBlockState(BlockPos pos) { + return Blocks.AIR.getDefaultState(); + } + + @Override + public boolean isAir(BlockPos pos) { + return false; + } + + @Override + public Biome getBiome(BlockPos pos) { + return null; + } + + @Override + public int getStrongRedstonePower(BlockPos pos, Direction direction) { + return 0; + } + + @Override + public LevelGeneratorType getGeneratorType() { + return null; + } +} diff --git a/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/util/Registries.java b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/util/Registries.java new file mode 100644 index 00000000..adbbaf36 --- /dev/null +++ b/1.12.2/src/main/java/dev/u9g/minecraftdatagenerator/util/Registries.java @@ -0,0 +1,22 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.minecraft.block.Block; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; +import net.minecraft.util.Language; +import net.minecraft.util.registry.SimpleRegistry; +import net.minecraft.world.biome.Biome; + +public class Registries { + public static final SimpleRegistry BIOMES = Biome.REGISTRY; + public static final SimpleRegistry BLOCKS = Block.REGISTRY; + public static final SimpleRegistry ITEMS = Item.REGISTRY; + public static final SimpleRegistry STATUS_EFFECTS = StatusEffect.REGISTRY; + public static final SimpleRegistry ENCHANTMENTS = Enchantment.REGISTRY; + public static final SimpleRegistry> ENTITY_TYPES = EntityType.REGISTRY; + public static final Language LANGUAGE = new Language(); +} diff --git a/1.12.2/src/main/resources/fabric.mod.json b/1.12.2/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..57c11135 --- /dev/null +++ b/1.12.2/src/main/resources/fabric.mod.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": 1, + "id": "minecraft-data-generator", + "version": "${version}", + "name": "Minecraft Data Generator", + "description": "", + "authors": [ + "Archengius", + "U9G" + ], + "contact": {}, + "license": "MIT", + "environment": "server", + "entrypoints": { + "main": [ + "dev.u9g.minecraftdatagenerator.Main" + ] + }, + "mixins": [ + "minecraft-data-generator.mixins.json" + ], + "depends": { + "fabricloader": ">=0.13.3", + "minecraft": ">=1.12" + } +} diff --git a/1.12.2/src/main/resources/minecraft-data-generator.mixins.json b/1.12.2/src/main/resources/minecraft-data-generator.mixins.json new file mode 100644 index 00000000..fcdcd1a0 --- /dev/null +++ b/1.12.2/src/main/resources/minecraft-data-generator.mixins.json @@ -0,0 +1,26 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "dev.u9g.minecraftdatagenerator.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "BiomeAccessor", + "BlockAccessor", + "EndPortalFrameBlockAccessor", + "EndPortalFrameBlockMixin", + "LanguageAccessor", + "MiningToolItemAccessor", + "NoteBlockAccessor", + "ReadyMixin", + "SoundAccessor", + "StatusEffectAccessor" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "server": [ + "EULAMixin" + ] +} diff --git a/1.13/build.gradle b/1.13/build.gradle new file mode 100644 index 00000000..057916c6 --- /dev/null +++ b/1.13/build.gradle @@ -0,0 +1,63 @@ +plugins { + id 'fabric-loom' +} + +repositories { + maven { + name = "legacy-fabric" + url = "https://maven.legacyfabric.net" + } +} + +loom { + setIntermediaryUrl('https://maven.legacyfabric.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar'); + customMinecraftManifest.set("https://meta.legacyfabric.net/v2/manifest/${minecraft_version}") +} + +dependencies { + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API provides hooks for events, item registration, and more. As most mods will need this, it's included by default. + // If you know for a fact you don't, it's not required and can be safely removed. +// modImplementation ("net.legacyfabric.legacy-fabric-api:legacy-fabric-api:${fabric_version}") { +// exclude module: "legacy-fabric-entity-events-v1" +// } + + if (System.getProperty("os.name").toLowerCase().contains("mac")) { + implementation 'org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209' + implementation 'org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209' + implementation 'org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209' + } +} + +if (System.getProperty("os.name").toLowerCase().contains("mac")) { + configurations.configureEach { + resolutionStrategy { + dependencySubstitution { + substitute module('org.lwjgl.lwjgl:lwjgl_util:2.9.2-nightly-201408222') with module('org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209') + substitute module('org.lwjgl.lwjgl:lwjgl:2.9.2-nightly-201408222') with module('org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209') + } + force 'org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209' + } + } +} + +processResources { + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} diff --git a/1.13/gradle.properties b/1.13/gradle.properties new file mode 100644 index 00000000..495d46f4 --- /dev/null +++ b/1.13/gradle.properties @@ -0,0 +1,8 @@ +# Fabric Properties +# More versions available at: https://grayray75.github.io/LegacyFabric-Versions/ +minecraft_version=1.13.2 +yarn_mappings=1.13.2+build.202206020145 +loader_version=0.14.3 +# Dependencies +# More versions available at: https://grayray75.github.io/LegacyFabric-Versions/ +fabric_version = 1.4.2+1.8.9 diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeColors.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeColors.java new file mode 100644 index 00000000..a6105512 --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeColors.java @@ -0,0 +1,49 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.RenderBlockView; +import net.minecraft.world.biome.Biome; + +import java.util.Iterator; + +public class BiomeColors { + private static final ColorProvider field_21146 = Biome::getGrassColor; + private static final ColorProvider field_21147 = Biome::getFoliageColor; + private static final ColorProvider field_21148 = (biome, blockPos) -> biome.getWaterColor(); + private static final ColorProvider field_21149 = (biome, blockPos) -> biome.method_16447(); + + private static int method_19682(RenderBlockView renderBlockView, BlockPos blockPos, ColorProvider colorProvider) { + int i = 0; + int j = 0; + int k = 0; + int l = MinecraftClient.getInstance().options.field_19979; + int m = (l * 2 + 1) * (l * 2 + 1); + + int n; + for (Iterator var8 = BlockPos.mutableIterate(blockPos.getX() - l, blockPos.getY(), blockPos.getZ() - l, blockPos.getX() + l, blockPos.getY(), blockPos.getZ() + l).iterator(); var8.hasNext(); k += n & 255) { + BlockPos.Mutable mutable = var8.next(); + n = colorProvider.getColor(renderBlockView.method_8577(mutable), mutable); + i += (n & 16711680) >> 16; + j += (n & '\uff00') >> 8; + } + + return (i / m & 255) << 16 | (j / m & 255) << 8 | k / m & 255; + } + + public static int method_19681(RenderBlockView renderBlockView, BlockPos blockPos) { + return method_19682(renderBlockView, blockPos, field_21146); + } + + public static int method_19684(RenderBlockView renderBlockView, BlockPos blockPos) { + return method_19682(renderBlockView, blockPos, field_21147); + } + + public static int method_19686(RenderBlockView renderBlockView, BlockPos blockPos) { + return method_19682(renderBlockView, blockPos, field_21148); + } + + interface ColorProvider { + int getColor(Biome biome, BlockPos blockPos); + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColorable.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColorable.java new file mode 100644 index 00000000..280d2eeb --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColorable.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.RenderBlockView; +import org.jetbrains.annotations.Nullable; + +public interface BlockColorable { + int getColor(BlockState state, @Nullable RenderBlockView renderView, @Nullable BlockPos pos, int i); +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColors.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColors.java new file mode 100644 index 00000000..eec0c8e1 --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColors.java @@ -0,0 +1,66 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.block.*; +import net.minecraft.class_2181; +import net.minecraft.class_3721; +import net.minecraft.util.collection.IdList; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.RenderBlockView; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; + +public class BlockColors { + private final IdList BlockColor2Id = new IdList<>(32); + + public BlockColors() { + } + + public static BlockColors create() { + BlockColors blockColors = new BlockColors(); + blockColors.method_12158((blockState, renderBlockView, blockPos, i) -> renderBlockView != null && blockPos != null ? BiomeColors.method_19681(renderBlockView, blockState.getProperty(class_3721.field_18472) == class_2181.field_9374 ? blockPos.down() : blockPos) : -1, Blocks.LARGE_FERN, Blocks.TALL_GRASS); + blockColors.method_12158((blockState, renderBlockView, blockPos, i) -> renderBlockView != null && blockPos != null ? BiomeColors.method_19681(renderBlockView, blockPos) : GrassColors.getColor(0.5D, 1.0D), Blocks.GRASS_BLOCK, Blocks.FERN, Blocks.GRASS, Blocks.POTTED_FERN); + blockColors.method_12158((blockState, renderBlockView, blockPos, i) -> net.minecraft.client.color.world.FoliageColors.getSpruceColor(), Blocks.SPRUCE_LEAVES); + blockColors.method_12158((blockState, renderBlockView, blockPos, i) -> net.minecraft.client.color.world.FoliageColors.getBirchColor(), Blocks.BIRCH_LEAVES); + blockColors.method_12158((blockState, renderBlockView, blockPos, i) -> renderBlockView != null && blockPos != null ? BiomeColors.method_19684(renderBlockView, blockPos) : FoliageColors.getDefaultColor(), Blocks.OAK_LEAVES, Blocks.JUNGLE_LEAVES, Blocks.ACACIA_LEAVES, Blocks.DARK_OAK_LEAVES, Blocks.VINE); + blockColors.method_12158((blockState, renderBlockView, blockPos, i) -> renderBlockView != null && blockPos != null ? BiomeColors.method_19686(renderBlockView, blockPos) : -1, Blocks.WATER, Blocks.BUBBLE_COLUMN, Blocks.CAULDRON); + blockColors.method_12158((blockState, renderBlockView, blockPos, i) -> RedstoneWireBlock.method_8877(blockState.getProperty(RedstoneWireBlock.POWER)), Blocks.REDSTONE_WIRE); + blockColors.method_12158((blockState, renderBlockView, blockPos, i) -> renderBlockView != null && blockPos != null ? BiomeColors.method_19681(renderBlockView, blockPos) : -1, Blocks.SUGAR_CANE); + blockColors.method_12158((blockState, renderBlockView, blockPos, i) -> 14731036, Blocks.ATTACHED_MELON_STEM, Blocks.ATTACHED_PUMPKIN_STEM); + blockColors.method_12158((blockState, renderBlockView, blockPos, i) -> { + int j = blockState.getProperty(AttachedStemBlock.field_18518); + int k = j * 32; + int l = 255 - j * 8; + int m = j * 4; + return k << 16 | l << 8 | m; + }, Blocks.MELON_STEM, Blocks.PUMPKIN_STEM); + blockColors.method_12158((blockState, renderBlockView, blockPos, i) -> renderBlockView != null && blockPos != null ? 2129968 : 7455580, Blocks.LILY_PAD); + return blockColors; + } + + public int method_13410(BlockState blockState, World world, BlockPos blockPos) { + BlockColorable blockColorable = this.BlockColor2Id.fromId(Registry.BLOCK.getRawId(blockState.getBlock())); + if (blockColorable != null) { + return blockColorable.getColor(blockState, null, null, 0); + } else { + MaterialColor materialColor = blockState.method_16892(world, blockPos); + return materialColor != null ? materialColor.color : -1; + } + } + + public int method_18332(BlockState blockState, @Nullable RenderBlockView renderBlockView, @Nullable BlockPos blockPos, int i) { + BlockColorable blockColorable = this.BlockColor2Id.fromId(Registry.BLOCK.getRawId(blockState.getBlock())); + return blockColorable == null ? -1 : blockColorable.getColor(blockState, renderBlockView, blockPos, i); + } + + public void method_12158(BlockColorable blockColorable, Block... blocks) { + int var4 = blocks.length; + + for (int var5 = 0; var5 < var4; ++var5) { + Block block = blocks[var5]; + this.BlockColor2Id.set(blockColorable, Registry.BLOCK.getRawId(block)); + } + + } +} + diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java new file mode 100644 index 00000000..9a7ad297 --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java @@ -0,0 +1,36 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +public class FoliageColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] pixels) { + colorMap = pixels; + } + + private static int getColor(double temperature, double humidity) { + int i = (int) ((1.0 - temperature) * 255.0); + int j = (int) ((1.0 - (humidity *= temperature)) * 255.0); + return colorMap[j << 8 | i]; + } + + public static int getSpruceColor() { + return 0x619961; + } + + public static int getBirchColor() { + return 8431445; + } + + public static int getDefaultColor() { + return 4764952; + } + + public static int getFoliageColor(Biome biome) { + double d = MathHelper.clamp(biome.getTemperature(), 0.0f, 1.0f); + double e = MathHelper.clamp(biome.getRainfall(), 0.0f, 1.0f); + return getColor(d, e); + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java new file mode 100644 index 00000000..d6fe5bd7 --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java @@ -0,0 +1,29 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +public class GrassColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] map) { + colorMap = map; + } + + public static int getColor(double temperature, double humidity) { + int j = (int) ((1.0 - (humidity *= temperature)) * 255.0); + int i = (int) ((1.0 - temperature) * 255.0); + int k = j << 8 | i; + if (k > colorMap.length) { + return -65281; + } + return colorMap[k]; + } + + public static int getGrassColor(Biome biome) { + double d = MathHelper.clamp(biome.getTemperature(), 0.0f, 1.0f); + double e = MathHelper.clamp(biome.getRainfall(), 0.0f, 1.0f); + return getColor(d, e); + } +} + diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java new file mode 100644 index 00000000..c6b2943e --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java @@ -0,0 +1,25 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; + +public class ServerSideRedstoneWireBlock { + public static int getWireColor(int powerLevel) { + float f = (float) powerLevel / 15.0f; + float g = f * 0.6f + 0.4f; + if (powerLevel == 0) { + g = 0.3f; + } + float h = f * f * 0.7f - 0.5f; + float j = f * f * 0.6f - 0.7f; + if (h < 0.0f) { + h = 0.0f; + } + if (j < 0.0f) { + j = 0.0f; + } + int k = MathHelper.clamp((int) (g * 255.0f), 0, 255); + int l = MathHelper.clamp((int) (h * 255.0f), 0, 255); + int m = MathHelper.clamp((int) (j * 255.0f), 0, 255); + return 0xFF000000 | k << 16 | l << 8 | m; + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/Main.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/Main.java new file mode 100644 index 00000000..d23c5ba2 --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/Main.java @@ -0,0 +1,13 @@ +package dev.u9g.minecraftdatagenerator; + +import net.fabricmc.api.ModInitializer; + +import java.util.logging.Logger; + +public class Main implements ModInitializer { + public static final Logger LOGGER = Logger.getLogger("mc-data-gen-serv"); + + @Override + public void onInitialize() { + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java new file mode 100644 index 00000000..133b8ef0 --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java @@ -0,0 +1,141 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; + +import java.util.Locale; +import java.util.Objects; + +public class BiomesDataGenerator implements IDataGenerator { + + private static String guessBiomeDimensionFromCategory(Biome biome) { + String category = switch (biome.getCategory()) { + case NETHER -> "nether"; + case THEEND -> "end"; + default -> "overworld"; + }; + return category; + } + + private static int getBiomeColorFor(String biomeName) { + return switch (biomeName) { + case "ocean" -> 112; + case "plains" -> 9286496; + case "desert" -> 16421912; + case "mountains" -> 6316128; + case "forest" -> 353825; + case "taiga" -> 747097; + case "swamp" -> 522674; + case "river" -> 255; + case "nether_wastes" -> 12532539; + case "the_end" -> 8421631; + case "frozen_ocean" -> 7368918; + case "frozen_river" -> 10526975; + case "snowy_tundra" -> 16777215; + case "snowy_mountains" -> 10526880; + case "mushroom_fields" -> 16711935; + case "mushroom_field_shore" -> 10486015; + case "beach" -> 16440917; + case "desert_hills" -> 13786898; + case "wooded_hills" -> 2250012; + case "taiga_hills" -> 1456435; + case "mountain_edge" -> 7501978; + case "jungle" -> 5470985; + case "jungle_hills" -> 2900485; + case "jungle_edge" -> 6458135; + case "deep_ocean" -> 48; + case "stone_shore" -> 10658436; + case "snowy_beach" -> 16445632; + case "birch_forest" -> 3175492; + case "birch_forest_hills" -> 2055986; + case "dark_forest" -> 4215066; + case "snowy_taiga" -> 3233098; + case "snowy_taiga_hills" -> 2375478; + case "giant_tree_taiga" -> 5858897; + case "giant_tree_taiga_hills" -> 4542270; + case "wooded_mountains" -> 5271632; + case "savanna" -> 12431967; + case "savanna_plateau" -> 10984804; + case "badlands" -> 14238997; + case "wooded_badlands_plateau" -> 11573093; + case "badlands_plateau" -> 13274213; + case "small_end_islands" -> 42; + case "end_midlands" -> 15464630; + case "end_highlands" -> 12828041; + case "end_barrens" -> 9474162; + case "warm_ocean" -> 172; + case "lukewarm_ocean" -> 144; + case "cold_ocean" -> 2105456; + case "deep_warm_ocean" -> 80; + case "deep_lukewarm_ocean" -> 64; + case "deep_cold_ocean" -> 2105400; + case "deep_frozen_ocean" -> 4210832; + case "the_void" -> 0; + case "sunflower_plains" -> 11918216; + case "desert_lakes" -> 16759872; + case "gravelly_mountains" -> 8947848; + case "flower_forest" -> 2985545; + case "taiga_mountains" -> 3378817; + case "swamp_hills" -> 3145690; + case "ice_spikes" -> 11853020; + case "modified_jungle" -> 8102705; + case "modified_jungle_edge" -> 9089855; + case "tall_birch_forest" -> 5807212; + case "tall_birch_hills" -> 4687706; + case "dark_forest_hills" -> 6846786; + case "snowy_taiga_mountains" -> 5864818; + case "giant_spruce_taiga" -> 8490617; + case "giant_spruce_taiga_hills" -> 7173990; + case "modified_gravelly_mountains" -> 7903352; + case "shattered_savanna" -> 15063687; + case "shattered_savanna_plateau" -> 13616524; + case "eroded_badlands" -> 16739645; + case "modified_wooded_badlands_plateau" -> 14204813; + case "modified_badlands_plateau" -> 15905933; + case "bamboo_jungle" -> 7769620; + case "bamboo_jungle_hills" -> 3884810; + case "nether" -> 16711680; + default -> throw new Error("Unexpected biome, with name: '" + biomeName + "'"); + }; + } + + public static JsonObject generateBiomeInfo(Registry registry, Biome biome) { + JsonObject biomeDesc = new JsonObject(); + Identifier registryKey = registry.getId(biome); + String localizationKey = String.format("biome.%s.%s", Objects.requireNonNull(registryKey).getNamespace(), registryKey.getPath()); + + biomeDesc.addProperty("id", registry.getRawId(biome)); + biomeDesc.addProperty("name", registryKey.getPath()); + + biomeDesc.addProperty("category", biome.getCategory().name().toLowerCase(Locale.ENGLISH)); + biomeDesc.addProperty("temperature", biome.getTemperature()); + biomeDesc.addProperty("precipitation", biome.getPrecipitation().name().toLowerCase(Locale.ENGLISH)); + biomeDesc.addProperty("depth", biome.getDepth()); + biomeDesc.addProperty("dimension", guessBiomeDimensionFromCategory(biome)); + biomeDesc.addProperty("displayName", DGU.translateText(localizationKey)); + biomeDesc.addProperty("color", getBiomeColorFor(registryKey.getPath())); + biomeDesc.addProperty("rainfall", biome.getRainfall()); + + return biomeDesc; + } + + @Override + public String getDataName() { + return "biomes"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray biomesArray = new JsonArray(); + Registry biomeRegistry = Registry.BIOME; + + biomeRegistry.stream() + .map(biome -> generateBiomeInfo(biomeRegistry, biome)) + .forEach(biomesArray::add); + return biomesArray; + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java new file mode 100644 index 00000000..573ffce4 --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java @@ -0,0 +1,109 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.shapes.VoxelShape; + +import java.util.*; + +public class BlockCollisionShapesDataGenerator implements IDataGenerator { + + @Override + public String getDataName() { + return "blockCollisionShapes"; + } + + @Override + public JsonObject generateDataJson() { + Registry blockRegistry = Registry.BLOCK; + BlockShapesCache blockShapesCache = new BlockShapesCache(); + for (Block block : (Iterable) blockRegistry) { + blockShapesCache.processBlock(block); + } + + JsonObject resultObject = new JsonObject(); + + resultObject.add("blocks", blockShapesCache.dumpBlockShapeIndices(blockRegistry)); + resultObject.add("shapes", blockShapesCache.dumpShapesObject()); + + return resultObject; + } + + private static class BlockShapesCache { + public final Map uniqueBlockShapes = new HashMap<>(); + public final Map> blockCollisionShapes = new HashMap<>(); + private int lastCollisionShapeId = 0; + + public void processBlock(Block block) { + List blockStates = block.getStateManager().getBlockStates(); + List blockCollisionShapes = new ArrayList<>(); + + for (BlockState blockState : blockStates) { + VoxelShape blockShape = blockState.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + Integer blockShapeIndex = uniqueBlockShapes.get(blockShape); + + if (blockShapeIndex == null) { + blockShapeIndex = lastCollisionShapeId++; + uniqueBlockShapes.put(blockShape, blockShapeIndex); + } + blockCollisionShapes.add(blockShapeIndex); + } + + this.blockCollisionShapes.put(block, blockCollisionShapes); + } + + public JsonObject dumpBlockShapeIndices(Registry blockRegistry) { + JsonObject resultObject = new JsonObject(); + + for (Map.Entry> entry : blockCollisionShapes.entrySet()) { + List blockCollisions = entry.getValue(); + long distinctShapesCount = blockCollisions.stream().distinct().count(); + JsonElement blockCollision; + if (distinctShapesCount == 1L) { + blockCollision = new JsonPrimitive(blockCollisions.get(0)); + } else { + blockCollision = new JsonArray(); + for (int collisionId : blockCollisions) { + ((JsonArray) blockCollision).add(collisionId); + } + } + + Identifier registryKey = blockRegistry.getId(entry.getKey()); + resultObject.add(Objects.requireNonNull(registryKey).getPath(), blockCollision); + } + + return resultObject; + } + + public JsonObject dumpShapesObject() { + JsonObject shapesObject = new JsonObject(); + + for (Map.Entry entry : uniqueBlockShapes.entrySet()) { + JsonArray boxesArray = new JsonArray(); + entry.getKey().forEachBox((x1, y1, z1, x2, y2, z2) -> { + JsonArray oneBoxJsonArray = new JsonArray(); + + oneBoxJsonArray.add(x1); + oneBoxJsonArray.add(y1); + oneBoxJsonArray.add(z1); + + oneBoxJsonArray.add(x2); + oneBoxJsonArray.add(y2); + oneBoxJsonArray.add(z2); + + boxesArray.add(oneBoxJsonArray); + }); + shapesObject.add(Integer.toString(entry.getValue()), boxesArray); + } + return shapesObject; + } + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java new file mode 100644 index 00000000..d9d29c7a --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java @@ -0,0 +1,143 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.base.CaseFormat; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.mixin.MiningToolItemAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import net.minecraft.block.AirBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ToolItem; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.state.property.EnumProperty; +import net.minecraft.state.property.IntProperty; +import net.minecraft.state.property.Property; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.shapes.VoxelShape; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +public class BlocksDataGenerator implements IDataGenerator { + + private static final Logger logger = Main.LOGGER; + + private static List getItemsEffectiveForBlock(Block block) { + return Registry.ITEM.stream() + .filter(item -> item instanceof ToolItem) + .filter(item -> ((MiningToolItemAccessor) item).getEffectiveBlocks().contains(block)) + .collect(Collectors.toList()); + } + + private static List populateDropsIfPossible(BlockState blockState, Item firstToolItem) { + return new ArrayList<>(); + } + + private static String getPropertyTypeName(Property property) { + //Explicitly handle default minecraft properties + if (property instanceof BooleanProperty) { + return "bool"; + } + if (property instanceof IntProperty) { + return "int"; + } + if (property instanceof EnumProperty) { + return "enum"; + } + + //Use simple class name as fallback, this code will give something like + //example_type for ExampleTypeProperty class name + String rawPropertyName = property.getClass().getSimpleName().replace("Property", ""); + return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, rawPropertyName); + } + + private static > JsonObject generateStateProperty(Property property) { + JsonObject propertyObject = new JsonObject(); + Collection propertyValues = property.getValues(); + + propertyObject.addProperty("name", property.getName()); + propertyObject.addProperty("type", getPropertyTypeName(property)); + propertyObject.addProperty("num_values", propertyValues.size()); + + //Do not add values for vanilla boolean properties, they are known by default + if (!(property instanceof BooleanProperty)) { + JsonArray propertyValuesArray = new JsonArray(); + for (T propertyValue : propertyValues) { + propertyValuesArray.add(property.name(propertyValue)); + } + propertyObject.add("values", propertyValuesArray); + } + return propertyObject; + } + + public static JsonObject generateBlock(Registry blockRegistry, Block block) { + JsonObject blockDesc = new JsonObject(); + + List blockStates = block.getStateManager().getBlockStates(); + BlockState defaultState = block.getDefaultState(); + Identifier registryKey = blockRegistry.getId(block); + String localizationKey = block.getTranslationKey(); + List effectiveTools = getItemsEffectiveForBlock(block); + + blockDesc.addProperty("id", blockRegistry.getRawId(block)); + blockDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + blockDesc.addProperty("displayName", DGU.translateText(localizationKey)); + + float hardness = block.getDefaultState().getHardness(null, null); + + blockDesc.addProperty("hardness", hardness); + blockDesc.addProperty("resistance", block.getBlastResistance()); + blockDesc.addProperty("stackSize", block.getItem().getMaxCount()); + blockDesc.addProperty("diggable", hardness != -1.0f && !(block instanceof AirBlock)); + JsonObject effTools = new JsonObject(); + effectiveTools.forEach(item -> effTools.addProperty( + String.valueOf(Registry.ITEM.getRawId(item)), // key + item.getBlockBreakingSpeed(DGU.stackFor(item), defaultState) // value + )); + blockDesc.add("effectiveTools", effTools); + + blockDesc.addProperty("transparent", !defaultState.isFullOpaque(EmptyBlockView.INSTANCE, BlockPos.ORIGIN)); + blockDesc.addProperty("emitLight", defaultState.getLuminance()); + blockDesc.addProperty("filterLight", block.getLightSubtracted(block.getDefaultState(), EmptyBlockView.INSTANCE, BlockPos.ORIGIN)); + + blockDesc.addProperty("defaultState", Block.getRawIdFromState(defaultState)); + blockDesc.addProperty("minStateId", Block.getRawIdFromState(blockStates.get(0))); + blockDesc.addProperty("maxStateId", Block.getRawIdFromState(blockStates.get(blockStates.size() - 1))); + JsonArray stateProperties = new JsonArray(); + for (Property property : block.getStateManager().getProperties()) { + stateProperties.add(generateStateProperty(property)); + } + blockDesc.add("states", stateProperties); + blockDesc.add("drops", new JsonArray()); + VoxelShape blockCollisionShape = defaultState.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + blockDesc.addProperty("boundingBox", blockCollisionShape.isEmpty() ? "empty" : "block"); + + return blockDesc; + } + + @Override + public String getDataName() { + return "blocks"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultBlocksArray = new JsonArray(); + Registry blockRegistry = Registry.BLOCK; + for (Block block : (Iterable) blockRegistry) { + resultBlocksArray.add(generateBlock(blockRegistry, block)); + } + return resultBlocksArray; + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java new file mode 100644 index 00000000..3c05c30f --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java @@ -0,0 +1,79 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonWriter; +import dev.u9g.minecraftdatagenerator.Main; + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class DataGenerators { + + private static final List GENERATORS = new ArrayList<>(); + private static final Logger logger = Main.LOGGER; + + static { + register(new BiomesDataGenerator()); + register(new BlockCollisionShapesDataGenerator()); + register(new BlocksDataGenerator()); + register(new EffectsDataGenerator()); + register(new EnchantmentsDataGenerator()); + register(new EntitiesDataGenerator()); + register(new FoodsDataGenerator()); + register(new ItemsDataGenerator()); + register(new ParticlesDataGenerator()); + register(new TintsDataGenerator()); + register(new LanguageDataGenerator()); + register(new InstrumentsDataGenerator()); + } + + public static void register(IDataGenerator generator) { + GENERATORS.add(generator); + } + + public static boolean runDataGenerators(Path outputDirectory) { + try { + Files.createDirectories(outputDirectory); + } catch (IOException exception) { + logger.log(Level.INFO, "Failed to create data generator output directory at " + outputDirectory); + exception.printStackTrace(); + return false; + } + + int generatorsFailed = 0; + logger.log(Level.INFO, MessageFormat.format("Running minecraft data generators, output at {0}", outputDirectory)); + + for (IDataGenerator dataGenerator : GENERATORS) { + logger.log(Level.INFO, MessageFormat.format("Running generator {0}", dataGenerator.getDataName())); + try { + String outputFileName = String.format("%s.json", dataGenerator.getDataName()); + JsonElement outputElement = dataGenerator.generateDataJson(); + Path outputFilePath = outputDirectory.resolve(outputFileName); + + try (Writer writer = Files.newBufferedWriter(outputFilePath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + JsonWriter jsonWriter = new JsonWriter(writer); + jsonWriter.setIndent(" "); + Streams.write(outputElement, jsonWriter); + } + logger.log(Level.INFO, MessageFormat.format("Generator: {0} -> {1}", dataGenerator.getDataName(), outputFileName)); + + } catch (Throwable exception) { + logger.log(Level.INFO, MessageFormat.format("Failed to run data generator {0}", dataGenerator.getDataName())); + exception.printStackTrace(); + generatorsFailed++; + } + } + + logger.log(Level.INFO, "Finishing running data generators"); + return generatorsFailed == 0; + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java new file mode 100644 index 00000000..33d93eb2 --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java @@ -0,0 +1,51 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.StatusEffectAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.Collectors; + +public class EffectsDataGenerator implements IDataGenerator { + + public static JsonObject generateEffect(Registry registry, StatusEffect statusEffect) { + JsonObject effectDesc = new JsonObject(); + @NotNull Identifier registryKey = Objects.requireNonNull(registry.getId(statusEffect)); + + effectDesc.addProperty("id", registry.getRawId(statusEffect)); + if (statusEffect == StatusEffects.UNLUCK) { + effectDesc.addProperty("name", "BadLuck"); + effectDesc.addProperty("displayName", "Bad Luck"); + } else { + effectDesc.addProperty("name", Arrays.stream(registryKey.getPath().split("_")).map(StringUtils::capitalize).collect(Collectors.joining())); + effectDesc.addProperty("displayName", DGU.translateText(statusEffect.getTranslationKey())); + } + + effectDesc.addProperty("type", !((StatusEffectAccessor) statusEffect).negative() ? "good" : "bad"); + return effectDesc; + } + + @Override + public String getDataName() { + return "effects"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry statusEffectRegistry = Registry.MOB_EFFECT; + for (StatusEffect effect : (Iterable) statusEffectRegistry) { + resultsArray.add(generateEffect(statusEffectRegistry, effect)); + } + return resultsArray; + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java new file mode 100644 index 00000000..51f508d6 --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java @@ -0,0 +1,114 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +public class EnchantmentsDataGenerator implements IDataGenerator { + + private static final ImmutableMap ENCHANTMENT_TARGET_NAMES = ImmutableMap.builder() + .put(EnchantmentTarget.ALL_ARMOR, "armor") + .put(EnchantmentTarget.FEET, "armor_feet") + .put(EnchantmentTarget.LEGS, "armor_legs") + .put(EnchantmentTarget.ARMOR_CHEST, "armor_chest") + .put(EnchantmentTarget.HEAD, "armor_head") + .put(EnchantmentTarget.WEAPON, "weapon") + .put(EnchantmentTarget.DIGGER, "digger") + .put(EnchantmentTarget.FISHING_ROD, "fishing_rod") + .put(EnchantmentTarget.TRIDENT, "trident") + .put(EnchantmentTarget.BREAKABLE, "breakable") + .put(EnchantmentTarget.BOW, "bow") + .put(EnchantmentTarget.WEARABLE, "wearable") + .put(EnchantmentTarget.ALL, "vanishable") // according to VanishingCurseEnchantment + .build(); + + public static String getEnchantmentTargetName(EnchantmentTarget target) { + return ENCHANTMENT_TARGET_NAMES.getOrDefault(target, target.name().toLowerCase(Locale.ROOT)); + } + + // Equation enchantment costs follow is a * level + b, so we can easily retrieve a and b by passing zero level + private static JsonObject generateEnchantmentMinPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMinimumPower(0); + int a = enchantment.getMinimumPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + private static JsonObject generateEnchantmentMaxPowerCoefficients(Enchantment enchantment) { + int b = getMaximumPower(enchantment, 0); + int a = getMaximumPower(enchantment, 1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + public static JsonObject generateEnchantment(Registry registry, Enchantment enchantment) { + JsonObject enchantmentDesc = new JsonObject(); + Identifier registryKey = registry.getId(enchantment); + + enchantmentDesc.addProperty("id", registry.getRawId(enchantment)); + enchantmentDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + enchantmentDesc.addProperty("displayName", DGU.translateText(enchantment.getTranslationKey())); + + enchantmentDesc.addProperty("maxLevel", enchantment.getMaximumLevel()); + enchantmentDesc.add("minCost", generateEnchantmentMinPowerCoefficients(enchantment)); + enchantmentDesc.add("maxCost", generateEnchantmentMaxPowerCoefficients(enchantment)); + + enchantmentDesc.addProperty("treasureOnly", enchantment.isTreasure()); + enchantmentDesc.addProperty("curse", enchantment.isCursed()); + + List incompatibleEnchantments = new ArrayList<>(); + for (Enchantment other : (Iterable) registry) { + if (enchantment.isDifferent(other) && other != enchantment) { + incompatibleEnchantments.add(other); + } + } + + JsonArray excludes = new JsonArray(); + for (Enchantment excludedEnchantment : incompatibleEnchantments) { + Identifier otherKey = registry.getId(excludedEnchantment); + excludes.add(Objects.requireNonNull(otherKey).getPath()); + } + enchantmentDesc.add("exclude", excludes); + + enchantmentDesc.addProperty("category", getEnchantmentTargetName(enchantment.target)); + enchantmentDesc.addProperty("weight", enchantment.getRarity().getChance()); + enchantmentDesc.addProperty("tradeable", true); // the first non-tradeable enchant came in 1.16, soul speed + enchantmentDesc.addProperty("discoverable", true); // the first non-enchantable enchant came in 1.16, soul speed + + return enchantmentDesc; + } + + private static int getMaximumPower(Enchantment ench, int level) { + return ench.getMinimumPower(level) + 5; + } + + @Override + public String getDataName() { + return "enchantments"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry enchantmentRegistry = Registry.ENCHANTMENT; + enchantmentRegistry.stream() + .forEach(enchantment -> resultsArray.add(generateEnchantment(enchantmentRegistry, enchantment))); + return resultsArray; + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java new file mode 100644 index 00000000..f56096df --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java @@ -0,0 +1,144 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.mob.AmbientEntity; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.mob.WaterCreatureEntity; +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.passive.PassiveEntity; +import net.minecraft.entity.projectile.Projectile; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.util.Objects; + +public class EntitiesDataGenerator implements IDataGenerator { + + public static JsonObject generateEntity(Registry> entityRegistry, EntityType entityType) { + JsonObject entityDesc = new JsonObject(); + Identifier registryKey = entityRegistry.getId(entityType); + int entityRawId = entityRegistry.getRawId(entityType); + Class entityClass = getEntityClass(entityType); + @Nullable Entity entity = makeEntity(entityType); + + entityDesc.addProperty("id", entityRawId); + entityDesc.addProperty("internalId", entityRawId); + entityDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + entityDesc.addProperty("displayName", DGU.translateText(entityType.getTranslationKey())); + entityDesc.addProperty("width", entity == null ? 0 : entity.width); + entityDesc.addProperty("height", entity == null ? 0 : entity.height); + + String entityTypeString = "UNKNOWN"; + entityTypeString = getEntityTypeForClass(entityClass); + entityDesc.addProperty("type", entityTypeString); + entityDesc.addProperty("category", getCategoryFrom(entityType)); + + return entityDesc; + } + + private static Entity makeEntity(EntityType type) { + Entity entity; + try { + entity = type.spawn(DGU.getWorld()); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + return entity; + } + + private static Class getEntityClass(EntityType entityType) { + Class entityClazz = null; + try { + for (Field field : EntityType.class.getFields()) + if (entityType == field.get(EntityType.class)) + entityClazz = (Class) ((ParameterizedType) TypeToken.get(field.getGenericType()).getType()).getActualTypeArguments()[0]; + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + if (entityClazz == null) throw new RuntimeException("Shouldn't be null..."); + return entityClazz; + } + + private static String getCategoryFrom(@NotNull EntityType entityType) { + if (entityType == EntityType.PLAYER) return "other"; // fail early for player entities + Class entityClazz = getEntityClass(entityType); + String packageName = entityClazz.getPackage().getName(); + String category = switch (packageName) { + case "net.minecraft.entity.decoration", "net.minecraft.entity.decoration.painting" -> "Immobile"; + case "net.minecraft.entity.boss", "net.minecraft.entity.mob", "net.minecraft.entity.boss.dragon" -> + "Hostile mobs"; + case "net.minecraft.entity.projectile", "net.minecraft.entity.thrown" -> "Projectiles"; + case "net.minecraft.entity.passive" -> "Passive mobs"; + case "net.minecraft.entity.vehicle" -> "Vehicles"; + case "net.minecraft.entity" -> "other"; + default -> throw new Error("Unexpected entity type: " + packageName); + }; + return category; + } + + //Honestly, both "type" and "category" fields in the schema and examples do not contain any useful information + //Since category is optional, I will just leave it out, and for type I will assume general entity classification + //by the Entity class hierarchy (which has some weirdness too by the way) + private static String getEntityTypeForClass(Class entityClass) { + //Top-level classifications + if (WaterCreatureEntity.class.isAssignableFrom(entityClass)) { + return "water_creature"; + } + if (AnimalEntity.class.isAssignableFrom(entityClass)) { + return "animal"; + } + if (HostileEntity.class.isAssignableFrom(entityClass)) { + return "hostile"; + } + if (AmbientEntity.class.isAssignableFrom(entityClass)) { + return "ambient"; + } + + //Second level classifications. PathAwareEntity is not included because it + //doesn't really make much sense to categorize by it + if (PassiveEntity.class.isAssignableFrom(entityClass)) { + return "passive"; + } + if (MobEntity.class.isAssignableFrom(entityClass)) { + return "mob"; + } + + //Other classifications only include living entities and projectiles. everything else is categorized as other + if (LivingEntity.class.isAssignableFrom(entityClass)) { + return "living"; + } + if (Projectile.class.isAssignableFrom(entityClass)) { + return "projectile"; + } + return "other"; + } + + @Override + public String getDataName() { + return "entities"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry> entityTypeRegistry = Registry.ENTITY_TYPE; + for (EntityType entityType : (Iterable>) entityTypeRegistry) { + resultArray.add(generateEntity(entityTypeRegistry, entityType)); + } + return resultArray; + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java new file mode 100644 index 00000000..7ae7adfd --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java @@ -0,0 +1,56 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.item.FoodItem; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Objects; + +public class FoodsDataGenerator implements IDataGenerator { + + public static JsonObject generateFoodDescriptor(Registry registry, FoodItem foodItem) { + JsonObject foodDesc = new JsonObject(); + Identifier registryKey = registry.getId(foodItem); + + foodDesc.addProperty("id", registry.getRawId(foodItem)); + foodDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + foodDesc.addProperty("stackSize", foodItem.getMaxCount()); + foodDesc.addProperty("displayName", DGU.translateText(foodItem.getTranslationKey())); + float foodPoints = foodItem.getHungerPoints(getDefaultStack(foodItem)); + float saturationRatio = foodItem.getSaturation(getDefaultStack(foodItem)) * 2.0F; + float saturation = foodPoints * saturationRatio; + + foodDesc.addProperty("foodPoints", foodPoints); + foodDesc.addProperty("saturation", saturation); + + foodDesc.addProperty("effectiveQuality", foodPoints + saturation); + foodDesc.addProperty("saturationRatio", saturationRatio); + return foodDesc; + } + + private static ItemStack getDefaultStack(FoodItem foodItem) { + return new ItemStack(foodItem); + } + + @Override + public String getDataName() { + return "foods"; + } + + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry itemRegistry = Registry.ITEM; + for (Item item : (Iterable) itemRegistry) { + if (item instanceof FoodItem) { + resultsArray.add(generateFoodDescriptor(itemRegistry, (FoodItem) item)); + } + } + return resultsArray; + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java new file mode 100644 index 00000000..81fe4e20 --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; + +public interface IDataGenerator { + + String getDataName(); + + JsonElement generateDataJson(); +} diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java similarity index 96% rename from src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java rename to 1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java index 981aa867..e7b9b54f 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java @@ -1,25 +1,25 @@ -package dev.u9g.minecraftdatagenerator.generators; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import net.minecraft.block.enums.Instrument; - -public class InstrumentsDataGenerator implements IDataGenerator { - @Override - public String getDataName() { - return "instruments"; - } - - @Override - public JsonElement generateDataJson() { - JsonArray array = new JsonArray(); - for (Instrument instrument : Instrument.values()) { - JsonObject object = new JsonObject(); - object.addProperty("id", instrument.ordinal()); - object.addProperty("name", instrument.asString()); - array.add(object); - } - return array; - } -} +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.minecraft.block.enums.Instrument; + +public class InstrumentsDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "instruments"; + } + + @Override + public JsonElement generateDataJson() { + JsonArray array = new JsonArray(); + for (Instrument instrument : Instrument.values()) { + JsonObject object = new JsonObject(); + object.addProperty("id", instrument.ordinal()); + object.addProperty("name", instrument.asString()); + array.add(object); + } + return array; + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java new file mode 100644 index 00000000..f9e1edc2 --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java @@ -0,0 +1,87 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class ItemsDataGenerator implements IDataGenerator { + + private static List calculateItemsToRepairWith(Registry itemRegistry, Item sourceItem) { + ItemStack sourceItemStack = DGU.stackFor(sourceItem); + return itemRegistry.stream() + .filter(otherItem -> sourceItem.canRepair(sourceItemStack, DGU.stackFor(otherItem))) + .collect(Collectors.toList()); + } + + private static List getApplicableEnchantmentTargets(Item sourceItem) { + List targets = new ArrayList<>(); + for (EnchantmentTarget target : EnchantmentTarget.values()) { + if (!target.isCompatible(sourceItem)) continue; + targets.add(target); + } + return targets; + } + + public static JsonObject generateItem(Registry itemRegistry, Item item) { + JsonObject itemDesc = new JsonObject(); + Identifier registryKey = itemRegistry.getId(item); + + itemDesc.addProperty("id", itemRegistry.getRawId(item)); + itemDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + itemDesc.addProperty("displayName", DGU.translateText(item.getTranslationKey())); + itemDesc.addProperty("stackSize", item.getMaxCount()); + + List enchantmentTargets = getApplicableEnchantmentTargets(item); + + JsonArray enchantCategoriesArray = new JsonArray(); + for (EnchantmentTarget target : enchantmentTargets) { + enchantCategoriesArray.add(EnchantmentsDataGenerator.getEnchantmentTargetName(target)); + } + if (enchantCategoriesArray.size() > 0) { + itemDesc.add("enchantCategories", enchantCategoriesArray); + } + + if (item.isDamageable()) { + List repairWithItems = calculateItemsToRepairWith(itemRegistry, item); + + JsonArray fixedWithArray = new JsonArray(); + for (Item repairWithItem : repairWithItems) { + Identifier repairWithName = itemRegistry.getId(repairWithItem); + fixedWithArray.add(Objects.requireNonNull(repairWithName).getPath()); + } + if (fixedWithArray.size() > 0) { + itemDesc.add("repairWith", fixedWithArray); + } + + int maxDurability = item.getMaxDamage(); + itemDesc.addProperty("maxDurability", maxDurability); + } + return itemDesc; + } + + @Override + public String getDataName() { + return "items"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry itemRegistry = Registry.ITEM; + for (Item item : (Iterable) itemRegistry) { + resultArray.add(generateItem(itemRegistry, item)); + } + return resultArray; + } +} diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java similarity index 92% rename from src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java rename to 1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java index ab2d3edb..7a3c6ced 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java @@ -1,26 +1,27 @@ -package dev.u9g.minecraftdatagenerator.generators; - -import com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.Objects; - -public class LanguageDataGenerator implements IDataGenerator { - @Override - public String getDataName() { - return "language"; - } - - @Override - public JsonElement generateDataJson() { - try { - InputStream inputStream = Objects.requireNonNull(this.getClass().getResourceAsStream("/assets/minecraft/lang/en_us.json")); - return new Gson().fromJson(new InputStreamReader(inputStream, StandardCharsets.UTF_8), JsonObject.class); - } catch (Exception ignored) {} - throw new RuntimeException("Failed to generate language file"); - } -} +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +public class LanguageDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "language"; + } + + @Override + public JsonElement generateDataJson() { + try { + InputStream inputStream = Objects.requireNonNull(this.getClass().getResourceAsStream("/assets/minecraft/lang/en_us.json")); + return new Gson().fromJson(new InputStreamReader(inputStream, StandardCharsets.UTF_8), JsonObject.class); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to generate language file"); + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java new file mode 100644 index 00000000..20f0b729 --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java @@ -0,0 +1,36 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.minecraft.client.particle.ParticleType; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Objects; + +public class ParticlesDataGenerator implements IDataGenerator { + + public static JsonObject generateParticleType(Registry> registry, ParticleType particleType) { + JsonObject effectDesc = new JsonObject(); + Identifier registryKey = registry.getId(particleType); + + effectDesc.addProperty("id", registry.getRawId(particleType)); + effectDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + return effectDesc; + } + + @Override + public String getDataName() { + return "particles"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry> particleTypeRegistry = Registry.PARTICLE_TYPE; + for (ParticleType particleType : (Iterable) particleTypeRegistry) { + resultsArray.add(generateParticleType(particleTypeRegistry, particleType)); + } + return resultsArray; + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java new file mode 100644 index 00000000..5e4587dd --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java @@ -0,0 +1,121 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import net.minecraft.item.Item; +import net.minecraft.util.registry.Registry; + +public class RecipeDataGenerator implements IDataGenerator { + + private static int getRawIdFor(Item item) { + return Registry.ITEM.getRawId(item); + } + + @Override + public String getDataName() { + return "recipes"; + } + + @Override + public JsonElement generateDataJson() { +// JsonObject finalObj = new JsonObject(); +// Multimap recipes = ArrayListMultimap.create(); +// for (Recipe recipe : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) { +// if (recipe instanceof ShapedRecipe sr) { +// var ingredients = sr.getIngredients(); +// List ingr = new ArrayList<>(); +// for (int i = 0; i < 9; i++) { +// if (i >= ingredients.size()) { +// ingr.add(-1); +// continue; +// } +// var stacks = ingredients.get(i); +//// var matching = stacks.getMatchingStacks(); +//// if (matching.length == 0) { +//// ingr.add(-1); +//// } else { +//// ingr.add(getRawIdFor(matching[0].getItem())); +//// } +// } +// Lists.reverse(ingr); +// +// JsonArray inShape = new JsonArray(); +// +// var iter = ingr.iterator(); +// for (int y = 0; y < 3; y++) { +// var jsonRow = new JsonArray(); +// int one = iter.next(); +// int two = iter.next(); +// int three = iter.next(); +// if (y > 0 && one == -1 && two == -1 && three == -1) continue; +// jsonRow.add(one); +// jsonRow.add(two); +// jsonRow.add(three); +// inShape.add(jsonRow); +// } +// +// JsonObject finalRecipe = new JsonObject(); +// finalRecipe.add("inShape", inShape); +// +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +// resultObject.addProperty("count", sr.getOutput().getCount()); +// finalRecipe.add("result", resultObject); +// +// String id = ((Integer) getRawIdFor(sr.getOutput().getItem())).toString(); +// +// if (!finalObj.has(id)) { +// finalObj.add(id, new JsonArray()); +// } +// finalObj.get(id).getAsJsonArray().add(finalRecipe); +//// var input = new JsonArray(); +//// var ingredients = sr.getIngredients().stream().toList(); +//// for (int y = 0; y < sr.getHeight(); y++) { +//// var arr = new JsonArray(); +//// for (int x = 0; x < sr.getWidth(); x++) { +//// if ((y*3)+x >= ingredients.size()) { +//// arr.add(JsonNull.INSTANCE); +//// continue; +//// } +//// var ingredient = ingredients.get((y*3)+x).getMatchingStacks(); // FIXME: fix when there are more than one matching stack +//// if (ingredient.length == 0) { +//// arr.add(JsonNull.INSTANCE); +//// } else { +//// arr.add(getRawIdFor(ingredient[0].getItem())); +//// } +//// } +//// input.add(arr); +//// } +//// var rootRecipeObject = new JsonObject(); +//// rootRecipeObject.add("inShape", input); +//// var resultObject = new JsonObject(); +//// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +//// resultObject.addProperty("count", sr.getOutput().getCount()); +//// rootRecipeObject.add("result", resultObject); +//// recipes.put(getRawIdFor(sr.getOutput().getItem()), rootRecipeObject); +// } else if (recipe instanceof ShapelessRecipe sl) { + +// var ingredients = new JsonArray(); +// for (Ingredient ingredient : sl.getIngredients()) { +// if (ingredient.isEmpty()) continue; +//// ingredients.add(getRawIdFor(ingredient.getMatchingStacks()[0].getItem())); +// } +// var rootRecipeObject = new JsonObject(); +// rootRecipeObject.add("ingredients", ingredients); +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sl.getOutput().getItem())); +// resultObject.addProperty("count", sl.getOutput().getCount()); +// rootRecipeObject.add("result", resultObject); +// recipes.put(getRawIdFor(sl.getOutput().getItem()), rootRecipeObject); +// } +// } +// recipes.forEach((a, b) -> { +// if (!finalObj.has(a.toString())) { +// finalObj.add(a.toString(), new JsonArray()); +// } +// finalObj.get(a.toString()).getAsJsonArray().add(b); +// }); +// return finalObj; + return JsonNull.INSTANCE; + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java new file mode 100644 index 00000000..a6bd0635 --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java @@ -0,0 +1,158 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.BlockColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.FoliageColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.GrassColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.ServerSideRedstoneWireBlock; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.block.RedstoneWireBlock; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; + +import java.util.*; + +public class TintsDataGenerator implements IDataGenerator { + + public static BiomeTintColors generateBiomeTintColors(Registry biomeRegistry) { + BiomeTintColors colors = new BiomeTintColors(); + for (Biome biome : (Iterable) biomeRegistry) { + int biomeGrassColor = GrassColors.getGrassColor(biome); + int biomeFoliageColor = FoliageColors.getFoliageColor(biome); + int biomeWaterColor = biome.getWaterColor(); + + colors.grassColoursMap.computeIfAbsent(biomeGrassColor, k -> new ArrayList<>()).add(biome); + colors.foliageColoursMap.computeIfAbsent(biomeFoliageColor, k -> new ArrayList<>()).add(biome); + colors.waterColourMap.computeIfAbsent(biomeWaterColor, k -> new ArrayList<>()).add(biome); + } + return colors; + } + + public static Map generateRedstoneTintColors() { + Map resultColors = new HashMap<>(); + + for (int redstoneLevel : RedstoneWireBlock.POWER.getValues()) { + int color = ServerSideRedstoneWireBlock.getWireColor(redstoneLevel); + resultColors.put(redstoneLevel, color); + } + return resultColors; + } + + private static int getBlockColor(Block block, BlockColors blockColors) { + return blockColors.method_13410(block.getDefaultState(), DGU.getWorld(), BlockPos.ORIGIN); + } + + public static Map generateConstantTintColors() { + Map resultColors = new HashMap<>(); + BlockColors blockColors = BlockColors.create(); + + resultColors.put(Blocks.BIRCH_LEAVES, FoliageColors.getBirchColor()); + resultColors.put(Blocks.SPRUCE_LEAVES, FoliageColors.getSpruceColor()); + + resultColors.put(Blocks.LILY_PAD, getBlockColor(Blocks.LILY_PAD, blockColors)); + resultColors.put(Blocks.ATTACHED_MELON_STEM, getBlockColor(Blocks.ATTACHED_MELON_STEM, blockColors)); + resultColors.put(Blocks.ATTACHED_PUMPKIN_STEM, getBlockColor(Blocks.ATTACHED_PUMPKIN_STEM, blockColors)); + + //not really constant, depend on the block age, but kinda have to be handled since textures are literally white without them + resultColors.put(Blocks.MELON_STEM, getBlockColor(Blocks.MELON_STEM, blockColors)); + resultColors.put(Blocks.PUMPKIN_STEM, getBlockColor(Blocks.PUMPKIN_STEM, blockColors)); + + return resultColors; + } + + private static JsonObject encodeBiomeColorMap(Registry biomeRegistry, Map> colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry> entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + for (Biome biome : entry.getValue()) { + Identifier registryKey = biomeRegistry.getId(biome); + keysArray.add(Objects.requireNonNull(registryKey).getPath()); + } + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getKey()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeRedstoneColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + keysArray.add(entry.getKey()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeBlocksColorMap(Registry blockRegistry, Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + Identifier registryKey = blockRegistry.getId(entry.getKey()); + keysArray.add(Objects.requireNonNull(registryKey).getPath()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + @Override + public String getDataName() { + return "tints"; + } + + @Override + public JsonObject generateDataJson() { +// DynamicRegistryManager registryManager = DynamicRegistryManager.create(); + Registry biomeRegistry = Registry.BIOME; + Registry blockRegistry = Registry.BLOCK; + + BiomeTintColors biomeTintColors = generateBiomeTintColors(biomeRegistry); + Map redstoneColors = generateRedstoneTintColors(); + Map constantTintColors = generateConstantTintColors(); + + JsonObject resultObject = new JsonObject(); + + resultObject.add("grass", encodeBiomeColorMap(biomeRegistry, biomeTintColors.grassColoursMap)); + resultObject.add("foliage", encodeBiomeColorMap(biomeRegistry, biomeTintColors.foliageColoursMap)); + resultObject.add("water", encodeBiomeColorMap(biomeRegistry, biomeTintColors.waterColourMap)); + + resultObject.add("redstone", encodeRedstoneColorMap(redstoneColors)); + resultObject.add("constant", encodeBlocksColorMap(blockRegistry, constantTintColors)); + + return resultObject; + } + + public static class BiomeTintColors { + final Map> grassColoursMap = new HashMap<>(); + final Map> foliageColoursMap = new HashMap<>(); + final Map> waterColourMap = new HashMap<>(); + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java new file mode 100644 index 00000000..0d297e7a --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.server.dedicated.EulaReader; +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; + +@Mixin(EulaReader.class) +public class EULAMixin { + @Inject(method = "isEulaAgreedTo()Z", at = @At("TAIL"), cancellable = true) + public void init(CallbackInfoReturnable cir) { + cir.setReturnValue(true); + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java new file mode 100644 index 00000000..53b8cdb5 --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Block; +import net.minecraft.item.ToolItem; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Set; + +@Mixin(ToolItem.class) +public interface MiningToolItemAccessor { + + @Accessor + Set getEffectiveBlocks(); +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java new file mode 100644 index 00000000..5e22dfed --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java @@ -0,0 +1,28 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.generators.DataGenerators; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; +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.nio.file.Path; +import java.util.logging.Level; + +@Mixin(MinecraftDedicatedServer.class) +public class ReadyMixin { + @Inject(method = "setupServer()Z", at = @At("TAIL")) + private void init(CallbackInfoReturnable cir) { + Main.LOGGER.log(Level.INFO, "Starting data generation!"); + String versionName = DGU.getCurrentlyRunningServer().getVersion(); + Path serverRootDirectory = DGU.getCurrentlyRunningServer().getRunDirectory().toPath().toAbsolutePath(); + Path dataDumpDirectory = serverRootDirectory.resolve("minecraft-data").resolve(versionName); + DataGenerators.runDataGenerators(dataDumpDirectory); + Main.LOGGER.log(Level.INFO, "Done data generation!"); + DGU.getCurrentlyRunningServer().stopServer(); + System.exit(0); + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java new file mode 100644 index 00000000..33d4cfcd --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java @@ -0,0 +1,11 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.entity.effect.StatusEffect; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(StatusEffect.class) +public interface StatusEffectAccessor { + @Accessor("negative") + boolean negative(); +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java new file mode 100644 index 00000000..4ce9292c --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java @@ -0,0 +1,69 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Itemable; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Language; +import net.minecraft.world.World; +import net.minecraft.world.dimension.DimensionType; +import org.jetbrains.annotations.NotNull; + +public class DGU { + + private static final Language language = Language.getInstance(); + + @Environment(EnvType.CLIENT) + private static MinecraftServer getCurrentlyRunningServerClient() { + return MinecraftClient.getInstance().getServer(); + } + + @SuppressWarnings("deprecation") + private static MinecraftServer getCurrentlyRunningServerDedicated() { + return (MinecraftServer) FabricLoader.getInstance().getGameInstance(); + } + + public static MinecraftServer getCurrentlyRunningServer() { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return getCurrentlyRunningServerClient(); + } else if (environmentType == EnvType.SERVER) { + return getCurrentlyRunningServerDedicated(); + } + throw new UnsupportedOperationException(); + } + + @Environment(EnvType.CLIENT) + private static String translateTextClient(String translationKey) { + return I18n.translate(translationKey); + } + + private static String translateTextFallback(String translationKey) { + try { + return language.translate(translationKey); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to translate: '" + translationKey + "'"); + } + + public static String translateText(String translationKey) { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return translateTextClient(translationKey); + } + return translateTextFallback(translationKey); + } + + @NotNull + public static World getWorld() { + return getCurrentlyRunningServer().method_20312(DimensionType.OVERWORLD); + } + + public static ItemStack stackFor(Itemable ic) { + return new ItemStack(ic); + } +} diff --git a/1.13/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java new file mode 100644 index 00000000..0cfb920d --- /dev/null +++ b/1.13/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java @@ -0,0 +1,32 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.fluid.FluidState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.BlockView; +import org.jetbrains.annotations.Nullable; + +public enum EmptyBlockView implements BlockView { + INSTANCE; + + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + return null; + } + + public BlockState getBlockState(BlockPos pos) { + return Blocks.AIR.getDefaultState(); + } + + @Override + public FluidState getFluidState(BlockPos blockPos) { + return null; + } + + @Override + public int getMaxLightLevel() { + return 15; + } +} diff --git a/1.13/src/main/resources/fabric.mod.json b/1.13/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..1bd022a0 --- /dev/null +++ b/1.13/src/main/resources/fabric.mod.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": 1, + "id": "minecraft-data-generator", + "version": "${version}", + "name": "Minecraft Data Generator", + "description": "", + "authors": [ + "Archengius", + "U9G" + ], + "contact": {}, + "license": "MIT", + "environment": "server", + "entrypoints": { + "main": [ + "dev.u9g.minecraftdatagenerator.Main" + ] + }, + "mixins": [ + "minecraft-data-generator.mixins.json" + ], + "depends": { + "fabricloader": ">=0.13.3", + "minecraft": ">=1.13" + } +} diff --git a/1.13/src/main/resources/minecraft-data-generator.mixins.json b/1.13/src/main/resources/minecraft-data-generator.mixins.json new file mode 100644 index 00000000..ea255f59 --- /dev/null +++ b/1.13/src/main/resources/minecraft-data-generator.mixins.json @@ -0,0 +1,19 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "dev.u9g.minecraftdatagenerator.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "MiningToolItemAccessor", + "ReadyMixin", + "StatusEffectAccessor" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "server": [ + "EULAMixin" + ] +} diff --git a/1.14/build.gradle b/1.14/build.gradle new file mode 100644 index 00000000..d7f2e703 --- /dev/null +++ b/1.14/build.gradle @@ -0,0 +1,31 @@ +plugins { + id 'fabric-loom' +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API. This is technically optional, but you probably want it anyway. + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" +} + +processResources { + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} diff --git a/1.14/gradle.properties b/1.14/gradle.properties new file mode 100644 index 00000000..8986e709 --- /dev/null +++ b/1.14/gradle.properties @@ -0,0 +1,8 @@ +# Fabric Properties +# check these on https://modmuss50.me/fabric.html +minecraft_version=1.14 +yarn_mappings=1.14+build.21 +loader_version=0.13.3 +# Dependencies +# check this on https://modmuss50.me/fabric.html +fabric_version=0.3.0-pre+build.156 diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java new file mode 100644 index 00000000..97c57352 --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java @@ -0,0 +1,36 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +public class FoliageColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] pixels) { + colorMap = pixels; + } + + public static int getColor(double temperature, double humidity) { + int i = (int) ((1.0 - temperature) * 255.0); + int j = (int) ((1.0 - (humidity *= temperature)) * 255.0); + return colorMap[j << 8 | i]; + } + + public static int getSpruceColor() { + return 0x619961; + } + + public static int getBirchColor() { + return 8431445; + } + + public static int getDefaultColor() { + return 4764952; + } + + public static int getFoliageColor(Biome biome) { + double d = MathHelper.clamp(biome.getTemperature(), 0.0f, 1.0f); + double e = MathHelper.clamp(biome.getRainfall(), 0.0f, 1.0f); + return getColor(d, e); + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java new file mode 100644 index 00000000..ff1ce7b1 --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java @@ -0,0 +1,29 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +public class GrassColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] is) { + colorMap = is; + } + + public static int getColor(double d, double e) { + int j = (int) ((1.0 - (e *= d)) * 255.0); + int i = (int) ((1.0 - d) * 255.0); + int k = j << 8 | i; + if (k > colorMap.length) { + return -65281; + } + return colorMap[k]; + } + + public static int getGrassColorAt(Biome biome) { + double d = MathHelper.clamp(biome.getTemperature(), 0.0f, 1.0f); + double e = MathHelper.clamp(biome.getRainfall(), 0.0f, 1.0f); + return getColor(d, e); + } +} + diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java new file mode 100644 index 00000000..c6b2943e --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java @@ -0,0 +1,25 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; + +public class ServerSideRedstoneWireBlock { + public static int getWireColor(int powerLevel) { + float f = (float) powerLevel / 15.0f; + float g = f * 0.6f + 0.4f; + if (powerLevel == 0) { + g = 0.3f; + } + float h = f * f * 0.7f - 0.5f; + float j = f * f * 0.6f - 0.7f; + if (h < 0.0f) { + h = 0.0f; + } + if (j < 0.0f) { + j = 0.0f; + } + int k = MathHelper.clamp((int) (g * 255.0f), 0, 255); + int l = MathHelper.clamp((int) (h * 255.0f), 0, 255); + int m = MathHelper.clamp((int) (j * 255.0f), 0, 255); + return 0xFF000000 | k << 16 | l << 8 | m; + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/Main.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/Main.java new file mode 100644 index 00000000..52a20201 --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/Main.java @@ -0,0 +1,14 @@ +package dev.u9g.minecraftdatagenerator; + +import net.fabricmc.api.ModInitializer; + +import java.util.logging.Logger; + +public class Main implements ModInitializer { + public static final Logger LOGGER = Logger.getLogger("mc-data-gen-serv"); + + @Override + public void onInitialize() { + + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java new file mode 100644 index 00000000..f7c4d666 --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java @@ -0,0 +1,139 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; + +import java.util.Objects; + +public class BiomesDataGenerator implements IDataGenerator { + + private static String guessBiomeDimensionFromCategory(Biome biome) { + return switch (biome.getCategory()) { + case NETHER -> "nether"; + case THEEND -> "end"; + default -> "overworld"; + }; + } + + private static int getBiomeColorFor(String biomeName) { + return switch (biomeName) { + case "ocean" -> 112; + case "plains" -> 9286496; + case "desert" -> 16421912; + case "mountains" -> 6316128; + case "forest" -> 353825; + case "taiga" -> 747097; + case "swamp" -> 522674; + case "river" -> 255; + case "nether_wastes" -> 12532539; + case "the_end" -> 8421631; + case "frozen_ocean" -> 7368918; + case "frozen_river" -> 10526975; + case "snowy_tundra" -> 16777215; + case "snowy_mountains" -> 10526880; + case "mushroom_fields" -> 16711935; + case "mushroom_field_shore" -> 10486015; + case "beach" -> 16440917; + case "desert_hills" -> 13786898; + case "wooded_hills" -> 2250012; + case "taiga_hills" -> 1456435; + case "mountain_edge" -> 7501978; + case "jungle" -> 5470985; + case "jungle_hills" -> 2900485; + case "jungle_edge" -> 6458135; + case "deep_ocean" -> 48; + case "stone_shore" -> 10658436; + case "snowy_beach" -> 16445632; + case "birch_forest" -> 3175492; + case "birch_forest_hills" -> 2055986; + case "dark_forest" -> 4215066; + case "snowy_taiga" -> 3233098; + case "snowy_taiga_hills" -> 2375478; + case "giant_tree_taiga" -> 5858897; + case "giant_tree_taiga_hills" -> 4542270; + case "wooded_mountains" -> 5271632; + case "savanna" -> 12431967; + case "savanna_plateau" -> 10984804; + case "badlands" -> 14238997; + case "wooded_badlands_plateau" -> 11573093; + case "badlands_plateau" -> 13274213; + case "small_end_islands" -> 42; + case "end_midlands" -> 15464630; + case "end_highlands" -> 12828041; + case "end_barrens" -> 9474162; + case "warm_ocean" -> 172; + case "lukewarm_ocean" -> 144; + case "cold_ocean" -> 2105456; + case "deep_warm_ocean" -> 80; + case "deep_lukewarm_ocean" -> 64; + case "deep_cold_ocean" -> 2105400; + case "deep_frozen_ocean" -> 4210832; + case "the_void" -> 0; + case "sunflower_plains" -> 11918216; + case "desert_lakes" -> 16759872; + case "gravelly_mountains" -> 8947848; + case "flower_forest" -> 2985545; + case "taiga_mountains" -> 3378817; + case "swamp_hills" -> 3145690; + case "ice_spikes" -> 11853020; + case "modified_jungle" -> 8102705; + case "modified_jungle_edge" -> 9089855; + case "tall_birch_forest" -> 5807212; + case "tall_birch_hills" -> 4687706; + case "dark_forest_hills" -> 6846786; + case "snowy_taiga_mountains" -> 5864818; + case "giant_spruce_taiga" -> 8490617; + case "giant_spruce_taiga_hills" -> 7173990; + case "modified_gravelly_mountains" -> 7903352; + case "shattered_savanna" -> 15063687; + case "shattered_savanna_plateau" -> 13616524; + case "eroded_badlands" -> 16739645; + case "modified_wooded_badlands_plateau" -> 14204813; + case "modified_badlands_plateau" -> 15905933; + case "bamboo_jungle" -> 7769620; + case "bamboo_jungle_hills" -> 3884810; + case "nether" -> 16711680; + default -> throw new Error("Unexpected biome, with name: '" + biomeName + "'"); + }; + } + + public static JsonObject generateBiomeInfo(Registry registry, Biome biome) { + JsonObject biomeDesc = new JsonObject(); + Identifier registryKey = registry.getId(biome); + String localizationKey = String.format("biome.%s.%s", Objects.requireNonNull(registryKey).getNamespace(), registryKey.getPath()); + + biomeDesc.addProperty("id", registry.getRawId(biome)); + biomeDesc.addProperty("name", registryKey.getPath()); + + biomeDesc.addProperty("category", biome.getCategory().getName()); + biomeDesc.addProperty("temperature", biome.getTemperature()); + biomeDesc.addProperty("precipitation", biome.getPrecipitation().getName()); + biomeDesc.addProperty("depth", biome.getDepth()); + biomeDesc.addProperty("dimension", guessBiomeDimensionFromCategory(biome)); + biomeDesc.addProperty("displayName", DGU.translateText(localizationKey)); + biomeDesc.addProperty("color", getBiomeColorFor(registryKey.getPath())); + biomeDesc.addProperty("rainfall", biome.getRainfall()); + + return biomeDesc; + } + + @Override + public String getDataName() { + return "biomes"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray biomesArray = new JsonArray(); + Registry biomeRegistry = Registry.BIOME; + + biomeRegistry.stream() + .map(biome -> generateBiomeInfo(biomeRegistry, biome)) + .forEach(biomesArray::add); + return biomesArray; + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java new file mode 100644 index 00000000..6f0d2d43 --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java @@ -0,0 +1,108 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.EmptyBlockView; + +import java.util.*; + +public class BlockCollisionShapesDataGenerator implements IDataGenerator { + + @Override + public String getDataName() { + return "blockCollisionShapes"; + } + + @Override + public JsonObject generateDataJson() { + Registry blockRegistry = Registry.BLOCK; + BlockShapesCache blockShapesCache = new BlockShapesCache(); + + blockRegistry.forEach(blockShapesCache::processBlock); + + JsonObject resultObject = new JsonObject(); + + resultObject.add("blocks", blockShapesCache.dumpBlockShapeIndices(blockRegistry)); + resultObject.add("shapes", blockShapesCache.dumpShapesObject()); + + return resultObject; + } + + private static class BlockShapesCache { + public final Map uniqueBlockShapes = new HashMap<>(); + public final Map> blockCollisionShapes = new HashMap<>(); + private int lastCollisionShapeId = 0; + + public void processBlock(Block block) { + List blockStates = block.getStateFactory().getStates(); + List blockCollisionShapes = new ArrayList<>(); + + for (BlockState blockState : blockStates) { + VoxelShape blockShape = blockState.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + Integer blockShapeIndex = uniqueBlockShapes.get(blockShape); + + if (blockShapeIndex == null) { + blockShapeIndex = lastCollisionShapeId++; + uniqueBlockShapes.put(blockShape, blockShapeIndex); + } + blockCollisionShapes.add(blockShapeIndex); + } + + this.blockCollisionShapes.put(block, blockCollisionShapes); + } + + public JsonObject dumpBlockShapeIndices(Registry blockRegistry) { + JsonObject resultObject = new JsonObject(); + + for (var entry : blockCollisionShapes.entrySet()) { + List blockCollisions = entry.getValue(); + long distinctShapesCount = blockCollisions.stream().distinct().count(); + JsonElement blockCollision; + if (distinctShapesCount == 1L) { + blockCollision = new JsonPrimitive(blockCollisions.get(0)); + } else { + blockCollision = new JsonArray(); + for (int collisionId : blockCollisions) { + ((JsonArray) blockCollision).add(collisionId); + } + } + + Identifier registryKey = blockRegistry.getId(entry.getKey()); + resultObject.add(Objects.requireNonNull(registryKey).getPath(), blockCollision); + } + + return resultObject; + } + + public JsonObject dumpShapesObject() { + JsonObject shapesObject = new JsonObject(); + + for (var entry : uniqueBlockShapes.entrySet()) { + JsonArray boxesArray = new JsonArray(); + entry.getKey().forEachBox((x1, y1, z1, x2, y2, z2) -> { + JsonArray oneBoxJsonArray = new JsonArray(); + + oneBoxJsonArray.add(x1); + oneBoxJsonArray.add(y1); + oneBoxJsonArray.add(z1); + + oneBoxJsonArray.add(x2); + oneBoxJsonArray.add(y2); + oneBoxJsonArray.add(z2); + + boxesArray.add(oneBoxJsonArray); + }); + shapesObject.add(Integer.toString(entry.getValue()), boxesArray); + } + return shapesObject; + } + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java new file mode 100644 index 00000000..6c47c0e2 --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java @@ -0,0 +1,157 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.base.CaseFormat; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.mixin.MiningToolItemAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.block.AirBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.item.MiningToolItem; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.state.property.EnumProperty; +import net.minecraft.state.property.IntegerProperty; +import net.minecraft.state.property.Property; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.EmptyBlockView; +import net.minecraft.world.loot.context.LootContext; +import net.minecraft.world.loot.context.LootContextParameters; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +public class BlocksDataGenerator implements IDataGenerator { + + private static final Logger logger = Main.LOGGER; + + private static List getItemsEffectiveForBlock(Block block) { + return Registry.ITEM.stream() + .filter(item -> item instanceof MiningToolItem) + .filter(item -> ((MiningToolItemAccessor) item).getEffectiveBlocks().contains(block)) + .collect(Collectors.toList()); + } + + private static List populateDropsIfPossible(BlockState blockState, Item firstToolItem) { + //If we have local world context, we can actually evaluate loot tables and determine actual data + ServerWorld serverWorld = (ServerWorld) DGU.getWorld(); + var lootContext = new LootContext.Builder(serverWorld) + .put(LootContextParameters.POSITION, BlockPos.ORIGIN) + .put(LootContextParameters.TOOL, DGU.stackFor(firstToolItem)); + blockState.getDroppedStacks(lootContext); + return blockState.getDroppedStacks(lootContext); + } + + private static String getPropertyTypeName(Property property) { + //Explicitly handle default minecraft properties + if (property instanceof BooleanProperty) { + return "bool"; + } + if (property instanceof IntegerProperty) { + return "int"; + } + if (property instanceof EnumProperty) { + return "enum"; + } + + //Use simple class name as fallback, this code will give something like + //example_type for ExampleTypeProperty class name + String rawPropertyName = property.getClass().getSimpleName().replace("Property", ""); + return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, rawPropertyName); + } + + private static > JsonObject generateStateProperty(Property property) { + JsonObject propertyObject = new JsonObject(); + Collection propertyValues = property.getValues(); + + propertyObject.addProperty("name", property.getName()); + propertyObject.addProperty("type", getPropertyTypeName(property)); + propertyObject.addProperty("num_values", propertyValues.size()); + + //Do not add values for vanilla boolean properties, they are known by default + if (!(property instanceof BooleanProperty)) { + JsonArray propertyValuesArray = new JsonArray(); + for (T propertyValue : propertyValues) { + propertyValuesArray.add(property.getValueAsString(propertyValue)); + } + propertyObject.add("values", propertyValuesArray); + } + return propertyObject; + } + + public static JsonObject generateBlock(Registry blockRegistry, Block block) { + JsonObject blockDesc = new JsonObject(); + + List blockStates = block.getStateFactory().getStates(); + BlockState defaultState = block.getDefaultState(); + Identifier registryKey = blockRegistry.getId(block); + String localizationKey = block.getTranslationKey(); + List effectiveTools = getItemsEffectiveForBlock(block); + + blockDesc.addProperty("id", blockRegistry.getRawId(block)); + blockDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + blockDesc.addProperty("displayName", DGU.translateText(localizationKey)); + + float hardness = block.getDefaultState().getHardness(null, null); + + blockDesc.addProperty("hardness", hardness); + blockDesc.addProperty("resistance", block.getBlastResistance()); + blockDesc.addProperty("stackSize", block.asItem().getMaxAmount()); + blockDesc.addProperty("diggable", hardness != -1.0f && !(block instanceof AirBlock)); + JsonObject effTools = new JsonObject(); + effectiveTools.forEach(item -> effTools.addProperty( + String.valueOf(Registry.ITEM.getRawId(item)), // key + item.getBlockBreakingSpeed(DGU.stackFor(item), defaultState) // value + )); + blockDesc.add("effectiveTools", effTools); + + blockDesc.addProperty("transparent", !defaultState.isFullOpaque(EmptyBlockView.INSTANCE, BlockPos.ORIGIN)); + blockDesc.addProperty("emitLight", defaultState.getLuminance()); + blockDesc.addProperty("filterLight", block.getLightSubtracted(block.getDefaultState(), EmptyBlockView.INSTANCE, BlockPos.ORIGIN)); + + blockDesc.addProperty("defaultState", Block.getRawIdFromState(defaultState)); + blockDesc.addProperty("minStateId", Block.getRawIdFromState(blockStates.get(0))); + blockDesc.addProperty("maxStateId", Block.getRawIdFromState(blockStates.get(blockStates.size() - 1))); + JsonArray stateProperties = new JsonArray(); + for (Property property : block.getStateFactory().getProperties()) { + stateProperties.add(generateStateProperty(property)); + } + blockDesc.add("states", stateProperties); + + List drops = populateDropsIfPossible(defaultState, effectiveTools.stream().findFirst().orElse(Items.AIR)); + + JsonArray dropsArray = new JsonArray(); + drops.forEach(dropped -> dropsArray.add(Item.getRawIdByItem(dropped.getItem()))); + blockDesc.add("drops", dropsArray); + + VoxelShape blockCollisionShape = defaultState.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + blockDesc.addProperty("boundingBox", blockCollisionShape.isEmpty() ? "empty" : "block"); + + return blockDesc; + } + + @Override + public String getDataName() { + return "blocks"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultBlocksArray = new JsonArray(); + Registry blockRegistry = Registry.BLOCK; + + blockRegistry.forEach(block -> resultBlocksArray.add(generateBlock(blockRegistry, block))); + return resultBlocksArray; + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java new file mode 100644 index 00000000..3c05c30f --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java @@ -0,0 +1,79 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonWriter; +import dev.u9g.minecraftdatagenerator.Main; + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class DataGenerators { + + private static final List GENERATORS = new ArrayList<>(); + private static final Logger logger = Main.LOGGER; + + static { + register(new BiomesDataGenerator()); + register(new BlockCollisionShapesDataGenerator()); + register(new BlocksDataGenerator()); + register(new EffectsDataGenerator()); + register(new EnchantmentsDataGenerator()); + register(new EntitiesDataGenerator()); + register(new FoodsDataGenerator()); + register(new ItemsDataGenerator()); + register(new ParticlesDataGenerator()); + register(new TintsDataGenerator()); + register(new LanguageDataGenerator()); + register(new InstrumentsDataGenerator()); + } + + public static void register(IDataGenerator generator) { + GENERATORS.add(generator); + } + + public static boolean runDataGenerators(Path outputDirectory) { + try { + Files.createDirectories(outputDirectory); + } catch (IOException exception) { + logger.log(Level.INFO, "Failed to create data generator output directory at " + outputDirectory); + exception.printStackTrace(); + return false; + } + + int generatorsFailed = 0; + logger.log(Level.INFO, MessageFormat.format("Running minecraft data generators, output at {0}", outputDirectory)); + + for (IDataGenerator dataGenerator : GENERATORS) { + logger.log(Level.INFO, MessageFormat.format("Running generator {0}", dataGenerator.getDataName())); + try { + String outputFileName = String.format("%s.json", dataGenerator.getDataName()); + JsonElement outputElement = dataGenerator.generateDataJson(); + Path outputFilePath = outputDirectory.resolve(outputFileName); + + try (Writer writer = Files.newBufferedWriter(outputFilePath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + JsonWriter jsonWriter = new JsonWriter(writer); + jsonWriter.setIndent(" "); + Streams.write(outputElement, jsonWriter); + } + logger.log(Level.INFO, MessageFormat.format("Generator: {0} -> {1}", dataGenerator.getDataName(), outputFileName)); + + } catch (Throwable exception) { + logger.log(Level.INFO, MessageFormat.format("Failed to run data generator {0}", dataGenerator.getDataName())); + exception.printStackTrace(); + generatorsFailed++; + } + } + + logger.log(Level.INFO, "Finishing running data generators"); + return generatorsFailed == 0; + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java new file mode 100644 index 00000000..87620c01 --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java @@ -0,0 +1,48 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.StatusEffectAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.entity.effect.StatusEffectType; +import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.stream.Collectors; + +public class EffectsDataGenerator implements IDataGenerator { + + public static JsonObject generateEffect(Registry registry, StatusEffect statusEffect) { + JsonObject effectDesc = new JsonObject(); + Identifier registryKey = registry.getId(statusEffect); + + effectDesc.addProperty("id", registry.getRawId(statusEffect)); + if (statusEffect == StatusEffects.UNLUCK) { + effectDesc.addProperty("name", "BadLuck"); + effectDesc.addProperty("displayName", "Bad Luck"); + } else { + effectDesc.addProperty("name", Arrays.stream(registryKey.getPath().split("_")).map(StringUtils::capitalize).collect(Collectors.joining())); + effectDesc.addProperty("displayName", DGU.translateText(statusEffect.getTranslationKey())); + } + + effectDesc.addProperty("type", ((StatusEffectAccessor) statusEffect).type() == StatusEffectType.BENEFICIAL ? "good" : "bad"); + return effectDesc; + } + + @Override + public String getDataName() { + return "effects"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry statusEffectRegistry = Registry.STATUS_EFFECT; + statusEffectRegistry.forEach(effect -> resultsArray.add(generateEffect(statusEffectRegistry, effect))); + return resultsArray; + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java new file mode 100644 index 00000000..3df0725e --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java @@ -0,0 +1,111 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +public class EnchantmentsDataGenerator implements IDataGenerator { + + private static final ImmutableMap ENCHANTMENT_TARGET_NAMES = ImmutableMap.builder() + .put(EnchantmentTarget.ARMOR, "armor") + .put(EnchantmentTarget.ARMOR_FEET, "armor_feet") + .put(EnchantmentTarget.ARMOR_LEGS, "armor_legs") + .put(EnchantmentTarget.ARMOR_CHEST, "armor_chest") + .put(EnchantmentTarget.ARMOR_HEAD, "armor_head") + .put(EnchantmentTarget.WEAPON, "weapon") + .put(EnchantmentTarget.DIGGER, "digger") + .put(EnchantmentTarget.FISHING_ROD, "fishing_rod") + .put(EnchantmentTarget.TRIDENT, "trident") + .put(EnchantmentTarget.BREAKABLE, "breakable") + .put(EnchantmentTarget.BOW, "bow") + .put(EnchantmentTarget.WEARABLE, "wearable") + .put(EnchantmentTarget.CROSSBOW, "crossbow") + .put(EnchantmentTarget.ALL, "vanishable") // according to VanishingCurseEnchantment + .build(); + + public static String getEnchantmentTargetName(EnchantmentTarget target) { + return ENCHANTMENT_TARGET_NAMES.getOrDefault(target, target.name().toLowerCase(Locale.ROOT)); + } + + // Equation enchantment costs follow is a * level + b, so we can easily retrieve a and b by passing zero level + private static JsonObject generateEnchantmentMinPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMinimumPower(0); + int a = enchantment.getMinimumPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + private static JsonObject generateEnchantmentMaxPowerCoefficients(Enchantment enchantment) { + int b = getMaximumPower(enchantment, 0); + int a = getMaximumPower(enchantment, 1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + public static JsonObject generateEnchantment(Registry registry, Enchantment enchantment) { + JsonObject enchantmentDesc = new JsonObject(); + Identifier registryKey = registry.getId(enchantment); + + enchantmentDesc.addProperty("id", registry.getRawId(enchantment)); + enchantmentDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + enchantmentDesc.addProperty("displayName", DGU.translateText(enchantment.getTranslationKey())); + + enchantmentDesc.addProperty("maxLevel", enchantment.getMaximumLevel()); + enchantmentDesc.add("minCost", generateEnchantmentMinPowerCoefficients(enchantment)); + enchantmentDesc.add("maxCost", generateEnchantmentMaxPowerCoefficients(enchantment)); + + enchantmentDesc.addProperty("treasureOnly", enchantment.isTreasure()); + enchantmentDesc.addProperty("curse", enchantment.isCursed()); + + List incompatibleEnchantments = registry.stream() + .filter(other -> !enchantment.isDifferent(other)) + .filter(other -> other != enchantment).toList(); + + JsonArray excludes = new JsonArray(); + for (Enchantment excludedEnchantment : incompatibleEnchantments) { + Identifier otherKey = registry.getId(excludedEnchantment); + excludes.add(Objects.requireNonNull(otherKey).getPath()); + } + enchantmentDesc.add("exclude", excludes); + + enchantmentDesc.addProperty("category", getEnchantmentTargetName(enchantment.type)); + enchantmentDesc.addProperty("weight", enchantment.getWeight().getWeight()); + enchantmentDesc.addProperty("tradeable", true); // the first non-tradeable enchant came in 1.16, soul speed + enchantmentDesc.addProperty("discoverable", true); // the first non-enchantable enchant came in 1.16, soul speed + + return enchantmentDesc; + } + + private static int getMaximumPower(Enchantment ench, int level) { + return ench.getMinimumPower(level) + 5; + } + + @Override + public String getDataName() { + return "enchantments"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry enchantmentRegistry = Registry.ENCHANTMENT; + enchantmentRegistry.stream() + .forEach(enchantment -> resultsArray.add(generateEnchantment(enchantmentRegistry, enchantment))); + return resultsArray; + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java new file mode 100644 index 00000000..1ab7c94d --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java @@ -0,0 +1,121 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.WaterCreatureEntity; +import net.minecraft.entity.mob.AmbientEntity; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.passive.PassiveEntity; +import net.minecraft.entity.projectile.ProjectileEntity; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.ParameterizedType; +import java.util.Objects; + +public class EntitiesDataGenerator implements IDataGenerator { + + public static JsonObject generateEntity(Registry> entityRegistry, EntityType entityType) { + JsonObject entityDesc = new JsonObject(); + Identifier registryKey = entityRegistry.getId(entityType); + int entityRawId = entityRegistry.getRawId(entityType); + + entityDesc.addProperty("id", entityRawId); + entityDesc.addProperty("internalId", entityRawId); + entityDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + entityDesc.addProperty("displayName", DGU.translateText(entityType.getTranslationKey())); + entityDesc.addProperty("width", entityType.getWidth()); + entityDesc.addProperty("height", entityType.getHeight()); + + String entityTypeString = "UNKNOWN"; + Entity entityObject = entityType.create(DGU.getWorld()); + entityTypeString = entityObject != null ? getEntityTypeForClass(entityObject.getClass()) : "player"; + entityDesc.addProperty("type", entityTypeString); + entityDesc.addProperty("category", getCategoryFrom(entityType)); + + return entityDesc; + } + + private static String getCategoryFrom(@NotNull EntityType entityType) { + if (entityType == EntityType.PLAYER) return "other"; // fail early for player entities + Class entityClazz = null; + try { + for (var field : EntityType.class.getFields()) + if (entityType == field.get(EntityType.class)) + entityClazz = (Class) ((ParameterizedType) TypeToken.get(field.getGenericType()).getType()).getActualTypeArguments()[0]; + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + if (entityClazz == null) throw new RuntimeException("Shouldn't be null..."); + return switch (entityClazz.getPackageName()) { + case "net.minecraft.entity.decoration", "net.minecraft.entity.decoration.painting" -> "Immobile"; + case "net.minecraft.entity.boss", "net.minecraft.entity.mob", "net.minecraft.entity.boss.dragon" -> + "Hostile mobs"; + case "net.minecraft.entity.projectile", "net.minecraft.entity.thrown" -> "Projectiles"; + case "net.minecraft.entity.passive" -> "Passive mobs"; + case "net.minecraft.entity.vehicle" -> "Vehicles"; + case "net.minecraft.entity" -> "other"; + default -> throw new Error("Unexpected entity type: " + entityClazz.getPackageName()); + }; + } + + //Honestly, both "type" and "category" fields in the schema and examples do not contain any useful information + //Since category is optional, I will just leave it out, and for type I will assume general entity classification + //by the Entity class hierarchy (which has some weirdness too by the way) + private static String getEntityTypeForClass(Class entityClass) { + //Top-level classifications + if (WaterCreatureEntity.class.isAssignableFrom(entityClass)) { + return "water_creature"; + } + if (AnimalEntity.class.isAssignableFrom(entityClass)) { + return "animal"; + } + if (HostileEntity.class.isAssignableFrom(entityClass)) { + return "hostile"; + } + if (AmbientEntity.class.isAssignableFrom(entityClass)) { + return "ambient"; + } + + //Second level classifications. PathAwareEntity is not included because it + //doesn't really make much sense to categorize by it + if (PassiveEntity.class.isAssignableFrom(entityClass)) { + return "passive"; + } + if (MobEntity.class.isAssignableFrom(entityClass)) { + return "mob"; + } + + //Other classifications only include living entities and projectiles. everything else is categorized as other + if (LivingEntity.class.isAssignableFrom(entityClass)) { + return "living"; + } + if (ProjectileEntity.class.isAssignableFrom(entityClass)) { + return "projectile"; + } + return "other"; + } + + @Override + public String getDataName() { + return "entities"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry> entityTypeRegistry = Registry.ENTITY_TYPE; + entityTypeRegistry.forEach(entity -> resultArray.add(generateEntity(entityTypeRegistry, entity))); + return resultArray; + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java new file mode 100644 index 00000000..2a0c137e --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java @@ -0,0 +1,50 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Objects; + +public class FoodsDataGenerator implements IDataGenerator { + + public static JsonObject generateFoodDescriptor(Registry registry, Item foodItem) { + JsonObject foodDesc = new JsonObject(); + Identifier registryKey = registry.getId(foodItem); + + foodDesc.addProperty("id", registry.getRawId(foodItem)); + foodDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + foodDesc.addProperty("stackSize", foodItem.getMaxAmount()); + foodDesc.addProperty("displayName", DGU.translateText(foodItem.getTranslationKey())); + + var foodSettings = Objects.requireNonNull(foodItem.getFoodSetting()); + float foodPoints = foodSettings.getHunger(); + float saturationRatio = foodSettings.getSaturationModifier() * 2.0F; + float saturation = foodPoints * saturationRatio; + + foodDesc.addProperty("foodPoints", foodPoints); + foodDesc.addProperty("saturation", saturation); + + foodDesc.addProperty("effectiveQuality", foodPoints + saturation); + foodDesc.addProperty("saturationRatio", saturationRatio); + return foodDesc; + } + + @Override + public String getDataName() { + return "foods"; + } + + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry itemRegistry = Registry.ITEM; + itemRegistry.stream() + .filter(Item::isFood) + .forEach(food -> resultsArray.add(generateFoodDescriptor(itemRegistry, food))); + return resultsArray; + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java new file mode 100644 index 00000000..81fe4e20 --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; + +public interface IDataGenerator { + + String getDataName(); + + JsonElement generateDataJson(); +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java new file mode 100644 index 00000000..ebedf488 --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java @@ -0,0 +1,25 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.minecraft.block.enums.Instrument; + +public class InstrumentsDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "instruments"; + } + + @Override + public JsonElement generateDataJson() { + JsonArray array = new JsonArray(); + for (Instrument instrument : Instrument.values()) { + JsonObject object = new JsonObject(); + object.addProperty("id", instrument.ordinal()); + object.addProperty("name", instrument.toSnakeCase()); + array.add(object); + } + return array; + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java new file mode 100644 index 00000000..9dda381b --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java @@ -0,0 +1,82 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class ItemsDataGenerator implements IDataGenerator { + + private static List calculateItemsToRepairWith(Registry itemRegistry, Item sourceItem) { + ItemStack sourceItemStack = DGU.stackFor(sourceItem); + return itemRegistry.stream() + .filter(otherItem -> sourceItem.canRepair(sourceItemStack, DGU.stackFor(otherItem))) + .collect(Collectors.toList()); + } + + private static List getApplicableEnchantmentTargets(Item sourceItem) { + return Arrays.stream(EnchantmentTarget.values()) + .filter(target -> target.isAcceptableItem(sourceItem)) + .collect(Collectors.toList()); + } + + public static JsonObject generateItem(Registry itemRegistry, Item item) { + JsonObject itemDesc = new JsonObject(); + Identifier registryKey = itemRegistry.getId(item); + + itemDesc.addProperty("id", itemRegistry.getRawId(item)); + itemDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + itemDesc.addProperty("displayName", DGU.translateText(item.getTranslationKey())); + itemDesc.addProperty("stackSize", item.getMaxAmount()); + + List enchantmentTargets = getApplicableEnchantmentTargets(item); + + JsonArray enchantCategoriesArray = new JsonArray(); + for (EnchantmentTarget target : enchantmentTargets) { + enchantCategoriesArray.add(EnchantmentsDataGenerator.getEnchantmentTargetName(target)); + } + if (enchantCategoriesArray.size() > 0) { + itemDesc.add("enchantCategories", enchantCategoriesArray); + } + + if (item.canDamage()) { + List repairWithItems = calculateItemsToRepairWith(itemRegistry, item); + + JsonArray fixedWithArray = new JsonArray(); + for (Item repairWithItem : repairWithItems) { + Identifier repairWithName = itemRegistry.getId(repairWithItem); + fixedWithArray.add(Objects.requireNonNull(repairWithName).getPath()); + } + if (fixedWithArray.size() > 0) { + itemDesc.add("repairWith", fixedWithArray); + } + + int maxDurability = item.getMaxAmount(); + itemDesc.addProperty("maxDurability", maxDurability); + } + return itemDesc; + } + + @Override + public String getDataName() { + return "items"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry itemRegistry = Registry.ITEM; + itemRegistry.stream().forEach(item -> resultArray.add(generateItem(itemRegistry, item))); + return resultArray; + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java new file mode 100644 index 00000000..7a3c6ced --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +public class LanguageDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "language"; + } + + @Override + public JsonElement generateDataJson() { + try { + InputStream inputStream = Objects.requireNonNull(this.getClass().getResourceAsStream("/assets/minecraft/lang/en_us.json")); + return new Gson().fromJson(new InputStreamReader(inputStream, StandardCharsets.UTF_8), JsonObject.class); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to generate language file"); + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java new file mode 100644 index 00000000..d537280d --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java @@ -0,0 +1,34 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.minecraft.particle.ParticleType; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Objects; + +public class ParticlesDataGenerator implements IDataGenerator { + + public static JsonObject generateParticleType(Registry> registry, ParticleType particleType) { + JsonObject effectDesc = new JsonObject(); + Identifier registryKey = registry.getId(particleType); + + effectDesc.addProperty("id", registry.getRawId(particleType)); + effectDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + return effectDesc; + } + + @Override + public String getDataName() { + return "particles"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry> particleTypeRegistry = Registry.PARTICLE_TYPE; + particleTypeRegistry.forEach(particleType -> resultsArray.add(generateParticleType(particleTypeRegistry, particleType))); + return resultsArray; + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java new file mode 100644 index 00000000..5e4587dd --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java @@ -0,0 +1,121 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import net.minecraft.item.Item; +import net.minecraft.util.registry.Registry; + +public class RecipeDataGenerator implements IDataGenerator { + + private static int getRawIdFor(Item item) { + return Registry.ITEM.getRawId(item); + } + + @Override + public String getDataName() { + return "recipes"; + } + + @Override + public JsonElement generateDataJson() { +// JsonObject finalObj = new JsonObject(); +// Multimap recipes = ArrayListMultimap.create(); +// for (Recipe recipe : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) { +// if (recipe instanceof ShapedRecipe sr) { +// var ingredients = sr.getIngredients(); +// List ingr = new ArrayList<>(); +// for (int i = 0; i < 9; i++) { +// if (i >= ingredients.size()) { +// ingr.add(-1); +// continue; +// } +// var stacks = ingredients.get(i); +//// var matching = stacks.getMatchingStacks(); +//// if (matching.length == 0) { +//// ingr.add(-1); +//// } else { +//// ingr.add(getRawIdFor(matching[0].getItem())); +//// } +// } +// Lists.reverse(ingr); +// +// JsonArray inShape = new JsonArray(); +// +// var iter = ingr.iterator(); +// for (int y = 0; y < 3; y++) { +// var jsonRow = new JsonArray(); +// int one = iter.next(); +// int two = iter.next(); +// int three = iter.next(); +// if (y > 0 && one == -1 && two == -1 && three == -1) continue; +// jsonRow.add(one); +// jsonRow.add(two); +// jsonRow.add(three); +// inShape.add(jsonRow); +// } +// +// JsonObject finalRecipe = new JsonObject(); +// finalRecipe.add("inShape", inShape); +// +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +// resultObject.addProperty("count", sr.getOutput().getCount()); +// finalRecipe.add("result", resultObject); +// +// String id = ((Integer) getRawIdFor(sr.getOutput().getItem())).toString(); +// +// if (!finalObj.has(id)) { +// finalObj.add(id, new JsonArray()); +// } +// finalObj.get(id).getAsJsonArray().add(finalRecipe); +//// var input = new JsonArray(); +//// var ingredients = sr.getIngredients().stream().toList(); +//// for (int y = 0; y < sr.getHeight(); y++) { +//// var arr = new JsonArray(); +//// for (int x = 0; x < sr.getWidth(); x++) { +//// if ((y*3)+x >= ingredients.size()) { +//// arr.add(JsonNull.INSTANCE); +//// continue; +//// } +//// var ingredient = ingredients.get((y*3)+x).getMatchingStacks(); // FIXME: fix when there are more than one matching stack +//// if (ingredient.length == 0) { +//// arr.add(JsonNull.INSTANCE); +//// } else { +//// arr.add(getRawIdFor(ingredient[0].getItem())); +//// } +//// } +//// input.add(arr); +//// } +//// var rootRecipeObject = new JsonObject(); +//// rootRecipeObject.add("inShape", input); +//// var resultObject = new JsonObject(); +//// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +//// resultObject.addProperty("count", sr.getOutput().getCount()); +//// rootRecipeObject.add("result", resultObject); +//// recipes.put(getRawIdFor(sr.getOutput().getItem()), rootRecipeObject); +// } else if (recipe instanceof ShapelessRecipe sl) { + +// var ingredients = new JsonArray(); +// for (Ingredient ingredient : sl.getIngredients()) { +// if (ingredient.isEmpty()) continue; +//// ingredients.add(getRawIdFor(ingredient.getMatchingStacks()[0].getItem())); +// } +// var rootRecipeObject = new JsonObject(); +// rootRecipeObject.add("ingredients", ingredients); +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sl.getOutput().getItem())); +// resultObject.addProperty("count", sl.getOutput().getCount()); +// rootRecipeObject.add("result", resultObject); +// recipes.put(getRawIdFor(sl.getOutput().getItem()), rootRecipeObject); +// } +// } +// recipes.forEach((a, b) -> { +// if (!finalObj.has(a.toString())) { +// finalObj.add(a.toString(), new JsonArray()); +// } +// finalObj.get(a.toString()).getAsJsonArray().add(b); +// }); +// return finalObj; + return JsonNull.INSTANCE; + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java new file mode 100644 index 00000000..754d7dbf --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java @@ -0,0 +1,166 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.FoliageColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.GrassColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.ServerSideRedstoneWireBlock; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.fabricmc.api.EnvType; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.block.RedstoneWireBlock; +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; + +import java.util.*; + +public class TintsDataGenerator implements IDataGenerator { + + public static BiomeTintColors generateBiomeTintColors(Registry biomeRegistry) { + BiomeTintColors colors = new BiomeTintColors(); + + biomeRegistry.forEach(biome -> { + int biomeGrassColor = GrassColors.getGrassColorAt(biome); + int biomeFoliageColor = FoliageColors.getFoliageColor(biome); + int biomeWaterColor = biome.getWaterColor(); + + colors.grassColoursMap.computeIfAbsent(biomeGrassColor, k -> new ArrayList<>()).add(biome); + colors.foliageColoursMap.computeIfAbsent(biomeFoliageColor, k -> new ArrayList<>()).add(biome); + colors.waterColourMap.computeIfAbsent(biomeWaterColor, k -> new ArrayList<>()).add(biome); + }); + return colors; + } + + public static Map generateRedstoneTintColors() { + Map resultColors = new HashMap<>(); + + for (int redstoneLevel : RedstoneWireBlock.POWER.getValues()) { + int color = ServerSideRedstoneWireBlock.getWireColor(redstoneLevel); + resultColors.put(redstoneLevel, color); + } + return resultColors; + } + + private static int getBlockColor(Block block, BlockColors blockColors) { + return blockColors.getColor(block.getDefaultState(), DGU.getWorld(), BlockPos.ORIGIN); + } + + public static Map generateConstantTintColors() { + Map resultColors = new HashMap<>(); + BlockColors blockColors = BlockColors.create(); + + resultColors.put(Blocks.BIRCH_LEAVES, FoliageColors.getBirchColor()); + resultColors.put(Blocks.SPRUCE_LEAVES, FoliageColors.getSpruceColor()); + + resultColors.put(Blocks.LILY_PAD, getBlockColor(Blocks.LILY_PAD, blockColors)); + resultColors.put(Blocks.ATTACHED_MELON_STEM, getBlockColor(Blocks.ATTACHED_MELON_STEM, blockColors)); + resultColors.put(Blocks.ATTACHED_PUMPKIN_STEM, getBlockColor(Blocks.ATTACHED_PUMPKIN_STEM, blockColors)); + + //not really constant, depend on the block age, but kinda have to be handled since textures are literally white without them + resultColors.put(Blocks.MELON_STEM, getBlockColor(Blocks.MELON_STEM, blockColors)); + resultColors.put(Blocks.PUMPKIN_STEM, getBlockColor(Blocks.PUMPKIN_STEM, blockColors)); + + return resultColors; + } + + private static JsonObject encodeBiomeColorMap(Registry biomeRegistry, Map> colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + for (Biome biome : entry.getValue()) { + Identifier registryKey = biomeRegistry.getId(biome); + keysArray.add(Objects.requireNonNull(registryKey).getPath()); + } + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getKey()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeRedstoneColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + keysArray.add(entry.getKey()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeBlocksColorMap(Registry blockRegistry, Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + Identifier registryKey = blockRegistry.getId(entry.getKey()); + keysArray.add(Objects.requireNonNull(registryKey).getPath()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + @Override + public String getDataName() { + return "tints"; + } + + @Override + public JsonObject generateDataJson() { +// DynamicRegistryManager registryManager = DynamicRegistryManager.create(); + Registry biomeRegistry = Registry.BIOME; + Registry blockRegistry = Registry.BLOCK; + + BiomeTintColors biomeTintColors = generateBiomeTintColors(biomeRegistry); + Map redstoneColors = generateRedstoneTintColors(); + Map constantTintColors = Collections.emptyMap(); + + EnvType currentEnvironment = FabricLoader.getInstance().getEnvironmentType(); + if (currentEnvironment == EnvType.CLIENT) { + constantTintColors = generateConstantTintColors(); + } + + JsonObject resultObject = new JsonObject(); + + resultObject.add("grass", encodeBiomeColorMap(biomeRegistry, biomeTintColors.grassColoursMap)); + resultObject.add("foliage", encodeBiomeColorMap(biomeRegistry, biomeTintColors.foliageColoursMap)); + resultObject.add("water", encodeBiomeColorMap(biomeRegistry, biomeTintColors.waterColourMap)); + + resultObject.add("redstone", encodeRedstoneColorMap(redstoneColors)); + resultObject.add("constant", encodeBlocksColorMap(blockRegistry, constantTintColors)); + + return resultObject; + } + + public static class BiomeTintColors { + final Map> grassColoursMap = new HashMap<>(); + final Map> foliageColoursMap = new HashMap<>(); + final Map> waterColourMap = new HashMap<>(); + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java new file mode 100644 index 00000000..0d297e7a --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.server.dedicated.EulaReader; +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; + +@Mixin(EulaReader.class) +public class EULAMixin { + @Inject(method = "isEulaAgreedTo()Z", at = @At("TAIL"), cancellable = true) + public void init(CallbackInfoReturnable cir) { + cir.setReturnValue(true); + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java new file mode 100644 index 00000000..2468492a --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Block; +import net.minecraft.item.MiningToolItem; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Set; + +@Mixin(MiningToolItem.class) +public interface MiningToolItemAccessor { + + @Accessor + Set getEffectiveBlocks(); +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java new file mode 100644 index 00000000..24804499 --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java @@ -0,0 +1,28 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.generators.DataGenerators; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.MinecraftVersion; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; +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.nio.file.Path; +import java.util.logging.Level; + +@Mixin(MinecraftDedicatedServer.class) +public class ReadyMixin { + @Inject(method = "setupServer()Z", at = @At("TAIL")) + private void init(CallbackInfoReturnable cir) { + Main.LOGGER.log(Level.INFO, "Starting data generation!"); + String versionName = MinecraftVersion.create().getName(); + Path serverRootDirectory = DGU.getCurrentlyRunningServer().getRunDirectory().toPath().toAbsolutePath(); + Path dataDumpDirectory = serverRootDirectory.resolve("minecraft-data").resolve(versionName); + DataGenerators.runDataGenerators(dataDumpDirectory); + Main.LOGGER.log(Level.INFO, "Done data generation!"); + DGU.getCurrentlyRunningServer().stop(false); + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java new file mode 100644 index 00000000..01d24c7f --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java @@ -0,0 +1,12 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.entity.effect.StatusEffectType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(StatusEffect.class) +public interface StatusEffectAccessor { + @Accessor("type") + StatusEffectType type(); +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java new file mode 100644 index 00000000..d85a31d6 --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java @@ -0,0 +1,69 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.item.ItemConvertible; +import net.minecraft.item.ItemStack; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Language; +import net.minecraft.world.World; +import net.minecraft.world.dimension.DimensionType; +import org.jetbrains.annotations.NotNull; + +public class DGU { + + private static final Language language = Language.getInstance(); + + @Environment(EnvType.CLIENT) + private static MinecraftServer getCurrentlyRunningServerClient() { + return MinecraftClient.getInstance().getServer(); + } + + @SuppressWarnings("deprecation") + private static MinecraftServer getCurrentlyRunningServerDedicated() { + return (MinecraftServer) FabricLoader.getInstance().getGameInstance(); + } + + public static MinecraftServer getCurrentlyRunningServer() { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return getCurrentlyRunningServerClient(); + } else if (environmentType == EnvType.SERVER) { + return getCurrentlyRunningServerDedicated(); + } + throw new UnsupportedOperationException(); + } + + @Environment(EnvType.CLIENT) + private static String translateTextClient(String translationKey) { + return I18n.translate(translationKey); + } + + private static String translateTextFallback(String translationKey) { + try { + return language.translate(translationKey); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to translate: '" + translationKey + "'"); + } + + public static String translateText(String translationKey) { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return translateTextClient(translationKey); + } + return translateTextFallback(translationKey); + } + + @NotNull + public static World getWorld() { + return getCurrentlyRunningServer().getWorld(DimensionType.OVERWORLD); + } + + public static ItemStack stackFor(ItemConvertible ic) { + return new ItemStack(ic); + } +} diff --git a/1.14/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java new file mode 100644 index 00000000..b9d28648 --- /dev/null +++ b/1.14/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java @@ -0,0 +1,57 @@ +//package dev.u9g.minecraftdatagenerator.util; +// +//import net.minecraft.block.BlockState; +//import net.minecraft.block.Blocks; +//import net.minecraft.block.entity.BlockEntity; +//import net.minecraft.fluid.FluidState; +//import net.minecraft.fluid.Fluids; +//import net.minecraft.util.math.BlockPos; +//import net.minecraft.world.LightType; +//import net.minecraft.world.biome.Biomes; +//import net.minecraft.world.chunk.light.LightingProvider; +//import org.jetbrains.annotations.Nullable; +// +//public enum EmptyRenderBlockView implements BlockRenderView { +// INSTANCE; +// +// @Nullable +// public BlockEntity getBlockEntity(BlockPos pos) { +// return null; +// } +// +// public BlockState getBlockState(BlockPos pos) { +// return Blocks.AIR.getDefaultState(); +// } +// +// public FluidState getFluidState(BlockPos pos) { +// return Fluids.EMPTY.getDefaultState(); +// } +// +// public int getBottomY() { +// return 0; +// } +// +// public int getHeight() { +// return 0; +// } +// +// @Override +// public LightingProvider getLightingProvider() { +// return null; +// } +// +// @Override +// public int getColor(BlockPos pos, ColorResolver colorResolver) { +// return colorResolver.getColor(Biomes.PLAINS, pos.getX(), pos.getY()); +// } +// +// @Override +// public int getLightLevel(LightType type, BlockPos pos) { +// return type == LightType.SKY ? getMaxLightLevel() : 0; +// } +// +// @Override +// public int getBaseLightLevel(BlockPos pos, int ambientDarkness) { +// return ambientDarkness; +// } +//} diff --git a/1.14/src/main/resources/fabric.mod.json b/1.14/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..060b2637 --- /dev/null +++ b/1.14/src/main/resources/fabric.mod.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "id": "minecraft-data-generator", + "version": "${version}", + "name": "Minecraft Data Generator", + "description": "", + "authors": [ + "Archengius", + "U9G" + ], + "contact": {}, + "license": "MIT", + "environment": "server", + "entrypoints": { + "main": [ + "dev.u9g.minecraftdatagenerator.Main" + ] + }, + "mixins": [ + "minecraft-data-generator.mixins.json" + ], + "depends": { + "fabricloader": ">=0.13.3", + "fabric": "*", + "minecraft": "1.14" + } +} diff --git a/1.14/src/main/resources/minecraft-data-generator.mixins.json b/1.14/src/main/resources/minecraft-data-generator.mixins.json new file mode 100644 index 00000000..ea255f59 --- /dev/null +++ b/1.14/src/main/resources/minecraft-data-generator.mixins.json @@ -0,0 +1,19 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "dev.u9g.minecraftdatagenerator.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "MiningToolItemAccessor", + "ReadyMixin", + "StatusEffectAccessor" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "server": [ + "EULAMixin" + ] +} diff --git a/1.15/build.gradle b/1.15/build.gradle new file mode 100644 index 00000000..e85de392 --- /dev/null +++ b/1.15/build.gradle @@ -0,0 +1,31 @@ +plugins { + id 'fabric-loom' +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API. This is technically optional, but you probably want it anyway. + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" +} + +processResources { + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} diff --git a/1.15/gradle.properties b/1.15/gradle.properties new file mode 100644 index 00000000..055fa5cf --- /dev/null +++ b/1.15/gradle.properties @@ -0,0 +1,8 @@ +# Fabric Properties +# check these on https://modmuss50.me/fabric.html +minecraft_version=1.15 +yarn_mappings=1.15+build.2 +loader_version=0.13.3 +# Dependencies +# check this on https://modmuss50.me/fabric.html +fabric_version=0.28.5+1.15 diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java new file mode 100644 index 00000000..97c57352 --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java @@ -0,0 +1,36 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +public class FoliageColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] pixels) { + colorMap = pixels; + } + + public static int getColor(double temperature, double humidity) { + int i = (int) ((1.0 - temperature) * 255.0); + int j = (int) ((1.0 - (humidity *= temperature)) * 255.0); + return colorMap[j << 8 | i]; + } + + public static int getSpruceColor() { + return 0x619961; + } + + public static int getBirchColor() { + return 8431445; + } + + public static int getDefaultColor() { + return 4764952; + } + + public static int getFoliageColor(Biome biome) { + double d = MathHelper.clamp(biome.getTemperature(), 0.0f, 1.0f); + double e = MathHelper.clamp(biome.getRainfall(), 0.0f, 1.0f); + return getColor(d, e); + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java new file mode 100644 index 00000000..300131c1 --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +public class GrassColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] map) { + colorMap = map; + } + + public static int getColor(double temperature, double humidity) { + humidity *= temperature; + int i = (int) ((1.0D - temperature) * 255.0D); + int j = (int) ((1.0D - humidity) * 255.0D); + int k = j << 8 | i; + return k > colorMap.length ? -65281 : colorMap[k]; + } + + public static int getGrassColorAt(Biome biome) { + double d = MathHelper.clamp(biome.getTemperature(), 0.0f, 1.0f); + double e = MathHelper.clamp(biome.getRainfall(), 0.0f, 1.0f); + return getColor(d, e); + } +} + diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java new file mode 100644 index 00000000..c9b29873 --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java @@ -0,0 +1,34 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.client.util.math.Vector3f; +import net.minecraft.util.math.MathHelper; + +public class ServerSideRedstoneWireBlock { + private static final Vector3f[] data = new Vector3f[16]; + + static { + for (int i = 0; i <= 15; ++i) { + float f = 0; + float g = f * 0.6f + ((f = (float) i / 15.0f) > 0.0f ? 0.4f : 0.3f); + float h = MathHelper.clamp(f * f * 0.7f - 0.5f, 0.0f, 1.0f); + float j = MathHelper.clamp(f * f * 0.6f - 0.7f, 0.0f, 1.0f); + data[i] = new Vector3f(g, h, j); + } + } + + public static int getWireColor(int powerLevel) { + Vector3f vector3f = data[powerLevel]; + return packRgb(vector3f.getX(), vector3f.getY(), vector3f.getZ()); + } + + private static int packRgb(float r, float g, float b) { + return packRgb(MathHelper.floor(r * 255.0f), MathHelper.floor(g * 255.0f), MathHelper.floor(b * 255.0f)); + } + + private static int packRgb(int r, int g, int b) { + int i = r; + i = (i << 8) + g; + i = (i << 8) + b; + return i; + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/Main.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/Main.java new file mode 100644 index 00000000..52a20201 --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/Main.java @@ -0,0 +1,14 @@ +package dev.u9g.minecraftdatagenerator; + +import net.fabricmc.api.ModInitializer; + +import java.util.logging.Logger; + +public class Main implements ModInitializer { + public static final Logger LOGGER = Logger.getLogger("mc-data-gen-serv"); + + @Override + public void onInitialize() { + + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java new file mode 100644 index 00000000..7a66dbf6 --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java @@ -0,0 +1,138 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; + +import java.util.Objects; + +public class BiomesDataGenerator implements IDataGenerator { + + private static String guessBiomeDimensionFromCategory(Biome biome) { + return switch (biome.getCategory()) { + case NETHER -> "nether"; + case THEEND -> "end"; + default -> "overworld"; + }; + } + + private static int getBiomeColorFor(String biomeName) { + return switch (biomeName) { + case "ocean" -> 112; + case "plains" -> 9286496; + case "desert" -> 16421912; + case "mountains" -> 6316128; + case "forest" -> 353825; + case "taiga" -> 747097; + case "swamp" -> 522674; + case "river" -> 255; + case "nether_wastes" -> 12532539; + case "the_end" -> 8421631; + case "frozen_ocean" -> 7368918; + case "frozen_river" -> 10526975; + case "snowy_tundra" -> 16777215; + case "snowy_mountains" -> 10526880; + case "mushroom_fields" -> 16711935; + case "mushroom_field_shore" -> 10486015; + case "beach" -> 16440917; + case "desert_hills" -> 13786898; + case "wooded_hills" -> 2250012; + case "taiga_hills" -> 1456435; + case "mountain_edge" -> 7501978; + case "jungle" -> 5470985; + case "jungle_hills" -> 2900485; + case "jungle_edge" -> 6458135; + case "deep_ocean" -> 48; + case "stone_shore" -> 10658436; + case "snowy_beach" -> 16445632; + case "birch_forest" -> 3175492; + case "birch_forest_hills" -> 2055986; + case "dark_forest" -> 4215066; + case "snowy_taiga" -> 3233098; + case "snowy_taiga_hills" -> 2375478; + case "giant_tree_taiga" -> 5858897; + case "giant_tree_taiga_hills" -> 4542270; + case "wooded_mountains" -> 5271632; + case "savanna" -> 12431967; + case "savanna_plateau" -> 10984804; + case "badlands" -> 14238997; + case "wooded_badlands_plateau" -> 11573093; + case "badlands_plateau" -> 13274213; + case "small_end_islands" -> 42; + case "end_midlands" -> 15464630; + case "end_highlands" -> 12828041; + case "end_barrens" -> 9474162; + case "warm_ocean" -> 172; + case "lukewarm_ocean" -> 144; + case "cold_ocean" -> 2105456; + case "deep_warm_ocean" -> 80; + case "deep_lukewarm_ocean" -> 64; + case "deep_cold_ocean" -> 2105400; + case "deep_frozen_ocean" -> 4210832; + case "the_void" -> 0; + case "sunflower_plains" -> 11918216; + case "desert_lakes" -> 16759872; + case "gravelly_mountains" -> 8947848; + case "flower_forest" -> 2985545; + case "taiga_mountains" -> 3378817; + case "swamp_hills" -> 3145690; + case "ice_spikes" -> 11853020; + case "modified_jungle" -> 8102705; + case "modified_jungle_edge" -> 9089855; + case "tall_birch_forest" -> 5807212; + case "tall_birch_hills" -> 4687706; + case "dark_forest_hills" -> 6846786; + case "snowy_taiga_mountains" -> 5864818; + case "giant_spruce_taiga" -> 8490617; + case "giant_spruce_taiga_hills" -> 7173990; + case "modified_gravelly_mountains" -> 7903352; + case "shattered_savanna" -> 15063687; + case "shattered_savanna_plateau" -> 13616524; + case "eroded_badlands" -> 16739645; + case "modified_wooded_badlands_plateau" -> 14204813; + case "modified_badlands_plateau" -> 15905933; + case "bamboo_jungle" -> 7769620; + case "bamboo_jungle_hills" -> 3884810; + case "nether" -> 16711680; + default -> throw new Error("Unexpected biome, with name: '" + biomeName + "'"); + }; + } + + public static JsonObject generateBiomeInfo(Registry registry, Biome biome) { + JsonObject biomeDesc = new JsonObject(); + Identifier registryKey = registry.getId(biome); + String localizationKey = String.format("biome.%s.%s", Objects.requireNonNull(registryKey).getNamespace(), registryKey.getPath()); + biomeDesc.addProperty("id", registry.getRawId(biome)); + biomeDesc.addProperty("name", registryKey.getPath()); + + biomeDesc.addProperty("category", biome.getCategory().getName()); + biomeDesc.addProperty("temperature", biome.getTemperature()); + biomeDesc.addProperty("precipitation", biome.getPrecipitation().getName()); + biomeDesc.addProperty("depth", biome.getDepth()); + biomeDesc.addProperty("dimension", guessBiomeDimensionFromCategory(biome)); + biomeDesc.addProperty("displayName", DGU.translateText(localizationKey)); + biomeDesc.addProperty("color", getBiomeColorFor(registryKey.getPath())); + biomeDesc.addProperty("rainfall", biome.getRainfall()); + + return biomeDesc; + } + + @Override + public String getDataName() { + return "biomes"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray biomesArray = new JsonArray(); + Registry biomeRegistry = Registry.BIOME; + + biomeRegistry.stream() + .map(biome -> generateBiomeInfo(biomeRegistry, biome)) + .forEach(biomesArray::add); + return biomesArray; + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java new file mode 100644 index 00000000..7bd1ec44 --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java @@ -0,0 +1,108 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.EmptyBlockView; + +import java.util.*; + +public class BlockCollisionShapesDataGenerator implements IDataGenerator { + + @Override + public String getDataName() { + return "blockCollisionShapes"; + } + + @Override + public JsonObject generateDataJson() { + Registry blockRegistry = Registry.BLOCK; + BlockShapesCache blockShapesCache = new BlockShapesCache(); + + blockRegistry.forEach(blockShapesCache::processBlock); + + JsonObject resultObject = new JsonObject(); + + resultObject.add("blocks", blockShapesCache.dumpBlockShapeIndices(blockRegistry)); + resultObject.add("shapes", blockShapesCache.dumpShapesObject()); + + return resultObject; + } + + private static class BlockShapesCache { + public final Map uniqueBlockShapes = new HashMap<>(); + public final Map> blockCollisionShapes = new HashMap<>(); + private int lastCollisionShapeId = 0; + + public void processBlock(Block block) { + List blockStates = block.getStateManager().getStates(); + List blockCollisionShapes = new ArrayList<>(); + + for (BlockState blockState : blockStates) { + VoxelShape blockShape = blockState.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + Integer blockShapeIndex = uniqueBlockShapes.get(blockShape); + + if (blockShapeIndex == null) { + blockShapeIndex = lastCollisionShapeId++; + uniqueBlockShapes.put(blockShape, blockShapeIndex); + } + blockCollisionShapes.add(blockShapeIndex); + } + + this.blockCollisionShapes.put(block, blockCollisionShapes); + } + + public JsonObject dumpBlockShapeIndices(Registry blockRegistry) { + JsonObject resultObject = new JsonObject(); + + for (var entry : blockCollisionShapes.entrySet()) { + List blockCollisions = entry.getValue(); + long distinctShapesCount = blockCollisions.stream().distinct().count(); + JsonElement blockCollision; + if (distinctShapesCount == 1L) { + blockCollision = new JsonPrimitive(blockCollisions.get(0)); + } else { + blockCollision = new JsonArray(); + for (int collisionId : blockCollisions) { + ((JsonArray) blockCollision).add(collisionId); + } + } + + Identifier registryKey = blockRegistry.getId(entry.getKey()); + resultObject.add(Objects.requireNonNull(registryKey).getPath(), blockCollision); + } + + return resultObject; + } + + public JsonObject dumpShapesObject() { + JsonObject shapesObject = new JsonObject(); + + for (var entry : uniqueBlockShapes.entrySet()) { + JsonArray boxesArray = new JsonArray(); + entry.getKey().forEachBox((x1, y1, z1, x2, y2, z2) -> { + JsonArray oneBoxJsonArray = new JsonArray(); + + oneBoxJsonArray.add(x1); + oneBoxJsonArray.add(y1); + oneBoxJsonArray.add(z1); + + oneBoxJsonArray.add(x2); + oneBoxJsonArray.add(y2); + oneBoxJsonArray.add(z2); + + boxesArray.add(oneBoxJsonArray); + }); + shapesObject.add(Integer.toString(entry.getValue()), boxesArray); + } + return shapesObject; + } + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java new file mode 100644 index 00000000..e20bb439 --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java @@ -0,0 +1,152 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.base.CaseFormat; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.mixin.MiningToolItemAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.block.AirBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.MiningToolItem; +import net.minecraft.loot.context.LootContext; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.state.property.EnumProperty; +import net.minecraft.state.property.IntProperty; +import net.minecraft.state.property.Property; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.EmptyBlockView; + +import java.util.Collection; +import java.util.List; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +public class BlocksDataGenerator implements IDataGenerator { + + private static final Logger logger = Main.LOGGER; + + private static List getItemsEffectiveForBlock(Block block) { + return Registry.ITEM.stream() + .filter(item -> item instanceof MiningToolItem) + .filter(item -> ((MiningToolItemAccessor) item).getEffectiveBlocks().contains(block)) + .collect(Collectors.toList()); + } + + private static List populateDropsIfPossible(BlockState blockState, Item firstToolItem) { + //If we have local world context, we can actually evaluate loot tables and determine actual data + ServerWorld serverWorld = (ServerWorld) DGU.getWorld(); + LootContext.Builder lootContext = new LootContext.Builder(serverWorld) + .setRandom(0L); + return blockState.getDroppedStacks(lootContext); + } + + private static String getPropertyTypeName(Property property) { + //Explicitly handle default minecraft properties + if (property instanceof BooleanProperty) { + return "bool"; + } + if (property instanceof IntProperty) { + return "int"; + } + if (property instanceof EnumProperty) { + return "enum"; + } + + //Use simple class name as fallback, this code will give something like + //example_type for ExampleTypeProperty class name + String rawPropertyName = property.getClass().getSimpleName().replace("Property", ""); + return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, rawPropertyName); + } + + private static > JsonObject generateStateProperty(Property property) { + JsonObject propertyObject = new JsonObject(); + Collection propertyValues = property.getValues(); + + propertyObject.addProperty("name", property.getName()); + propertyObject.addProperty("type", getPropertyTypeName(property)); + propertyObject.addProperty("num_values", propertyValues.size()); + + //Do not add values for vanilla boolean properties, they are known by default + if (!(property instanceof BooleanProperty)) { + JsonArray propertyValuesArray = new JsonArray(); + for (T propertyValue : propertyValues) { + propertyValuesArray.add(property.name(propertyValue)); + } + propertyObject.add("values", propertyValuesArray); + } + return propertyObject; + } + + public static JsonObject generateBlock(Registry blockRegistry, Block block) { + JsonObject blockDesc = new JsonObject(); + + List blockStates = block.getStateManager().getStates(); + BlockState defaultState = block.getDefaultState(); + Identifier registryKey = blockRegistry.getId(block); + String localizationKey = block.getTranslationKey(); + List effectiveTools = getItemsEffectiveForBlock(block); + + blockDesc.addProperty("id", blockRegistry.getRawId(block)); + blockDesc.addProperty("name", registryKey.getPath()); + blockDesc.addProperty("displayName", DGU.translateText(localizationKey)); + + float hardness = block.getDefaultState().getHardness(null, null); + + blockDesc.addProperty("hardness", hardness); + blockDesc.addProperty("resistance", block.getBlastResistance()); + blockDesc.addProperty("stackSize", block.asItem().getMaxCount()); + blockDesc.addProperty("diggable", hardness != -1.0f && !(block instanceof AirBlock)); + JsonObject effTools = new JsonObject(); + effectiveTools.forEach(item -> effTools.addProperty( + String.valueOf(Registry.ITEM.getRawId(item)), // key + item.getMiningSpeed(DGU.asStack(item), defaultState) // value + )); + blockDesc.add("effectiveTools", effTools); + + blockDesc.addProperty("transparent", !defaultState.isOpaque()); + blockDesc.addProperty("emitLight", defaultState.getLuminance()); + blockDesc.addProperty("filterLight", defaultState.getOpacity(EmptyBlockView.INSTANCE, BlockPos.ORIGIN)); + + blockDesc.addProperty("defaultState", Block.getRawIdFromState(defaultState)); + blockDesc.addProperty("minStateId", Block.getRawIdFromState(blockStates.get(0))); + blockDesc.addProperty("maxStateId", Block.getRawIdFromState(blockStates.get(blockStates.size() - 1))); + JsonArray stateProperties = new JsonArray(); + for (Property property : block.getStateManager().getProperties()) { + stateProperties.add(generateStateProperty(property)); + } + blockDesc.add("states", stateProperties); + +// List drops = populateDropsIfPossible(defaultState, effectiveTools.stream().findFirst().orElse(Items.AIR)); + + JsonArray dropsArray = new JsonArray(); +// drops.forEach(dropped -> dropsArray.add(Item.getRawId(dropped.getItem()))); + blockDesc.add("drops", dropsArray); + + VoxelShape blockCollisionShape = defaultState.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + blockDesc.addProperty("boundingBox", blockCollisionShape.isEmpty() ? "empty" : "block"); + + return blockDesc; + } + + @Override + public String getDataName() { + return "blocks"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultBlocksArray = new JsonArray(); + Registry blockRegistry = Registry.BLOCK; + + blockRegistry.forEach(block -> resultBlocksArray.add(generateBlock(blockRegistry, block))); + return resultBlocksArray; + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java new file mode 100644 index 00000000..3c05c30f --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java @@ -0,0 +1,79 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonWriter; +import dev.u9g.minecraftdatagenerator.Main; + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class DataGenerators { + + private static final List GENERATORS = new ArrayList<>(); + private static final Logger logger = Main.LOGGER; + + static { + register(new BiomesDataGenerator()); + register(new BlockCollisionShapesDataGenerator()); + register(new BlocksDataGenerator()); + register(new EffectsDataGenerator()); + register(new EnchantmentsDataGenerator()); + register(new EntitiesDataGenerator()); + register(new FoodsDataGenerator()); + register(new ItemsDataGenerator()); + register(new ParticlesDataGenerator()); + register(new TintsDataGenerator()); + register(new LanguageDataGenerator()); + register(new InstrumentsDataGenerator()); + } + + public static void register(IDataGenerator generator) { + GENERATORS.add(generator); + } + + public static boolean runDataGenerators(Path outputDirectory) { + try { + Files.createDirectories(outputDirectory); + } catch (IOException exception) { + logger.log(Level.INFO, "Failed to create data generator output directory at " + outputDirectory); + exception.printStackTrace(); + return false; + } + + int generatorsFailed = 0; + logger.log(Level.INFO, MessageFormat.format("Running minecraft data generators, output at {0}", outputDirectory)); + + for (IDataGenerator dataGenerator : GENERATORS) { + logger.log(Level.INFO, MessageFormat.format("Running generator {0}", dataGenerator.getDataName())); + try { + String outputFileName = String.format("%s.json", dataGenerator.getDataName()); + JsonElement outputElement = dataGenerator.generateDataJson(); + Path outputFilePath = outputDirectory.resolve(outputFileName); + + try (Writer writer = Files.newBufferedWriter(outputFilePath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + JsonWriter jsonWriter = new JsonWriter(writer); + jsonWriter.setIndent(" "); + Streams.write(outputElement, jsonWriter); + } + logger.log(Level.INFO, MessageFormat.format("Generator: {0} -> {1}", dataGenerator.getDataName(), outputFileName)); + + } catch (Throwable exception) { + logger.log(Level.INFO, MessageFormat.format("Failed to run data generator {0}", dataGenerator.getDataName())); + exception.printStackTrace(); + generatorsFailed++; + } + } + + logger.log(Level.INFO, "Finishing running data generators"); + return generatorsFailed == 0; + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java new file mode 100644 index 00000000..87620c01 --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java @@ -0,0 +1,48 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.StatusEffectAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.entity.effect.StatusEffectType; +import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.stream.Collectors; + +public class EffectsDataGenerator implements IDataGenerator { + + public static JsonObject generateEffect(Registry registry, StatusEffect statusEffect) { + JsonObject effectDesc = new JsonObject(); + Identifier registryKey = registry.getId(statusEffect); + + effectDesc.addProperty("id", registry.getRawId(statusEffect)); + if (statusEffect == StatusEffects.UNLUCK) { + effectDesc.addProperty("name", "BadLuck"); + effectDesc.addProperty("displayName", "Bad Luck"); + } else { + effectDesc.addProperty("name", Arrays.stream(registryKey.getPath().split("_")).map(StringUtils::capitalize).collect(Collectors.joining())); + effectDesc.addProperty("displayName", DGU.translateText(statusEffect.getTranslationKey())); + } + + effectDesc.addProperty("type", ((StatusEffectAccessor) statusEffect).type() == StatusEffectType.BENEFICIAL ? "good" : "bad"); + return effectDesc; + } + + @Override + public String getDataName() { + return "effects"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry statusEffectRegistry = Registry.STATUS_EFFECT; + statusEffectRegistry.forEach(effect -> resultsArray.add(generateEffect(statusEffectRegistry, effect))); + return resultsArray; + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java new file mode 100644 index 00000000..f4f067b8 --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java @@ -0,0 +1,107 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +public class EnchantmentsDataGenerator implements IDataGenerator { + + private static final ImmutableMap ENCHANTMENT_TARGET_NAMES = ImmutableMap.builder() + .put(EnchantmentTarget.ARMOR, "armor") + .put(EnchantmentTarget.ARMOR_FEET, "armor_feet") + .put(EnchantmentTarget.ARMOR_LEGS, "armor_legs") + .put(EnchantmentTarget.ARMOR_CHEST, "armor_chest") + .put(EnchantmentTarget.ARMOR_HEAD, "armor_head") + .put(EnchantmentTarget.WEAPON, "weapon") + .put(EnchantmentTarget.DIGGER, "digger") + .put(EnchantmentTarget.FISHING_ROD, "fishing_rod") + .put(EnchantmentTarget.TRIDENT, "trident") + .put(EnchantmentTarget.BREAKABLE, "breakable") + .put(EnchantmentTarget.BOW, "bow") + .put(EnchantmentTarget.WEARABLE, "wearable") + .put(EnchantmentTarget.CROSSBOW, "crossbow") + .put(EnchantmentTarget.ALL, "vanishable") // according to VanishingCurseEnchantment + .build(); + + public static String getEnchantmentTargetName(EnchantmentTarget target) { + return ENCHANTMENT_TARGET_NAMES.getOrDefault(target, target.name().toLowerCase(Locale.ROOT)); + } + + // Equation enchantment costs follow is a * level + b, so we can easily retrieve a and b by passing zero level + private static JsonObject generateEnchantmentMinPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMinimumPower(0); + int a = enchantment.getMinimumPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + private static JsonObject generateEnchantmentMaxPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMaximumPower(0); + int a = enchantment.getMaximumPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + public static JsonObject generateEnchantment(Registry registry, Enchantment enchantment) { + JsonObject enchantmentDesc = new JsonObject(); + Identifier registryKey = registry.getId(enchantment); + + enchantmentDesc.addProperty("id", registry.getRawId(enchantment)); + enchantmentDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + enchantmentDesc.addProperty("displayName", DGU.translateText(enchantment.getTranslationKey())); + + enchantmentDesc.addProperty("maxLevel", enchantment.getMaximumLevel()); + enchantmentDesc.add("minCost", generateEnchantmentMinPowerCoefficients(enchantment)); + enchantmentDesc.add("maxCost", generateEnchantmentMaxPowerCoefficients(enchantment)); + + enchantmentDesc.addProperty("treasureOnly", enchantment.isTreasure()); + enchantmentDesc.addProperty("curse", enchantment.isCursed()); + + List incompatibleEnchantments = registry.stream() + .filter(other -> !enchantment.isDifferent(other)) + .filter(other -> other != enchantment).toList(); + + JsonArray excludes = new JsonArray(); + for (Enchantment excludedEnchantment : incompatibleEnchantments) { + Identifier otherKey = registry.getId(excludedEnchantment); + excludes.add(Objects.requireNonNull(otherKey).getPath()); + } + enchantmentDesc.add("exclude", excludes); + + enchantmentDesc.addProperty("category", getEnchantmentTargetName(enchantment.type)); + enchantmentDesc.addProperty("weight", enchantment.getWeight().getWeight()); + enchantmentDesc.addProperty("tradeable", true); // the first non-tradeable enchant came in 1.16, soul speed + enchantmentDesc.addProperty("discoverable", true); // the first non-enchantable enchant came in 1.16, soul speed + + return enchantmentDesc; + } + + @Override + public String getDataName() { + return "enchantments"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry enchantmentRegistry = Registry.ENCHANTMENT; + enchantmentRegistry.stream() + .forEach(enchantment -> resultsArray.add(generateEnchantment(enchantmentRegistry, enchantment))); + return resultsArray; + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java new file mode 100644 index 00000000..e80cded5 --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java @@ -0,0 +1,122 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.mob.AmbientEntity; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.mob.WaterCreatureEntity; +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.passive.PassiveEntity; +import net.minecraft.entity.projectile.ProjectileEntity; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.ParameterizedType; +import java.util.Objects; + +public class EntitiesDataGenerator implements IDataGenerator { + + public static JsonObject generateEntity(Registry> entityRegistry, EntityType entityType) { + JsonObject entityDesc = new JsonObject(); + Identifier registryKey = entityRegistry.getId(entityType); + int entityRawId = entityRegistry.getRawId(entityType); + + entityDesc.addProperty("id", entityRawId); + entityDesc.addProperty("internalId", entityRawId); + entityDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + entityDesc.addProperty("displayName", DGU.translateText(entityType.getTranslationKey())); + entityDesc.addProperty("width", entityType.getDimensions().width); + entityDesc.addProperty("height", entityType.getDimensions().height); + + String entityTypeString = "UNKNOWN"; + Entity entityObject = entityType.create(DGU.getWorld()); + entityTypeString = entityObject != null ? getEntityTypeForClass(entityObject.getClass()) : "player"; + entityDesc.addProperty("type", entityTypeString); + entityDesc.addProperty("category", getCategoryFrom(entityType)); + + return entityDesc; + } + + private static String getCategoryFrom(@NotNull EntityType entityType) { + if (entityType == EntityType.PLAYER) return "other"; // fail early for player entities + Class entityClazz = null; + try { + for (var field : EntityType.class.getFields()) + if (entityType == field.get(EntityType.class)) + entityClazz = (Class) ((ParameterizedType) TypeToken.get(field.getGenericType()).getType()).getActualTypeArguments()[0]; + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + if (entityClazz == null) throw new RuntimeException("Shouldn't be null..."); + return switch (entityClazz.getPackageName()) { + case "net.minecraft.entity.decoration", "net.minecraft.entity.decoration.painting" -> "Immobile"; + case "net.minecraft.entity.boss", "net.minecraft.entity.mob", "net.minecraft.entity.boss.dragon" -> + "Hostile mobs"; + case "net.minecraft.entity.projectile", "net.minecraft.entity.projectile.thrown", "net.minecraft.entity.thrown" -> + "Projectiles"; + case "net.minecraft.entity.passive" -> "Passive mobs"; + case "net.minecraft.entity.vehicle" -> "Vehicles"; + case "net.minecraft.entity" -> "other"; + default -> throw new Error("Unexpected entity type: " + entityClazz.getPackageName()); + }; + } + + //Honestly, both "type" and "category" fields in the schema and examples do not contain any useful information + //Since category is optional, I will just leave it out, and for type I will assume general entity classification + //by the Entity class hierarchy (which has some weirdness too by the way) + private static String getEntityTypeForClass(Class entityClass) { + //Top-level classifications + if (WaterCreatureEntity.class.isAssignableFrom(entityClass)) { + return "water_creature"; + } + if (AnimalEntity.class.isAssignableFrom(entityClass)) { + return "animal"; + } + if (HostileEntity.class.isAssignableFrom(entityClass)) { + return "hostile"; + } + if (AmbientEntity.class.isAssignableFrom(entityClass)) { + return "ambient"; + } + + //Second level classifications. PathAwareEntity is not included because it + //doesn't really make much sense to categorize by it + if (PassiveEntity.class.isAssignableFrom(entityClass)) { + return "passive"; + } + if (MobEntity.class.isAssignableFrom(entityClass)) { + return "mob"; + } + + //Other classifications only include living entities and projectiles. everything else is categorized as other + if (LivingEntity.class.isAssignableFrom(entityClass)) { + return "living"; + } + if (ProjectileEntity.class.isAssignableFrom(entityClass)) { + return "projectile"; + } + return "other"; + } + + @Override + public String getDataName() { + return "entities"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry> entityTypeRegistry = Registry.ENTITY_TYPE; + entityTypeRegistry.forEach(entity -> resultArray.add(generateEntity(entityTypeRegistry, entity))); + return resultArray; + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java new file mode 100644 index 00000000..fc494d1e --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java @@ -0,0 +1,51 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.item.FoodComponent; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Objects; + +public class FoodsDataGenerator implements IDataGenerator { + + public static JsonObject generateFoodDescriptor(Registry registry, Item foodItem) { + JsonObject foodDesc = new JsonObject(); + Identifier registryKey = registry.getId(foodItem); + + foodDesc.addProperty("id", registry.getRawId(foodItem)); + foodDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + foodDesc.addProperty("stackSize", foodItem.getMaxCount()); + foodDesc.addProperty("displayName", DGU.translateText(foodItem.getTranslationKey())); + + FoodComponent foodComponent = Objects.requireNonNull(foodItem.getFoodComponent()); + float foodPoints = foodComponent.getHunger(); + float saturationRatio = foodComponent.getSaturationModifier() * 2.0F; + float saturation = foodPoints * saturationRatio; + + foodDesc.addProperty("foodPoints", foodPoints); + foodDesc.addProperty("saturation", saturation); + + foodDesc.addProperty("effectiveQuality", foodPoints + saturation); + foodDesc.addProperty("saturationRatio", saturationRatio); + return foodDesc; + } + + @Override + public String getDataName() { + return "foods"; + } + + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry itemRegistry = Registry.ITEM; + itemRegistry.stream() + .filter(Item::isFood) + .forEach(food -> resultsArray.add(generateFoodDescriptor(itemRegistry, food))); + return resultsArray; + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java new file mode 100644 index 00000000..81fe4e20 --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; + +public interface IDataGenerator { + + String getDataName(); + + JsonElement generateDataJson(); +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java new file mode 100644 index 00000000..e7b9b54f --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java @@ -0,0 +1,25 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.minecraft.block.enums.Instrument; + +public class InstrumentsDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "instruments"; + } + + @Override + public JsonElement generateDataJson() { + JsonArray array = new JsonArray(); + for (Instrument instrument : Instrument.values()) { + JsonObject object = new JsonObject(); + object.addProperty("id", instrument.ordinal()); + object.addProperty("name", instrument.asString()); + array.add(object); + } + return array; + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java new file mode 100644 index 00000000..cd177279 --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java @@ -0,0 +1,82 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class ItemsDataGenerator implements IDataGenerator { + + private static List calculateItemsToRepairWith(Registry itemRegistry, Item sourceItem) { + ItemStack sourceItemStack = DGU.asStack(sourceItem); + return itemRegistry.stream() + .filter(otherItem -> sourceItem.canRepair(sourceItemStack, DGU.asStack(otherItem))) + .collect(Collectors.toList()); + } + + private static List getApplicableEnchantmentTargets(Item sourceItem) { + return Arrays.stream(EnchantmentTarget.values()) + .filter(target -> target.isAcceptableItem(sourceItem)) + .collect(Collectors.toList()); + } + + public static JsonObject generateItem(Registry itemRegistry, Item item) { + JsonObject itemDesc = new JsonObject(); + Identifier registryKey = itemRegistry.getId(item); + + itemDesc.addProperty("id", itemRegistry.getRawId(item)); + itemDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + itemDesc.addProperty("displayName", DGU.translateText(item.getTranslationKey())); + itemDesc.addProperty("stackSize", item.getMaxCount()); + + List enchantmentTargets = getApplicableEnchantmentTargets(item); + + JsonArray enchantCategoriesArray = new JsonArray(); + for (EnchantmentTarget target : enchantmentTargets) { + enchantCategoriesArray.add(EnchantmentsDataGenerator.getEnchantmentTargetName(target)); + } + if (enchantCategoriesArray.size() > 0) { + itemDesc.add("enchantCategories", enchantCategoriesArray); + } + + if (item.isDamageable()) { + List repairWithItems = calculateItemsToRepairWith(itemRegistry, item); + + JsonArray fixedWithArray = new JsonArray(); + for (Item repairWithItem : repairWithItems) { + Identifier repairWithName = itemRegistry.getId(repairWithItem); + fixedWithArray.add(Objects.requireNonNull(repairWithName).getPath()); + } + if (fixedWithArray.size() > 0) { + itemDesc.add("repairWith", fixedWithArray); + } + + int maxDurability = item.getMaxDamage(); + itemDesc.addProperty("maxDurability", maxDurability); + } + return itemDesc; + } + + @Override + public String getDataName() { + return "items"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry itemRegistry = Registry.ITEM; + itemRegistry.stream().forEach(item -> resultArray.add(generateItem(itemRegistry, item))); + return resultArray; + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java new file mode 100644 index 00000000..7a3c6ced --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +public class LanguageDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "language"; + } + + @Override + public JsonElement generateDataJson() { + try { + InputStream inputStream = Objects.requireNonNull(this.getClass().getResourceAsStream("/assets/minecraft/lang/en_us.json")); + return new Gson().fromJson(new InputStreamReader(inputStream, StandardCharsets.UTF_8), JsonObject.class); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to generate language file"); + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java new file mode 100644 index 00000000..d537280d --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java @@ -0,0 +1,34 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.minecraft.particle.ParticleType; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Objects; + +public class ParticlesDataGenerator implements IDataGenerator { + + public static JsonObject generateParticleType(Registry> registry, ParticleType particleType) { + JsonObject effectDesc = new JsonObject(); + Identifier registryKey = registry.getId(particleType); + + effectDesc.addProperty("id", registry.getRawId(particleType)); + effectDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + return effectDesc; + } + + @Override + public String getDataName() { + return "particles"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry> particleTypeRegistry = Registry.PARTICLE_TYPE; + particleTypeRegistry.forEach(particleType -> resultsArray.add(generateParticleType(particleTypeRegistry, particleType))); + return resultsArray; + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java new file mode 100644 index 00000000..5e4587dd --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java @@ -0,0 +1,121 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import net.minecraft.item.Item; +import net.minecraft.util.registry.Registry; + +public class RecipeDataGenerator implements IDataGenerator { + + private static int getRawIdFor(Item item) { + return Registry.ITEM.getRawId(item); + } + + @Override + public String getDataName() { + return "recipes"; + } + + @Override + public JsonElement generateDataJson() { +// JsonObject finalObj = new JsonObject(); +// Multimap recipes = ArrayListMultimap.create(); +// for (Recipe recipe : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) { +// if (recipe instanceof ShapedRecipe sr) { +// var ingredients = sr.getIngredients(); +// List ingr = new ArrayList<>(); +// for (int i = 0; i < 9; i++) { +// if (i >= ingredients.size()) { +// ingr.add(-1); +// continue; +// } +// var stacks = ingredients.get(i); +//// var matching = stacks.getMatchingStacks(); +//// if (matching.length == 0) { +//// ingr.add(-1); +//// } else { +//// ingr.add(getRawIdFor(matching[0].getItem())); +//// } +// } +// Lists.reverse(ingr); +// +// JsonArray inShape = new JsonArray(); +// +// var iter = ingr.iterator(); +// for (int y = 0; y < 3; y++) { +// var jsonRow = new JsonArray(); +// int one = iter.next(); +// int two = iter.next(); +// int three = iter.next(); +// if (y > 0 && one == -1 && two == -1 && three == -1) continue; +// jsonRow.add(one); +// jsonRow.add(two); +// jsonRow.add(three); +// inShape.add(jsonRow); +// } +// +// JsonObject finalRecipe = new JsonObject(); +// finalRecipe.add("inShape", inShape); +// +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +// resultObject.addProperty("count", sr.getOutput().getCount()); +// finalRecipe.add("result", resultObject); +// +// String id = ((Integer) getRawIdFor(sr.getOutput().getItem())).toString(); +// +// if (!finalObj.has(id)) { +// finalObj.add(id, new JsonArray()); +// } +// finalObj.get(id).getAsJsonArray().add(finalRecipe); +//// var input = new JsonArray(); +//// var ingredients = sr.getIngredients().stream().toList(); +//// for (int y = 0; y < sr.getHeight(); y++) { +//// var arr = new JsonArray(); +//// for (int x = 0; x < sr.getWidth(); x++) { +//// if ((y*3)+x >= ingredients.size()) { +//// arr.add(JsonNull.INSTANCE); +//// continue; +//// } +//// var ingredient = ingredients.get((y*3)+x).getMatchingStacks(); // FIXME: fix when there are more than one matching stack +//// if (ingredient.length == 0) { +//// arr.add(JsonNull.INSTANCE); +//// } else { +//// arr.add(getRawIdFor(ingredient[0].getItem())); +//// } +//// } +//// input.add(arr); +//// } +//// var rootRecipeObject = new JsonObject(); +//// rootRecipeObject.add("inShape", input); +//// var resultObject = new JsonObject(); +//// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +//// resultObject.addProperty("count", sr.getOutput().getCount()); +//// rootRecipeObject.add("result", resultObject); +//// recipes.put(getRawIdFor(sr.getOutput().getItem()), rootRecipeObject); +// } else if (recipe instanceof ShapelessRecipe sl) { + +// var ingredients = new JsonArray(); +// for (Ingredient ingredient : sl.getIngredients()) { +// if (ingredient.isEmpty()) continue; +//// ingredients.add(getRawIdFor(ingredient.getMatchingStacks()[0].getItem())); +// } +// var rootRecipeObject = new JsonObject(); +// rootRecipeObject.add("ingredients", ingredients); +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sl.getOutput().getItem())); +// resultObject.addProperty("count", sl.getOutput().getCount()); +// rootRecipeObject.add("result", resultObject); +// recipes.put(getRawIdFor(sl.getOutput().getItem()), rootRecipeObject); +// } +// } +// recipes.forEach((a, b) -> { +// if (!finalObj.has(a.toString())) { +// finalObj.add(a.toString(), new JsonArray()); +// } +// finalObj.get(a.toString()).getAsJsonArray().add(b); +// }); +// return finalObj; + return JsonNull.INSTANCE; + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java new file mode 100644 index 00000000..a9b7788d --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java @@ -0,0 +1,169 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.FoliageColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.GrassColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.ServerSideRedstoneWireBlock; +import dev.u9g.minecraftdatagenerator.util.EmptyRenderBlockView; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.block.RedstoneWireBlock; +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; + +import java.util.*; + +public class TintsDataGenerator implements IDataGenerator { + + public static BiomeTintColors generateBiomeTintColors(Registry biomeRegistry) { + BiomeTintColors colors = new BiomeTintColors(); + + biomeRegistry.forEach(biome -> { + int biomeGrassColor = GrassColors.getGrassColorAt(biome); + int biomeFoliageColor = FoliageColors.getFoliageColor(biome); + int biomeWaterColor = biome.getWaterColor(); + + colors.grassColoursMap.computeIfAbsent(biomeGrassColor, k -> new ArrayList<>()).add(biome); + colors.foliageColoursMap.computeIfAbsent(biomeFoliageColor, k -> new ArrayList<>()).add(biome); + colors.waterColourMap.computeIfAbsent(biomeWaterColor, k -> new ArrayList<>()).add(biome); + }); + return colors; + } + + public static Map generateRedstoneTintColors() { + Map resultColors = new HashMap<>(); + + for (int redstoneLevel : RedstoneWireBlock.POWER.getValues()) { + int color = ServerSideRedstoneWireBlock.getWireColor(redstoneLevel); + resultColors.put(redstoneLevel, color); + } + return resultColors; + } + + @Environment(EnvType.CLIENT) + private static int getBlockColor(Block block, BlockColors blockColors) { + return blockColors.getColor(block.getDefaultState(), EmptyRenderBlockView.INSTANCE, BlockPos.ORIGIN, 0xFFFFFF); + } + + @Environment(EnvType.CLIENT) + public static Map generateConstantTintColors() { + Map resultColors = new HashMap<>(); + BlockColors blockColors = BlockColors.create(); + + resultColors.put(Blocks.BIRCH_LEAVES, FoliageColors.getBirchColor()); + resultColors.put(Blocks.SPRUCE_LEAVES, FoliageColors.getSpruceColor()); + + resultColors.put(Blocks.LILY_PAD, getBlockColor(Blocks.LILY_PAD, blockColors)); + resultColors.put(Blocks.ATTACHED_MELON_STEM, getBlockColor(Blocks.ATTACHED_MELON_STEM, blockColors)); + resultColors.put(Blocks.ATTACHED_PUMPKIN_STEM, getBlockColor(Blocks.ATTACHED_PUMPKIN_STEM, blockColors)); + + //not really constant, depend on the block age, but kinda have to be handled since textures are literally white without them + resultColors.put(Blocks.MELON_STEM, getBlockColor(Blocks.MELON_STEM, blockColors)); + resultColors.put(Blocks.PUMPKIN_STEM, getBlockColor(Blocks.PUMPKIN_STEM, blockColors)); + + return resultColors; + } + + private static JsonObject encodeBiomeColorMap(Registry biomeRegistry, Map> colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + for (Biome biome : entry.getValue()) { + Identifier registryKey = biomeRegistry.getId(biome); + keysArray.add(Objects.requireNonNull(registryKey).getPath()); + } + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getKey()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeRedstoneColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + keysArray.add(entry.getKey()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeBlocksColorMap(Registry blockRegistry, Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + Identifier registryKey = blockRegistry.getId(entry.getKey()); + keysArray.add(Objects.requireNonNull(registryKey).getPath()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + @Override + public String getDataName() { + return "tints"; + } + + @Override + public JsonObject generateDataJson() { +// DynamicRegistryManager registryManager = DynamicRegistryManager.create(); + Registry biomeRegistry = Registry.BIOME; + Registry blockRegistry = Registry.BLOCK; + + BiomeTintColors biomeTintColors = generateBiomeTintColors(biomeRegistry); + Map redstoneColors = generateRedstoneTintColors(); + Map constantTintColors = Collections.emptyMap(); + + EnvType currentEnvironment = FabricLoader.getInstance().getEnvironmentType(); + if (currentEnvironment == EnvType.CLIENT) { + constantTintColors = generateConstantTintColors(); + } + + JsonObject resultObject = new JsonObject(); + + resultObject.add("grass", encodeBiomeColorMap(biomeRegistry, biomeTintColors.grassColoursMap)); + resultObject.add("foliage", encodeBiomeColorMap(biomeRegistry, biomeTintColors.foliageColoursMap)); + resultObject.add("water", encodeBiomeColorMap(biomeRegistry, biomeTintColors.waterColourMap)); + + resultObject.add("redstone", encodeRedstoneColorMap(redstoneColors)); + resultObject.add("constant", encodeBlocksColorMap(blockRegistry, constantTintColors)); + + return resultObject; + } + + public static class BiomeTintColors { + final Map> grassColoursMap = new HashMap<>(); + final Map> foliageColoursMap = new HashMap<>(); + final Map> waterColourMap = new HashMap<>(); + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java new file mode 100644 index 00000000..0d297e7a --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.server.dedicated.EulaReader; +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; + +@Mixin(EulaReader.class) +public class EULAMixin { + @Inject(method = "isEulaAgreedTo()Z", at = @At("TAIL"), cancellable = true) + public void init(CallbackInfoReturnable cir) { + cir.setReturnValue(true); + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java new file mode 100644 index 00000000..bca254e3 --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java @@ -0,0 +1,18 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Block; +import net.minecraft.item.MiningToolItem; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Set; + +@Mixin(MiningToolItem.class) +public interface MiningToolItemAccessor { + + @Accessor + Set getEffectiveBlocks(); + + @Accessor + float getMiningSpeed(); +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java new file mode 100644 index 00000000..24804499 --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java @@ -0,0 +1,28 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.generators.DataGenerators; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.MinecraftVersion; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; +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.nio.file.Path; +import java.util.logging.Level; + +@Mixin(MinecraftDedicatedServer.class) +public class ReadyMixin { + @Inject(method = "setupServer()Z", at = @At("TAIL")) + private void init(CallbackInfoReturnable cir) { + Main.LOGGER.log(Level.INFO, "Starting data generation!"); + String versionName = MinecraftVersion.create().getName(); + Path serverRootDirectory = DGU.getCurrentlyRunningServer().getRunDirectory().toPath().toAbsolutePath(); + Path dataDumpDirectory = serverRootDirectory.resolve("minecraft-data").resolve(versionName); + DataGenerators.runDataGenerators(dataDumpDirectory); + Main.LOGGER.log(Level.INFO, "Done data generation!"); + DGU.getCurrentlyRunningServer().stop(false); + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java new file mode 100644 index 00000000..01d24c7f --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java @@ -0,0 +1,12 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.entity.effect.StatusEffectType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(StatusEffect.class) +public interface StatusEffectAccessor { + @Accessor("type") + StatusEffectType type(); +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java new file mode 100644 index 00000000..c5cdc360 --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java @@ -0,0 +1,69 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Language; +import net.minecraft.world.World; +import net.minecraft.world.dimension.DimensionType; +import org.jetbrains.annotations.NotNull; + +public class DGU { + + private static final Language language = Language.getInstance(); + + @Environment(EnvType.CLIENT) + private static MinecraftServer getCurrentlyRunningServerClient() { + return MinecraftClient.getInstance().getServer(); + } + + @SuppressWarnings("deprecation") + private static MinecraftServer getCurrentlyRunningServerDedicated() { + return (MinecraftServer) FabricLoader.getInstance().getGameInstance(); + } + + public static MinecraftServer getCurrentlyRunningServer() { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return getCurrentlyRunningServerClient(); + } else if (environmentType == EnvType.SERVER) { + return getCurrentlyRunningServerDedicated(); + } + throw new UnsupportedOperationException(); + } + + @Environment(EnvType.CLIENT) + private static String translateTextClient(String translationKey) { + return I18n.translate(translationKey); + } + + private static String translateTextFallback(String translationKey) { + try { + return language.translate(translationKey); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to translate: '" + translationKey + "'"); + } + + public static String translateText(String translationKey) { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return translateTextClient(translationKey); + } + return translateTextFallback(translationKey); + } + + @NotNull + public static World getWorld() { + return getCurrentlyRunningServer().getWorld(DimensionType.OVERWORLD); + } + + public static ItemStack asStack(Item item) { + return new ItemStack(item); + } +} diff --git a/1.15/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java new file mode 100644 index 00000000..87acd3dc --- /dev/null +++ b/1.15/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java @@ -0,0 +1,59 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.fluid.FluidState; +import net.minecraft.fluid.Fluids; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.BlockRenderView; +import net.minecraft.world.LightType; +import net.minecraft.world.biome.Biomes; +import net.minecraft.world.chunk.light.LightingProvider; +import net.minecraft.world.level.ColorResolver; +import org.jetbrains.annotations.Nullable; + +public enum EmptyRenderBlockView implements BlockRenderView { + INSTANCE; + + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + return null; + } + + public BlockState getBlockState(BlockPos pos) { + return Blocks.AIR.getDefaultState(); + } + + public FluidState getFluidState(BlockPos pos) { + return Fluids.EMPTY.getDefaultState(); + } + + public int getBottomY() { + return 0; + } + + public int getHeight() { + return 0; + } + + @Override + public LightingProvider getLightingProvider() { + return null; + } + + @Override + public int getColor(BlockPos pos, ColorResolver colorResolver) { + return colorResolver.getColor(Biomes.PLAINS, pos.getX(), pos.getY()); + } + + @Override + public int getLightLevel(LightType type, BlockPos pos) { + return type == LightType.SKY ? getMaxLightLevel() : 0; + } + + @Override + public int getBaseLightLevel(BlockPos pos, int ambientDarkness) { + return ambientDarkness; + } +} diff --git a/1.15/src/main/resources/fabric.mod.json b/1.15/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..3ee60ac5 --- /dev/null +++ b/1.15/src/main/resources/fabric.mod.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "id": "minecraft-data-generator", + "version": "${version}", + "name": "Minecraft Data Generator", + "description": "", + "authors": [ + "Archengius", + "U9G" + ], + "contact": {}, + "license": "MIT", + "environment": "server", + "entrypoints": { + "main": [ + "dev.u9g.minecraftdatagenerator.Main" + ] + }, + "mixins": [ + "minecraft-data-generator.mixins.json" + ], + "depends": { + "fabricloader": ">=0.13.3", + "fabric": "*", + "minecraft": "1.15" + } +} diff --git a/1.15/src/main/resources/minecraft-data-generator.mixins.json b/1.15/src/main/resources/minecraft-data-generator.mixins.json new file mode 100644 index 00000000..ea255f59 --- /dev/null +++ b/1.15/src/main/resources/minecraft-data-generator.mixins.json @@ -0,0 +1,19 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "dev.u9g.minecraftdatagenerator.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "MiningToolItemAccessor", + "ReadyMixin", + "StatusEffectAccessor" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "server": [ + "EULAMixin" + ] +} diff --git a/1.16/build.gradle b/1.16/build.gradle new file mode 100644 index 00000000..e85de392 --- /dev/null +++ b/1.16/build.gradle @@ -0,0 +1,31 @@ +plugins { + id 'fabric-loom' +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API. This is technically optional, but you probably want it anyway. + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" +} + +processResources { + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} diff --git a/1.16/gradle.properties b/1.16/gradle.properties new file mode 100644 index 00000000..3fe3c1b0 --- /dev/null +++ b/1.16/gradle.properties @@ -0,0 +1,9 @@ +# Fabric Properties +# check these on https://modmuss50.me/fabric.html +minecraft_version=1.16 +yarn_mappings=1.16+build.4 +loader_version=0.13.3 +# Dependencies +# check this on https://modmuss50.me/fabric.html +# fabric_version=0.36.0+1.17 +fabric_version=0.13.1+build.370-1.16 diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java new file mode 100644 index 00000000..97c57352 --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java @@ -0,0 +1,36 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +public class FoliageColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] pixels) { + colorMap = pixels; + } + + public static int getColor(double temperature, double humidity) { + int i = (int) ((1.0 - temperature) * 255.0); + int j = (int) ((1.0 - (humidity *= temperature)) * 255.0); + return colorMap[j << 8 | i]; + } + + public static int getSpruceColor() { + return 0x619961; + } + + public static int getBirchColor() { + return 8431445; + } + + public static int getDefaultColor() { + return 4764952; + } + + public static int getFoliageColor(Biome biome) { + double d = MathHelper.clamp(biome.getTemperature(), 0.0f, 1.0f); + double e = MathHelper.clamp(biome.getRainfall(), 0.0f, 1.0f); + return getColor(d, e); + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java new file mode 100644 index 00000000..8273ed57 --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java @@ -0,0 +1,29 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +public class GrassColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] map) { + colorMap = map; + } + + public static int getColor(double temperature, double humidity) { + int j = (int) ((1.0 - (humidity *= temperature)) * 255.0); + int i = (int) ((1.0 - temperature) * 255.0); + int k = j << 8 | i; + if (k > colorMap.length) { + return -65281; + } + return colorMap[k]; + } + + public static int getGrassColorAt(Biome biome) { + double d = MathHelper.clamp(biome.getTemperature(), 0.0f, 1.0f); + double e = MathHelper.clamp(biome.getRainfall(), 0.0f, 1.0f); + return getColor(d, e); + } +} + diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java new file mode 100644 index 00000000..c9b29873 --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java @@ -0,0 +1,34 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.client.util.math.Vector3f; +import net.minecraft.util.math.MathHelper; + +public class ServerSideRedstoneWireBlock { + private static final Vector3f[] data = new Vector3f[16]; + + static { + for (int i = 0; i <= 15; ++i) { + float f = 0; + float g = f * 0.6f + ((f = (float) i / 15.0f) > 0.0f ? 0.4f : 0.3f); + float h = MathHelper.clamp(f * f * 0.7f - 0.5f, 0.0f, 1.0f); + float j = MathHelper.clamp(f * f * 0.6f - 0.7f, 0.0f, 1.0f); + data[i] = new Vector3f(g, h, j); + } + } + + public static int getWireColor(int powerLevel) { + Vector3f vector3f = data[powerLevel]; + return packRgb(vector3f.getX(), vector3f.getY(), vector3f.getZ()); + } + + private static int packRgb(float r, float g, float b) { + return packRgb(MathHelper.floor(r * 255.0f), MathHelper.floor(g * 255.0f), MathHelper.floor(b * 255.0f)); + } + + private static int packRgb(int r, int g, int b) { + int i = r; + i = (i << 8) + g; + i = (i << 8) + b; + return i; + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/Main.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/Main.java new file mode 100644 index 00000000..52a20201 --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/Main.java @@ -0,0 +1,14 @@ +package dev.u9g.minecraftdatagenerator; + +import net.fabricmc.api.ModInitializer; + +import java.util.logging.Logger; + +public class Main implements ModInitializer { + public static final Logger LOGGER = Logger.getLogger("mc-data-gen-serv"); + + @Override + public void onInitialize() { + + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java new file mode 100644 index 00000000..e8ad9e04 --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java @@ -0,0 +1,140 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; + +public class BiomesDataGenerator implements IDataGenerator { + + private static String guessBiomeDimensionFromCategory(Biome biome) { + return switch (biome.getCategory()) { + case NETHER -> "nether"; + case THEEND -> "end"; + default -> "overworld"; + }; + } + + private static int getBiomeColorFor(String biomeName) { + return switch (biomeName) { + case "ocean" -> 112; + case "plains" -> 9286496; + case "desert" -> 16421912; + case "mountains" -> 6316128; + case "forest" -> 353825; + case "taiga" -> 747097; + case "swamp" -> 522674; + case "river" -> 255; + case "nether_wastes" -> 12532539; + case "the_end" -> 8421631; + case "frozen_ocean" -> 7368918; + case "frozen_river" -> 10526975; + case "snowy_tundra" -> 16777215; + case "snowy_mountains" -> 10526880; + case "mushroom_fields" -> 16711935; + case "mushroom_field_shore" -> 10486015; + case "beach" -> 16440917; + case "desert_hills" -> 13786898; + case "wooded_hills" -> 2250012; + case "taiga_hills" -> 1456435; + case "mountain_edge" -> 7501978; + case "jungle" -> 5470985; + case "jungle_hills" -> 2900485; + case "jungle_edge" -> 6458135; + case "deep_ocean" -> 48; + case "stone_shore" -> 10658436; + case "snowy_beach" -> 16445632; + case "birch_forest" -> 3175492; + case "birch_forest_hills" -> 2055986; + case "dark_forest" -> 4215066; + case "snowy_taiga" -> 3233098; + case "snowy_taiga_hills" -> 2375478; + case "giant_tree_taiga" -> 5858897; + case "giant_tree_taiga_hills" -> 4542270; + case "wooded_mountains" -> 5271632; + case "savanna" -> 12431967; + case "savanna_plateau" -> 10984804; + case "badlands" -> 14238997; + case "wooded_badlands_plateau" -> 11573093; + case "badlands_plateau" -> 13274213; + case "small_end_islands" -> 42; + case "end_midlands" -> 15464630; + case "end_highlands" -> 12828041; + case "end_barrens" -> 9474162; + case "warm_ocean" -> 172; + case "lukewarm_ocean" -> 144; + case "cold_ocean" -> 2105456; + case "deep_warm_ocean" -> 80; + case "deep_lukewarm_ocean" -> 64; + case "deep_cold_ocean" -> 2105400; + case "deep_frozen_ocean" -> 4210832; + case "the_void" -> 0; + case "sunflower_plains" -> 11918216; + case "desert_lakes" -> 16759872; + case "gravelly_mountains" -> 8947848; + case "flower_forest" -> 2985545; + case "taiga_mountains" -> 3378817; + case "swamp_hills" -> 3145690; + case "ice_spikes" -> 11853020; + case "modified_jungle" -> 8102705; + case "modified_jungle_edge" -> 9089855; + case "tall_birch_forest" -> 5807212; + case "tall_birch_hills" -> 4687706; + case "dark_forest_hills" -> 6846786; + case "snowy_taiga_mountains" -> 5864818; + case "giant_spruce_taiga" -> 8490617; + case "giant_spruce_taiga_hills" -> 7173990; + case "modified_gravelly_mountains" -> 7903352; + case "shattered_savanna" -> 15063687; + case "shattered_savanna_plateau" -> 13616524; + case "eroded_badlands" -> 16739645; + case "modified_wooded_badlands_plateau" -> 14204813; + case "modified_badlands_plateau" -> 15905933; + case "bamboo_jungle" -> 7769620; + case "bamboo_jungle_hills" -> 3884810; + case "soul_sand_valley" -> 6174768; + case "crimson_forest" -> 14485512; + case "warped_forest" -> 4821115; + case "basalt_deltas" -> 4208182; + default -> throw new Error("Unexpected biome, with name: '" + biomeName + "'"); + }; + } + + public static JsonObject generateBiomeInfo(Registry registry, Biome biome) { + JsonObject biomeDesc = new JsonObject(); + Identifier registryKey = registry.getKey(biome).orElseThrow().getValue(); + String localizationKey = String.format("biome.%s.%s", registryKey.getNamespace(), registryKey.getPath()); + + biomeDesc.addProperty("id", registry.getRawId(biome)); + biomeDesc.addProperty("name", registryKey.getPath()); + + biomeDesc.addProperty("category", biome.getCategory().getName()); + biomeDesc.addProperty("temperature", biome.getTemperature()); + biomeDesc.addProperty("precipitation", biome.getPrecipitation().getName()); + biomeDesc.addProperty("depth", biome.getDepth()); + biomeDesc.addProperty("dimension", guessBiomeDimensionFromCategory(biome)); + biomeDesc.addProperty("displayName", DGU.translateText(localizationKey)); + biomeDesc.addProperty("color", getBiomeColorFor(registryKey.getPath())); + biomeDesc.addProperty("rainfall", biome.getRainfall()); + + return biomeDesc; + } + + @Override + public String getDataName() { + return "biomes"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray biomesArray = new JsonArray(); + Registry biomeRegistry = Registry.BIOME; + + biomeRegistry.stream() + .map(biome -> generateBiomeInfo(biomeRegistry, biome)) + .forEach(biomesArray::add); + return biomesArray; + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java new file mode 100644 index 00000000..52c3181d --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java @@ -0,0 +1,111 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.EmptyBlockView; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class BlockCollisionShapesDataGenerator implements IDataGenerator { + + @Override + public String getDataName() { + return "blockCollisionShapes"; + } + + @Override + public JsonObject generateDataJson() { + Registry blockRegistry = Registry.BLOCK; + BlockShapesCache blockShapesCache = new BlockShapesCache(); + + blockRegistry.forEach(blockShapesCache::processBlock); + + JsonObject resultObject = new JsonObject(); + + resultObject.add("blocks", blockShapesCache.dumpBlockShapeIndices(blockRegistry)); + resultObject.add("shapes", blockShapesCache.dumpShapesObject()); + + return resultObject; + } + + private static class BlockShapesCache { + public final Map uniqueBlockShapes = new HashMap<>(); + public final Map> blockCollisionShapes = new HashMap<>(); + private int lastCollisionShapeId = 0; + + public void processBlock(Block block) { + List blockStates = block.getStateManager().getStates(); + List blockCollisionShapes = new ArrayList<>(); + + for (BlockState blockState : blockStates) { + VoxelShape blockShape = blockState.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + Integer blockShapeIndex = uniqueBlockShapes.get(blockShape); + + if (blockShapeIndex == null) { + blockShapeIndex = lastCollisionShapeId++; + uniqueBlockShapes.put(blockShape, blockShapeIndex); + } + blockCollisionShapes.add(blockShapeIndex); + } + + this.blockCollisionShapes.put(block, blockCollisionShapes); + } + + public JsonObject dumpBlockShapeIndices(Registry blockRegistry) { + JsonObject resultObject = new JsonObject(); + + for (var entry : blockCollisionShapes.entrySet()) { + List blockCollisions = entry.getValue(); + long distinctShapesCount = blockCollisions.stream().distinct().count(); + JsonElement blockCollision; + if (distinctShapesCount == 1L) { + blockCollision = new JsonPrimitive(blockCollisions.get(0)); + } else { + blockCollision = new JsonArray(); + for (int collisionId : blockCollisions) { + ((JsonArray) blockCollision).add(collisionId); + } + } + + Identifier registryKey = blockRegistry.getKey(entry.getKey()).orElseThrow().getValue(); + resultObject.add(registryKey.getPath(), blockCollision); + } + + return resultObject; + } + + public JsonObject dumpShapesObject() { + JsonObject shapesObject = new JsonObject(); + + for (var entry : uniqueBlockShapes.entrySet()) { + JsonArray boxesArray = new JsonArray(); + entry.getKey().forEachBox((x1, y1, z1, x2, y2, z2) -> { + JsonArray oneBoxJsonArray = new JsonArray(); + + oneBoxJsonArray.add(x1); + oneBoxJsonArray.add(y1); + oneBoxJsonArray.add(z1); + + oneBoxJsonArray.add(x2); + oneBoxJsonArray.add(y2); + oneBoxJsonArray.add(z2); + + boxesArray.add(oneBoxJsonArray); + }); + shapesObject.add(Integer.toString(entry.getValue()), boxesArray); + } + return shapesObject; + } + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java new file mode 100644 index 00000000..d5c7ca00 --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java @@ -0,0 +1,159 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.base.CaseFormat; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.mixin.MiningToolItemAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.block.AirBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.item.MiningToolItem; +import net.minecraft.loot.context.LootContext; +import net.minecraft.loot.context.LootContextParameters; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.state.property.EnumProperty; +import net.minecraft.state.property.IntProperty; +import net.minecraft.state.property.Property; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.EmptyBlockView; + +import java.util.Collection; +import java.util.List; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +public class BlocksDataGenerator implements IDataGenerator { + + private static final Logger logger = Main.LOGGER; + + private static List getItemsEffectiveForBlock(Block block) { + return Registry.ITEM.stream() + .filter(item -> item instanceof MiningToolItem) + .filter(item -> ((MiningToolItemAccessor) item).getEffectiveBlocks().contains(block)) + .collect(Collectors.toList()); + } + + private static List populateDropsIfPossible(BlockState blockState, Item firstToolItem) { + MinecraftServer minecraftServer = DGU.getCurrentlyRunningServer(); + //If we have local world context, we can actually evaluate loot tables and determine actual data + ServerWorld serverWorld = minecraftServer.getOverworld(); + LootContext.Builder lootContext = new LootContext.Builder(serverWorld) + .parameter(LootContextParameters.BLOCK_STATE, blockState) + .parameter(LootContextParameters.POSITION, new BlockPos(0, 0, 0)) + .parameter(LootContextParameters.TOOL, firstToolItem.getStackForRender()) + .random(0L); + return blockState.getDroppedStacks(lootContext); + } + + private static String getPropertyTypeName(Property property) { + //Explicitly handle default minecraft properties + if (property instanceof BooleanProperty) { + return "bool"; + } + if (property instanceof IntProperty) { + return "int"; + } + if (property instanceof EnumProperty) { + return "enum"; + } + + //Use simple class name as fallback, this code will give something like + //example_type for ExampleTypeProperty class name + String rawPropertyName = property.getClass().getSimpleName().replace("Property", ""); + return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, rawPropertyName); + } + + private static > JsonObject generateStateProperty(Property property) { + JsonObject propertyObject = new JsonObject(); + Collection propertyValues = property.getValues(); + + propertyObject.addProperty("name", property.getName()); + propertyObject.addProperty("type", getPropertyTypeName(property)); + propertyObject.addProperty("num_values", propertyValues.size()); + + //Do not add values for vanilla boolean properties, they are known by default + if (!(property instanceof BooleanProperty)) { + JsonArray propertyValuesArray = new JsonArray(); + for (T propertyValue : propertyValues) { + propertyValuesArray.add(property.name(propertyValue)); + } + propertyObject.add("values", propertyValuesArray); + } + return propertyObject; + } + + public static JsonObject generateBlock(Registry blockRegistry, Block block) { + JsonObject blockDesc = new JsonObject(); + + List blockStates = block.getStateManager().getStates(); + BlockState defaultState = block.getDefaultState(); + Identifier registryKey = blockRegistry.getKey(block).orElseThrow().getValue(); + String localizationKey = block.getTranslationKey(); + List effectiveTools = getItemsEffectiveForBlock(block); + + blockDesc.addProperty("id", blockRegistry.getRawId(block)); + blockDesc.addProperty("name", registryKey.getPath()); + blockDesc.addProperty("displayName", DGU.translateText(localizationKey)); + + float hardness = block.getDefaultState().getHardness(null, null); + + blockDesc.addProperty("hardness", hardness); + blockDesc.addProperty("resistance", block.getBlastResistance()); + blockDesc.addProperty("stackSize", block.asItem().getMaxCount()); + blockDesc.addProperty("diggable", hardness != -1.0f && !(block instanceof AirBlock)); + JsonObject effTools = new JsonObject(); + effectiveTools.forEach(item -> effTools.addProperty( + String.valueOf(Registry.ITEM.getRawId(item)), // key + item.getMiningSpeedMultiplier(item.getStackForRender(), defaultState) // value + )); + blockDesc.add("effectiveTools", effTools); + + blockDesc.addProperty("transparent", !defaultState.isOpaque()); + blockDesc.addProperty("emitLight", defaultState.getLuminance()); + blockDesc.addProperty("filterLight", defaultState.getOpacity(EmptyBlockView.INSTANCE, BlockPos.ORIGIN)); + + blockDesc.addProperty("defaultState", Block.getRawIdFromState(defaultState)); + blockDesc.addProperty("minStateId", Block.getRawIdFromState(blockStates.get(0))); + blockDesc.addProperty("maxStateId", Block.getRawIdFromState(blockStates.get(blockStates.size() - 1))); + JsonArray stateProperties = new JsonArray(); + for (Property property : block.getStateManager().getProperties()) { + stateProperties.add(generateStateProperty(property)); + } + blockDesc.add("states", stateProperties); + + List drops = populateDropsIfPossible(defaultState, effectiveTools.stream().findFirst().orElse(Items.AIR)); + + JsonArray dropsArray = new JsonArray(); + drops.forEach(dropped -> dropsArray.add(Item.getRawId(dropped.getItem()))); + blockDesc.add("drops", dropsArray); + + VoxelShape blockCollisionShape = defaultState.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + blockDesc.addProperty("boundingBox", blockCollisionShape.isEmpty() ? "empty" : "block"); + + return blockDesc; + } + + @Override + public String getDataName() { + return "blocks"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultBlocksArray = new JsonArray(); + Registry blockRegistry = Registry.BLOCK; + + blockRegistry.forEach(block -> resultBlocksArray.add(generateBlock(blockRegistry, block))); + return resultBlocksArray; + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java new file mode 100644 index 00000000..3c05c30f --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java @@ -0,0 +1,79 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonWriter; +import dev.u9g.minecraftdatagenerator.Main; + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class DataGenerators { + + private static final List GENERATORS = new ArrayList<>(); + private static final Logger logger = Main.LOGGER; + + static { + register(new BiomesDataGenerator()); + register(new BlockCollisionShapesDataGenerator()); + register(new BlocksDataGenerator()); + register(new EffectsDataGenerator()); + register(new EnchantmentsDataGenerator()); + register(new EntitiesDataGenerator()); + register(new FoodsDataGenerator()); + register(new ItemsDataGenerator()); + register(new ParticlesDataGenerator()); + register(new TintsDataGenerator()); + register(new LanguageDataGenerator()); + register(new InstrumentsDataGenerator()); + } + + public static void register(IDataGenerator generator) { + GENERATORS.add(generator); + } + + public static boolean runDataGenerators(Path outputDirectory) { + try { + Files.createDirectories(outputDirectory); + } catch (IOException exception) { + logger.log(Level.INFO, "Failed to create data generator output directory at " + outputDirectory); + exception.printStackTrace(); + return false; + } + + int generatorsFailed = 0; + logger.log(Level.INFO, MessageFormat.format("Running minecraft data generators, output at {0}", outputDirectory)); + + for (IDataGenerator dataGenerator : GENERATORS) { + logger.log(Level.INFO, MessageFormat.format("Running generator {0}", dataGenerator.getDataName())); + try { + String outputFileName = String.format("%s.json", dataGenerator.getDataName()); + JsonElement outputElement = dataGenerator.generateDataJson(); + Path outputFilePath = outputDirectory.resolve(outputFileName); + + try (Writer writer = Files.newBufferedWriter(outputFilePath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + JsonWriter jsonWriter = new JsonWriter(writer); + jsonWriter.setIndent(" "); + Streams.write(outputElement, jsonWriter); + } + logger.log(Level.INFO, MessageFormat.format("Generator: {0} -> {1}", dataGenerator.getDataName(), outputFileName)); + + } catch (Throwable exception) { + logger.log(Level.INFO, MessageFormat.format("Failed to run data generator {0}", dataGenerator.getDataName())); + exception.printStackTrace(); + generatorsFailed++; + } + } + + logger.log(Level.INFO, "Finishing running data generators"); + return generatorsFailed == 0; + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java new file mode 100644 index 00000000..18a9dcdb --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java @@ -0,0 +1,48 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.StatusEffectAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.entity.effect.StatusEffectType; +import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.stream.Collectors; + +public class EffectsDataGenerator implements IDataGenerator { + + public static JsonObject generateEffect(Registry registry, StatusEffect statusEffect) { + JsonObject effectDesc = new JsonObject(); + Identifier registryKey = registry.getKey(statusEffect).orElseThrow().getValue(); + + effectDesc.addProperty("id", registry.getRawId(statusEffect)); + if (statusEffect == StatusEffects.UNLUCK) { + effectDesc.addProperty("name", "BadLuck"); + effectDesc.addProperty("displayName", "Bad Luck"); + } else { + effectDesc.addProperty("name", Arrays.stream(registryKey.getPath().split("_")).map(StringUtils::capitalize).collect(Collectors.joining())); + effectDesc.addProperty("displayName", DGU.translateText(statusEffect.getTranslationKey())); + } + + effectDesc.addProperty("type", ((StatusEffectAccessor) statusEffect).type() == StatusEffectType.BENEFICIAL ? "good" : "bad"); + return effectDesc; + } + + @Override + public String getDataName() { + return "effects"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry statusEffectRegistry = Registry.STATUS_EFFECT; + statusEffectRegistry.forEach(effect -> resultsArray.add(generateEffect(statusEffectRegistry, effect))); + return resultsArray; + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java new file mode 100644 index 00000000..a71961c9 --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java @@ -0,0 +1,108 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +public class EnchantmentsDataGenerator implements IDataGenerator { + + private static final ImmutableMap ENCHANTMENT_TARGET_NAMES = ImmutableMap.builder() + .put(EnchantmentTarget.ARMOR, "armor") + .put(EnchantmentTarget.ARMOR_FEET, "armor_feet") + .put(EnchantmentTarget.ARMOR_LEGS, "armor_legs") + .put(EnchantmentTarget.ARMOR_CHEST, "armor_chest") + .put(EnchantmentTarget.ARMOR_HEAD, "armor_head") + .put(EnchantmentTarget.WEAPON, "weapon") + .put(EnchantmentTarget.DIGGER, "digger") + .put(EnchantmentTarget.FISHING_ROD, "fishing_rod") + .put(EnchantmentTarget.TRIDENT, "trident") + .put(EnchantmentTarget.BREAKABLE, "breakable") + .put(EnchantmentTarget.BOW, "bow") + .put(EnchantmentTarget.WEARABLE, "wearable") + .put(EnchantmentTarget.CROSSBOW, "crossbow") + .put(EnchantmentTarget.VANISHABLE, "vanishable") + .build(); + + public static String getEnchantmentTargetName(EnchantmentTarget target) { + return ENCHANTMENT_TARGET_NAMES.getOrDefault(target, target.name().toLowerCase(Locale.ROOT)); + } + + //Equation enchantment costs follow is a * level + b, so we can easily retrieve a and b by passing zero level + private static JsonObject generateEnchantmentMinPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMinPower(0); + int a = enchantment.getMinPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + private static JsonObject generateEnchantmentMaxPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMaxPower(0); + int a = enchantment.getMaxPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + public static JsonObject generateEnchantment(Registry registry, Enchantment enchantment) { + JsonObject enchantmentDesc = new JsonObject(); + Identifier registryKey = registry.getKey(enchantment).orElseThrow().getValue(); + + enchantmentDesc.addProperty("id", registry.getRawId(enchantment)); + enchantmentDesc.addProperty("name", registryKey.getPath()); + enchantmentDesc.addProperty("displayName", DGU.translateText(enchantment.getTranslationKey())); + + enchantmentDesc.addProperty("maxLevel", enchantment.getMaxLevel()); + enchantmentDesc.add("minCost", generateEnchantmentMinPowerCoefficients(enchantment)); + enchantmentDesc.add("maxCost", generateEnchantmentMaxPowerCoefficients(enchantment)); + + enchantmentDesc.addProperty("treasureOnly", enchantment.isTreasure()); + enchantmentDesc.addProperty("curse", enchantment.isCursed()); + + List incompatibleEnchantments = registry.stream() + .filter(other -> !enchantment.canCombine(other)) + .filter(other -> other != enchantment) + .collect(Collectors.toList()); + + JsonArray excludes = new JsonArray(); + for (Enchantment excludedEnchantment : incompatibleEnchantments) { + Identifier otherKey = registry.getKey(excludedEnchantment).orElseThrow().getValue(); + excludes.add(otherKey.getPath()); + } + enchantmentDesc.add("exclude", excludes); + + enchantmentDesc.addProperty("category", getEnchantmentTargetName(enchantment.type)); + enchantmentDesc.addProperty("weight", enchantment.getRarity().getWeight()); + enchantmentDesc.addProperty("tradeable", enchantment.isAvailableForEnchantedBookOffer()); + enchantmentDesc.addProperty("discoverable", enchantment.isAvailableForRandomSelection()); + + return enchantmentDesc; + } + + @Override + public String getDataName() { + return "enchantments"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry enchantmentRegistry = Registry.ENCHANTMENT; + enchantmentRegistry.stream() + .forEach(enchantment -> resultsArray.add(generateEnchantment(enchantmentRegistry, enchantment))); + return resultsArray; + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java new file mode 100644 index 00000000..d0edd6c3 --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java @@ -0,0 +1,122 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.mob.AmbientEntity; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.mob.WaterCreatureEntity; +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.passive.PassiveEntity; +import net.minecraft.entity.projectile.ProjectileEntity; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.ParameterizedType; + +public class EntitiesDataGenerator implements IDataGenerator { + + public static JsonObject generateEntity(Registry> entityRegistry, EntityType entityType) { + JsonObject entityDesc = new JsonObject(); + Identifier registryKey = entityRegistry.getKey(entityType).orElseThrow().getValue(); + int entityRawId = entityRegistry.getRawId(entityType); + + entityDesc.addProperty("id", entityRawId); + entityDesc.addProperty("internalId", entityRawId); + entityDesc.addProperty("name", registryKey.getPath()); + + entityDesc.addProperty("displayName", DGU.translateText(entityType.getTranslationKey())); + entityDesc.addProperty("width", entityType.getDimensions().width); + entityDesc.addProperty("height", entityType.getDimensions().height); + + String entityTypeString = "UNKNOWN"; + MinecraftServer minecraftServer = DGU.getCurrentlyRunningServer(); + Entity entityObject = entityType.create(minecraftServer.getOverworld()); + entityTypeString = entityObject != null ? getEntityTypeForClass(entityObject.getClass()) : "player"; + entityDesc.addProperty("type", entityTypeString); + entityDesc.addProperty("category", getCategoryFrom(entityType)); + + return entityDesc; + } + + private static String getCategoryFrom(@NotNull EntityType entityType) { + if (entityType == EntityType.PLAYER) return "other"; // fail early for player entities + Class entityClazz = null; + try { + for (var field : EntityType.class.getFields()) + if (entityType == field.get(EntityType.class)) + entityClazz = (Class) ((ParameterizedType) TypeToken.get(field.getGenericType()).getType()).getActualTypeArguments()[0]; + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + if (entityClazz == null) throw new RuntimeException("Shouldn't be null..."); + return switch (entityClazz.getPackageName()) { + case "net.minecraft.entity.decoration", "net.minecraft.entity.decoration.painting" -> "Immobile"; + case "net.minecraft.entity.boss", "net.minecraft.entity.mob", "net.minecraft.entity.boss.dragon" -> + "Hostile mobs"; + case "net.minecraft.entity.projectile", "net.minecraft.entity.projectile.thrown" -> "Projectiles"; + case "net.minecraft.entity.passive" -> "Passive mobs"; + case "net.minecraft.entity.vehicle" -> "Vehicles"; + case "net.minecraft.entity" -> "other"; + default -> throw new Error("Unexpected entity type: " + entityClazz.getPackageName()); + }; + } + + //Honestly, both "type" and "category" fields in the schema and examples do not contain any useful information + //Since category is optional, I will just leave it out, and for type I will assume general entity classification + //by the Entity class hierarchy (which has some weirdness too by the way) + private static String getEntityTypeForClass(Class entityClass) { + //Top-level classifications + if (WaterCreatureEntity.class.isAssignableFrom(entityClass)) { + return "water_creature"; + } + if (AnimalEntity.class.isAssignableFrom(entityClass)) { + return "animal"; + } + if (HostileEntity.class.isAssignableFrom(entityClass)) { + return "hostile"; + } + if (AmbientEntity.class.isAssignableFrom(entityClass)) { + return "ambient"; + } + + //Second level classifications. PathAwareEntity is not included because it + //doesn't really make much sense to categorize by it + if (PassiveEntity.class.isAssignableFrom(entityClass)) { + return "passive"; + } + if (MobEntity.class.isAssignableFrom(entityClass)) { + return "mob"; + } + + //Other classifications only include living entities and projectiles. everything else is categorized as other + if (LivingEntity.class.isAssignableFrom(entityClass)) { + return "living"; + } + if (ProjectileEntity.class.isAssignableFrom(entityClass)) { + return "projectile"; + } + return "other"; + } + + @Override + public String getDataName() { + return "entities"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry> entityTypeRegistry = Registry.ENTITY_TYPE; + entityTypeRegistry.forEach(entity -> resultArray.add(generateEntity(entityTypeRegistry, entity))); + return resultArray; + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java new file mode 100644 index 00000000..45a0b35f --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java @@ -0,0 +1,51 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.item.FoodComponent; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Objects; + +public class FoodsDataGenerator implements IDataGenerator { + + public static JsonObject generateFoodDescriptor(Registry registry, Item foodItem) { + JsonObject foodDesc = new JsonObject(); + Identifier registryKey = registry.getKey(foodItem).orElseThrow().getValue(); + + foodDesc.addProperty("id", registry.getRawId(foodItem)); + foodDesc.addProperty("name", registryKey.getPath()); + + foodDesc.addProperty("stackSize", foodItem.getMaxCount()); + foodDesc.addProperty("displayName", DGU.translateText(foodItem.getTranslationKey())); + + FoodComponent foodComponent = Objects.requireNonNull(foodItem.getFoodComponent()); + float foodPoints = foodComponent.getHunger(); + float saturationRatio = foodComponent.getSaturationModifier() * 2.0F; + float saturation = foodPoints * saturationRatio; + + foodDesc.addProperty("foodPoints", foodPoints); + foodDesc.addProperty("saturation", saturation); + + foodDesc.addProperty("effectiveQuality", foodPoints + saturation); + foodDesc.addProperty("saturationRatio", saturationRatio); + return foodDesc; + } + + @Override + public String getDataName() { + return "foods"; + } + + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry itemRegistry = Registry.ITEM; + itemRegistry.stream() + .filter(Item::isFood) + .forEach(food -> resultsArray.add(generateFoodDescriptor(itemRegistry, food))); + return resultsArray; + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java new file mode 100644 index 00000000..81fe4e20 --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; + +public interface IDataGenerator { + + String getDataName(); + + JsonElement generateDataJson(); +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java new file mode 100644 index 00000000..e7b9b54f --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java @@ -0,0 +1,25 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.minecraft.block.enums.Instrument; + +public class InstrumentsDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "instruments"; + } + + @Override + public JsonElement generateDataJson() { + JsonArray array = new JsonArray(); + for (Instrument instrument : Instrument.values()) { + JsonObject object = new JsonObject(); + object.addProperty("id", instrument.ordinal()); + object.addProperty("name", instrument.asString()); + array.add(object); + } + return array; + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java new file mode 100644 index 00000000..805ca63f --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java @@ -0,0 +1,81 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class ItemsDataGenerator implements IDataGenerator { + + private static List calculateItemsToRepairWith(Registry itemRegistry, Item sourceItem) { + ItemStack sourceItemStack = sourceItem.getStackForRender(); + return itemRegistry.stream() + .filter(otherItem -> sourceItem.canRepair(sourceItemStack, otherItem.getStackForRender())) + .collect(Collectors.toList()); + } + + private static List getApplicableEnchantmentTargets(Item sourceItem) { + return Arrays.stream(EnchantmentTarget.values()) + .filter(target -> target.isAcceptableItem(sourceItem)) + .collect(Collectors.toList()); + } + + public static JsonObject generateItem(Registry itemRegistry, Item item) { + JsonObject itemDesc = new JsonObject(); + Identifier registryKey = itemRegistry.getKey(item).orElseThrow().getValue(); + + itemDesc.addProperty("id", itemRegistry.getRawId(item)); + itemDesc.addProperty("name", registryKey.getPath()); + + itemDesc.addProperty("displayName", DGU.translateText(item.getTranslationKey())); + itemDesc.addProperty("stackSize", item.getMaxCount()); + + List enchantmentTargets = getApplicableEnchantmentTargets(item); + + JsonArray enchantCategoriesArray = new JsonArray(); + for (EnchantmentTarget target : enchantmentTargets) { + enchantCategoriesArray.add(EnchantmentsDataGenerator.getEnchantmentTargetName(target)); + } + if (enchantCategoriesArray.size() > 0) { + itemDesc.add("enchantCategories", enchantCategoriesArray); + } + + if (item.isDamageable()) { + List repairWithItems = calculateItemsToRepairWith(itemRegistry, item); + + JsonArray fixedWithArray = new JsonArray(); + for (Item repairWithItem : repairWithItems) { + Identifier repairWithName = itemRegistry.getKey(repairWithItem).orElseThrow().getValue(); + fixedWithArray.add(repairWithName.getPath()); + } + if (fixedWithArray.size() > 0) { + itemDesc.add("repairWith", fixedWithArray); + } + + int maxDurability = item.getMaxDamage(); + itemDesc.addProperty("maxDurability", maxDurability); + } + return itemDesc; + } + + @Override + public String getDataName() { + return "items"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry itemRegistry = Registry.ITEM; + itemRegistry.stream().forEach(item -> resultArray.add(generateItem(itemRegistry, item))); + return resultArray; + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java new file mode 100644 index 00000000..7a3c6ced --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +public class LanguageDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "language"; + } + + @Override + public JsonElement generateDataJson() { + try { + InputStream inputStream = Objects.requireNonNull(this.getClass().getResourceAsStream("/assets/minecraft/lang/en_us.json")); + return new Gson().fromJson(new InputStreamReader(inputStream, StandardCharsets.UTF_8), JsonObject.class); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to generate language file"); + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java new file mode 100644 index 00000000..5a6b1081 --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java @@ -0,0 +1,32 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.minecraft.particle.ParticleType; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +public class ParticlesDataGenerator implements IDataGenerator { + + public static JsonObject generateParticleType(Registry> registry, ParticleType particleType) { + JsonObject effectDesc = new JsonObject(); + Identifier registryKey = registry.getKey(particleType).orElseThrow().getValue(); + + effectDesc.addProperty("id", registry.getRawId(particleType)); + effectDesc.addProperty("name", registryKey.getPath()); + return effectDesc; + } + + @Override + public String getDataName() { + return "particles"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry> particleTypeRegistry = Registry.PARTICLE_TYPE; + particleTypeRegistry.forEach(particleType -> resultsArray.add(generateParticleType(particleTypeRegistry, particleType))); + return resultsArray; + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java new file mode 100644 index 00000000..5e4587dd --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java @@ -0,0 +1,121 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import net.minecraft.item.Item; +import net.minecraft.util.registry.Registry; + +public class RecipeDataGenerator implements IDataGenerator { + + private static int getRawIdFor(Item item) { + return Registry.ITEM.getRawId(item); + } + + @Override + public String getDataName() { + return "recipes"; + } + + @Override + public JsonElement generateDataJson() { +// JsonObject finalObj = new JsonObject(); +// Multimap recipes = ArrayListMultimap.create(); +// for (Recipe recipe : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) { +// if (recipe instanceof ShapedRecipe sr) { +// var ingredients = sr.getIngredients(); +// List ingr = new ArrayList<>(); +// for (int i = 0; i < 9; i++) { +// if (i >= ingredients.size()) { +// ingr.add(-1); +// continue; +// } +// var stacks = ingredients.get(i); +//// var matching = stacks.getMatchingStacks(); +//// if (matching.length == 0) { +//// ingr.add(-1); +//// } else { +//// ingr.add(getRawIdFor(matching[0].getItem())); +//// } +// } +// Lists.reverse(ingr); +// +// JsonArray inShape = new JsonArray(); +// +// var iter = ingr.iterator(); +// for (int y = 0; y < 3; y++) { +// var jsonRow = new JsonArray(); +// int one = iter.next(); +// int two = iter.next(); +// int three = iter.next(); +// if (y > 0 && one == -1 && two == -1 && three == -1) continue; +// jsonRow.add(one); +// jsonRow.add(two); +// jsonRow.add(three); +// inShape.add(jsonRow); +// } +// +// JsonObject finalRecipe = new JsonObject(); +// finalRecipe.add("inShape", inShape); +// +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +// resultObject.addProperty("count", sr.getOutput().getCount()); +// finalRecipe.add("result", resultObject); +// +// String id = ((Integer) getRawIdFor(sr.getOutput().getItem())).toString(); +// +// if (!finalObj.has(id)) { +// finalObj.add(id, new JsonArray()); +// } +// finalObj.get(id).getAsJsonArray().add(finalRecipe); +//// var input = new JsonArray(); +//// var ingredients = sr.getIngredients().stream().toList(); +//// for (int y = 0; y < sr.getHeight(); y++) { +//// var arr = new JsonArray(); +//// for (int x = 0; x < sr.getWidth(); x++) { +//// if ((y*3)+x >= ingredients.size()) { +//// arr.add(JsonNull.INSTANCE); +//// continue; +//// } +//// var ingredient = ingredients.get((y*3)+x).getMatchingStacks(); // FIXME: fix when there are more than one matching stack +//// if (ingredient.length == 0) { +//// arr.add(JsonNull.INSTANCE); +//// } else { +//// arr.add(getRawIdFor(ingredient[0].getItem())); +//// } +//// } +//// input.add(arr); +//// } +//// var rootRecipeObject = new JsonObject(); +//// rootRecipeObject.add("inShape", input); +//// var resultObject = new JsonObject(); +//// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +//// resultObject.addProperty("count", sr.getOutput().getCount()); +//// rootRecipeObject.add("result", resultObject); +//// recipes.put(getRawIdFor(sr.getOutput().getItem()), rootRecipeObject); +// } else if (recipe instanceof ShapelessRecipe sl) { + +// var ingredients = new JsonArray(); +// for (Ingredient ingredient : sl.getIngredients()) { +// if (ingredient.isEmpty()) continue; +//// ingredients.add(getRawIdFor(ingredient.getMatchingStacks()[0].getItem())); +// } +// var rootRecipeObject = new JsonObject(); +// rootRecipeObject.add("ingredients", ingredients); +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sl.getOutput().getItem())); +// resultObject.addProperty("count", sl.getOutput().getCount()); +// rootRecipeObject.add("result", resultObject); +// recipes.put(getRawIdFor(sl.getOutput().getItem()), rootRecipeObject); +// } +// } +// recipes.forEach((a, b) -> { +// if (!finalObj.has(a.toString())) { +// finalObj.add(a.toString(), new JsonArray()); +// } +// finalObj.get(a.toString()).getAsJsonArray().add(b); +// }); +// return finalObj; + return JsonNull.INSTANCE; + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java new file mode 100644 index 00000000..3d0a895d --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java @@ -0,0 +1,170 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.FoliageColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.GrassColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.ServerSideRedstoneWireBlock; +import dev.u9g.minecraftdatagenerator.mixin.BiomeEffectsAccessor; +import dev.u9g.minecraftdatagenerator.util.EmptyRenderBlockView; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.block.RedstoneWireBlock; +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; + +import java.util.*; + +public class TintsDataGenerator implements IDataGenerator { + + public static BiomeTintColors generateBiomeTintColors(Registry biomeRegistry) { + BiomeTintColors colors = new BiomeTintColors(); + + biomeRegistry.forEach(biome -> { + int biomeGrassColor = GrassColors.getGrassColorAt(biome); + int biomeFoliageColor = FoliageColors.getFoliageColor(biome); + int biomeWaterColor = ((BiomeEffectsAccessor) biome.getEffects()).waterColor(); + + colors.grassColoursMap.computeIfAbsent(biomeGrassColor, k -> new ArrayList<>()).add(biome); + colors.foliageColoursMap.computeIfAbsent(biomeFoliageColor, k -> new ArrayList<>()).add(biome); + colors.waterColourMap.computeIfAbsent(biomeWaterColor, k -> new ArrayList<>()).add(biome); + }); + return colors; + } + + public static Map generateRedstoneTintColors() { + Map resultColors = new HashMap<>(); + + for (int redstoneLevel : RedstoneWireBlock.POWER.getValues()) { + int color = ServerSideRedstoneWireBlock.getWireColor(redstoneLevel); + resultColors.put(redstoneLevel, color); + } + return resultColors; + } + + @Environment(EnvType.CLIENT) + private static int getBlockColor(Block block, BlockColors blockColors) { + return blockColors.getColor(block.getDefaultState(), EmptyRenderBlockView.INSTANCE, BlockPos.ORIGIN, 0xFFFFFF); + } + + @Environment(EnvType.CLIENT) + public static Map generateConstantTintColors() { + Map resultColors = new HashMap<>(); + BlockColors blockColors = BlockColors.create(); + + resultColors.put(Blocks.BIRCH_LEAVES, FoliageColors.getBirchColor()); + resultColors.put(Blocks.SPRUCE_LEAVES, FoliageColors.getSpruceColor()); + + resultColors.put(Blocks.LILY_PAD, getBlockColor(Blocks.LILY_PAD, blockColors)); + resultColors.put(Blocks.ATTACHED_MELON_STEM, getBlockColor(Blocks.ATTACHED_MELON_STEM, blockColors)); + resultColors.put(Blocks.ATTACHED_PUMPKIN_STEM, getBlockColor(Blocks.ATTACHED_PUMPKIN_STEM, blockColors)); + + //not really constant, depend on the block age, but kinda have to be handled since textures are literally white without them + resultColors.put(Blocks.MELON_STEM, getBlockColor(Blocks.MELON_STEM, blockColors)); + resultColors.put(Blocks.PUMPKIN_STEM, getBlockColor(Blocks.PUMPKIN_STEM, blockColors)); + + return resultColors; + } + + private static JsonObject encodeBiomeColorMap(Registry biomeRegistry, Map> colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + for (Biome biome : entry.getValue()) { + Identifier registryKey = biomeRegistry.getKey(biome).orElseThrow().getValue(); + keysArray.add(registryKey.getPath()); + } + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getKey()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeRedstoneColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + keysArray.add(entry.getKey()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeBlocksColorMap(Registry blockRegistry, Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + Identifier registryKey = blockRegistry.getKey(entry.getKey()).orElseThrow().getValue(); + keysArray.add(registryKey.getPath()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + @Override + public String getDataName() { + return "tints"; + } + + @Override + public JsonObject generateDataJson() { +// DynamicRegistryManager registryManager = DynamicRegistryManager.create(); + Registry biomeRegistry = Registry.BIOME; + Registry blockRegistry = Registry.BLOCK; + + BiomeTintColors biomeTintColors = generateBiomeTintColors(biomeRegistry); + Map redstoneColors = generateRedstoneTintColors(); + Map constantTintColors = Collections.emptyMap(); + + EnvType currentEnvironment = FabricLoader.getInstance().getEnvironmentType(); + if (currentEnvironment == EnvType.CLIENT) { + constantTintColors = generateConstantTintColors(); + } + + JsonObject resultObject = new JsonObject(); + + resultObject.add("grass", encodeBiomeColorMap(biomeRegistry, biomeTintColors.grassColoursMap)); + resultObject.add("foliage", encodeBiomeColorMap(biomeRegistry, biomeTintColors.foliageColoursMap)); + resultObject.add("water", encodeBiomeColorMap(biomeRegistry, biomeTintColors.waterColourMap)); + + resultObject.add("redstone", encodeRedstoneColorMap(redstoneColors)); + resultObject.add("constant", encodeBlocksColorMap(blockRegistry, constantTintColors)); + + return resultObject; + } + + public static class BiomeTintColors { + final Map> grassColoursMap = new HashMap<>(); + final Map> foliageColoursMap = new HashMap<>(); + final Map> waterColourMap = new HashMap<>(); + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BiomeEffectsAccessor.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BiomeEffectsAccessor.java new file mode 100644 index 00000000..5dcbe1cc --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BiomeEffectsAccessor.java @@ -0,0 +1,11 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.world.biome.BiomeEffects; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(BiomeEffects.class) +public interface BiomeEffectsAccessor { + @Accessor("waterColor") + int waterColor(); +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java new file mode 100644 index 00000000..0d297e7a --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.server.dedicated.EulaReader; +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; + +@Mixin(EulaReader.class) +public class EULAMixin { + @Inject(method = "isEulaAgreedTo()Z", at = @At("TAIL"), cancellable = true) + public void init(CallbackInfoReturnable cir) { + cir.setReturnValue(true); + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java new file mode 100644 index 00000000..bca254e3 --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java @@ -0,0 +1,18 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Block; +import net.minecraft.item.MiningToolItem; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Set; + +@Mixin(MiningToolItem.class) +public interface MiningToolItemAccessor { + + @Accessor + Set getEffectiveBlocks(); + + @Accessor + float getMiningSpeed(); +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java new file mode 100644 index 00000000..97356444 --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java @@ -0,0 +1,28 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.generators.DataGenerators; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.MinecraftVersion; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; +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.nio.file.Path; +import java.util.logging.Level; + +@Mixin(MinecraftDedicatedServer.class) +public class ReadyMixin { + @Inject(method = "setupServer()Z", at = @At("TAIL")) + private void init(CallbackInfoReturnable cir) { + Main.LOGGER.log(Level.INFO, "Starting data generation!"); + String versionName = MinecraftVersion.field_25319.getName(); + Path serverRootDirectory = DGU.getCurrentlyRunningServer().getRunDirectory().toPath().toAbsolutePath(); + Path dataDumpDirectory = serverRootDirectory.resolve("minecraft-data").resolve(versionName); + DataGenerators.runDataGenerators(dataDumpDirectory); + Main.LOGGER.log(Level.INFO, "Done data generation!"); + DGU.getCurrentlyRunningServer().stop(false); + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java new file mode 100644 index 00000000..01d24c7f --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java @@ -0,0 +1,12 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.entity.effect.StatusEffectType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(StatusEffect.class) +public interface StatusEffectAccessor { + @Accessor("type") + StatusEffectType type(); +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java new file mode 100644 index 00000000..91eed91b --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java @@ -0,0 +1,62 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Language; +import net.minecraft.world.World; +import org.jetbrains.annotations.NotNull; + +public class DGU { + + private static final Language language = Language.getInstance(); + + @Environment(EnvType.CLIENT) + private static MinecraftServer getCurrentlyRunningServerClient() { + return MinecraftClient.getInstance().getServer(); + } + + @SuppressWarnings("deprecation") + private static MinecraftServer getCurrentlyRunningServerDedicated() { + return (MinecraftServer) FabricLoader.getInstance().getGameInstance(); + } + + public static MinecraftServer getCurrentlyRunningServer() { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return getCurrentlyRunningServerClient(); + } else if (environmentType == EnvType.SERVER) { + return getCurrentlyRunningServerDedicated(); + } + throw new UnsupportedOperationException(); + } + + @Environment(EnvType.CLIENT) + private static String translateTextClient(String translationKey) { + return I18n.translate(translationKey); + } + + private static String translateTextFallback(String translationKey) { + try { + return language.get(translationKey); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to translate: '" + translationKey + "'"); + } + + public static String translateText(String translationKey) { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return translateTextClient(translationKey); + } + return translateTextFallback(translationKey); + } + + @NotNull + public static World getWorld() { + return getCurrentlyRunningServer().getOverworld(); + } +} diff --git a/1.16/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java new file mode 100644 index 00000000..177f8607 --- /dev/null +++ b/1.16/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java @@ -0,0 +1,66 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.fluid.FluidState; +import net.minecraft.fluid.Fluids; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.BlockRenderView; +import net.minecraft.world.LightType; +import net.minecraft.world.biome.Biomes; +import net.minecraft.world.chunk.light.LightingProvider; +import net.minecraft.world.level.ColorResolver; +import org.jetbrains.annotations.Nullable; + +public enum EmptyRenderBlockView implements BlockRenderView { + INSTANCE; + + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + return null; + } + + public BlockState getBlockState(BlockPos pos) { + return Blocks.AIR.getDefaultState(); + } + + public FluidState getFluidState(BlockPos pos) { + return Fluids.EMPTY.getDefaultState(); + } + + public int getBottomY() { + return 0; + } + + public int getHeight() { + return 0; + } + + + @Override + public float getBrightness(Direction direction, boolean shaded) { + return 0.0f; + } + + @Override + public LightingProvider getLightingProvider() { + return null; + } + + @Override + public int getColor(BlockPos pos, ColorResolver colorResolver) { + return colorResolver.getColor(Biomes.PLAINS, pos.getX(), pos.getY()); + } + + @Override + public int getLightLevel(LightType type, BlockPos pos) { + return type == LightType.SKY ? getMaxLightLevel() : 0; + } + + @Override + public int getBaseLightLevel(BlockPos pos, int ambientDarkness) { + return ambientDarkness; + } +} diff --git a/1.16/src/main/resources/fabric.mod.json b/1.16/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..9d668672 --- /dev/null +++ b/1.16/src/main/resources/fabric.mod.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "id": "minecraft-data-generator", + "version": "${version}", + "name": "Minecraft Data Generator", + "description": "", + "authors": [ + "Archengius", + "U9G" + ], + "contact": {}, + "license": "MIT", + "environment": "server", + "entrypoints": { + "main": [ + "dev.u9g.minecraftdatagenerator.Main" + ] + }, + "mixins": [ + "minecraft-data-generator.mixins.json" + ], + "depends": { + "fabricloader": ">=0.13.3", + "fabric": "*", + "minecraft": "1.16" + } +} diff --git a/1.16/src/main/resources/minecraft-data-generator.mixins.json b/1.16/src/main/resources/minecraft-data-generator.mixins.json new file mode 100644 index 00000000..7154c902 --- /dev/null +++ b/1.16/src/main/resources/minecraft-data-generator.mixins.json @@ -0,0 +1,18 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "dev.u9g.minecraftdatagenerator.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "BiomeEffectsAccessor", + "EULAMixin", + "MiningToolItemAccessor", + "ReadyMixin", + "StatusEffectAccessor" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/1.17/build.gradle b/1.17/build.gradle new file mode 100644 index 00000000..e85de392 --- /dev/null +++ b/1.17/build.gradle @@ -0,0 +1,31 @@ +plugins { + id 'fabric-loom' +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API. This is technically optional, but you probably want it anyway. + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" +} + +processResources { + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} diff --git a/1.17/gradle.properties b/1.17/gradle.properties new file mode 100644 index 00000000..e181bfd6 --- /dev/null +++ b/1.17/gradle.properties @@ -0,0 +1,8 @@ +# Fabric Properties +# check these on https://modmuss50.me/fabric.html +minecraft_version=1.17 +yarn_mappings=1.17+build.13 +loader_version=0.13.3 +# Dependencies +# check this on https://modmuss50.me/fabric.html +fabric_version=0.36.0+1.17 diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/Main.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/Main.java similarity index 72% rename from src/main/java/dev/u9g/minecraftdatagenerator/Main.java rename to 1.17/src/main/java/dev/u9g/minecraftdatagenerator/Main.java index db9cd7ca..f8d17372 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/Main.java +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/Main.java @@ -1,13 +1,14 @@ -package dev.u9g.minecraftdatagenerator; - -import net.fabricmc.api.ModInitializer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class Main implements ModInitializer { - public static Logger LOGGER = LoggerFactory.getLogger("mc-data-gen-serv"); - @Override - public void onInitialize() { - - } -} +package dev.u9g.minecraftdatagenerator; + +import net.fabricmc.api.ModInitializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Main implements ModInitializer { + public static final Logger LOGGER = LoggerFactory.getLogger("mc-data-gen-serv"); + + @Override + public void onInitialize() { + + } +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java new file mode 100644 index 00000000..259df88f --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java @@ -0,0 +1,124 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; + +public class BiomesDataGenerator implements IDataGenerator { + + private static String guessBiomeDimensionFromCategory(Biome biome) { + return switch (biome.getCategory()) { + case NETHER -> "nether"; + case THEEND -> "end"; + default -> "overworld"; + }; + } + + private static int getBiomeColorFor(String biomeName) { + return switch (biomeName) { + case "the_void" -> 0; + case "plains" -> 9286496; + case "sunflower_plains" -> 11918216; + case "snowy_plains" -> 16777215; + case "ice_spikes" -> 11853020; + case "desert" -> 16421912; + case "swamp" -> 522674; + case "forest" -> 353825; + case "flower_forest" -> 2985545; + case "birch_forest" -> 3175492; + case "dark_forest" -> 4215066; + case "old_growth_birch_forest" -> 5807212; + case "old_growth_pine_taiga" -> 5858897; + case "old_growth_spruce_taiga" -> 8490617; + case "taiga" -> 747097; + case "snowy_taiga" -> 3233098; + case "savanna" -> 12431967; + case "savanna_plateau" -> 10984804; + case "windswept_hills" -> 6316128; + case "windswept_gravelly_hills" -> 8947848; + case "windswept_forest" -> 2250012; + case "windswept_savanna" -> 15063687; + case "jungle" -> 5470985; + case "sparse_jungle" -> 6458135; + case "bamboo_jungle" -> 7769620; + case "badlands" -> 14238997; + case "eroded_badlands" -> 16739645; + case "wooded_badlands" -> 11573093; + case "meadow" -> 9217136; + case "grove" -> 14675173; + case "snowy_slopes" -> 14348785; + case "frozen_peaks" -> 15399931; + case "jagged_peaks" -> 14937325; + case "stony_peaks" -> 13750737; + case "river" -> 255; + case "frozen_river" -> 10526975; + case "beach" -> 16440917; + case "snowy_beach" -> 16445632; + case "stony_shore" -> 10658436; + case "warm_ocean" -> 172; + case "lukewarm_ocean" -> 144; + case "deep_lukewarm_ocean" -> 64; + case "ocean" -> 112; + case "deep_ocean" -> 48; + case "cold_ocean" -> 2105456; + case "deep_cold_ocean" -> 2105400; + case "frozen_ocean" -> 7368918; + case "deep_frozen_ocean" -> 4210832; + case "mushroom_fields" -> 16711935; + case "dripstone_caves" -> 12690831; + case "lush_caves" -> 14652980; + case "nether_wastes" -> 12532539; + case "warped_forest" -> 4821115; + case "crimson_forest" -> 14485512; + case "soul_sand_valley" -> 6174768; + case "basalt_deltas" -> 4208182; + case "the_end" -> 8421631; + case "end_highlands" -> 12828041; + case "end_midlands" -> 15464630; + case "small_end_islands" -> 42; + case "end_barrens" -> 9474162; + default -> throw new Error("Unexpected biome, with name: '" + biomeName + "'"); + }; + } + + public static JsonObject generateBiomeInfo(Registry registry, Biome biome) { + JsonObject biomeDesc = new JsonObject(); + Identifier registryKey = registry.getKey(biome).orElseThrow().getValue(); + String localizationKey = String.format("biome.%s.%s", registryKey.getNamespace(), registryKey.getPath()); + + biomeDesc.addProperty("id", registry.getRawId(biome)); + biomeDesc.addProperty("name", registryKey.getPath()); + + biomeDesc.addProperty("category", biome.getCategory().getName()); + biomeDesc.addProperty("temperature", biome.getTemperature()); + biomeDesc.addProperty("precipitation", biome.getPrecipitation().getName()); + biomeDesc.addProperty("depth", biome.getDepth()); + biomeDesc.addProperty("dimension", guessBiomeDimensionFromCategory(biome)); + biomeDesc.addProperty("displayName", DGU.translateText(localizationKey)); + biomeDesc.addProperty("color", getBiomeColorFor(registryKey.getPath())); + biomeDesc.addProperty("rainfall", biome.getDownfall()); + + return biomeDesc; + } + + @Override + public String getDataName() { + return "biomes"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray biomesArray = new JsonArray(); + DynamicRegistryManager registryManager = DynamicRegistryManager.create(); + Registry biomeRegistry = registryManager.get(Registry.BIOME_KEY); + + biomeRegistry.stream() + .map(biome -> generateBiomeInfo(biomeRegistry, biome)) + .forEach(biomesArray::add); + return biomesArray; + } +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java new file mode 100644 index 00000000..52c3181d --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java @@ -0,0 +1,111 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.EmptyBlockView; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class BlockCollisionShapesDataGenerator implements IDataGenerator { + + @Override + public String getDataName() { + return "blockCollisionShapes"; + } + + @Override + public JsonObject generateDataJson() { + Registry blockRegistry = Registry.BLOCK; + BlockShapesCache blockShapesCache = new BlockShapesCache(); + + blockRegistry.forEach(blockShapesCache::processBlock); + + JsonObject resultObject = new JsonObject(); + + resultObject.add("blocks", blockShapesCache.dumpBlockShapeIndices(blockRegistry)); + resultObject.add("shapes", blockShapesCache.dumpShapesObject()); + + return resultObject; + } + + private static class BlockShapesCache { + public final Map uniqueBlockShapes = new HashMap<>(); + public final Map> blockCollisionShapes = new HashMap<>(); + private int lastCollisionShapeId = 0; + + public void processBlock(Block block) { + List blockStates = block.getStateManager().getStates(); + List blockCollisionShapes = new ArrayList<>(); + + for (BlockState blockState : blockStates) { + VoxelShape blockShape = blockState.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + Integer blockShapeIndex = uniqueBlockShapes.get(blockShape); + + if (blockShapeIndex == null) { + blockShapeIndex = lastCollisionShapeId++; + uniqueBlockShapes.put(blockShape, blockShapeIndex); + } + blockCollisionShapes.add(blockShapeIndex); + } + + this.blockCollisionShapes.put(block, blockCollisionShapes); + } + + public JsonObject dumpBlockShapeIndices(Registry blockRegistry) { + JsonObject resultObject = new JsonObject(); + + for (var entry : blockCollisionShapes.entrySet()) { + List blockCollisions = entry.getValue(); + long distinctShapesCount = blockCollisions.stream().distinct().count(); + JsonElement blockCollision; + if (distinctShapesCount == 1L) { + blockCollision = new JsonPrimitive(blockCollisions.get(0)); + } else { + blockCollision = new JsonArray(); + for (int collisionId : blockCollisions) { + ((JsonArray) blockCollision).add(collisionId); + } + } + + Identifier registryKey = blockRegistry.getKey(entry.getKey()).orElseThrow().getValue(); + resultObject.add(registryKey.getPath(), blockCollision); + } + + return resultObject; + } + + public JsonObject dumpShapesObject() { + JsonObject shapesObject = new JsonObject(); + + for (var entry : uniqueBlockShapes.entrySet()) { + JsonArray boxesArray = new JsonArray(); + entry.getKey().forEachBox((x1, y1, z1, x2, y2, z2) -> { + JsonArray oneBoxJsonArray = new JsonArray(); + + oneBoxJsonArray.add(x1); + oneBoxJsonArray.add(y1); + oneBoxJsonArray.add(z1); + + oneBoxJsonArray.add(x2); + oneBoxJsonArray.add(y2); + oneBoxJsonArray.add(z2); + + boxesArray.add(oneBoxJsonArray); + }); + shapesObject.add(Integer.toString(entry.getValue()), boxesArray); + } + return shapesObject; + } + } +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java new file mode 100644 index 00000000..51139655 --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java @@ -0,0 +1,198 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.base.CaseFormat; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.block.AirBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.loot.context.LootContext; +import net.minecraft.loot.context.LootContextParameters; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.state.property.EnumProperty; +import net.minecraft.state.property.IntProperty; +import net.minecraft.state.property.Property; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.EmptyBlockView; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +public class BlocksDataGenerator implements IDataGenerator { + + private static final Logger logger = LoggerFactory.getLogger(BlocksDataGenerator.class); + + private static List getItemsEffectiveForBlock(BlockState blockState) { + return Registry.ITEM.stream() + .filter(item -> item.getDefaultStack().isSuitableFor(blockState)) + .collect(Collectors.toList()); + } + + private static void populateDropsIfPossible(BlockState blockState, Item firstToolItem, List outDrops) { + MinecraftServer minecraftServer = DGU.getCurrentlyRunningServer(); + if (minecraftServer != null) { + //If we have local world context, we can actually evaluate loot tables and determine actual data + ServerWorld serverWorld = minecraftServer.getOverworld(); + LootContext.Builder lootContext = new LootContext.Builder(serverWorld) + .parameter(LootContextParameters.BLOCK_STATE, blockState) + .parameter(LootContextParameters.ORIGIN, Vec3d.ZERO) + .parameter(LootContextParameters.TOOL, firstToolItem.getDefaultStack()) + .random(0L); + outDrops.addAll(blockState.getDroppedStacks(lootContext)); + } else { + //If we're lacking world context to correctly determine drops, assume that default drop is ItemBlock stack in quantity of 1 + Item itemBlock = blockState.getBlock().asItem(); + if (itemBlock != Items.AIR) { + outDrops.add(itemBlock.getDefaultStack()); + } + } + } + + private static String getPropertyTypeName(Property property) { + //Explicitly handle default minecraft properties + if (property instanceof BooleanProperty) { + return "bool"; + } + if (property instanceof IntProperty) { + return "int"; + } + if (property instanceof EnumProperty) { + return "enum"; + } + + //Use simple class name as fallback, this code will give something like + //example_type for ExampleTypeProperty class name + String rawPropertyName = property.getClass().getSimpleName().replace("Property", ""); + return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, rawPropertyName); + } + + private static > JsonObject generateStateProperty(Property property) { + JsonObject propertyObject = new JsonObject(); + Collection propertyValues = property.getValues(); + + propertyObject.addProperty("name", property.getName()); + propertyObject.addProperty("type", getPropertyTypeName(property)); + propertyObject.addProperty("num_values", propertyValues.size()); + + //Do not add values for vanilla boolean properties, they are known by default + if (!(property instanceof BooleanProperty)) { + JsonArray propertyValuesArray = new JsonArray(); + for (T propertyValue : propertyValues) { + propertyValuesArray.add(property.name(propertyValue)); + } + propertyObject.add("values", propertyValuesArray); + } + return propertyObject; + } + + private static String findMatchingBlockMaterial(BlockState blockState, List materials) { + List matchingMaterials = materials.stream() + .filter(material -> material.getPredicate().test(blockState)) + .collect(Collectors.toList()); + + if (matchingMaterials.size() > 1) { + var firstMaterial = matchingMaterials.get(0); + var otherMaterials = matchingMaterials.subList(1, matchingMaterials.size()); + + if (!otherMaterials.stream().allMatch(firstMaterial::includesMaterial)) { + logger.error("Block {} matches multiple materials: {}", blockState.getBlock(), matchingMaterials); + } + } + if (matchingMaterials.isEmpty()) { + return "default"; + } + return matchingMaterials.get(0).getMaterialName(); + } + + public static JsonObject generateBlock(Registry blockRegistry, List materials, Block block) { + JsonObject blockDesc = new JsonObject(); + + List blockStates = block.getStateManager().getStates(); + BlockState defaultState = block.getDefaultState(); + Identifier registryKey = blockRegistry.getKey(block).orElseThrow().getValue(); + String localizationKey = block.getTranslationKey(); + List effectiveTools = getItemsEffectiveForBlock(defaultState); + + blockDesc.addProperty("id", blockRegistry.getRawId(block)); + blockDesc.addProperty("name", registryKey.getPath()); + blockDesc.addProperty("displayName", DGU.translateText(localizationKey)); + + blockDesc.addProperty("hardness", block.getHardness()); + blockDesc.addProperty("resistance", block.getBlastResistance()); + blockDesc.addProperty("stackSize", block.asItem().getMaxCount()); + blockDesc.addProperty("diggable", block.getHardness() != -1.0f && !(block instanceof AirBlock)); +// JsonObject effTools = new JsonObject(); +// effectiveTools.forEach(item -> effTools.addProperty( +// String.valueOf(Registry.ITEM.getRawId(item)), // key +// item.getMiningSpeedMultiplier(item.getDefaultStack(), defaultState) // value +// )); +// blockDesc.add("effectiveTools", effTools); + blockDesc.addProperty("material", findMatchingBlockMaterial(defaultState, materials)); + + blockDesc.addProperty("transparent", !defaultState.isOpaque()); + blockDesc.addProperty("emitLight", defaultState.getLuminance()); + blockDesc.addProperty("filterLight", defaultState.getOpacity(EmptyBlockView.INSTANCE, BlockPos.ORIGIN)); + + blockDesc.addProperty("defaultState", Block.getRawIdFromState(defaultState)); + blockDesc.addProperty("minStateId", Block.getRawIdFromState(blockStates.get(0))); + blockDesc.addProperty("maxStateId", Block.getRawIdFromState(blockStates.get(blockStates.size() - 1))); + + JsonArray stateProperties = new JsonArray(); + for (Property property : block.getStateManager().getProperties()) { + stateProperties.add(generateStateProperty(property)); + } + blockDesc.add("states", stateProperties); + + //Only add harvest tools if tool is required for harvesting this block + if (defaultState.isToolRequired()) { + JsonObject effectiveToolsObject = new JsonObject(); + for (Item effectiveItem : effectiveTools) { + effectiveToolsObject.addProperty(Integer.toString(Item.getRawId(effectiveItem)), true); + } + blockDesc.add("harvestTools", effectiveToolsObject); + } + + List actualBlockDrops = new ArrayList<>(); + populateDropsIfPossible(defaultState, effectiveTools.isEmpty() ? Items.AIR : effectiveTools.get(0), actualBlockDrops); + + JsonArray dropsArray = new JsonArray(); + for (ItemStack dropStack : actualBlockDrops) { + dropsArray.add(Item.getRawId(dropStack.getItem())); + } + blockDesc.add("drops", dropsArray); + + VoxelShape blockCollisionShape = defaultState.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + blockDesc.addProperty("boundingBox", blockCollisionShape.isEmpty() ? "empty" : "block"); + + return blockDesc; + } + + @Override + public String getDataName() { + return "blocks"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultBlocksArray = new JsonArray(); + Registry blockRegistry = Registry.BLOCK; + List availableMaterials = MaterialsDataGenerator.getGlobalMaterialInfo(); + + blockRegistry.forEach(block -> resultBlocksArray.add(generateBlock(blockRegistry, availableMaterials, block))); + return resultBlocksArray; + } +} diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java similarity index 97% rename from src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java rename to 1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java index b04162ad..c47684ab 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java @@ -16,8 +16,25 @@ public class DataGenerators { - private static List GENERATORS = new ArrayList<>(); private static final Logger logger = LoggerFactory.getLogger(DataGenerators.class); + private static final List GENERATORS = new ArrayList<>(); + + static { + register(new BiomesDataGenerator()); + register(new BlockCollisionShapesDataGenerator()); + register(new BlocksDataGenerator()); + register(new EffectsDataGenerator()); + register(new EnchantmentsDataGenerator()); + register(new EntitiesDataGenerator()); + register(new FoodsDataGenerator()); + register(new ItemsDataGenerator()); + register(new ParticlesDataGenerator()); + register(new TintsDataGenerator()); + register(new MaterialsDataGenerator()); +// register(new RecipeDataGenerator()); - On hold until mcdata supports multiple materials for a recipe + register(new LanguageDataGenerator()); + register(new InstrumentsDataGenerator()); + } public static void register(IDataGenerator generator) { GENERATORS.add(generator); @@ -57,21 +74,4 @@ public static boolean runDataGenerators(Path outputDirectory) { logger.info("Finishing running data generators"); return generatorsFailed == 0; } - - static { - register(new BiomesDataGenerator()); - register(new BlockCollisionShapesDataGenerator()); - register(new BlocksDataGenerator()); - register(new EffectsDataGenerator()); - register(new EnchantmentsDataGenerator()); - register(new EntitiesDataGenerator()); - register(new FoodsDataGenerator()); - register(new ItemsDataGenerator()); - register(new ParticlesDataGenerator()); - register(new TintsDataGenerator()); - register(new MaterialsDataGenerator()); -// register(new RecipeDataGenerator()); - On hold until mcdata supports multiple materials for a recipe - register(new LanguageDataGenerator()); - register(new InstrumentsDataGenerator()); - } } diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java new file mode 100644 index 00000000..1c9849e6 --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java @@ -0,0 +1,46 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.stream.Collectors; + +public class EffectsDataGenerator implements IDataGenerator { + + public static JsonObject generateEffect(Registry registry, StatusEffect statusEffect) { + JsonObject effectDesc = new JsonObject(); + Identifier registryKey = registry.getKey(statusEffect).orElseThrow().getValue(); + + effectDesc.addProperty("id", registry.getRawId(statusEffect)); + if (statusEffect == StatusEffects.UNLUCK) { + effectDesc.addProperty("name", "BadLuck"); + effectDesc.addProperty("displayName", "Bad Luck"); + } else { + effectDesc.addProperty("name", Arrays.stream(registryKey.getPath().split("_")).map(StringUtils::capitalize).collect(Collectors.joining())); + effectDesc.addProperty("displayName", DGU.translateText(statusEffect.getTranslationKey())); + } + + effectDesc.addProperty("type", statusEffect.isBeneficial() ? "good" : "bad"); + return effectDesc; + } + + @Override + public String getDataName() { + return "effects"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry statusEffectRegistry = Registry.STATUS_EFFECT; + statusEffectRegistry.forEach(effect -> resultsArray.add(generateEffect(statusEffectRegistry, effect))); + return resultsArray; + } +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java new file mode 100644 index 00000000..a71961c9 --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java @@ -0,0 +1,108 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +public class EnchantmentsDataGenerator implements IDataGenerator { + + private static final ImmutableMap ENCHANTMENT_TARGET_NAMES = ImmutableMap.builder() + .put(EnchantmentTarget.ARMOR, "armor") + .put(EnchantmentTarget.ARMOR_FEET, "armor_feet") + .put(EnchantmentTarget.ARMOR_LEGS, "armor_legs") + .put(EnchantmentTarget.ARMOR_CHEST, "armor_chest") + .put(EnchantmentTarget.ARMOR_HEAD, "armor_head") + .put(EnchantmentTarget.WEAPON, "weapon") + .put(EnchantmentTarget.DIGGER, "digger") + .put(EnchantmentTarget.FISHING_ROD, "fishing_rod") + .put(EnchantmentTarget.TRIDENT, "trident") + .put(EnchantmentTarget.BREAKABLE, "breakable") + .put(EnchantmentTarget.BOW, "bow") + .put(EnchantmentTarget.WEARABLE, "wearable") + .put(EnchantmentTarget.CROSSBOW, "crossbow") + .put(EnchantmentTarget.VANISHABLE, "vanishable") + .build(); + + public static String getEnchantmentTargetName(EnchantmentTarget target) { + return ENCHANTMENT_TARGET_NAMES.getOrDefault(target, target.name().toLowerCase(Locale.ROOT)); + } + + //Equation enchantment costs follow is a * level + b, so we can easily retrieve a and b by passing zero level + private static JsonObject generateEnchantmentMinPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMinPower(0); + int a = enchantment.getMinPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + private static JsonObject generateEnchantmentMaxPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMaxPower(0); + int a = enchantment.getMaxPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + public static JsonObject generateEnchantment(Registry registry, Enchantment enchantment) { + JsonObject enchantmentDesc = new JsonObject(); + Identifier registryKey = registry.getKey(enchantment).orElseThrow().getValue(); + + enchantmentDesc.addProperty("id", registry.getRawId(enchantment)); + enchantmentDesc.addProperty("name", registryKey.getPath()); + enchantmentDesc.addProperty("displayName", DGU.translateText(enchantment.getTranslationKey())); + + enchantmentDesc.addProperty("maxLevel", enchantment.getMaxLevel()); + enchantmentDesc.add("minCost", generateEnchantmentMinPowerCoefficients(enchantment)); + enchantmentDesc.add("maxCost", generateEnchantmentMaxPowerCoefficients(enchantment)); + + enchantmentDesc.addProperty("treasureOnly", enchantment.isTreasure()); + enchantmentDesc.addProperty("curse", enchantment.isCursed()); + + List incompatibleEnchantments = registry.stream() + .filter(other -> !enchantment.canCombine(other)) + .filter(other -> other != enchantment) + .collect(Collectors.toList()); + + JsonArray excludes = new JsonArray(); + for (Enchantment excludedEnchantment : incompatibleEnchantments) { + Identifier otherKey = registry.getKey(excludedEnchantment).orElseThrow().getValue(); + excludes.add(otherKey.getPath()); + } + enchantmentDesc.add("exclude", excludes); + + enchantmentDesc.addProperty("category", getEnchantmentTargetName(enchantment.type)); + enchantmentDesc.addProperty("weight", enchantment.getRarity().getWeight()); + enchantmentDesc.addProperty("tradeable", enchantment.isAvailableForEnchantedBookOffer()); + enchantmentDesc.addProperty("discoverable", enchantment.isAvailableForRandomSelection()); + + return enchantmentDesc; + } + + @Override + public String getDataName() { + return "enchantments"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry enchantmentRegistry = Registry.ENCHANTMENT; + enchantmentRegistry.stream() + .forEach(enchantment -> resultsArray.add(generateEnchantment(enchantmentRegistry, enchantment))); + return resultsArray; + } +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java new file mode 100644 index 00000000..8d94a6b7 --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java @@ -0,0 +1,115 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.mob.AmbientEntity; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.mob.WaterCreatureEntity; +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.passive.PassiveEntity; +import net.minecraft.entity.projectile.ProjectileEntity; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +public class EntitiesDataGenerator implements IDataGenerator { + + public static JsonObject generateEntity(Registry> entityRegistry, EntityType entityType) { + JsonObject entityDesc = new JsonObject(); + Identifier registryKey = entityRegistry.getKey(entityType).orElseThrow().getValue(); + int entityRawId = entityRegistry.getRawId(entityType); + + entityDesc.addProperty("id", entityRawId); + entityDesc.addProperty("internalId", entityRawId); + entityDesc.addProperty("name", registryKey.getPath()); + + entityDesc.addProperty("displayName", DGU.translateText(entityType.getTranslationKey())); + entityDesc.addProperty("width", entityType.getDimensions().width); + entityDesc.addProperty("height", entityType.getDimensions().height); + + String entityTypeString = "UNKNOWN"; + MinecraftServer minecraftServer = DGU.getCurrentlyRunningServer(); + + if (minecraftServer != null) { + Entity entityObject = entityType.create(minecraftServer.getOverworld()); + entityTypeString = entityObject != null ? getEntityTypeForClass(entityObject.getClass()) : "player"; + } + entityDesc.addProperty("type", entityTypeString); + entityDesc.addProperty("category", getCategoryFrom(entityType)); + + return entityDesc; + } + + private static String getCategoryFrom(EntityType entityType) { + if (entityType == EntityType.PLAYER) return "UNKNOWN"; // fail early for player entities + Entity entity = EntityType.createInstanceFromId(Registry.ENTITY_TYPE.getRawId(entityType), DGU.getWorld()); + if (entity == null) + throw new Error("Entity was null after trying to create a: " + DGU.translateText(entityType.getTranslationKey())); + entity.discard(); + return switch (entity.getClass().getPackageName()) { + case "net.minecraft.entity.decoration", "net.minecraft.entity.decoration.painting" -> "Immobile"; + case "net.minecraft.entity.boss", "net.minecraft.entity.mob", "net.minecraft.entity.boss.dragon" -> + "Hostile mobs"; + case "net.minecraft.entity.projectile", "net.minecraft.entity.projectile.thrown" -> "Projectiles"; + case "net.minecraft.entity.passive" -> "Passive mobs"; + case "net.minecraft.entity.vehicle" -> "Vehicles"; + case "net.minecraft.entity" -> "UNKNOWN"; + default -> throw new Error("Unexpected entity type: " + entity.getClass().getPackageName()); + }; + } + + //Honestly, both "type" and "category" fields in the schema and examples do not contain any useful information + //Since category is optional, I will just leave it out, and for type I will assume general entity classification + //by the Entity class hierarchy (which has some weirdness too by the way) + private static String getEntityTypeForClass(Class entityClass) { + //Top-level classifications + if (WaterCreatureEntity.class.isAssignableFrom(entityClass)) { + return "water_creature"; + } + if (AnimalEntity.class.isAssignableFrom(entityClass)) { + return "animal"; + } + if (HostileEntity.class.isAssignableFrom(entityClass)) { + return "hostile"; + } + if (AmbientEntity.class.isAssignableFrom(entityClass)) { + return "ambient"; + } + + //Second level classifications. PathAwareEntity is not included because it + //doesn't really make much sense to categorize by it + if (PassiveEntity.class.isAssignableFrom(entityClass)) { + return "passive"; + } + if (MobEntity.class.isAssignableFrom(entityClass)) { + return "mob"; + } + + //Other classifications only include living entities and projectiles. everything else is categorized as other + if (LivingEntity.class.isAssignableFrom(entityClass)) { + return "living"; + } + if (ProjectileEntity.class.isAssignableFrom(entityClass)) { + return "projectile"; + } + return "other"; + } + + @Override + public String getDataName() { + return "entities"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry> entityTypeRegistry = Registry.ENTITY_TYPE; + entityTypeRegistry.forEach(entity -> resultArray.add(generateEntity(entityTypeRegistry, entity))); + return resultArray; + } +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java new file mode 100644 index 00000000..45a0b35f --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java @@ -0,0 +1,51 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.item.FoodComponent; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Objects; + +public class FoodsDataGenerator implements IDataGenerator { + + public static JsonObject generateFoodDescriptor(Registry registry, Item foodItem) { + JsonObject foodDesc = new JsonObject(); + Identifier registryKey = registry.getKey(foodItem).orElseThrow().getValue(); + + foodDesc.addProperty("id", registry.getRawId(foodItem)); + foodDesc.addProperty("name", registryKey.getPath()); + + foodDesc.addProperty("stackSize", foodItem.getMaxCount()); + foodDesc.addProperty("displayName", DGU.translateText(foodItem.getTranslationKey())); + + FoodComponent foodComponent = Objects.requireNonNull(foodItem.getFoodComponent()); + float foodPoints = foodComponent.getHunger(); + float saturationRatio = foodComponent.getSaturationModifier() * 2.0F; + float saturation = foodPoints * saturationRatio; + + foodDesc.addProperty("foodPoints", foodPoints); + foodDesc.addProperty("saturation", saturation); + + foodDesc.addProperty("effectiveQuality", foodPoints + saturation); + foodDesc.addProperty("saturationRatio", saturationRatio); + return foodDesc; + } + + @Override + public String getDataName() { + return "foods"; + } + + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry itemRegistry = Registry.ITEM; + itemRegistry.stream() + .filter(Item::isFood) + .forEach(food -> resultsArray.add(generateFoodDescriptor(itemRegistry, food))); + return resultsArray; + } +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java new file mode 100644 index 00000000..81fe4e20 --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; + +public interface IDataGenerator { + + String getDataName(); + + JsonElement generateDataJson(); +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java new file mode 100644 index 00000000..e7b9b54f --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java @@ -0,0 +1,25 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.minecraft.block.enums.Instrument; + +public class InstrumentsDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "instruments"; + } + + @Override + public JsonElement generateDataJson() { + JsonArray array = new JsonArray(); + for (Instrument instrument : Instrument.values()) { + JsonObject object = new JsonObject(); + object.addProperty("id", instrument.ordinal()); + object.addProperty("name", instrument.asString()); + array.add(object); + } + return array; + } +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java new file mode 100644 index 00000000..b0c959dc --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java @@ -0,0 +1,81 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class ItemsDataGenerator implements IDataGenerator { + + private static List calculateItemsToRepairWith(Registry itemRegistry, Item sourceItem) { + ItemStack sourceItemStack = sourceItem.getDefaultStack(); + return itemRegistry.stream() + .filter(otherItem -> sourceItem.canRepair(sourceItemStack, otherItem.getDefaultStack())) + .collect(Collectors.toList()); + } + + private static List getApplicableEnchantmentTargets(Item sourceItem) { + return Arrays.stream(EnchantmentTarget.values()) + .filter(target -> target.isAcceptableItem(sourceItem)) + .collect(Collectors.toList()); + } + + public static JsonObject generateItem(Registry itemRegistry, Item item) { + JsonObject itemDesc = new JsonObject(); + Identifier registryKey = itemRegistry.getKey(item).orElseThrow().getValue(); + + itemDesc.addProperty("id", itemRegistry.getRawId(item)); + itemDesc.addProperty("name", registryKey.getPath()); + + itemDesc.addProperty("displayName", DGU.translateText(item.getTranslationKey())); + itemDesc.addProperty("stackSize", item.getMaxCount()); + + List enchantmentTargets = getApplicableEnchantmentTargets(item); + + JsonArray enchantCategoriesArray = new JsonArray(); + for (EnchantmentTarget target : enchantmentTargets) { + enchantCategoriesArray.add(EnchantmentsDataGenerator.getEnchantmentTargetName(target)); + } + if (enchantCategoriesArray.size() > 0) { + itemDesc.add("enchantCategories", enchantCategoriesArray); + } + + if (item.isDamageable()) { + List repairWithItems = calculateItemsToRepairWith(itemRegistry, item); + + JsonArray fixedWithArray = new JsonArray(); + for (Item repairWithItem : repairWithItems) { + Identifier repairWithName = itemRegistry.getKey(repairWithItem).orElseThrow().getValue(); + fixedWithArray.add(repairWithName.getPath()); + } + if (fixedWithArray.size() > 0) { + itemDesc.add("repairWith", fixedWithArray); + } + + int maxDurability = item.getMaxDamage(); + itemDesc.addProperty("maxDurability", maxDurability); + } + return itemDesc; + } + + @Override + public String getDataName() { + return "items"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry itemRegistry = Registry.ITEM; + itemRegistry.stream().forEach(item -> resultArray.add(generateItem(itemRegistry, item))); + return resultArray; + } +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java new file mode 100644 index 00000000..7a3c6ced --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +public class LanguageDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "language"; + } + + @Override + public JsonElement generateDataJson() { + try { + InputStream inputStream = Objects.requireNonNull(this.getClass().getResourceAsStream("/assets/minecraft/lang/en_us.json")); + return new Gson().fromJson(new InputStreamReader(inputStream, StandardCharsets.UTF_8), JsonObject.class); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to generate language file"); + } +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java new file mode 100644 index 00000000..3b96085d --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java @@ -0,0 +1,198 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.MiningToolItemAccessor; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.Material; +import net.minecraft.item.Item; +import net.minecraft.item.Items; +import net.minecraft.item.MiningToolItem; +import net.minecraft.item.SwordItem; +import net.minecraft.tag.BlockTags; +import net.minecraft.tag.Tag; +import net.minecraft.util.registry.Registry; + +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +//TODO entire idea of linking materials to tool speeds is obsolete and just wrong now, +//TODO but we kinda have to support it to let old code work for computing digging times, +//TODO so for now we will handle materials as "virtual" ones based on which tools can break blocks +public class MaterialsDataGenerator implements IDataGenerator { + + private static final List> COMPOSITE_MATERIALS = ImmutableList.>builder() + .add(ImmutableList.of("plant", makeMaterialNameForTag(BlockTags.AXE_MINEABLE))) + .add(ImmutableList.of("gourd", makeMaterialNameForTag(BlockTags.AXE_MINEABLE))) + .add(ImmutableList.of(makeMaterialNameForTag(BlockTags.LEAVES), makeMaterialNameForTag(BlockTags.HOE_MINEABLE))) + .add(ImmutableList.of(makeMaterialNameForTag(BlockTags.LEAVES), makeMaterialNameForTag(BlockTags.AXE_MINEABLE), makeMaterialNameForTag(BlockTags.HOE_MINEABLE))) + .add(ImmutableList.of("vine_or_glow_lichen", "plant", makeMaterialNameForTag(BlockTags.AXE_MINEABLE) + )).build(); + + private static String makeMaterialNameForTag(Tag tag) { + Tag.Identified identifiedTag = (Tag.Identified) tag; + return identifiedTag.getId().getPath(); + } + + private static void createCompositeMaterialInfo(List allMaterials, List combinedMaterials) { + String compositeMaterialName = Joiner.on(';').join(combinedMaterials); + + List mappedMaterials = combinedMaterials.stream() + .map(otherName -> allMaterials.stream() + .filter(other -> other.getMaterialName().equals(otherName)) + .findFirst().orElseThrow(() -> new RuntimeException("Material not found with name " + otherName))) + .collect(Collectors.toList()); + + Predicate compositePredicate = blockState -> + mappedMaterials.stream().allMatch(it -> it.getPredicate().test(blockState)); + + MaterialInfo materialInfo = new MaterialInfo(compositeMaterialName, compositePredicate).includes(mappedMaterials); + allMaterials.add(0, materialInfo); + } + + private static void createCompositeMaterial(Map> allMaterials, List combinedMaterials) { + String compositeMaterialName = Joiner.on(';').join(combinedMaterials); + + Map resultingToolSpeeds = new HashMap<>(); + combinedMaterials.stream() + .map(allMaterials::get) + .forEach(resultingToolSpeeds::putAll); + allMaterials.put(compositeMaterialName, resultingToolSpeeds); + } + + public static List getGlobalMaterialInfo() { + ArrayList resultList = new ArrayList<>(); + + resultList.add(new MaterialInfo("vine_or_glow_lichen", blockState -> blockState.isOf(Blocks.VINE) || blockState.isOf(Blocks.GLOW_LICHEN))); + resultList.add(new MaterialInfo("coweb", blockState -> blockState.isOf(Blocks.COBWEB))); + + resultList.add(new MaterialInfo("leaves", blockState -> blockState.isIn(BlockTags.LEAVES))); + resultList.add(new MaterialInfo("wool", blockState -> blockState.isIn(BlockTags.WOOL))); + + resultList.add(new MaterialInfo("gourd", blockState -> blockState.getMaterial() == Material.GOURD)); + resultList.add(new MaterialInfo("plant", blockState -> blockState.getMaterial() == Material.PLANT || blockState.getMaterial() == Material.REPLACEABLE_PLANT)); + + HashSet uniqueMaterialNames = new HashSet<>(); + + Registry itemRegistry = Registry.ITEM; + itemRegistry.forEach(item -> { + if (item instanceof MiningToolItem toolItem) { + Tag effectiveBlocks = ((MiningToolItemAccessor) toolItem).getEffectiveBlocks(); + String materialName = makeMaterialNameForTag(effectiveBlocks); + + if (!uniqueMaterialNames.contains(materialName)) { + uniqueMaterialNames.add(materialName); + resultList.add(new MaterialInfo(materialName, blockState -> blockState.isIn(effectiveBlocks))); + } + } + }); + + COMPOSITE_MATERIALS.forEach(values -> createCompositeMaterialInfo(resultList, values)); + return resultList; + } + + @Override + public String getDataName() { + return "materials"; + } + + @Override + public JsonElement generateDataJson() { + Registry itemRegistry = Registry.ITEM; + + Map> materialMiningSpeeds = new HashMap<>(); + materialMiningSpeeds.put("default", ImmutableMap.of()); + + //Special materials used for shears and swords special mining speed logic + Map leavesMaterialSpeeds = new HashMap<>(); + Map cowebMaterialSpeeds = new HashMap<>(); + Map plantMaterialSpeeds = new HashMap<>(); + Map gourdMaterialSpeeds = new HashMap<>(); + + materialMiningSpeeds.put(makeMaterialNameForTag(BlockTags.LEAVES), leavesMaterialSpeeds); + materialMiningSpeeds.put("coweb", cowebMaterialSpeeds); + materialMiningSpeeds.put("plant", plantMaterialSpeeds); + materialMiningSpeeds.put("gourd", gourdMaterialSpeeds); + + //Shears need special handling because they do not follow normal rules like tools + leavesMaterialSpeeds.put(Items.SHEARS, 15.0f); + cowebMaterialSpeeds.put(Items.SHEARS, 15.0f); + materialMiningSpeeds.put("vine_or_glow_lichen", ImmutableMap.of(Items.SHEARS, 2.0f)); + materialMiningSpeeds.put("wool", ImmutableMap.of(Items.SHEARS, 5.0f)); + + itemRegistry.forEach(item -> { + //Tools are handled rather easily and do not require anything else + if (item instanceof MiningToolItem toolItem) { + Tag effectiveBlocks = ((MiningToolItemAccessor) toolItem).getEffectiveBlocks(); + String materialName = makeMaterialNameForTag(effectiveBlocks); + + Map materialSpeeds = materialMiningSpeeds.computeIfAbsent(materialName, k -> new HashMap<>()); + float miningSpeed = ((MiningToolItemAccessor) toolItem).getMiningSpeed(); + materialSpeeds.put(item, miningSpeed); + } + + //Swords require special treatment + if (item instanceof SwordItem) { + cowebMaterialSpeeds.put(item, 15.0f); + plantMaterialSpeeds.put(item, 1.5f); + leavesMaterialSpeeds.put(item, 1.5f); + gourdMaterialSpeeds.put(item, 1.5f); + } + }); + + COMPOSITE_MATERIALS.forEach(values -> createCompositeMaterial(materialMiningSpeeds, values)); + + JsonObject resultObject = new JsonObject(); + + for (var entry : materialMiningSpeeds.entrySet()) { + JsonObject toolSpeedsObject = new JsonObject(); + + for (var toolEntry : entry.getValue().entrySet()) { + int rawItemId = itemRegistry.getRawId(toolEntry.getKey()); + toolSpeedsObject.addProperty(Integer.toString(rawItemId), toolEntry.getValue()); + } + resultObject.add(entry.getKey(), toolSpeedsObject); + } + + return resultObject; + } + + public static class MaterialInfo { + private final String materialName; + private final Predicate predicate; + private final List includedMaterials = new ArrayList<>(); + + public MaterialInfo(String materialName, Predicate predicate) { + this.materialName = materialName; + this.predicate = predicate; + } + + protected MaterialInfo includes(List otherMaterials) { + this.includedMaterials.addAll(otherMaterials); + return this; + } + + public String getMaterialName() { + return materialName; + } + + public Predicate getPredicate() { + return predicate; + } + + public boolean includesMaterial(MaterialInfo materialInfo) { + return includedMaterials.contains(materialInfo); + } + + @Override + public String toString() { + return materialName; + } + } +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java new file mode 100644 index 00000000..5a6b1081 --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java @@ -0,0 +1,32 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.minecraft.particle.ParticleType; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +public class ParticlesDataGenerator implements IDataGenerator { + + public static JsonObject generateParticleType(Registry> registry, ParticleType particleType) { + JsonObject effectDesc = new JsonObject(); + Identifier registryKey = registry.getKey(particleType).orElseThrow().getValue(); + + effectDesc.addProperty("id", registry.getRawId(particleType)); + effectDesc.addProperty("name", registryKey.getPath()); + return effectDesc; + } + + @Override + public String getDataName() { + return "particles"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry> particleTypeRegistry = Registry.PARTICLE_TYPE; + particleTypeRegistry.forEach(particleType -> resultsArray.add(generateParticleType(particleTypeRegistry, particleType))); + return resultsArray; + } +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java new file mode 100644 index 00000000..3ec1d826 --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java @@ -0,0 +1,132 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.item.Item; +import net.minecraft.recipe.Ingredient; +import net.minecraft.recipe.Recipe; +import net.minecraft.recipe.ShapedRecipe; +import net.minecraft.recipe.ShapelessRecipe; +import net.minecraft.util.registry.Registry; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class RecipeDataGenerator implements IDataGenerator { + + private static int getRawIdFor(Item item) { + return Registry.ITEM.getRawId(item); + } + + @Override + public String getDataName() { + return "recipes"; + } + + @Override + public JsonElement generateDataJson() { + JsonObject finalObj = new JsonObject(); + Multimap recipes = ArrayListMultimap.create(); + for (Recipe recipe : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) { + if (recipe instanceof ShapedRecipe sr) { + var ingredients = sr.getIngredients(); + List ingr = new ArrayList<>(); + for (int i = 0; i < 9; i++) { + if (i >= ingredients.size()) { + ingr.add(-1); + continue; + } + var stacks = ingredients.get(i); +// var matching = stacks.getMatchingStacks(); +// if (matching.length == 0) { +// ingr.add(-1); +// } else { +// ingr.add(getRawIdFor(matching[0].getItem())); +// } + } + Lists.reverse(ingr); + + JsonArray inShape = new JsonArray(); + + var iter = ingr.iterator(); + for (int y = 0; y < 3; y++) { + var jsonRow = new JsonArray(); + int one = iter.next(); + int two = iter.next(); + int three = iter.next(); + if (y > 0 && one == -1 && two == -1 && three == -1) continue; + jsonRow.add(one); + jsonRow.add(two); + jsonRow.add(three); + inShape.add(jsonRow); + } + + JsonObject finalRecipe = new JsonObject(); + finalRecipe.add("inShape", inShape); + + var resultObject = new JsonObject(); + resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); + resultObject.addProperty("count", sr.getOutput().getCount()); + finalRecipe.add("result", resultObject); + + String id = ((Integer) getRawIdFor(sr.getOutput().getItem())).toString(); + + if (!finalObj.has(id)) { + finalObj.add(id, new JsonArray()); + } + finalObj.get(id).getAsJsonArray().add(finalRecipe); +// var input = new JsonArray(); +// var ingredients = sr.getIngredients().stream().toList(); +// for (int y = 0; y < sr.getHeight(); y++) { +// var arr = new JsonArray(); +// for (int x = 0; x < sr.getWidth(); x++) { +// if ((y*3)+x >= ingredients.size()) { +// arr.add(JsonNull.INSTANCE); +// continue; +// } +// var ingredient = ingredients.get((y*3)+x).getMatchingStacks(); // FIXME: fix when there are more than one matching stack +// if (ingredient.length == 0) { +// arr.add(JsonNull.INSTANCE); +// } else { +// arr.add(getRawIdFor(ingredient[0].getItem())); +// } +// } +// input.add(arr); +// } +// var rootRecipeObject = new JsonObject(); +// rootRecipeObject.add("inShape", input); +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +// resultObject.addProperty("count", sr.getOutput().getCount()); +// rootRecipeObject.add("result", resultObject); +// recipes.put(getRawIdFor(sr.getOutput().getItem()), rootRecipeObject); + } else if (recipe instanceof ShapelessRecipe sl) { + var ingredients = new JsonArray(); + for (Ingredient ingredient : sl.getIngredients()) { + if (ingredient.isEmpty()) continue; +// ingredients.add(getRawIdFor(ingredient.getMatchingStacks()[0].getItem())); + } + var rootRecipeObject = new JsonObject(); + rootRecipeObject.add("ingredients", ingredients); + var resultObject = new JsonObject(); + resultObject.addProperty("id", getRawIdFor(sl.getOutput().getItem())); + resultObject.addProperty("count", sl.getOutput().getCount()); + rootRecipeObject.add("result", resultObject); + recipes.put(getRawIdFor(sl.getOutput().getItem()), rootRecipeObject); + } + } + recipes.forEach((a, b) -> { + if (!finalObj.has(a.toString())) { + finalObj.add(a.toString(), new JsonArray()); + } + finalObj.get(a.toString()).getAsJsonArray().add(b); + }); + return finalObj; + } +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java new file mode 100644 index 00000000..3ceef91b --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java @@ -0,0 +1,168 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.EmptyRenderBlockView; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.block.RedstoneWireBlock; +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.client.color.world.FoliageColors; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; + +import java.util.*; + +public class TintsDataGenerator implements IDataGenerator { + + public static BiomeTintColors generateBiomeTintColors(Registry biomeRegistry) { + BiomeTintColors colors = new BiomeTintColors(); + + biomeRegistry.forEach(biome -> { + int biomeGrassColor = biome.getGrassColorAt(0.0, 0.0); + int biomeFoliageColor = biome.getFoliageColor(); + int biomeWaterColor = biome.getWaterColor(); + + colors.grassColoursMap.computeIfAbsent(biomeGrassColor, k -> new ArrayList<>()).add(biome); + colors.foliageColoursMap.computeIfAbsent(biomeFoliageColor, k -> new ArrayList<>()).add(biome); + colors.waterColourMap.computeIfAbsent(biomeWaterColor, k -> new ArrayList<>()).add(biome); + }); + return colors; + } + + public static Map generateRedstoneTintColors() { + Map resultColors = new HashMap<>(); + + for (int redstoneLevel : RedstoneWireBlock.POWER.getValues()) { + int color = RedstoneWireBlock.getWireColor(redstoneLevel); + resultColors.put(redstoneLevel, color); + } + return resultColors; + } + + @Environment(EnvType.CLIENT) + private static int getBlockColor(Block block, BlockColors blockColors) { + return blockColors.getColor(block.getDefaultState(), EmptyRenderBlockView.INSTANCE, BlockPos.ORIGIN, 0xFFFFFF); + } + + @Environment(EnvType.CLIENT) + public static Map generateConstantTintColors() { + Map resultColors = new HashMap<>(); + BlockColors blockColors = BlockColors.create(); + + resultColors.put(Blocks.BIRCH_LEAVES, FoliageColors.getBirchColor()); + resultColors.put(Blocks.SPRUCE_LEAVES, FoliageColors.getSpruceColor()); + + resultColors.put(Blocks.LILY_PAD, getBlockColor(Blocks.LILY_PAD, blockColors)); + resultColors.put(Blocks.ATTACHED_MELON_STEM, getBlockColor(Blocks.ATTACHED_MELON_STEM, blockColors)); + resultColors.put(Blocks.ATTACHED_PUMPKIN_STEM, getBlockColor(Blocks.ATTACHED_PUMPKIN_STEM, blockColors)); + + //not really constant, depend on the block age, but kinda have to be handled since textures are literally white without them + resultColors.put(Blocks.MELON_STEM, getBlockColor(Blocks.MELON_STEM, blockColors)); + resultColors.put(Blocks.PUMPKIN_STEM, getBlockColor(Blocks.PUMPKIN_STEM, blockColors)); + + return resultColors; + } + + private static JsonObject encodeBiomeColorMap(Registry biomeRegistry, Map> colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + for (Biome biome : entry.getValue()) { + Identifier registryKey = biomeRegistry.getKey(biome).orElseThrow().getValue(); + keysArray.add(registryKey.getPath()); + } + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getKey()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeRedstoneColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + keysArray.add(entry.getKey()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeBlocksColorMap(Registry blockRegistry, Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + Identifier registryKey = blockRegistry.getKey(entry.getKey()).orElseThrow().getValue(); + keysArray.add(registryKey.getPath()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + @Override + public String getDataName() { + return "tints"; + } + + @Override + public JsonObject generateDataJson() { + DynamicRegistryManager registryManager = DynamicRegistryManager.create(); + Registry biomeRegistry = registryManager.get(Registry.BIOME_KEY); + Registry blockRegistry = registryManager.get(Registry.BLOCK_KEY); + + BiomeTintColors biomeTintColors = generateBiomeTintColors(biomeRegistry); + Map redstoneColors = generateRedstoneTintColors(); + Map constantTintColors = Collections.emptyMap(); + + EnvType currentEnvironment = FabricLoader.getInstance().getEnvironmentType(); + if (currentEnvironment == EnvType.CLIENT) { + constantTintColors = generateConstantTintColors(); + } + + JsonObject resultObject = new JsonObject(); + + resultObject.add("grass", encodeBiomeColorMap(biomeRegistry, biomeTintColors.grassColoursMap)); + resultObject.add("foliage", encodeBiomeColorMap(biomeRegistry, biomeTintColors.foliageColoursMap)); + resultObject.add("water", encodeBiomeColorMap(biomeRegistry, biomeTintColors.waterColourMap)); + + resultObject.add("redstone", encodeRedstoneColorMap(redstoneColors)); + resultObject.add("constant", encodeBlocksColorMap(blockRegistry, constantTintColors)); + + return resultObject; + } + + public static class BiomeTintColors { + final Map> grassColoursMap = new HashMap<>(); + final Map> foliageColoursMap = new HashMap<>(); + final Map> waterColourMap = new HashMap<>(); + } +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ClientPlayerInteractionManagerMixin.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ClientPlayerInteractionManagerMixin.java new file mode 100644 index 00000000..40e926af --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ClientPlayerInteractionManagerMixin.java @@ -0,0 +1,57 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import com.google.common.base.Preconditions; +import net.minecraft.block.BlockState; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerInteractionManager; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.World; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +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.CallbackInfoReturnable; + +@Mixin(ClientPlayerInteractionManager.class) +public abstract class ClientPlayerInteractionManagerMixin { + @Unique + private long blockBreakingStartTicks; + @Unique + private BlockState stateBeingBroken; + + @Inject(method = "attackBlock", at = @At(value = "FIELD", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;currentBreakingProgress:F", opcode = Opcodes.PUTFIELD)) + private void onBlockAttacked(BlockPos pos, Direction direction, CallbackInfoReturnable callbackInfo) { + World world = Preconditions.checkNotNull(MinecraftClient.getInstance().world); + this.blockBreakingStartTicks = world.getTime(); + } + + @Inject(method = "cancelBlockBreaking", at = @At("HEAD")) + private void onBlockBreakingCanceled(CallbackInfo callbackInfo) { + this.blockBreakingStartTicks = -1L; + } + + @Inject(method = "breakBlock", at = @At("HEAD")) + private void onBreakBlockHead(BlockPos pos, CallbackInfoReturnable callbackInfo) { + World world = Preconditions.checkNotNull(MinecraftClient.getInstance().world); + this.stateBeingBroken = world.getBlockState(pos); + } + + @Inject(method = "breakBlock", at = @At("RETURN")) + private void onBreakBlockReturn(BlockPos pos, CallbackInfoReturnable callbackInfo) { + World world = Preconditions.checkNotNull(MinecraftClient.getInstance().world); + if (callbackInfo.getReturnValue()) { + Preconditions.checkNotNull(stateBeingBroken); + if (blockBreakingStartTicks != -1L) { + long totalTicksElapsed = world.getTime() - this.blockBreakingStartTicks; + long totalTimeElapsed = totalTicksElapsed * 50L; + System.out.printf("Breaking block %s took %dms%n", stateBeingBroken.getBlock(), totalTimeElapsed); + } else { + System.out.printf("Breaking block %s took 0ms [INSTANT BREAK]%n", stateBeingBroken.getBlock()); + } + } + this.blockBreakingStartTicks = -1L; + } +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java new file mode 100644 index 00000000..0d297e7a --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.server.dedicated.EulaReader; +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; + +@Mixin(EulaReader.class) +public class EULAMixin { + @Inject(method = "isEulaAgreedTo()Z", at = @At("TAIL"), cancellable = true) + public void init(CallbackInfoReturnable cir) { + cir.setReturnValue(true); + } +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java new file mode 100644 index 00000000..d1ed130c --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java @@ -0,0 +1,17 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Block; +import net.minecraft.item.MiningToolItem; +import net.minecraft.tag.Tag; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(MiningToolItem.class) +public interface MiningToolItemAccessor { + + @Accessor + Tag getEffectiveBlocks(); + + @Accessor + float getMiningSpeed(); +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java new file mode 100644 index 00000000..f83555bb --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.generators.DataGenerators; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.MinecraftVersion; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; +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.nio.file.Path; + +@Mixin(MinecraftDedicatedServer.class) +public class ReadyMixin { + @Inject(method = "setupServer()Z", at = @At("TAIL")) + private void init(CallbackInfoReturnable cir) { + Main.LOGGER.info("Starting data generation!"); + String versionName = MinecraftVersion.GAME_VERSION.getName(); + Path serverRootDirectory = DGU.getCurrentlyRunningServer().getRunDirectory().toPath().toAbsolutePath(); + Path dataDumpDirectory = serverRootDirectory.resolve("minecraft-data").resolve(versionName); + DataGenerators.runDataGenerators(dataDumpDirectory); + Main.LOGGER.info("Done data generation!"); + DGU.getCurrentlyRunningServer().stop(false); + } +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java new file mode 100644 index 00000000..5945fa02 --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java @@ -0,0 +1,60 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Language; +import net.minecraft.world.World; + +public class DGU { + + private static final Language language = Language.getInstance(); + + @Environment(EnvType.CLIENT) + private static MinecraftServer getCurrentlyRunningServerClient() { + return MinecraftClient.getInstance().getServer(); + } + + @SuppressWarnings("deprecation") + private static MinecraftServer getCurrentlyRunningServerDedicated() { + return (MinecraftServer) FabricLoader.getInstance().getGameInstance(); + } + + public static MinecraftServer getCurrentlyRunningServer() { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return getCurrentlyRunningServerClient(); + } else if (environmentType == EnvType.SERVER) { + return getCurrentlyRunningServerDedicated(); + } + throw new UnsupportedOperationException(); + } + + @Environment(EnvType.CLIENT) + private static String translateTextClient(String translationKey) { + return I18n.translate(translationKey); + } + + private static String translateTextFallback(String translationKey) { + try { + return language.get(translationKey); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to translate: '" + translationKey + "'"); + } + + public static String translateText(String translationKey) { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return translateTextClient(translationKey); + } + return translateTextFallback(translationKey); + } + + public static World getWorld() { + return getCurrentlyRunningServer().getOverworld(); + } +} diff --git a/1.17/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java new file mode 100644 index 00000000..11540e9a --- /dev/null +++ b/1.17/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java @@ -0,0 +1,73 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.fluid.FluidState; +import net.minecraft.fluid.Fluids; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.BlockRenderView; +import net.minecraft.world.LightType; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.BiomeKeys; +import net.minecraft.world.chunk.light.LightingProvider; +import net.minecraft.world.level.ColorResolver; +import org.jetbrains.annotations.Nullable; + +public enum EmptyRenderBlockView implements BlockRenderView { + INSTANCE; + + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + return null; + } + + public BlockState getBlockState(BlockPos pos) { + return Blocks.AIR.getDefaultState(); + } + + public FluidState getFluidState(BlockPos pos) { + return Fluids.EMPTY.getDefaultState(); + } + + public int getBottomY() { + return 0; + } + + public int getHeight() { + return 0; + } + + + @Override + public float getBrightness(Direction direction, boolean shaded) { + return 0.0f; + } + + @Override + public LightingProvider getLightingProvider() { + return null; + } + + @Override + public int getColor(BlockPos pos, ColorResolver colorResolver) { + DynamicRegistryManager registryManager = DynamicRegistryManager.create(); + Registry biomeRegistry = registryManager.get(Registry.BIOME_KEY); + Biome plainsBiome = biomeRegistry.get(BiomeKeys.PLAINS); + + return colorResolver.getColor(plainsBiome, pos.getX(), pos.getY()); + } + + @Override + public int getLightLevel(LightType type, BlockPos pos) { + return type == LightType.SKY ? getMaxLightLevel() : 0; + } + + @Override + public int getBaseLightLevel(BlockPos pos, int ambientDarkness) { + return ambientDarkness; + } +} diff --git a/1.17/src/main/resources/fabric.mod.json b/1.17/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..951064b8 --- /dev/null +++ b/1.17/src/main/resources/fabric.mod.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "id": "minecraft-data-generator", + "version": "${version}", + "name": "Minecraft Data Generator", + "description": "", + "authors": [ + "Archengius", + "U9G" + ], + "contact": {}, + "license": "MIT", + "environment": "server", + "entrypoints": { + "main": [ + "dev.u9g.minecraftdatagenerator.Main" + ] + }, + "mixins": [ + "minecraft-data-generator.mixins.json" + ], + "depends": { + "fabricloader": ">=0.13.3", + "fabric": "*", + "minecraft": "1.17" + } +} diff --git a/1.17/src/main/resources/minecraft-data-generator.mixins.json b/1.17/src/main/resources/minecraft-data-generator.mixins.json new file mode 100644 index 00000000..0b916b11 --- /dev/null +++ b/1.17/src/main/resources/minecraft-data-generator.mixins.json @@ -0,0 +1,19 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "dev.u9g.minecraftdatagenerator.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "ClientPlayerInteractionManagerMixin", + "MiningToolItemAccessor", + "ReadyMixin" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "server": [ + "EULAMixin" + ] +} diff --git a/1.18/build.gradle b/1.18/build.gradle new file mode 100644 index 00000000..e85de392 --- /dev/null +++ b/1.18/build.gradle @@ -0,0 +1,31 @@ +plugins { + id 'fabric-loom' +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API. This is technically optional, but you probably want it anyway. + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" +} + +processResources { + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} diff --git a/1.18/gradle.properties b/1.18/gradle.properties new file mode 100644 index 00000000..a13d73ad --- /dev/null +++ b/1.18/gradle.properties @@ -0,0 +1,8 @@ +# Fabric Properties +# check these on https://modmuss50.me/fabric.html +minecraft_version=1.18 +yarn_mappings=1.18+build.1 +loader_version=0.14.4 +# Dependencies +# check this on https://modmuss50.me/fabric.html +fabric_version=0.44.0+1.18 diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/Main.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/Main.java new file mode 100644 index 00000000..f8d17372 --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/Main.java @@ -0,0 +1,14 @@ +package dev.u9g.minecraftdatagenerator; + +import net.fabricmc.api.ModInitializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Main implements ModInitializer { + public static final Logger LOGGER = LoggerFactory.getLogger("mc-data-gen-serv"); + + @Override + public void onInitialize() { + + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java new file mode 100644 index 00000000..76e948a1 --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java @@ -0,0 +1,124 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; + +public class BiomesDataGenerator implements IDataGenerator { + + private static String guessBiomeDimensionFromCategory(Biome biome) { + return switch (biome.getCategory()) { + case NETHER -> "nether"; + case THEEND -> "end"; + default -> "overworld"; + }; + } + + private static int getBiomeColorFor(String biomeName) { + return switch (biomeName) { + case "the_void" -> 0; + case "plains" -> 9286496; + case "sunflower_plains" -> 11918216; + case "snowy_plains" -> 16777215; + case "ice_spikes" -> 11853020; + case "desert" -> 16421912; + case "swamp" -> 522674; + case "forest" -> 353825; + case "flower_forest" -> 2985545; + case "birch_forest" -> 3175492; + case "dark_forest" -> 4215066; + case "old_growth_birch_forest" -> 5807212; + case "old_growth_pine_taiga" -> 5858897; + case "old_growth_spruce_taiga" -> 8490617; + case "taiga" -> 747097; + case "snowy_taiga" -> 3233098; + case "savanna" -> 12431967; + case "savanna_plateau" -> 10984804; + case "windswept_hills" -> 6316128; + case "windswept_gravelly_hills" -> 8947848; + case "windswept_forest" -> 2250012; + case "windswept_savanna" -> 15063687; + case "jungle" -> 5470985; + case "sparse_jungle" -> 6458135; + case "bamboo_jungle" -> 7769620; + case "badlands" -> 14238997; + case "eroded_badlands" -> 16739645; + case "wooded_badlands" -> 11573093; + case "meadow" -> 9217136; + case "grove" -> 14675173; + case "snowy_slopes" -> 14348785; + case "frozen_peaks" -> 15399931; + case "jagged_peaks" -> 14937325; + case "stony_peaks" -> 13750737; + case "river" -> 255; + case "frozen_river" -> 10526975; + case "beach" -> 16440917; + case "snowy_beach" -> 16445632; + case "stony_shore" -> 10658436; + case "warm_ocean" -> 172; + case "lukewarm_ocean" -> 144; + case "deep_lukewarm_ocean" -> 64; + case "ocean" -> 112; + case "deep_ocean" -> 48; + case "cold_ocean" -> 2105456; + case "deep_cold_ocean" -> 2105400; + case "frozen_ocean" -> 7368918; + case "deep_frozen_ocean" -> 4210832; + case "mushroom_fields" -> 16711935; + case "dripstone_caves" -> 12690831; + case "lush_caves" -> 14652980; + case "nether_wastes" -> 12532539; + case "warped_forest" -> 4821115; + case "crimson_forest" -> 14485512; + case "soul_sand_valley" -> 6174768; + case "basalt_deltas" -> 4208182; + case "the_end" -> 8421631; + case "end_highlands" -> 12828041; + case "end_midlands" -> 15464630; + case "small_end_islands" -> 42; + case "end_barrens" -> 9474162; + default -> throw new Error("Unexpected biome, with name: '" + biomeName + "'"); + }; + } + + public static JsonObject generateBiomeInfo(Registry registry, Biome biome) { + JsonObject biomeDesc = new JsonObject(); + Identifier registryKey = registry.getKey(biome).orElseThrow().getValue(); + String localizationKey = String.format("biome.%s.%s", registryKey.getNamespace(), registryKey.getPath()); + + biomeDesc.addProperty("id", registry.getRawId(biome)); + biomeDesc.addProperty("name", registryKey.getPath()); + + biomeDesc.addProperty("category", biome.getCategory().getName()); + biomeDesc.addProperty("temperature", biome.getTemperature()); + biomeDesc.addProperty("precipitation", biome.getPrecipitation().getName()); + //biomeDesc.addProperty("depth", biome.getDepth()); - Doesn't exist anymore in minecraft source + biomeDesc.addProperty("dimension", guessBiomeDimensionFromCategory(biome)); + biomeDesc.addProperty("displayName", DGU.translateText(localizationKey)); + biomeDesc.addProperty("color", getBiomeColorFor(registryKey.getPath())); + biomeDesc.addProperty("rainfall", biome.getDownfall()); + + return biomeDesc; + } + + @Override + public String getDataName() { + return "biomes"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray biomesArray = new JsonArray(); + DynamicRegistryManager registryManager = DynamicRegistryManager.create(); + Registry biomeRegistry = registryManager.get(Registry.BIOME_KEY); + + biomeRegistry.stream() + .map(biome -> generateBiomeInfo(biomeRegistry, biome)) + .forEach(biomesArray::add); + return biomesArray; + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java new file mode 100644 index 00000000..52c3181d --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java @@ -0,0 +1,111 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.EmptyBlockView; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class BlockCollisionShapesDataGenerator implements IDataGenerator { + + @Override + public String getDataName() { + return "blockCollisionShapes"; + } + + @Override + public JsonObject generateDataJson() { + Registry blockRegistry = Registry.BLOCK; + BlockShapesCache blockShapesCache = new BlockShapesCache(); + + blockRegistry.forEach(blockShapesCache::processBlock); + + JsonObject resultObject = new JsonObject(); + + resultObject.add("blocks", blockShapesCache.dumpBlockShapeIndices(blockRegistry)); + resultObject.add("shapes", blockShapesCache.dumpShapesObject()); + + return resultObject; + } + + private static class BlockShapesCache { + public final Map uniqueBlockShapes = new HashMap<>(); + public final Map> blockCollisionShapes = new HashMap<>(); + private int lastCollisionShapeId = 0; + + public void processBlock(Block block) { + List blockStates = block.getStateManager().getStates(); + List blockCollisionShapes = new ArrayList<>(); + + for (BlockState blockState : blockStates) { + VoxelShape blockShape = blockState.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + Integer blockShapeIndex = uniqueBlockShapes.get(blockShape); + + if (blockShapeIndex == null) { + blockShapeIndex = lastCollisionShapeId++; + uniqueBlockShapes.put(blockShape, blockShapeIndex); + } + blockCollisionShapes.add(blockShapeIndex); + } + + this.blockCollisionShapes.put(block, blockCollisionShapes); + } + + public JsonObject dumpBlockShapeIndices(Registry blockRegistry) { + JsonObject resultObject = new JsonObject(); + + for (var entry : blockCollisionShapes.entrySet()) { + List blockCollisions = entry.getValue(); + long distinctShapesCount = blockCollisions.stream().distinct().count(); + JsonElement blockCollision; + if (distinctShapesCount == 1L) { + blockCollision = new JsonPrimitive(blockCollisions.get(0)); + } else { + blockCollision = new JsonArray(); + for (int collisionId : blockCollisions) { + ((JsonArray) blockCollision).add(collisionId); + } + } + + Identifier registryKey = blockRegistry.getKey(entry.getKey()).orElseThrow().getValue(); + resultObject.add(registryKey.getPath(), blockCollision); + } + + return resultObject; + } + + public JsonObject dumpShapesObject() { + JsonObject shapesObject = new JsonObject(); + + for (var entry : uniqueBlockShapes.entrySet()) { + JsonArray boxesArray = new JsonArray(); + entry.getKey().forEachBox((x1, y1, z1, x2, y2, z2) -> { + JsonArray oneBoxJsonArray = new JsonArray(); + + oneBoxJsonArray.add(x1); + oneBoxJsonArray.add(y1); + oneBoxJsonArray.add(z1); + + oneBoxJsonArray.add(x2); + oneBoxJsonArray.add(y2); + oneBoxJsonArray.add(z2); + + boxesArray.add(oneBoxJsonArray); + }); + shapesObject.add(Integer.toString(entry.getValue()), boxesArray); + } + return shapesObject; + } + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java new file mode 100644 index 00000000..3c082458 --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java @@ -0,0 +1,190 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.base.CaseFormat; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.block.AirBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.loot.context.LootContext; +import net.minecraft.loot.context.LootContextParameters; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.state.property.EnumProperty; +import net.minecraft.state.property.IntProperty; +import net.minecraft.state.property.Property; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.EmptyBlockView; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +public class BlocksDataGenerator implements IDataGenerator { + + private static final Logger logger = LoggerFactory.getLogger(BlocksDataGenerator.class); + + private static List getItemsEffectiveForBlock(BlockState blockState) { + return Registry.ITEM.stream() + .filter(item -> item.getDefaultStack().isSuitableFor(blockState)) + .collect(Collectors.toList()); + } + + private static void populateDropsIfPossible(BlockState blockState, Item firstToolItem, List outDrops) { + MinecraftServer minecraftServer = DGU.getCurrentlyRunningServer(); + if (minecraftServer != null) { + //If we have local world context, we can actually evaluate loot tables and determine actual data + ServerWorld serverWorld = minecraftServer.getOverworld(); + LootContext.Builder lootContext = new LootContext.Builder(serverWorld) + .parameter(LootContextParameters.BLOCK_STATE, blockState) + .parameter(LootContextParameters.ORIGIN, Vec3d.ZERO) + .parameter(LootContextParameters.TOOL, firstToolItem.getDefaultStack()) + .random(0L); + outDrops.addAll(blockState.getDroppedStacks(lootContext)); + } else { + //If we're lacking world context to correctly determine drops, assume that default drop is ItemBlock stack in quantity of 1 + Item itemBlock = blockState.getBlock().asItem(); + if (itemBlock != Items.AIR) { + outDrops.add(itemBlock.getDefaultStack()); + } + } + } + + private static String getPropertyTypeName(Property property) { + //Explicitly handle default minecraft properties + if (property instanceof BooleanProperty) { + return "bool"; + } + if (property instanceof IntProperty) { + return "int"; + } + if (property instanceof EnumProperty) { + return "enum"; + } + + //Use simple class name as fallback, this code will give something like + //example_type for ExampleTypeProperty class name + String rawPropertyName = property.getClass().getSimpleName().replace("Property", ""); + return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, rawPropertyName); + } + + private static > JsonObject generateStateProperty(Property property) { + JsonObject propertyObject = new JsonObject(); + Collection propertyValues = property.getValues(); + + propertyObject.addProperty("name", property.getName()); + propertyObject.addProperty("type", getPropertyTypeName(property)); + propertyObject.addProperty("num_values", propertyValues.size()); + + //Do not add values for vanilla boolean properties, they are known by default + if (!(property instanceof BooleanProperty) && !(property instanceof IntProperty && property.name(propertyValues.iterator().next()).equals("0"))) { + JsonArray propertyValuesArray = new JsonArray(); + for (T propertyValue : propertyValues) { + propertyValuesArray.add(property.name(propertyValue)); + } + propertyObject.add("values", propertyValuesArray); + } + return propertyObject; + } + + private static String findMatchingBlockMaterial(BlockState blockState, List materials) { + List matchingMaterials = materials.stream() + .filter(material -> material.getPredicate().test(blockState)) + .collect(Collectors.toList()); + + if (matchingMaterials.size() > 1) { + var firstMaterial = matchingMaterials.get(0); + var otherMaterials = matchingMaterials.subList(1, matchingMaterials.size()); + + if (!otherMaterials.stream().allMatch(firstMaterial::includesMaterial)) { + logger.error("Block {} matches multiple materials: {}", blockState.getBlock(), matchingMaterials); + } + } + if (matchingMaterials.isEmpty()) { + return "default"; + } + return matchingMaterials.get(0).getMaterialName(); + } + + public static JsonObject generateBlock(Registry blockRegistry, List materials, Block block) { + JsonObject blockDesc = new JsonObject(); + + List blockStates = block.getStateManager().getStates(); + BlockState defaultState = block.getDefaultState(); + Identifier registryKey = blockRegistry.getKey(block).orElseThrow().getValue(); + String localizationKey = block.getTranslationKey(); + List effectiveTools = getItemsEffectiveForBlock(defaultState); + + blockDesc.addProperty("id", blockRegistry.getRawId(block)); + blockDesc.addProperty("displayName", DGU.translateText(localizationKey)); + blockDesc.addProperty("name", registryKey.getPath()); + blockDesc.addProperty("hardness", block.getHardness()); + blockDesc.addProperty("resistance", block.getBlastResistance()); + blockDesc.addProperty("minStateId", Block.getRawIdFromState(blockStates.get(0))); + blockDesc.addProperty("maxStateId", Block.getRawIdFromState(blockStates.get(blockStates.size() - 1))); + JsonArray stateProperties = new JsonArray(); + for (Property property : block.getStateManager().getProperties()) { + stateProperties.add(generateStateProperty(property)); + } + blockDesc.add("states", stateProperties); + // Let's not generate block drops... + // List actualBlockDrops = new ArrayList<>(); + // populateDropsIfPossible(defaultState, effectiveTools.isEmpty() ? Items.AIR : effectiveTools.get(0), actualBlockDrops); + + // for (ItemStack dropStack : actualBlockDrops) { + // dropsArray.add(Item.getRawId(dropStack.getItem())); + // } + JsonArray dropsArray = new JsonArray(); + blockDesc.add("drops", dropsArray); + blockDesc.addProperty("diggable", block.getHardness() != -1.0f && !(block instanceof AirBlock)); + blockDesc.addProperty("transparent", !defaultState.isOpaque()); + blockDesc.addProperty("filterLight", defaultState.getOpacity(EmptyBlockView.INSTANCE, BlockPos.ORIGIN)); + blockDesc.addProperty("emitLight", defaultState.getLuminance()); + VoxelShape blockCollisionShape = defaultState.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + blockDesc.addProperty("boundingBox", blockCollisionShape.isEmpty() ? "empty" : "block"); + blockDesc.addProperty("stackSize", block.asItem().getMaxCount()); + blockDesc.addProperty("material", findMatchingBlockMaterial(defaultState, materials)); + //Only add harvest tools if tool is required for harvesting this block + if (defaultState.isToolRequired()) { + JsonObject effectiveToolsObject = new JsonObject(); + for (Item effectiveItem : effectiveTools) { + effectiveToolsObject.addProperty(Integer.toString(Item.getRawId(effectiveItem)), true); + } + blockDesc.add("harvestTools", effectiveToolsObject); + } + blockDesc.addProperty("defaultState", Block.getRawIdFromState(defaultState)); +// JsonObject effTools = new JsonObject(); +// effectiveTools.forEach(item -> effTools.addProperty( +// String.valueOf(Registry.ITEM.getRawId(item)), // key +// item.getMiningSpeedMultiplier(item.getDefaultStack(), defaultState) // value +// )); +// blockDesc.add("effectiveTools", effTools); + return blockDesc; + } + + @Override + public String getDataName() { + return "blocks"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultBlocksArray = new JsonArray(); + Registry blockRegistry = Registry.BLOCK; + List availableMaterials = MaterialsDataGenerator.getGlobalMaterialInfo(); + + blockRegistry.forEach(block -> resultBlocksArray.add(generateBlock(blockRegistry, availableMaterials, block))); + return resultBlocksArray; + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java new file mode 100644 index 00000000..b771fa61 --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java @@ -0,0 +1,74 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import dev.u9g.minecraftdatagenerator.util.DGU; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.List; + +public class DataGenerators { + + private static final Logger logger = LoggerFactory.getLogger(DataGenerators.class); + private static final List GENERATORS = new ArrayList<>(); + + static { + register(new BiomesDataGenerator()); + register(new BlockCollisionShapesDataGenerator()); + register(new BlocksDataGenerator()); + register(new EffectsDataGenerator()); + register(new EnchantmentsDataGenerator()); + register(new EntitiesDataGenerator()); + register(new FoodsDataGenerator()); + register(new ItemsDataGenerator()); + register(new ParticlesDataGenerator()); + register(new TintsDataGenerator()); + register(new MaterialsDataGenerator()); +// register(new RecipeDataGenerator()); - On hold until mcdata supports multiple materials for a recipe + register(new LanguageDataGenerator()); + register(new InstrumentsDataGenerator()); + } + + public static void register(IDataGenerator generator) { + GENERATORS.add(generator); + } + + public static boolean runDataGenerators(Path outputDirectory) { + try { + Files.createDirectories(outputDirectory); + } catch (IOException exception) { + logger.error("Failed to create data generator output directory at {}", outputDirectory, exception); + return false; + } + + int generatorsFailed = 0; + logger.info("Running minecraft data generators, output at {}", outputDirectory); + + for (IDataGenerator dataGenerator : GENERATORS) { + logger.info("Running generator {}", dataGenerator.getDataName()); + try { + String outputFileName = String.format("%s.json", dataGenerator.getDataName()); + JsonElement outputElement = dataGenerator.generateDataJson(); + Path outputFilePath = outputDirectory.resolve(outputFileName); + + try (Writer writer = Files.newBufferedWriter(outputFilePath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + DGU.JSON_ELEMENT.write(DGU.GSON.newJsonWriter(writer), outputElement); + } + logger.info("Generator: {} -> {}", dataGenerator.getDataName(), outputFileName); + + } catch (Throwable exception) { + logger.error("Failed to run data generator {}", dataGenerator.getDataName(), exception); + generatorsFailed++; + } + } + + logger.info("Finishing running data generators"); + return generatorsFailed == 0; + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java new file mode 100644 index 00000000..1c9849e6 --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java @@ -0,0 +1,46 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.stream.Collectors; + +public class EffectsDataGenerator implements IDataGenerator { + + public static JsonObject generateEffect(Registry registry, StatusEffect statusEffect) { + JsonObject effectDesc = new JsonObject(); + Identifier registryKey = registry.getKey(statusEffect).orElseThrow().getValue(); + + effectDesc.addProperty("id", registry.getRawId(statusEffect)); + if (statusEffect == StatusEffects.UNLUCK) { + effectDesc.addProperty("name", "BadLuck"); + effectDesc.addProperty("displayName", "Bad Luck"); + } else { + effectDesc.addProperty("name", Arrays.stream(registryKey.getPath().split("_")).map(StringUtils::capitalize).collect(Collectors.joining())); + effectDesc.addProperty("displayName", DGU.translateText(statusEffect.getTranslationKey())); + } + + effectDesc.addProperty("type", statusEffect.isBeneficial() ? "good" : "bad"); + return effectDesc; + } + + @Override + public String getDataName() { + return "effects"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry statusEffectRegistry = Registry.STATUS_EFFECT; + statusEffectRegistry.forEach(effect -> resultsArray.add(generateEffect(statusEffectRegistry, effect))); + return resultsArray; + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java new file mode 100644 index 00000000..a71961c9 --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java @@ -0,0 +1,108 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +public class EnchantmentsDataGenerator implements IDataGenerator { + + private static final ImmutableMap ENCHANTMENT_TARGET_NAMES = ImmutableMap.builder() + .put(EnchantmentTarget.ARMOR, "armor") + .put(EnchantmentTarget.ARMOR_FEET, "armor_feet") + .put(EnchantmentTarget.ARMOR_LEGS, "armor_legs") + .put(EnchantmentTarget.ARMOR_CHEST, "armor_chest") + .put(EnchantmentTarget.ARMOR_HEAD, "armor_head") + .put(EnchantmentTarget.WEAPON, "weapon") + .put(EnchantmentTarget.DIGGER, "digger") + .put(EnchantmentTarget.FISHING_ROD, "fishing_rod") + .put(EnchantmentTarget.TRIDENT, "trident") + .put(EnchantmentTarget.BREAKABLE, "breakable") + .put(EnchantmentTarget.BOW, "bow") + .put(EnchantmentTarget.WEARABLE, "wearable") + .put(EnchantmentTarget.CROSSBOW, "crossbow") + .put(EnchantmentTarget.VANISHABLE, "vanishable") + .build(); + + public static String getEnchantmentTargetName(EnchantmentTarget target) { + return ENCHANTMENT_TARGET_NAMES.getOrDefault(target, target.name().toLowerCase(Locale.ROOT)); + } + + //Equation enchantment costs follow is a * level + b, so we can easily retrieve a and b by passing zero level + private static JsonObject generateEnchantmentMinPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMinPower(0); + int a = enchantment.getMinPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + private static JsonObject generateEnchantmentMaxPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMaxPower(0); + int a = enchantment.getMaxPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + public static JsonObject generateEnchantment(Registry registry, Enchantment enchantment) { + JsonObject enchantmentDesc = new JsonObject(); + Identifier registryKey = registry.getKey(enchantment).orElseThrow().getValue(); + + enchantmentDesc.addProperty("id", registry.getRawId(enchantment)); + enchantmentDesc.addProperty("name", registryKey.getPath()); + enchantmentDesc.addProperty("displayName", DGU.translateText(enchantment.getTranslationKey())); + + enchantmentDesc.addProperty("maxLevel", enchantment.getMaxLevel()); + enchantmentDesc.add("minCost", generateEnchantmentMinPowerCoefficients(enchantment)); + enchantmentDesc.add("maxCost", generateEnchantmentMaxPowerCoefficients(enchantment)); + + enchantmentDesc.addProperty("treasureOnly", enchantment.isTreasure()); + enchantmentDesc.addProperty("curse", enchantment.isCursed()); + + List incompatibleEnchantments = registry.stream() + .filter(other -> !enchantment.canCombine(other)) + .filter(other -> other != enchantment) + .collect(Collectors.toList()); + + JsonArray excludes = new JsonArray(); + for (Enchantment excludedEnchantment : incompatibleEnchantments) { + Identifier otherKey = registry.getKey(excludedEnchantment).orElseThrow().getValue(); + excludes.add(otherKey.getPath()); + } + enchantmentDesc.add("exclude", excludes); + + enchantmentDesc.addProperty("category", getEnchantmentTargetName(enchantment.type)); + enchantmentDesc.addProperty("weight", enchantment.getRarity().getWeight()); + enchantmentDesc.addProperty("tradeable", enchantment.isAvailableForEnchantedBookOffer()); + enchantmentDesc.addProperty("discoverable", enchantment.isAvailableForRandomSelection()); + + return enchantmentDesc; + } + + @Override + public String getDataName() { + return "enchantments"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry enchantmentRegistry = Registry.ENCHANTMENT; + enchantmentRegistry.stream() + .forEach(enchantment -> resultsArray.add(generateEnchantment(enchantmentRegistry, enchantment))); + return resultsArray; + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java new file mode 100644 index 00000000..8d94a6b7 --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java @@ -0,0 +1,115 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.mob.AmbientEntity; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.mob.WaterCreatureEntity; +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.passive.PassiveEntity; +import net.minecraft.entity.projectile.ProjectileEntity; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +public class EntitiesDataGenerator implements IDataGenerator { + + public static JsonObject generateEntity(Registry> entityRegistry, EntityType entityType) { + JsonObject entityDesc = new JsonObject(); + Identifier registryKey = entityRegistry.getKey(entityType).orElseThrow().getValue(); + int entityRawId = entityRegistry.getRawId(entityType); + + entityDesc.addProperty("id", entityRawId); + entityDesc.addProperty("internalId", entityRawId); + entityDesc.addProperty("name", registryKey.getPath()); + + entityDesc.addProperty("displayName", DGU.translateText(entityType.getTranslationKey())); + entityDesc.addProperty("width", entityType.getDimensions().width); + entityDesc.addProperty("height", entityType.getDimensions().height); + + String entityTypeString = "UNKNOWN"; + MinecraftServer minecraftServer = DGU.getCurrentlyRunningServer(); + + if (minecraftServer != null) { + Entity entityObject = entityType.create(minecraftServer.getOverworld()); + entityTypeString = entityObject != null ? getEntityTypeForClass(entityObject.getClass()) : "player"; + } + entityDesc.addProperty("type", entityTypeString); + entityDesc.addProperty("category", getCategoryFrom(entityType)); + + return entityDesc; + } + + private static String getCategoryFrom(EntityType entityType) { + if (entityType == EntityType.PLAYER) return "UNKNOWN"; // fail early for player entities + Entity entity = EntityType.createInstanceFromId(Registry.ENTITY_TYPE.getRawId(entityType), DGU.getWorld()); + if (entity == null) + throw new Error("Entity was null after trying to create a: " + DGU.translateText(entityType.getTranslationKey())); + entity.discard(); + return switch (entity.getClass().getPackageName()) { + case "net.minecraft.entity.decoration", "net.minecraft.entity.decoration.painting" -> "Immobile"; + case "net.minecraft.entity.boss", "net.minecraft.entity.mob", "net.minecraft.entity.boss.dragon" -> + "Hostile mobs"; + case "net.minecraft.entity.projectile", "net.minecraft.entity.projectile.thrown" -> "Projectiles"; + case "net.minecraft.entity.passive" -> "Passive mobs"; + case "net.minecraft.entity.vehicle" -> "Vehicles"; + case "net.minecraft.entity" -> "UNKNOWN"; + default -> throw new Error("Unexpected entity type: " + entity.getClass().getPackageName()); + }; + } + + //Honestly, both "type" and "category" fields in the schema and examples do not contain any useful information + //Since category is optional, I will just leave it out, and for type I will assume general entity classification + //by the Entity class hierarchy (which has some weirdness too by the way) + private static String getEntityTypeForClass(Class entityClass) { + //Top-level classifications + if (WaterCreatureEntity.class.isAssignableFrom(entityClass)) { + return "water_creature"; + } + if (AnimalEntity.class.isAssignableFrom(entityClass)) { + return "animal"; + } + if (HostileEntity.class.isAssignableFrom(entityClass)) { + return "hostile"; + } + if (AmbientEntity.class.isAssignableFrom(entityClass)) { + return "ambient"; + } + + //Second level classifications. PathAwareEntity is not included because it + //doesn't really make much sense to categorize by it + if (PassiveEntity.class.isAssignableFrom(entityClass)) { + return "passive"; + } + if (MobEntity.class.isAssignableFrom(entityClass)) { + return "mob"; + } + + //Other classifications only include living entities and projectiles. everything else is categorized as other + if (LivingEntity.class.isAssignableFrom(entityClass)) { + return "living"; + } + if (ProjectileEntity.class.isAssignableFrom(entityClass)) { + return "projectile"; + } + return "other"; + } + + @Override + public String getDataName() { + return "entities"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry> entityTypeRegistry = Registry.ENTITY_TYPE; + entityTypeRegistry.forEach(entity -> resultArray.add(generateEntity(entityTypeRegistry, entity))); + return resultArray; + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java new file mode 100644 index 00000000..45a0b35f --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java @@ -0,0 +1,51 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.item.FoodComponent; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Objects; + +public class FoodsDataGenerator implements IDataGenerator { + + public static JsonObject generateFoodDescriptor(Registry registry, Item foodItem) { + JsonObject foodDesc = new JsonObject(); + Identifier registryKey = registry.getKey(foodItem).orElseThrow().getValue(); + + foodDesc.addProperty("id", registry.getRawId(foodItem)); + foodDesc.addProperty("name", registryKey.getPath()); + + foodDesc.addProperty("stackSize", foodItem.getMaxCount()); + foodDesc.addProperty("displayName", DGU.translateText(foodItem.getTranslationKey())); + + FoodComponent foodComponent = Objects.requireNonNull(foodItem.getFoodComponent()); + float foodPoints = foodComponent.getHunger(); + float saturationRatio = foodComponent.getSaturationModifier() * 2.0F; + float saturation = foodPoints * saturationRatio; + + foodDesc.addProperty("foodPoints", foodPoints); + foodDesc.addProperty("saturation", saturation); + + foodDesc.addProperty("effectiveQuality", foodPoints + saturation); + foodDesc.addProperty("saturationRatio", saturationRatio); + return foodDesc; + } + + @Override + public String getDataName() { + return "foods"; + } + + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry itemRegistry = Registry.ITEM; + itemRegistry.stream() + .filter(Item::isFood) + .forEach(food -> resultsArray.add(generateFoodDescriptor(itemRegistry, food))); + return resultsArray; + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java new file mode 100644 index 00000000..81fe4e20 --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; + +public interface IDataGenerator { + + String getDataName(); + + JsonElement generateDataJson(); +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java new file mode 100644 index 00000000..e7b9b54f --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java @@ -0,0 +1,25 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.minecraft.block.enums.Instrument; + +public class InstrumentsDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "instruments"; + } + + @Override + public JsonElement generateDataJson() { + JsonArray array = new JsonArray(); + for (Instrument instrument : Instrument.values()) { + JsonObject object = new JsonObject(); + object.addProperty("id", instrument.ordinal()); + object.addProperty("name", instrument.asString()); + array.add(object); + } + return array; + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java new file mode 100644 index 00000000..2f66e5f4 --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java @@ -0,0 +1,80 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class ItemsDataGenerator implements IDataGenerator { + + private static List calculateItemsToRepairWith(Registry itemRegistry, Item sourceItem) { + ItemStack sourceItemStack = sourceItem.getDefaultStack(); + return itemRegistry.stream() + .filter(otherItem -> sourceItem.canRepair(sourceItemStack, otherItem.getDefaultStack())) + .collect(Collectors.toList()); + } + + private static List getApplicableEnchantmentTargets(Item sourceItem) { + return Arrays.stream(EnchantmentTarget.values()) + .filter(target -> target.isAcceptableItem(sourceItem)) + .collect(Collectors.toList()); + } + + public static JsonObject generateItem(Registry itemRegistry, Item item) { + JsonObject itemDesc = new JsonObject(); + Identifier registryKey = itemRegistry.getKey(item).orElseThrow().getValue(); + + itemDesc.addProperty("id", itemRegistry.getRawId(item)); + itemDesc.addProperty("displayName", DGU.translateText(item.getTranslationKey())); + itemDesc.addProperty("name", registryKey.getPath()); + + itemDesc.addProperty("stackSize", item.getMaxCount()); + + List enchantmentTargets = getApplicableEnchantmentTargets(item); + + JsonArray enchantCategoriesArray = new JsonArray(); + for (EnchantmentTarget target : enchantmentTargets) { + enchantCategoriesArray.add(EnchantmentsDataGenerator.getEnchantmentTargetName(target)); + } + + if (item.isDamageable()) itemDesc.addProperty("maxDurability", item.getMaxDamage()); + if (enchantCategoriesArray.size() > 0) { + itemDesc.add("enchantCategories", enchantCategoriesArray); + } + + if (item.isDamageable()) { + List repairWithItems = calculateItemsToRepairWith(itemRegistry, item); + + JsonArray fixedWithArray = new JsonArray(); + for (Item repairWithItem : repairWithItems) { + Identifier repairWithName = itemRegistry.getKey(repairWithItem).orElseThrow().getValue(); + fixedWithArray.add(repairWithName.getPath()); + } + if (fixedWithArray.size() > 0) { + itemDesc.add("repairWith", fixedWithArray); + } + } + return itemDesc; + } + + @Override + public String getDataName() { + return "items"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry itemRegistry = Registry.ITEM; + itemRegistry.stream().forEach(item -> resultArray.add(generateItem(itemRegistry, item))); + return resultArray; + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java new file mode 100644 index 00000000..7a3c6ced --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +public class LanguageDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "language"; + } + + @Override + public JsonElement generateDataJson() { + try { + InputStream inputStream = Objects.requireNonNull(this.getClass().getResourceAsStream("/assets/minecraft/lang/en_us.json")); + return new Gson().fromJson(new InputStreamReader(inputStream, StandardCharsets.UTF_8), JsonObject.class); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to generate language file"); + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java new file mode 100644 index 00000000..3b96085d --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java @@ -0,0 +1,198 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.MiningToolItemAccessor; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.Material; +import net.minecraft.item.Item; +import net.minecraft.item.Items; +import net.minecraft.item.MiningToolItem; +import net.minecraft.item.SwordItem; +import net.minecraft.tag.BlockTags; +import net.minecraft.tag.Tag; +import net.minecraft.util.registry.Registry; + +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +//TODO entire idea of linking materials to tool speeds is obsolete and just wrong now, +//TODO but we kinda have to support it to let old code work for computing digging times, +//TODO so for now we will handle materials as "virtual" ones based on which tools can break blocks +public class MaterialsDataGenerator implements IDataGenerator { + + private static final List> COMPOSITE_MATERIALS = ImmutableList.>builder() + .add(ImmutableList.of("plant", makeMaterialNameForTag(BlockTags.AXE_MINEABLE))) + .add(ImmutableList.of("gourd", makeMaterialNameForTag(BlockTags.AXE_MINEABLE))) + .add(ImmutableList.of(makeMaterialNameForTag(BlockTags.LEAVES), makeMaterialNameForTag(BlockTags.HOE_MINEABLE))) + .add(ImmutableList.of(makeMaterialNameForTag(BlockTags.LEAVES), makeMaterialNameForTag(BlockTags.AXE_MINEABLE), makeMaterialNameForTag(BlockTags.HOE_MINEABLE))) + .add(ImmutableList.of("vine_or_glow_lichen", "plant", makeMaterialNameForTag(BlockTags.AXE_MINEABLE) + )).build(); + + private static String makeMaterialNameForTag(Tag tag) { + Tag.Identified identifiedTag = (Tag.Identified) tag; + return identifiedTag.getId().getPath(); + } + + private static void createCompositeMaterialInfo(List allMaterials, List combinedMaterials) { + String compositeMaterialName = Joiner.on(';').join(combinedMaterials); + + List mappedMaterials = combinedMaterials.stream() + .map(otherName -> allMaterials.stream() + .filter(other -> other.getMaterialName().equals(otherName)) + .findFirst().orElseThrow(() -> new RuntimeException("Material not found with name " + otherName))) + .collect(Collectors.toList()); + + Predicate compositePredicate = blockState -> + mappedMaterials.stream().allMatch(it -> it.getPredicate().test(blockState)); + + MaterialInfo materialInfo = new MaterialInfo(compositeMaterialName, compositePredicate).includes(mappedMaterials); + allMaterials.add(0, materialInfo); + } + + private static void createCompositeMaterial(Map> allMaterials, List combinedMaterials) { + String compositeMaterialName = Joiner.on(';').join(combinedMaterials); + + Map resultingToolSpeeds = new HashMap<>(); + combinedMaterials.stream() + .map(allMaterials::get) + .forEach(resultingToolSpeeds::putAll); + allMaterials.put(compositeMaterialName, resultingToolSpeeds); + } + + public static List getGlobalMaterialInfo() { + ArrayList resultList = new ArrayList<>(); + + resultList.add(new MaterialInfo("vine_or_glow_lichen", blockState -> blockState.isOf(Blocks.VINE) || blockState.isOf(Blocks.GLOW_LICHEN))); + resultList.add(new MaterialInfo("coweb", blockState -> blockState.isOf(Blocks.COBWEB))); + + resultList.add(new MaterialInfo("leaves", blockState -> blockState.isIn(BlockTags.LEAVES))); + resultList.add(new MaterialInfo("wool", blockState -> blockState.isIn(BlockTags.WOOL))); + + resultList.add(new MaterialInfo("gourd", blockState -> blockState.getMaterial() == Material.GOURD)); + resultList.add(new MaterialInfo("plant", blockState -> blockState.getMaterial() == Material.PLANT || blockState.getMaterial() == Material.REPLACEABLE_PLANT)); + + HashSet uniqueMaterialNames = new HashSet<>(); + + Registry itemRegistry = Registry.ITEM; + itemRegistry.forEach(item -> { + if (item instanceof MiningToolItem toolItem) { + Tag effectiveBlocks = ((MiningToolItemAccessor) toolItem).getEffectiveBlocks(); + String materialName = makeMaterialNameForTag(effectiveBlocks); + + if (!uniqueMaterialNames.contains(materialName)) { + uniqueMaterialNames.add(materialName); + resultList.add(new MaterialInfo(materialName, blockState -> blockState.isIn(effectiveBlocks))); + } + } + }); + + COMPOSITE_MATERIALS.forEach(values -> createCompositeMaterialInfo(resultList, values)); + return resultList; + } + + @Override + public String getDataName() { + return "materials"; + } + + @Override + public JsonElement generateDataJson() { + Registry itemRegistry = Registry.ITEM; + + Map> materialMiningSpeeds = new HashMap<>(); + materialMiningSpeeds.put("default", ImmutableMap.of()); + + //Special materials used for shears and swords special mining speed logic + Map leavesMaterialSpeeds = new HashMap<>(); + Map cowebMaterialSpeeds = new HashMap<>(); + Map plantMaterialSpeeds = new HashMap<>(); + Map gourdMaterialSpeeds = new HashMap<>(); + + materialMiningSpeeds.put(makeMaterialNameForTag(BlockTags.LEAVES), leavesMaterialSpeeds); + materialMiningSpeeds.put("coweb", cowebMaterialSpeeds); + materialMiningSpeeds.put("plant", plantMaterialSpeeds); + materialMiningSpeeds.put("gourd", gourdMaterialSpeeds); + + //Shears need special handling because they do not follow normal rules like tools + leavesMaterialSpeeds.put(Items.SHEARS, 15.0f); + cowebMaterialSpeeds.put(Items.SHEARS, 15.0f); + materialMiningSpeeds.put("vine_or_glow_lichen", ImmutableMap.of(Items.SHEARS, 2.0f)); + materialMiningSpeeds.put("wool", ImmutableMap.of(Items.SHEARS, 5.0f)); + + itemRegistry.forEach(item -> { + //Tools are handled rather easily and do not require anything else + if (item instanceof MiningToolItem toolItem) { + Tag effectiveBlocks = ((MiningToolItemAccessor) toolItem).getEffectiveBlocks(); + String materialName = makeMaterialNameForTag(effectiveBlocks); + + Map materialSpeeds = materialMiningSpeeds.computeIfAbsent(materialName, k -> new HashMap<>()); + float miningSpeed = ((MiningToolItemAccessor) toolItem).getMiningSpeed(); + materialSpeeds.put(item, miningSpeed); + } + + //Swords require special treatment + if (item instanceof SwordItem) { + cowebMaterialSpeeds.put(item, 15.0f); + plantMaterialSpeeds.put(item, 1.5f); + leavesMaterialSpeeds.put(item, 1.5f); + gourdMaterialSpeeds.put(item, 1.5f); + } + }); + + COMPOSITE_MATERIALS.forEach(values -> createCompositeMaterial(materialMiningSpeeds, values)); + + JsonObject resultObject = new JsonObject(); + + for (var entry : materialMiningSpeeds.entrySet()) { + JsonObject toolSpeedsObject = new JsonObject(); + + for (var toolEntry : entry.getValue().entrySet()) { + int rawItemId = itemRegistry.getRawId(toolEntry.getKey()); + toolSpeedsObject.addProperty(Integer.toString(rawItemId), toolEntry.getValue()); + } + resultObject.add(entry.getKey(), toolSpeedsObject); + } + + return resultObject; + } + + public static class MaterialInfo { + private final String materialName; + private final Predicate predicate; + private final List includedMaterials = new ArrayList<>(); + + public MaterialInfo(String materialName, Predicate predicate) { + this.materialName = materialName; + this.predicate = predicate; + } + + protected MaterialInfo includes(List otherMaterials) { + this.includedMaterials.addAll(otherMaterials); + return this; + } + + public String getMaterialName() { + return materialName; + } + + public Predicate getPredicate() { + return predicate; + } + + public boolean includesMaterial(MaterialInfo materialInfo) { + return includedMaterials.contains(materialInfo); + } + + @Override + public String toString() { + return materialName; + } + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java new file mode 100644 index 00000000..5a6b1081 --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java @@ -0,0 +1,32 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.minecraft.particle.ParticleType; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +public class ParticlesDataGenerator implements IDataGenerator { + + public static JsonObject generateParticleType(Registry> registry, ParticleType particleType) { + JsonObject effectDesc = new JsonObject(); + Identifier registryKey = registry.getKey(particleType).orElseThrow().getValue(); + + effectDesc.addProperty("id", registry.getRawId(particleType)); + effectDesc.addProperty("name", registryKey.getPath()); + return effectDesc; + } + + @Override + public String getDataName() { + return "particles"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry> particleTypeRegistry = Registry.PARTICLE_TYPE; + particleTypeRegistry.forEach(particleType -> resultsArray.add(generateParticleType(particleTypeRegistry, particleType))); + return resultsArray; + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java new file mode 100644 index 00000000..389bcc4e --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java @@ -0,0 +1,132 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.item.Item; +import net.minecraft.recipe.Ingredient; +import net.minecraft.recipe.Recipe; +import net.minecraft.recipe.ShapedRecipe; +import net.minecraft.recipe.ShapelessRecipe; +import net.minecraft.util.registry.Registry; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class RecipeDataGenerator implements IDataGenerator { + + private static int getRawIdFor(Item item) { + return Registry.ITEM.getRawId(item); + } + + @Override + public String getDataName() { + return "recipes"; + } + + @Override + public JsonElement generateDataJson() { + JsonObject finalObj = new JsonObject(); + Multimap recipes = ArrayListMultimap.create(); + for (Recipe recipe : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) { + if (recipe instanceof ShapedRecipe sr) { + var ingredients = sr.getIngredients(); + List ingr = new ArrayList<>(); + for (int i = 0; i < 9; i++) { + if (i >= ingredients.size()) { + ingr.add(-1); + continue; + } + var stacks = ingredients.get(i); + var matching = stacks.getMatchingStacks(); + if (matching.length == 0) { + ingr.add(-1); + } else { + ingr.add(getRawIdFor(matching[0].getItem())); + } + } + Lists.reverse(ingr); + + JsonArray inShape = new JsonArray(); + + var iter = ingr.iterator(); + for (int y = 0; y < 3; y++) { + var jsonRow = new JsonArray(); + int one = iter.next(); + int two = iter.next(); + int three = iter.next(); + if (y > 0 && one == -1 && two == -1 && three == -1) continue; + jsonRow.add(one); + jsonRow.add(two); + jsonRow.add(three); + inShape.add(jsonRow); + } + + JsonObject finalRecipe = new JsonObject(); + finalRecipe.add("inShape", inShape); + + var resultObject = new JsonObject(); + resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); + resultObject.addProperty("count", sr.getOutput().getCount()); + finalRecipe.add("result", resultObject); + + String id = ((Integer) getRawIdFor(sr.getOutput().getItem())).toString(); + + if (!finalObj.has(id)) { + finalObj.add(id, new JsonArray()); + } + finalObj.get(id).getAsJsonArray().add(finalRecipe); +// var input = new JsonArray(); +// var ingredients = sr.getIngredients().stream().toList(); +// for (int y = 0; y < sr.getHeight(); y++) { +// var arr = new JsonArray(); +// for (int x = 0; x < sr.getWidth(); x++) { +// if ((y*3)+x >= ingredients.size()) { +// arr.add(JsonNull.INSTANCE); +// continue; +// } +// var ingredient = ingredients.get((y*3)+x).getMatchingStacks(); // FIXME: fix when there are more than one matching stack +// if (ingredient.length == 0) { +// arr.add(JsonNull.INSTANCE); +// } else { +// arr.add(getRawIdFor(ingredient[0].getItem())); +// } +// } +// input.add(arr); +// } +// var rootRecipeObject = new JsonObject(); +// rootRecipeObject.add("inShape", input); +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +// resultObject.addProperty("count", sr.getOutput().getCount()); +// rootRecipeObject.add("result", resultObject); +// recipes.put(getRawIdFor(sr.getOutput().getItem()), rootRecipeObject); + } else if (recipe instanceof ShapelessRecipe sl) { + var ingredients = new JsonArray(); + for (Ingredient ingredient : sl.getIngredients()) { + if (ingredient.isEmpty()) continue; + ingredients.add(getRawIdFor(ingredient.getMatchingStacks()[0].getItem())); + } + var rootRecipeObject = new JsonObject(); + rootRecipeObject.add("ingredients", ingredients); + var resultObject = new JsonObject(); + resultObject.addProperty("id", getRawIdFor(sl.getOutput().getItem())); + resultObject.addProperty("count", sl.getOutput().getCount()); + rootRecipeObject.add("result", resultObject); + recipes.put(getRawIdFor(sl.getOutput().getItem()), rootRecipeObject); + } + } + recipes.forEach((a, b) -> { + if (!finalObj.has(a.toString())) { + finalObj.add(a.toString(), new JsonArray()); + } + finalObj.get(a.toString()).getAsJsonArray().add(b); + }); + return finalObj; + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java new file mode 100644 index 00000000..3ceef91b --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java @@ -0,0 +1,168 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.EmptyRenderBlockView; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.block.RedstoneWireBlock; +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.client.color.world.FoliageColors; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; + +import java.util.*; + +public class TintsDataGenerator implements IDataGenerator { + + public static BiomeTintColors generateBiomeTintColors(Registry biomeRegistry) { + BiomeTintColors colors = new BiomeTintColors(); + + biomeRegistry.forEach(biome -> { + int biomeGrassColor = biome.getGrassColorAt(0.0, 0.0); + int biomeFoliageColor = biome.getFoliageColor(); + int biomeWaterColor = biome.getWaterColor(); + + colors.grassColoursMap.computeIfAbsent(biomeGrassColor, k -> new ArrayList<>()).add(biome); + colors.foliageColoursMap.computeIfAbsent(biomeFoliageColor, k -> new ArrayList<>()).add(biome); + colors.waterColourMap.computeIfAbsent(biomeWaterColor, k -> new ArrayList<>()).add(biome); + }); + return colors; + } + + public static Map generateRedstoneTintColors() { + Map resultColors = new HashMap<>(); + + for (int redstoneLevel : RedstoneWireBlock.POWER.getValues()) { + int color = RedstoneWireBlock.getWireColor(redstoneLevel); + resultColors.put(redstoneLevel, color); + } + return resultColors; + } + + @Environment(EnvType.CLIENT) + private static int getBlockColor(Block block, BlockColors blockColors) { + return blockColors.getColor(block.getDefaultState(), EmptyRenderBlockView.INSTANCE, BlockPos.ORIGIN, 0xFFFFFF); + } + + @Environment(EnvType.CLIENT) + public static Map generateConstantTintColors() { + Map resultColors = new HashMap<>(); + BlockColors blockColors = BlockColors.create(); + + resultColors.put(Blocks.BIRCH_LEAVES, FoliageColors.getBirchColor()); + resultColors.put(Blocks.SPRUCE_LEAVES, FoliageColors.getSpruceColor()); + + resultColors.put(Blocks.LILY_PAD, getBlockColor(Blocks.LILY_PAD, blockColors)); + resultColors.put(Blocks.ATTACHED_MELON_STEM, getBlockColor(Blocks.ATTACHED_MELON_STEM, blockColors)); + resultColors.put(Blocks.ATTACHED_PUMPKIN_STEM, getBlockColor(Blocks.ATTACHED_PUMPKIN_STEM, blockColors)); + + //not really constant, depend on the block age, but kinda have to be handled since textures are literally white without them + resultColors.put(Blocks.MELON_STEM, getBlockColor(Blocks.MELON_STEM, blockColors)); + resultColors.put(Blocks.PUMPKIN_STEM, getBlockColor(Blocks.PUMPKIN_STEM, blockColors)); + + return resultColors; + } + + private static JsonObject encodeBiomeColorMap(Registry biomeRegistry, Map> colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + for (Biome biome : entry.getValue()) { + Identifier registryKey = biomeRegistry.getKey(biome).orElseThrow().getValue(); + keysArray.add(registryKey.getPath()); + } + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getKey()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeRedstoneColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + keysArray.add(entry.getKey()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeBlocksColorMap(Registry blockRegistry, Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + Identifier registryKey = blockRegistry.getKey(entry.getKey()).orElseThrow().getValue(); + keysArray.add(registryKey.getPath()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + @Override + public String getDataName() { + return "tints"; + } + + @Override + public JsonObject generateDataJson() { + DynamicRegistryManager registryManager = DynamicRegistryManager.create(); + Registry biomeRegistry = registryManager.get(Registry.BIOME_KEY); + Registry blockRegistry = registryManager.get(Registry.BLOCK_KEY); + + BiomeTintColors biomeTintColors = generateBiomeTintColors(biomeRegistry); + Map redstoneColors = generateRedstoneTintColors(); + Map constantTintColors = Collections.emptyMap(); + + EnvType currentEnvironment = FabricLoader.getInstance().getEnvironmentType(); + if (currentEnvironment == EnvType.CLIENT) { + constantTintColors = generateConstantTintColors(); + } + + JsonObject resultObject = new JsonObject(); + + resultObject.add("grass", encodeBiomeColorMap(biomeRegistry, biomeTintColors.grassColoursMap)); + resultObject.add("foliage", encodeBiomeColorMap(biomeRegistry, biomeTintColors.foliageColoursMap)); + resultObject.add("water", encodeBiomeColorMap(biomeRegistry, biomeTintColors.waterColourMap)); + + resultObject.add("redstone", encodeRedstoneColorMap(redstoneColors)); + resultObject.add("constant", encodeBlocksColorMap(blockRegistry, constantTintColors)); + + return resultObject; + } + + public static class BiomeTintColors { + final Map> grassColoursMap = new HashMap<>(); + final Map> foliageColoursMap = new HashMap<>(); + final Map> waterColourMap = new HashMap<>(); + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ClientPlayerInteractionManagerMixin.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ClientPlayerInteractionManagerMixin.java new file mode 100644 index 00000000..40e926af --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ClientPlayerInteractionManagerMixin.java @@ -0,0 +1,57 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import com.google.common.base.Preconditions; +import net.minecraft.block.BlockState; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerInteractionManager; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.World; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +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.CallbackInfoReturnable; + +@Mixin(ClientPlayerInteractionManager.class) +public abstract class ClientPlayerInteractionManagerMixin { + @Unique + private long blockBreakingStartTicks; + @Unique + private BlockState stateBeingBroken; + + @Inject(method = "attackBlock", at = @At(value = "FIELD", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;currentBreakingProgress:F", opcode = Opcodes.PUTFIELD)) + private void onBlockAttacked(BlockPos pos, Direction direction, CallbackInfoReturnable callbackInfo) { + World world = Preconditions.checkNotNull(MinecraftClient.getInstance().world); + this.blockBreakingStartTicks = world.getTime(); + } + + @Inject(method = "cancelBlockBreaking", at = @At("HEAD")) + private void onBlockBreakingCanceled(CallbackInfo callbackInfo) { + this.blockBreakingStartTicks = -1L; + } + + @Inject(method = "breakBlock", at = @At("HEAD")) + private void onBreakBlockHead(BlockPos pos, CallbackInfoReturnable callbackInfo) { + World world = Preconditions.checkNotNull(MinecraftClient.getInstance().world); + this.stateBeingBroken = world.getBlockState(pos); + } + + @Inject(method = "breakBlock", at = @At("RETURN")) + private void onBreakBlockReturn(BlockPos pos, CallbackInfoReturnable callbackInfo) { + World world = Preconditions.checkNotNull(MinecraftClient.getInstance().world); + if (callbackInfo.getReturnValue()) { + Preconditions.checkNotNull(stateBeingBroken); + if (blockBreakingStartTicks != -1L) { + long totalTicksElapsed = world.getTime() - this.blockBreakingStartTicks; + long totalTimeElapsed = totalTicksElapsed * 50L; + System.out.printf("Breaking block %s took %dms%n", stateBeingBroken.getBlock(), totalTimeElapsed); + } else { + System.out.printf("Breaking block %s took 0ms [INSTANT BREAK]%n", stateBeingBroken.getBlock()); + } + } + this.blockBreakingStartTicks = -1L; + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java new file mode 100644 index 00000000..0d297e7a --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.server.dedicated.EulaReader; +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; + +@Mixin(EulaReader.class) +public class EULAMixin { + @Inject(method = "isEulaAgreedTo()Z", at = @At("TAIL"), cancellable = true) + public void init(CallbackInfoReturnable cir) { + cir.setReturnValue(true); + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java new file mode 100644 index 00000000..d1ed130c --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java @@ -0,0 +1,17 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Block; +import net.minecraft.item.MiningToolItem; +import net.minecraft.tag.Tag; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(MiningToolItem.class) +public interface MiningToolItemAccessor { + + @Accessor + Tag getEffectiveBlocks(); + + @Accessor + float getMiningSpeed(); +} diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java similarity index 97% rename from src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java rename to 1.18/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java index 1d38b679..6d9415eb 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java @@ -1,27 +1,27 @@ -package dev.u9g.minecraftdatagenerator.mixin; - -import dev.u9g.minecraftdatagenerator.Main; -import dev.u9g.minecraftdatagenerator.generators.DataGenerators; -import dev.u9g.minecraftdatagenerator.util.DGU; -import net.minecraft.MinecraftVersion; -import net.minecraft.server.dedicated.MinecraftDedicatedServer; -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.nio.file.Path; - -@Mixin(MinecraftDedicatedServer.class) -public class ReadyMixin { - @Inject(method = "setupServer()Z", at = @At("TAIL")) - private void init(CallbackInfoReturnable cir) { - Main.LOGGER.info("Starting data generation!"); - String versionName = MinecraftVersion.CURRENT.getName(); - Path serverRootDirectory = DGU.getCurrentlyRunningServer().getRunDirectory().toPath().toAbsolutePath(); - Path dataDumpDirectory = serverRootDirectory.resolve("minecraft-data").resolve(versionName); - DataGenerators.runDataGenerators(dataDumpDirectory); - Main.LOGGER.info("Done data generation!"); - DGU.getCurrentlyRunningServer().stop(false); - } -} +package dev.u9g.minecraftdatagenerator.mixin; + +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.generators.DataGenerators; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.MinecraftVersion; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; +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.nio.file.Path; + +@Mixin(MinecraftDedicatedServer.class) +public class ReadyMixin { + @Inject(method = "setupServer()Z", at = @At("TAIL")) + private void init(CallbackInfoReturnable cir) { + Main.LOGGER.info("Starting data generation!"); + String versionName = MinecraftVersion.CURRENT.getName(); + Path serverRootDirectory = DGU.getCurrentlyRunningServer().getRunDirectory().toPath().toAbsolutePath(); + Path dataDumpDirectory = serverRootDirectory.resolve("minecraft-data").resolve(versionName); + DataGenerators.runDataGenerators(dataDumpDirectory); + Main.LOGGER.info("Done data generation!"); + DGU.getCurrentlyRunningServer().stop(false); + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java new file mode 100644 index 00000000..dfb640d5 --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java @@ -0,0 +1,146 @@ +package dev.u9g.minecraftdatagenerator.util; + +import com.google.gson.*; +import com.google.gson.internal.LazilyParsedNumber; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Language; +import net.minecraft.world.World; + +import java.io.IOException; +import java.util.Map; + +public class DGU { + + public static final TypeAdapter JSON_ELEMENT = new TypeAdapter<>() { + @Override + public JsonElement read(JsonReader in) throws IOException { + switch (in.peek()) { + case STRING: + return new JsonPrimitive(in.nextString()); + case NUMBER: + String number = in.nextString(); + return new JsonPrimitive(new LazilyParsedNumber(number)); + case BOOLEAN: + return new JsonPrimitive(in.nextBoolean()); + case NULL: + in.nextNull(); + return JsonNull.INSTANCE; + case BEGIN_ARRAY: + JsonArray array = new JsonArray(); + in.beginArray(); + while (in.hasNext()) { + array.add(read(in)); + } + in.endArray(); + return array; + case BEGIN_OBJECT: + JsonObject object = new JsonObject(); + in.beginObject(); + while (in.hasNext()) { + object.add(in.nextName(), read(in)); + } + in.endObject(); + return object; + case END_DOCUMENT: + case NAME: + case END_OBJECT: + case END_ARRAY: + default: + throw new IllegalArgumentException(); + } + } + + @Override + public void write(JsonWriter out, JsonElement value) throws IOException { + if (value == null || value.isJsonNull()) { + out.nullValue(); + } else if (value.isJsonPrimitive()) { + JsonPrimitive primitive = value.getAsJsonPrimitive(); + if (primitive.isNumber()) { + if (primitive.getAsDouble() == primitive.getAsLong()) { + out.value(primitive.getAsLong()); + } else { + out.value(primitive.getAsNumber()); + } + } else if (primitive.isBoolean()) { + out.value(primitive.getAsBoolean()); + } else { + out.value(primitive.getAsString()); + } + + } else if (value.isJsonArray()) { + out.beginArray(); + for (JsonElement e : value.getAsJsonArray()) { + write(out, e); + } + out.endArray(); + + } else if (value.isJsonObject()) { + out.beginObject(); + for (Map.Entry e : value.getAsJsonObject().entrySet()) { + out.name(e.getKey()); + write(out, e.getValue()); + } + out.endObject(); + + } else { + throw new IllegalArgumentException("Couldn't write " + value.getClass()); + } + } + }; + public static final Gson GSON = new GsonBuilder() + .registerTypeAdapter(JsonElement.class, JSON_ELEMENT).setPrettyPrinting().create(); + private static final Language language = Language.getInstance(); + + @Environment(EnvType.CLIENT) + private static MinecraftServer getCurrentlyRunningServerClient() { + return MinecraftClient.getInstance().getServer(); + } + + @SuppressWarnings("deprecation") + private static MinecraftServer getCurrentlyRunningServerDedicated() { + return (MinecraftServer) FabricLoader.getInstance().getGameInstance(); + } + + public static MinecraftServer getCurrentlyRunningServer() { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return getCurrentlyRunningServerClient(); + } else if (environmentType == EnvType.SERVER) { + return getCurrentlyRunningServerDedicated(); + } + throw new UnsupportedOperationException(); + } + + @Environment(EnvType.CLIENT) + private static String translateTextClient(String translationKey) { + return I18n.translate(translationKey); + } + + private static String translateTextFallback(String translationKey) { + try { + return language.get(translationKey); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to translate: '" + translationKey + "'"); + } + + public static String translateText(String translationKey) { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return translateTextClient(translationKey); + } + return translateTextFallback(translationKey); + } + + public static World getWorld() { + return getCurrentlyRunningServer().getOverworld(); + } +} diff --git a/1.18/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java new file mode 100644 index 00000000..11540e9a --- /dev/null +++ b/1.18/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java @@ -0,0 +1,73 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.fluid.FluidState; +import net.minecraft.fluid.Fluids; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.BlockRenderView; +import net.minecraft.world.LightType; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.BiomeKeys; +import net.minecraft.world.chunk.light.LightingProvider; +import net.minecraft.world.level.ColorResolver; +import org.jetbrains.annotations.Nullable; + +public enum EmptyRenderBlockView implements BlockRenderView { + INSTANCE; + + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + return null; + } + + public BlockState getBlockState(BlockPos pos) { + return Blocks.AIR.getDefaultState(); + } + + public FluidState getFluidState(BlockPos pos) { + return Fluids.EMPTY.getDefaultState(); + } + + public int getBottomY() { + return 0; + } + + public int getHeight() { + return 0; + } + + + @Override + public float getBrightness(Direction direction, boolean shaded) { + return 0.0f; + } + + @Override + public LightingProvider getLightingProvider() { + return null; + } + + @Override + public int getColor(BlockPos pos, ColorResolver colorResolver) { + DynamicRegistryManager registryManager = DynamicRegistryManager.create(); + Registry biomeRegistry = registryManager.get(Registry.BIOME_KEY); + Biome plainsBiome = biomeRegistry.get(BiomeKeys.PLAINS); + + return colorResolver.getColor(plainsBiome, pos.getX(), pos.getY()); + } + + @Override + public int getLightLevel(LightType type, BlockPos pos) { + return type == LightType.SKY ? getMaxLightLevel() : 0; + } + + @Override + public int getBaseLightLevel(BlockPos pos, int ambientDarkness) { + return ambientDarkness; + } +} diff --git a/1.18/src/main/resources/fabric.mod.json b/1.18/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..4eeec5d2 --- /dev/null +++ b/1.18/src/main/resources/fabric.mod.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "id": "minecraft-data-generator", + "version": "${version}", + "name": "Minecraft Data Generator", + "description": "", + "authors": [ + "Archengius", + "U9G" + ], + "contact": {}, + "license": "MIT", + "environment": "server", + "entrypoints": { + "main": [ + "dev.u9g.minecraftdatagenerator.Main" + ] + }, + "mixins": [ + "minecraft-data-generator.mixins.json" + ], + "depends": { + "fabricloader": ">=0.14.4", + "fabric": "*", + "minecraft": "1.18" + } +} diff --git a/1.18/src/main/resources/minecraft-data-generator.mixins.json b/1.18/src/main/resources/minecraft-data-generator.mixins.json new file mode 100644 index 00000000..5d80b76e --- /dev/null +++ b/1.18/src/main/resources/minecraft-data-generator.mixins.json @@ -0,0 +1,17 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "dev.u9g.minecraftdatagenerator.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "ClientPlayerInteractionManagerMixin", + "EULAMixin", + "MiningToolItemAccessor", + "ReadyMixin" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/1.19.2/build.gradle b/1.19.2/build.gradle new file mode 100644 index 00000000..e85de392 --- /dev/null +++ b/1.19.2/build.gradle @@ -0,0 +1,31 @@ +plugins { + id 'fabric-loom' +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API. This is technically optional, but you probably want it anyway. + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" +} + +processResources { + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} diff --git a/1.19.2/gradle.properties b/1.19.2/gradle.properties new file mode 100644 index 00000000..ff2fb68f --- /dev/null +++ b/1.19.2/gradle.properties @@ -0,0 +1,8 @@ +# Fabric Properties +# check these on https://modmuss50.me/fabric.html +minecraft_version=1.19.2 +yarn_mappings=1.19.2+build.28 +loader_version=0.14.10 +# Dependencies +# check this on https://modmuss50.me/fabric.html +fabric_version=0.67.0+1.19.2 diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/Main.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/Main.java new file mode 100644 index 00000000..f8d17372 --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/Main.java @@ -0,0 +1,14 @@ +package dev.u9g.minecraftdatagenerator; + +import net.fabricmc.api.ModInitializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Main implements ModInitializer { + public static final Logger LOGGER = LoggerFactory.getLogger("mc-data-gen-serv"); + + @Override + public void onInitialize() { + + } +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java new file mode 100644 index 00000000..88200df7 --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java @@ -0,0 +1,315 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.TheEndBiomeDataAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.fabricmc.fabric.api.biome.v1.NetherBiomes; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.RegistryKey; +import net.minecraft.world.biome.Biome; + +import java.util.HashSet; +import java.util.Set; + +public class BiomesDataGenerator implements IDataGenerator { + private static final Set> END_BIOMES = new HashSet<>(); + + static { + END_BIOMES.addAll(TheEndBiomeDataAccessor.END_BIOMES_MAP().keySet()); + END_BIOMES.addAll(TheEndBiomeDataAccessor.END_BARRENS_MAP().keySet()); + END_BIOMES.addAll(TheEndBiomeDataAccessor.END_MIDLANDS_MAP().keySet()); + } + + private static String guessBiomeDimensionFromCategory(Biome biome, String biomeName) { + var key = DynamicRegistryManager.BUILTIN.get().get(Registry.BIOME_KEY).getKey(biome).orElseThrow(); + if (NetherBiomes.canGenerateInNether(key)) { + return "nether"; + } else if (END_BIOMES.contains(key) || biomeName.startsWith("end_")) { + return "end"; + } + return "overworld"; + } + + private static String guessCategoryBasedOnName(String name, String dimension) { + if (dimension.equals("nether")) { + return "nether"; + } else if (dimension.equals("end")) { + return "the_end"; + } + + if (name.contains("end")) { + System.out.println(); + } + + if (name.contains("hills")) { + return "extreme_hills"; + } else if (name.contains("ocean")) { + return "ocean"; + } else if (name.contains("plains")) { + return "plains"; + } else if (name.contains("ice") || name.contains("frozen")) { + return "ice"; + } else if (name.contains("jungle")) { + return "jungle"; + } else if (name.contains("desert")) { + return "desert"; + } else if (name.contains("forest") || name.contains("grove")) { + return "forest"; + } else if (name.contains("taiga")) { + return "taiga"; + } else if (name.contains("swamp")) { + return "swamp"; + } else if (name.contains("river")) { + return "river"; + } else if (name.equals("the_end")) { + return "the_end"; + } else if (name.contains("mushroom")) { + return "mushroom"; + } else if (name.contains("beach") || name.equals("stony_shore")) { + return "beach"; + } else if (name.contains("savanna")) { + return "savanna"; + } else if (name.contains("badlands")) { + return "mesa"; + } else if (name.contains("peaks") || name.equals("snowy_slopes") || name.equals("meadow")) { + return "mountain"; + } else if (name.equals("the_void")) { + return "none"; + } else if (name.contains("cave") || name.equals("deep_dark")) { + return "underground"; + } else { + System.out.println("Unable to find biome category for biome with name: '" + name + "'"); + return "none"; + } + } + + private static int getBiomeColorFor(String biomeName) { + switch (biomeName) { + case "the_void" -> { + return 0; + } + case "plains" -> { + return 9286496; + } + case "sunflower_plains" -> { + return 11918216; + } + case "snowy_plains" -> { + return 16777215; + } + case "ice_spikes" -> { + return 11853020; + } + case "desert" -> { + return 16421912; + } + case "swamp" -> { + return 522674; + } + case "forest" -> { + return 353825; + } + case "flower_forest" -> { + return 2985545; + } + case "birch_forest" -> { + return 3175492; + } + case "dark_forest" -> { + return 4215066; + } + case "old_growth_birch_forest" -> { + return 5807212; + } + case "old_growth_pine_taiga" -> { + return 5858897; + } + case "old_growth_spruce_taiga" -> { + return 8490617; + } + case "taiga" -> { + return 747097; + } + case "snowy_taiga" -> { + return 3233098; + } + case "savanna" -> { + return 12431967; + } + case "savanna_plateau" -> { + return 10984804; + } + case "windswept_hills" -> { + return 6316128; + } + case "windswept_gravelly_hills" -> { + return 8947848; + } + case "windswept_forest" -> { + return 2250012; + } + case "windswept_savanna" -> { + return 15063687; + } + case "jungle" -> { + return 5470985; + } + case "sparse_jungle" -> { + return 6458135; + } + case "bamboo_jungle" -> { + return 7769620; + } + case "badlands" -> { + return 14238997; + } + case "eroded_badlands" -> { + return 16739645; + } + case "wooded_badlands" -> { + return 11573093; + } + case "meadow" -> { + return 9217136; + } + case "grove" -> { + return 14675173; + } + case "snowy_slopes" -> { + return 14348785; + } + case "frozen_peaks" -> { + return 15399931; + } + case "jagged_peaks" -> { + return 14937325; + } + case "stony_peaks" -> { + return 13750737; + } + case "river" -> { + return 255; + } + case "frozen_river" -> { + return 10526975; + } + case "beach" -> { + return 16440917; + } + case "snowy_beach" -> { + return 16445632; + } + case "stony_shore" -> { + return 10658436; + } + case "warm_ocean" -> { + return 172; + } + case "lukewarm_ocean" -> { + return 144; + } + case "deep_lukewarm_ocean" -> { + return 64; + } + case "ocean" -> { + return 112; + } + case "deep_ocean" -> { + return 48; + } + case "cold_ocean" -> { + return 2105456; + } + case "deep_cold_ocean" -> { + return 2105400; + } + case "frozen_ocean" -> { + return 7368918; + } + case "deep_frozen_ocean" -> { + return 4210832; + } + case "mushroom_fields" -> { + return 16711935; + } + case "dripstone_caves" -> { + return 12690831; + } + case "lush_caves" -> { + return 14652980; + } + case "nether_wastes" -> { + return 12532539; + } + case "warped_forest" -> { + return 4821115; + } + case "crimson_forest" -> { + return 14485512; + } + case "soul_sand_valley" -> { + return 6174768; + } + case "basalt_deltas" -> { + return 4208182; + } + case "the_end" -> { + return 8421631; + } + case "end_highlands" -> { + return 12828041; + } + case "end_midlands" -> { + return 15464630; + } + case "small_end_islands" -> { + return 42; + } + case "end_barrens" -> { + return 9474162; + } + } + System.out.println("Don't know the color of biome: '" + biomeName + "'"); + return 0; + } + + public static JsonObject generateBiomeInfo(Registry registry, Biome biome) { + JsonObject biomeDesc = new JsonObject(); + Identifier registryKey = registry.getKey(biome).orElseThrow().getValue(); + String localizationKey = String.format("biome.%s.%s", registryKey.getNamespace(), registryKey.getPath()); + String name = registryKey.getPath(); + biomeDesc.addProperty("id", registry.getRawId(biome)); + biomeDesc.addProperty("name", name); + String dimension = guessBiomeDimensionFromCategory(biome, name); + biomeDesc.addProperty("category", guessCategoryBasedOnName(name, dimension)); + biomeDesc.addProperty("temperature", biome.getTemperature()); + biomeDesc.addProperty("precipitation", biome.getPrecipitation().getName()); + //biomeDesc.addProperty("depth", biome.getDepth()); - Doesn't exist anymore in minecraft source + biomeDesc.addProperty("dimension", dimension); + biomeDesc.addProperty("displayName", DGU.translateText(localizationKey)); + biomeDesc.addProperty("color", getBiomeColorFor(registryKey.getPath())); + biomeDesc.addProperty("rainfall", biome.getDownfall()); + + return biomeDesc; + } + + @Override + public String getDataName() { + return "biomes"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray biomesArray = new JsonArray(); + DynamicRegistryManager registryManager = DynamicRegistryManager.BUILTIN.get(); + Registry biomeRegistry = registryManager.get(Registry.BIOME_KEY); + + biomeRegistry.stream() + .map(biome -> generateBiomeInfo(biomeRegistry, biome)) + .forEach(biomesArray::add); + return biomesArray; + } +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java new file mode 100644 index 00000000..52c3181d --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java @@ -0,0 +1,111 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.EmptyBlockView; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class BlockCollisionShapesDataGenerator implements IDataGenerator { + + @Override + public String getDataName() { + return "blockCollisionShapes"; + } + + @Override + public JsonObject generateDataJson() { + Registry blockRegistry = Registry.BLOCK; + BlockShapesCache blockShapesCache = new BlockShapesCache(); + + blockRegistry.forEach(blockShapesCache::processBlock); + + JsonObject resultObject = new JsonObject(); + + resultObject.add("blocks", blockShapesCache.dumpBlockShapeIndices(blockRegistry)); + resultObject.add("shapes", blockShapesCache.dumpShapesObject()); + + return resultObject; + } + + private static class BlockShapesCache { + public final Map uniqueBlockShapes = new HashMap<>(); + public final Map> blockCollisionShapes = new HashMap<>(); + private int lastCollisionShapeId = 0; + + public void processBlock(Block block) { + List blockStates = block.getStateManager().getStates(); + List blockCollisionShapes = new ArrayList<>(); + + for (BlockState blockState : blockStates) { + VoxelShape blockShape = blockState.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + Integer blockShapeIndex = uniqueBlockShapes.get(blockShape); + + if (blockShapeIndex == null) { + blockShapeIndex = lastCollisionShapeId++; + uniqueBlockShapes.put(blockShape, blockShapeIndex); + } + blockCollisionShapes.add(blockShapeIndex); + } + + this.blockCollisionShapes.put(block, blockCollisionShapes); + } + + public JsonObject dumpBlockShapeIndices(Registry blockRegistry) { + JsonObject resultObject = new JsonObject(); + + for (var entry : blockCollisionShapes.entrySet()) { + List blockCollisions = entry.getValue(); + long distinctShapesCount = blockCollisions.stream().distinct().count(); + JsonElement blockCollision; + if (distinctShapesCount == 1L) { + blockCollision = new JsonPrimitive(blockCollisions.get(0)); + } else { + blockCollision = new JsonArray(); + for (int collisionId : blockCollisions) { + ((JsonArray) blockCollision).add(collisionId); + } + } + + Identifier registryKey = blockRegistry.getKey(entry.getKey()).orElseThrow().getValue(); + resultObject.add(registryKey.getPath(), blockCollision); + } + + return resultObject; + } + + public JsonObject dumpShapesObject() { + JsonObject shapesObject = new JsonObject(); + + for (var entry : uniqueBlockShapes.entrySet()) { + JsonArray boxesArray = new JsonArray(); + entry.getKey().forEachBox((x1, y1, z1, x2, y2, z2) -> { + JsonArray oneBoxJsonArray = new JsonArray(); + + oneBoxJsonArray.add(x1); + oneBoxJsonArray.add(y1); + oneBoxJsonArray.add(z1); + + oneBoxJsonArray.add(x2); + oneBoxJsonArray.add(y2); + oneBoxJsonArray.add(z2); + + boxesArray.add(oneBoxJsonArray); + }); + shapesObject.add(Integer.toString(entry.getValue()), boxesArray); + } + return shapesObject; + } + } +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java new file mode 100644 index 00000000..51139655 --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java @@ -0,0 +1,198 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.base.CaseFormat; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.block.AirBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.loot.context.LootContext; +import net.minecraft.loot.context.LootContextParameters; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.state.property.EnumProperty; +import net.minecraft.state.property.IntProperty; +import net.minecraft.state.property.Property; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.EmptyBlockView; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +public class BlocksDataGenerator implements IDataGenerator { + + private static final Logger logger = LoggerFactory.getLogger(BlocksDataGenerator.class); + + private static List getItemsEffectiveForBlock(BlockState blockState) { + return Registry.ITEM.stream() + .filter(item -> item.getDefaultStack().isSuitableFor(blockState)) + .collect(Collectors.toList()); + } + + private static void populateDropsIfPossible(BlockState blockState, Item firstToolItem, List outDrops) { + MinecraftServer minecraftServer = DGU.getCurrentlyRunningServer(); + if (minecraftServer != null) { + //If we have local world context, we can actually evaluate loot tables and determine actual data + ServerWorld serverWorld = minecraftServer.getOverworld(); + LootContext.Builder lootContext = new LootContext.Builder(serverWorld) + .parameter(LootContextParameters.BLOCK_STATE, blockState) + .parameter(LootContextParameters.ORIGIN, Vec3d.ZERO) + .parameter(LootContextParameters.TOOL, firstToolItem.getDefaultStack()) + .random(0L); + outDrops.addAll(blockState.getDroppedStacks(lootContext)); + } else { + //If we're lacking world context to correctly determine drops, assume that default drop is ItemBlock stack in quantity of 1 + Item itemBlock = blockState.getBlock().asItem(); + if (itemBlock != Items.AIR) { + outDrops.add(itemBlock.getDefaultStack()); + } + } + } + + private static String getPropertyTypeName(Property property) { + //Explicitly handle default minecraft properties + if (property instanceof BooleanProperty) { + return "bool"; + } + if (property instanceof IntProperty) { + return "int"; + } + if (property instanceof EnumProperty) { + return "enum"; + } + + //Use simple class name as fallback, this code will give something like + //example_type for ExampleTypeProperty class name + String rawPropertyName = property.getClass().getSimpleName().replace("Property", ""); + return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, rawPropertyName); + } + + private static > JsonObject generateStateProperty(Property property) { + JsonObject propertyObject = new JsonObject(); + Collection propertyValues = property.getValues(); + + propertyObject.addProperty("name", property.getName()); + propertyObject.addProperty("type", getPropertyTypeName(property)); + propertyObject.addProperty("num_values", propertyValues.size()); + + //Do not add values for vanilla boolean properties, they are known by default + if (!(property instanceof BooleanProperty)) { + JsonArray propertyValuesArray = new JsonArray(); + for (T propertyValue : propertyValues) { + propertyValuesArray.add(property.name(propertyValue)); + } + propertyObject.add("values", propertyValuesArray); + } + return propertyObject; + } + + private static String findMatchingBlockMaterial(BlockState blockState, List materials) { + List matchingMaterials = materials.stream() + .filter(material -> material.getPredicate().test(blockState)) + .collect(Collectors.toList()); + + if (matchingMaterials.size() > 1) { + var firstMaterial = matchingMaterials.get(0); + var otherMaterials = matchingMaterials.subList(1, matchingMaterials.size()); + + if (!otherMaterials.stream().allMatch(firstMaterial::includesMaterial)) { + logger.error("Block {} matches multiple materials: {}", blockState.getBlock(), matchingMaterials); + } + } + if (matchingMaterials.isEmpty()) { + return "default"; + } + return matchingMaterials.get(0).getMaterialName(); + } + + public static JsonObject generateBlock(Registry blockRegistry, List materials, Block block) { + JsonObject blockDesc = new JsonObject(); + + List blockStates = block.getStateManager().getStates(); + BlockState defaultState = block.getDefaultState(); + Identifier registryKey = blockRegistry.getKey(block).orElseThrow().getValue(); + String localizationKey = block.getTranslationKey(); + List effectiveTools = getItemsEffectiveForBlock(defaultState); + + blockDesc.addProperty("id", blockRegistry.getRawId(block)); + blockDesc.addProperty("name", registryKey.getPath()); + blockDesc.addProperty("displayName", DGU.translateText(localizationKey)); + + blockDesc.addProperty("hardness", block.getHardness()); + blockDesc.addProperty("resistance", block.getBlastResistance()); + blockDesc.addProperty("stackSize", block.asItem().getMaxCount()); + blockDesc.addProperty("diggable", block.getHardness() != -1.0f && !(block instanceof AirBlock)); +// JsonObject effTools = new JsonObject(); +// effectiveTools.forEach(item -> effTools.addProperty( +// String.valueOf(Registry.ITEM.getRawId(item)), // key +// item.getMiningSpeedMultiplier(item.getDefaultStack(), defaultState) // value +// )); +// blockDesc.add("effectiveTools", effTools); + blockDesc.addProperty("material", findMatchingBlockMaterial(defaultState, materials)); + + blockDesc.addProperty("transparent", !defaultState.isOpaque()); + blockDesc.addProperty("emitLight", defaultState.getLuminance()); + blockDesc.addProperty("filterLight", defaultState.getOpacity(EmptyBlockView.INSTANCE, BlockPos.ORIGIN)); + + blockDesc.addProperty("defaultState", Block.getRawIdFromState(defaultState)); + blockDesc.addProperty("minStateId", Block.getRawIdFromState(blockStates.get(0))); + blockDesc.addProperty("maxStateId", Block.getRawIdFromState(blockStates.get(blockStates.size() - 1))); + + JsonArray stateProperties = new JsonArray(); + for (Property property : block.getStateManager().getProperties()) { + stateProperties.add(generateStateProperty(property)); + } + blockDesc.add("states", stateProperties); + + //Only add harvest tools if tool is required for harvesting this block + if (defaultState.isToolRequired()) { + JsonObject effectiveToolsObject = new JsonObject(); + for (Item effectiveItem : effectiveTools) { + effectiveToolsObject.addProperty(Integer.toString(Item.getRawId(effectiveItem)), true); + } + blockDesc.add("harvestTools", effectiveToolsObject); + } + + List actualBlockDrops = new ArrayList<>(); + populateDropsIfPossible(defaultState, effectiveTools.isEmpty() ? Items.AIR : effectiveTools.get(0), actualBlockDrops); + + JsonArray dropsArray = new JsonArray(); + for (ItemStack dropStack : actualBlockDrops) { + dropsArray.add(Item.getRawId(dropStack.getItem())); + } + blockDesc.add("drops", dropsArray); + + VoxelShape blockCollisionShape = defaultState.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + blockDesc.addProperty("boundingBox", blockCollisionShape.isEmpty() ? "empty" : "block"); + + return blockDesc; + } + + @Override + public String getDataName() { + return "blocks"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultBlocksArray = new JsonArray(); + Registry blockRegistry = Registry.BLOCK; + List availableMaterials = MaterialsDataGenerator.getGlobalMaterialInfo(); + + blockRegistry.forEach(block -> resultBlocksArray.add(generateBlock(blockRegistry, availableMaterials, block))); + return resultBlocksArray; + } +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java new file mode 100644 index 00000000..c47684ab --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java @@ -0,0 +1,77 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.List; + +public class DataGenerators { + + private static final Logger logger = LoggerFactory.getLogger(DataGenerators.class); + private static final List GENERATORS = new ArrayList<>(); + + static { + register(new BiomesDataGenerator()); + register(new BlockCollisionShapesDataGenerator()); + register(new BlocksDataGenerator()); + register(new EffectsDataGenerator()); + register(new EnchantmentsDataGenerator()); + register(new EntitiesDataGenerator()); + register(new FoodsDataGenerator()); + register(new ItemsDataGenerator()); + register(new ParticlesDataGenerator()); + register(new TintsDataGenerator()); + register(new MaterialsDataGenerator()); +// register(new RecipeDataGenerator()); - On hold until mcdata supports multiple materials for a recipe + register(new LanguageDataGenerator()); + register(new InstrumentsDataGenerator()); + } + + public static void register(IDataGenerator generator) { + GENERATORS.add(generator); + } + + public static boolean runDataGenerators(Path outputDirectory) { + try { + Files.createDirectories(outputDirectory); + } catch (IOException exception) { + logger.error("Failed to create data generator output directory at {}", outputDirectory, exception); + return false; + } + + int generatorsFailed = 0; + logger.info("Running minecraft data generators, output at {}", outputDirectory); + + for (IDataGenerator dataGenerator : GENERATORS) { + logger.info("Running generator {}", dataGenerator.getDataName()); + try { + String outputFileName = String.format("%s.json", dataGenerator.getDataName()); + JsonElement outputElement = dataGenerator.generateDataJson(); + Path outputFilePath = outputDirectory.resolve(outputFileName); + + try (Writer writer = Files.newBufferedWriter(outputFilePath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + JsonWriter jsonWriter = new JsonWriter(writer); + jsonWriter.setIndent(" "); + Streams.write(outputElement, jsonWriter); + } + logger.info("Generator: {} -> {}", dataGenerator.getDataName(), outputFileName); + + } catch (Throwable exception) { + logger.error("Failed to run data generator {}", dataGenerator.getDataName(), exception); + generatorsFailed++; + } + } + + logger.info("Finishing running data generators"); + return generatorsFailed == 0; + } +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java new file mode 100644 index 00000000..1c9849e6 --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java @@ -0,0 +1,46 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.stream.Collectors; + +public class EffectsDataGenerator implements IDataGenerator { + + public static JsonObject generateEffect(Registry registry, StatusEffect statusEffect) { + JsonObject effectDesc = new JsonObject(); + Identifier registryKey = registry.getKey(statusEffect).orElseThrow().getValue(); + + effectDesc.addProperty("id", registry.getRawId(statusEffect)); + if (statusEffect == StatusEffects.UNLUCK) { + effectDesc.addProperty("name", "BadLuck"); + effectDesc.addProperty("displayName", "Bad Luck"); + } else { + effectDesc.addProperty("name", Arrays.stream(registryKey.getPath().split("_")).map(StringUtils::capitalize).collect(Collectors.joining())); + effectDesc.addProperty("displayName", DGU.translateText(statusEffect.getTranslationKey())); + } + + effectDesc.addProperty("type", statusEffect.isBeneficial() ? "good" : "bad"); + return effectDesc; + } + + @Override + public String getDataName() { + return "effects"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry statusEffectRegistry = Registry.STATUS_EFFECT; + statusEffectRegistry.forEach(effect -> resultsArray.add(generateEffect(statusEffectRegistry, effect))); + return resultsArray; + } +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java new file mode 100644 index 00000000..a71961c9 --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java @@ -0,0 +1,108 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +public class EnchantmentsDataGenerator implements IDataGenerator { + + private static final ImmutableMap ENCHANTMENT_TARGET_NAMES = ImmutableMap.builder() + .put(EnchantmentTarget.ARMOR, "armor") + .put(EnchantmentTarget.ARMOR_FEET, "armor_feet") + .put(EnchantmentTarget.ARMOR_LEGS, "armor_legs") + .put(EnchantmentTarget.ARMOR_CHEST, "armor_chest") + .put(EnchantmentTarget.ARMOR_HEAD, "armor_head") + .put(EnchantmentTarget.WEAPON, "weapon") + .put(EnchantmentTarget.DIGGER, "digger") + .put(EnchantmentTarget.FISHING_ROD, "fishing_rod") + .put(EnchantmentTarget.TRIDENT, "trident") + .put(EnchantmentTarget.BREAKABLE, "breakable") + .put(EnchantmentTarget.BOW, "bow") + .put(EnchantmentTarget.WEARABLE, "wearable") + .put(EnchantmentTarget.CROSSBOW, "crossbow") + .put(EnchantmentTarget.VANISHABLE, "vanishable") + .build(); + + public static String getEnchantmentTargetName(EnchantmentTarget target) { + return ENCHANTMENT_TARGET_NAMES.getOrDefault(target, target.name().toLowerCase(Locale.ROOT)); + } + + //Equation enchantment costs follow is a * level + b, so we can easily retrieve a and b by passing zero level + private static JsonObject generateEnchantmentMinPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMinPower(0); + int a = enchantment.getMinPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + private static JsonObject generateEnchantmentMaxPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMaxPower(0); + int a = enchantment.getMaxPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + public static JsonObject generateEnchantment(Registry registry, Enchantment enchantment) { + JsonObject enchantmentDesc = new JsonObject(); + Identifier registryKey = registry.getKey(enchantment).orElseThrow().getValue(); + + enchantmentDesc.addProperty("id", registry.getRawId(enchantment)); + enchantmentDesc.addProperty("name", registryKey.getPath()); + enchantmentDesc.addProperty("displayName", DGU.translateText(enchantment.getTranslationKey())); + + enchantmentDesc.addProperty("maxLevel", enchantment.getMaxLevel()); + enchantmentDesc.add("minCost", generateEnchantmentMinPowerCoefficients(enchantment)); + enchantmentDesc.add("maxCost", generateEnchantmentMaxPowerCoefficients(enchantment)); + + enchantmentDesc.addProperty("treasureOnly", enchantment.isTreasure()); + enchantmentDesc.addProperty("curse", enchantment.isCursed()); + + List incompatibleEnchantments = registry.stream() + .filter(other -> !enchantment.canCombine(other)) + .filter(other -> other != enchantment) + .collect(Collectors.toList()); + + JsonArray excludes = new JsonArray(); + for (Enchantment excludedEnchantment : incompatibleEnchantments) { + Identifier otherKey = registry.getKey(excludedEnchantment).orElseThrow().getValue(); + excludes.add(otherKey.getPath()); + } + enchantmentDesc.add("exclude", excludes); + + enchantmentDesc.addProperty("category", getEnchantmentTargetName(enchantment.type)); + enchantmentDesc.addProperty("weight", enchantment.getRarity().getWeight()); + enchantmentDesc.addProperty("tradeable", enchantment.isAvailableForEnchantedBookOffer()); + enchantmentDesc.addProperty("discoverable", enchantment.isAvailableForRandomSelection()); + + return enchantmentDesc; + } + + @Override + public String getDataName() { + return "enchantments"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry enchantmentRegistry = Registry.ENCHANTMENT; + enchantmentRegistry.stream() + .forEach(enchantment -> resultsArray.add(generateEnchantment(enchantmentRegistry, enchantment))); + return resultsArray; + } +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java new file mode 100644 index 00000000..4eacf202 --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java @@ -0,0 +1,119 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.EntityTypeAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.mob.AmbientEntity; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.mob.WaterCreatureEntity; +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.passive.PassiveEntity; +import net.minecraft.entity.projectile.ProjectileEntity; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +public class EntitiesDataGenerator implements IDataGenerator { + + public static JsonObject generateEntity(Registry> entityRegistry, EntityType entityType) { + JsonObject entityDesc = new JsonObject(); + Identifier registryKey = entityRegistry.getKey(entityType).orElseThrow().getValue(); + int entityRawId = entityRegistry.getRawId(entityType); + + entityDesc.addProperty("id", entityRawId); + entityDesc.addProperty("internalId", entityRawId); + entityDesc.addProperty("name", registryKey.getPath()); + + entityDesc.addProperty("displayName", DGU.translateText(entityType.getTranslationKey())); + entityDesc.addProperty("width", entityType.getDimensions().width); + entityDesc.addProperty("height", entityType.getDimensions().height); + + String entityTypeString = "UNKNOWN"; + MinecraftServer minecraftServer = DGU.getCurrentlyRunningServer(); + + if (minecraftServer != null) { + Entity entityObject = entityType.create(minecraftServer.getOverworld()); + entityTypeString = entityObject != null ? getEntityTypeForClass(entityObject.getClass()) : "player"; + } + entityDesc.addProperty("type", entityTypeString); + entityDesc.addProperty("category", getCategoryFrom(entityType)); + + return entityDesc; + } + + private static String getCategoryFrom(EntityType entityType) { + if (entityType == EntityType.PLAYER) return "UNKNOWN"; // fail early for player entities + /*public T create(World world) { + return this.factory.create(this, world); + }*/ + Entity entity = ((EntityTypeAccessor) entityType).factory().create(entityType, DGU.getWorld()); + if (entity == null) + throw new Error("Entity was null after trying to create a: " + DGU.translateText(entityType.getTranslationKey())); + entity.discard(); + return switch (entity.getClass().getPackageName()) { + case "net.minecraft.entity.decoration", "net.minecraft.entity.decoration.painting" -> "Immobile"; + case "net.minecraft.entity.boss", "net.minecraft.entity.mob", "net.minecraft.entity.boss.dragon" -> + "Hostile mobs"; + case "net.minecraft.entity.projectile", "net.minecraft.entity.projectile.thrown" -> "Projectiles"; + case "net.minecraft.entity.passive" -> "Passive mobs"; + case "net.minecraft.entity.vehicle" -> "Vehicles"; + case "net.minecraft.entity" -> "UNKNOWN"; + default -> throw new Error("Unexpected entity type: " + entity.getClass().getPackageName()); + }; + } + + //Honestly, both "type" and "category" fields in the schema and examples do not contain any useful information + //Since category is optional, I will just leave it out, and for type I will assume general entity classification + //by the Entity class hierarchy (which has some weirdness too by the way) + private static String getEntityTypeForClass(Class entityClass) { + //Top-level classifications + if (WaterCreatureEntity.class.isAssignableFrom(entityClass)) { + return "water_creature"; + } + if (AnimalEntity.class.isAssignableFrom(entityClass)) { + return "animal"; + } + if (HostileEntity.class.isAssignableFrom(entityClass)) { + return "hostile"; + } + if (AmbientEntity.class.isAssignableFrom(entityClass)) { + return "ambient"; + } + + //Second level classifications. PathAwareEntity is not included because it + //doesn't really make much sense to categorize by it + if (PassiveEntity.class.isAssignableFrom(entityClass)) { + return "passive"; + } + if (MobEntity.class.isAssignableFrom(entityClass)) { + return "mob"; + } + + //Other classifications only include living entities and projectiles. everything else is categorized as other + if (LivingEntity.class.isAssignableFrom(entityClass)) { + return "living"; + } + if (ProjectileEntity.class.isAssignableFrom(entityClass)) { + return "projectile"; + } + return "other"; + } + + @Override + public String getDataName() { + return "entities"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry> entityTypeRegistry = Registry.ENTITY_TYPE; + entityTypeRegistry.forEach(entity -> resultArray.add(generateEntity(entityTypeRegistry, entity))); + return resultArray; + } +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java new file mode 100644 index 00000000..45a0b35f --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java @@ -0,0 +1,51 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.item.FoodComponent; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Objects; + +public class FoodsDataGenerator implements IDataGenerator { + + public static JsonObject generateFoodDescriptor(Registry registry, Item foodItem) { + JsonObject foodDesc = new JsonObject(); + Identifier registryKey = registry.getKey(foodItem).orElseThrow().getValue(); + + foodDesc.addProperty("id", registry.getRawId(foodItem)); + foodDesc.addProperty("name", registryKey.getPath()); + + foodDesc.addProperty("stackSize", foodItem.getMaxCount()); + foodDesc.addProperty("displayName", DGU.translateText(foodItem.getTranslationKey())); + + FoodComponent foodComponent = Objects.requireNonNull(foodItem.getFoodComponent()); + float foodPoints = foodComponent.getHunger(); + float saturationRatio = foodComponent.getSaturationModifier() * 2.0F; + float saturation = foodPoints * saturationRatio; + + foodDesc.addProperty("foodPoints", foodPoints); + foodDesc.addProperty("saturation", saturation); + + foodDesc.addProperty("effectiveQuality", foodPoints + saturation); + foodDesc.addProperty("saturationRatio", saturationRatio); + return foodDesc; + } + + @Override + public String getDataName() { + return "foods"; + } + + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry itemRegistry = Registry.ITEM; + itemRegistry.stream() + .filter(Item::isFood) + .forEach(food -> resultsArray.add(generateFoodDescriptor(itemRegistry, food))); + return resultsArray; + } +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java new file mode 100644 index 00000000..81fe4e20 --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; + +public interface IDataGenerator { + + String getDataName(); + + JsonElement generateDataJson(); +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java new file mode 100644 index 00000000..e7b9b54f --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java @@ -0,0 +1,25 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.minecraft.block.enums.Instrument; + +public class InstrumentsDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "instruments"; + } + + @Override + public JsonElement generateDataJson() { + JsonArray array = new JsonArray(); + for (Instrument instrument : Instrument.values()) { + JsonObject object = new JsonObject(); + object.addProperty("id", instrument.ordinal()); + object.addProperty("name", instrument.asString()); + array.add(object); + } + return array; + } +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java new file mode 100644 index 00000000..b0c959dc --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java @@ -0,0 +1,81 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class ItemsDataGenerator implements IDataGenerator { + + private static List calculateItemsToRepairWith(Registry itemRegistry, Item sourceItem) { + ItemStack sourceItemStack = sourceItem.getDefaultStack(); + return itemRegistry.stream() + .filter(otherItem -> sourceItem.canRepair(sourceItemStack, otherItem.getDefaultStack())) + .collect(Collectors.toList()); + } + + private static List getApplicableEnchantmentTargets(Item sourceItem) { + return Arrays.stream(EnchantmentTarget.values()) + .filter(target -> target.isAcceptableItem(sourceItem)) + .collect(Collectors.toList()); + } + + public static JsonObject generateItem(Registry itemRegistry, Item item) { + JsonObject itemDesc = new JsonObject(); + Identifier registryKey = itemRegistry.getKey(item).orElseThrow().getValue(); + + itemDesc.addProperty("id", itemRegistry.getRawId(item)); + itemDesc.addProperty("name", registryKey.getPath()); + + itemDesc.addProperty("displayName", DGU.translateText(item.getTranslationKey())); + itemDesc.addProperty("stackSize", item.getMaxCount()); + + List enchantmentTargets = getApplicableEnchantmentTargets(item); + + JsonArray enchantCategoriesArray = new JsonArray(); + for (EnchantmentTarget target : enchantmentTargets) { + enchantCategoriesArray.add(EnchantmentsDataGenerator.getEnchantmentTargetName(target)); + } + if (enchantCategoriesArray.size() > 0) { + itemDesc.add("enchantCategories", enchantCategoriesArray); + } + + if (item.isDamageable()) { + List repairWithItems = calculateItemsToRepairWith(itemRegistry, item); + + JsonArray fixedWithArray = new JsonArray(); + for (Item repairWithItem : repairWithItems) { + Identifier repairWithName = itemRegistry.getKey(repairWithItem).orElseThrow().getValue(); + fixedWithArray.add(repairWithName.getPath()); + } + if (fixedWithArray.size() > 0) { + itemDesc.add("repairWith", fixedWithArray); + } + + int maxDurability = item.getMaxDamage(); + itemDesc.addProperty("maxDurability", maxDurability); + } + return itemDesc; + } + + @Override + public String getDataName() { + return "items"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry itemRegistry = Registry.ITEM; + itemRegistry.stream().forEach(item -> resultArray.add(generateItem(itemRegistry, item))); + return resultArray; + } +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java new file mode 100644 index 00000000..7a3c6ced --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +public class LanguageDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "language"; + } + + @Override + public JsonElement generateDataJson() { + try { + InputStream inputStream = Objects.requireNonNull(this.getClass().getResourceAsStream("/assets/minecraft/lang/en_us.json")); + return new Gson().fromJson(new InputStreamReader(inputStream, StandardCharsets.UTF_8), JsonObject.class); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to generate language file"); + } +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java new file mode 100644 index 00000000..4253ae69 --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java @@ -0,0 +1,197 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.MiningToolItemAccessor; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.Material; +import net.minecraft.item.Item; +import net.minecraft.item.Items; +import net.minecraft.item.MiningToolItem; +import net.minecraft.item.SwordItem; +import net.minecraft.tag.BlockTags; +import net.minecraft.tag.TagKey; +import net.minecraft.util.registry.Registry; + +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +//TODO entire idea of linking materials to tool speeds is obsolete and just wrong now, +//TODO but we kinda have to support it to let old code work for computing digging times, +//TODO so for now we will handle materials as "virtual" ones based on which tools can break blocks +public class MaterialsDataGenerator implements IDataGenerator { + + private static final List> COMPOSITE_MATERIALS = ImmutableList.>builder() + .add(ImmutableList.of("plant", makeMaterialNameForTag(BlockTags.AXE_MINEABLE))) + .add(ImmutableList.of("gourd", makeMaterialNameForTag(BlockTags.AXE_MINEABLE))) + .add(ImmutableList.of(makeMaterialNameForTag(BlockTags.LEAVES), makeMaterialNameForTag(BlockTags.HOE_MINEABLE))) + .add(ImmutableList.of(makeMaterialNameForTag(BlockTags.LEAVES), makeMaterialNameForTag(BlockTags.AXE_MINEABLE), makeMaterialNameForTag(BlockTags.HOE_MINEABLE))) + .add(ImmutableList.of("vine_or_glow_lichen", "plant", makeMaterialNameForTag(BlockTags.AXE_MINEABLE) + )).build(); + + private static String makeMaterialNameForTag(TagKey tag) { + return tag.id().getPath(); + } + + private static void createCompositeMaterialInfo(List allMaterials, List combinedMaterials) { + String compositeMaterialName = Joiner.on(';').join(combinedMaterials); + + List mappedMaterials = combinedMaterials.stream() + .map(otherName -> allMaterials.stream() + .filter(other -> other.getMaterialName().equals(otherName)) + .findFirst().orElseThrow(() -> new RuntimeException("Material not found with name " + otherName))) + .collect(Collectors.toList()); + + Predicate compositePredicate = blockState -> + mappedMaterials.stream().allMatch(it -> it.getPredicate().test(blockState)); + + MaterialInfo materialInfo = new MaterialInfo(compositeMaterialName, compositePredicate).includes(mappedMaterials); + allMaterials.add(0, materialInfo); + } + + private static void createCompositeMaterial(Map> allMaterials, List combinedMaterials) { + String compositeMaterialName = Joiner.on(';').join(combinedMaterials); + + Map resultingToolSpeeds = new HashMap<>(); + combinedMaterials.stream() + .map(allMaterials::get) + .forEach(resultingToolSpeeds::putAll); + allMaterials.put(compositeMaterialName, resultingToolSpeeds); + } + + public static List getGlobalMaterialInfo() { + ArrayList resultList = new ArrayList<>(); + + resultList.add(new MaterialInfo("vine_or_glow_lichen", blockState -> blockState.isOf(Blocks.VINE) || blockState.isOf(Blocks.GLOW_LICHEN))); + resultList.add(new MaterialInfo("coweb", blockState -> blockState.isOf(Blocks.COBWEB))); + + resultList.add(new MaterialInfo("leaves", blockState -> blockState.isIn(BlockTags.LEAVES))); + resultList.add(new MaterialInfo("wool", blockState -> blockState.isIn(BlockTags.WOOL))); + + resultList.add(new MaterialInfo("gourd", blockState -> blockState.getMaterial() == Material.GOURD)); + resultList.add(new MaterialInfo("plant", blockState -> blockState.getMaterial() == Material.PLANT || blockState.getMaterial() == Material.REPLACEABLE_PLANT)); + + HashSet uniqueMaterialNames = new HashSet<>(); + + Registry itemRegistry = Registry.ITEM; + itemRegistry.forEach(item -> { + if (item instanceof MiningToolItem toolItem) { + TagKey effectiveBlocks = ((MiningToolItemAccessor) toolItem).getEffectiveBlocks(); + String materialName = makeMaterialNameForTag(effectiveBlocks); + + if (!uniqueMaterialNames.contains(materialName)) { + uniqueMaterialNames.add(materialName); + resultList.add(new MaterialInfo(materialName, blockState -> blockState.isIn(effectiveBlocks))); + } + } + }); + + COMPOSITE_MATERIALS.forEach(values -> createCompositeMaterialInfo(resultList, values)); + return resultList; + } + + @Override + public String getDataName() { + return "materials"; + } + + @Override + public JsonElement generateDataJson() { + Registry itemRegistry = Registry.ITEM; + + Map> materialMiningSpeeds = new HashMap<>(); + materialMiningSpeeds.put("default", ImmutableMap.of()); + + //Special materials used for shears and swords special mining speed logic + Map leavesMaterialSpeeds = new HashMap<>(); + Map cowebMaterialSpeeds = new HashMap<>(); + Map plantMaterialSpeeds = new HashMap<>(); + Map gourdMaterialSpeeds = new HashMap<>(); + + materialMiningSpeeds.put(makeMaterialNameForTag(BlockTags.LEAVES), leavesMaterialSpeeds); + materialMiningSpeeds.put("coweb", cowebMaterialSpeeds); + materialMiningSpeeds.put("plant", plantMaterialSpeeds); + materialMiningSpeeds.put("gourd", gourdMaterialSpeeds); + + //Shears need special handling because they do not follow normal rules like tools + leavesMaterialSpeeds.put(Items.SHEARS, 15.0f); + cowebMaterialSpeeds.put(Items.SHEARS, 15.0f); + materialMiningSpeeds.put("vine_or_glow_lichen", ImmutableMap.of(Items.SHEARS, 2.0f)); + materialMiningSpeeds.put("wool", ImmutableMap.of(Items.SHEARS, 5.0f)); + + itemRegistry.forEach(item -> { + //Tools are handled rather easily and do not require anything else + if (item instanceof MiningToolItem toolItem) { + TagKey effectiveBlocks = ((MiningToolItemAccessor) toolItem).getEffectiveBlocks(); + String materialName = makeMaterialNameForTag(effectiveBlocks); + + Map materialSpeeds = materialMiningSpeeds.computeIfAbsent(materialName, k -> new HashMap<>()); + float miningSpeed = ((MiningToolItemAccessor) toolItem).getMiningSpeed(); + materialSpeeds.put(item, miningSpeed); + } + + //Swords require special treatment + if (item instanceof SwordItem) { + cowebMaterialSpeeds.put(item, 15.0f); + plantMaterialSpeeds.put(item, 1.5f); + leavesMaterialSpeeds.put(item, 1.5f); + gourdMaterialSpeeds.put(item, 1.5f); + } + }); + + COMPOSITE_MATERIALS.forEach(values -> createCompositeMaterial(materialMiningSpeeds, values)); + + JsonObject resultObject = new JsonObject(); + + for (var entry : materialMiningSpeeds.entrySet()) { + JsonObject toolSpeedsObject = new JsonObject(); + + for (var toolEntry : entry.getValue().entrySet()) { + int rawItemId = itemRegistry.getRawId(toolEntry.getKey()); + toolSpeedsObject.addProperty(Integer.toString(rawItemId), toolEntry.getValue()); + } + resultObject.add(entry.getKey(), toolSpeedsObject); + } + + return resultObject; + } + + public static class MaterialInfo { + private final String materialName; + private final Predicate predicate; + private final List includedMaterials = new ArrayList<>(); + + public MaterialInfo(String materialName, Predicate predicate) { + this.materialName = materialName; + this.predicate = predicate; + } + + protected MaterialInfo includes(List otherMaterials) { + this.includedMaterials.addAll(otherMaterials); + return this; + } + + public String getMaterialName() { + return materialName; + } + + public Predicate getPredicate() { + return predicate; + } + + public boolean includesMaterial(MaterialInfo materialInfo) { + return includedMaterials.contains(materialInfo); + } + + @Override + public String toString() { + return materialName; + } + } +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java new file mode 100644 index 00000000..5a6b1081 --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java @@ -0,0 +1,32 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.minecraft.particle.ParticleType; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +public class ParticlesDataGenerator implements IDataGenerator { + + public static JsonObject generateParticleType(Registry> registry, ParticleType particleType) { + JsonObject effectDesc = new JsonObject(); + Identifier registryKey = registry.getKey(particleType).orElseThrow().getValue(); + + effectDesc.addProperty("id", registry.getRawId(particleType)); + effectDesc.addProperty("name", registryKey.getPath()); + return effectDesc; + } + + @Override + public String getDataName() { + return "particles"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry> particleTypeRegistry = Registry.PARTICLE_TYPE; + particleTypeRegistry.forEach(particleType -> resultsArray.add(generateParticleType(particleTypeRegistry, particleType))); + return resultsArray; + } +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java new file mode 100644 index 00000000..389bcc4e --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java @@ -0,0 +1,132 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.item.Item; +import net.minecraft.recipe.Ingredient; +import net.minecraft.recipe.Recipe; +import net.minecraft.recipe.ShapedRecipe; +import net.minecraft.recipe.ShapelessRecipe; +import net.minecraft.util.registry.Registry; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class RecipeDataGenerator implements IDataGenerator { + + private static int getRawIdFor(Item item) { + return Registry.ITEM.getRawId(item); + } + + @Override + public String getDataName() { + return "recipes"; + } + + @Override + public JsonElement generateDataJson() { + JsonObject finalObj = new JsonObject(); + Multimap recipes = ArrayListMultimap.create(); + for (Recipe recipe : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) { + if (recipe instanceof ShapedRecipe sr) { + var ingredients = sr.getIngredients(); + List ingr = new ArrayList<>(); + for (int i = 0; i < 9; i++) { + if (i >= ingredients.size()) { + ingr.add(-1); + continue; + } + var stacks = ingredients.get(i); + var matching = stacks.getMatchingStacks(); + if (matching.length == 0) { + ingr.add(-1); + } else { + ingr.add(getRawIdFor(matching[0].getItem())); + } + } + Lists.reverse(ingr); + + JsonArray inShape = new JsonArray(); + + var iter = ingr.iterator(); + for (int y = 0; y < 3; y++) { + var jsonRow = new JsonArray(); + int one = iter.next(); + int two = iter.next(); + int three = iter.next(); + if (y > 0 && one == -1 && two == -1 && three == -1) continue; + jsonRow.add(one); + jsonRow.add(two); + jsonRow.add(three); + inShape.add(jsonRow); + } + + JsonObject finalRecipe = new JsonObject(); + finalRecipe.add("inShape", inShape); + + var resultObject = new JsonObject(); + resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); + resultObject.addProperty("count", sr.getOutput().getCount()); + finalRecipe.add("result", resultObject); + + String id = ((Integer) getRawIdFor(sr.getOutput().getItem())).toString(); + + if (!finalObj.has(id)) { + finalObj.add(id, new JsonArray()); + } + finalObj.get(id).getAsJsonArray().add(finalRecipe); +// var input = new JsonArray(); +// var ingredients = sr.getIngredients().stream().toList(); +// for (int y = 0; y < sr.getHeight(); y++) { +// var arr = new JsonArray(); +// for (int x = 0; x < sr.getWidth(); x++) { +// if ((y*3)+x >= ingredients.size()) { +// arr.add(JsonNull.INSTANCE); +// continue; +// } +// var ingredient = ingredients.get((y*3)+x).getMatchingStacks(); // FIXME: fix when there are more than one matching stack +// if (ingredient.length == 0) { +// arr.add(JsonNull.INSTANCE); +// } else { +// arr.add(getRawIdFor(ingredient[0].getItem())); +// } +// } +// input.add(arr); +// } +// var rootRecipeObject = new JsonObject(); +// rootRecipeObject.add("inShape", input); +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +// resultObject.addProperty("count", sr.getOutput().getCount()); +// rootRecipeObject.add("result", resultObject); +// recipes.put(getRawIdFor(sr.getOutput().getItem()), rootRecipeObject); + } else if (recipe instanceof ShapelessRecipe sl) { + var ingredients = new JsonArray(); + for (Ingredient ingredient : sl.getIngredients()) { + if (ingredient.isEmpty()) continue; + ingredients.add(getRawIdFor(ingredient.getMatchingStacks()[0].getItem())); + } + var rootRecipeObject = new JsonObject(); + rootRecipeObject.add("ingredients", ingredients); + var resultObject = new JsonObject(); + resultObject.addProperty("id", getRawIdFor(sl.getOutput().getItem())); + resultObject.addProperty("count", sl.getOutput().getCount()); + rootRecipeObject.add("result", resultObject); + recipes.put(getRawIdFor(sl.getOutput().getItem()), rootRecipeObject); + } + } + recipes.forEach((a, b) -> { + if (!finalObj.has(a.toString())) { + finalObj.add(a.toString(), new JsonArray()); + } + finalObj.get(a.toString()).getAsJsonArray().add(b); + }); + return finalObj; + } +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java new file mode 100644 index 00000000..dda76c54 --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java @@ -0,0 +1,161 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mojangannoyances.BlockColors; +import dev.u9g.minecraftdatagenerator.util.EmptyRenderBlockView; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.block.RedstoneWireBlock; +import net.minecraft.client.color.world.FoliageColors; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TintsDataGenerator implements IDataGenerator { + + public static BiomeTintColors generateBiomeTintColors(Registry biomeRegistry) { + BiomeTintColors colors = new BiomeTintColors(); + + biomeRegistry.forEach(biome -> { + int biomeGrassColor = biome.getGrassColorAt(0.0, 0.0); + int biomeFoliageColor = biome.getFoliageColor(); + int biomeWaterColor = biome.getWaterColor(); + + colors.grassColoursMap.computeIfAbsent(biomeGrassColor, k -> new ArrayList<>()).add(biome); + colors.foliageColoursMap.computeIfAbsent(biomeFoliageColor, k -> new ArrayList<>()).add(biome); + colors.waterColourMap.computeIfAbsent(biomeWaterColor, k -> new ArrayList<>()).add(biome); + }); + return colors; + } + + public static Map generateRedstoneTintColors() { + Map resultColors = new HashMap<>(); + + for (int redstoneLevel : RedstoneWireBlock.POWER.getValues()) { + int color = RedstoneWireBlock.getWireColor(redstoneLevel); + resultColors.put(redstoneLevel, color); + } + return resultColors; + } + + private static int getBlockColor(Block block, BlockColors blockColors) { + return blockColors.getColor(block.getDefaultState(), EmptyRenderBlockView.INSTANCE, BlockPos.ORIGIN, 0xFFFFFF); + } + + public static Map generateConstantTintColors() { + Map resultColors = new HashMap<>(); + BlockColors blockColors = BlockColors.create(); + + resultColors.put(Blocks.BIRCH_LEAVES, FoliageColors.getBirchColor()); + resultColors.put(Blocks.SPRUCE_LEAVES, FoliageColors.getSpruceColor()); + + resultColors.put(Blocks.LILY_PAD, getBlockColor(Blocks.LILY_PAD, blockColors)); + resultColors.put(Blocks.ATTACHED_MELON_STEM, getBlockColor(Blocks.ATTACHED_MELON_STEM, blockColors)); + resultColors.put(Blocks.ATTACHED_PUMPKIN_STEM, getBlockColor(Blocks.ATTACHED_PUMPKIN_STEM, blockColors)); + + //not really constant, depend on the block age, but kinda have to be handled since textures are literally white without them + resultColors.put(Blocks.MELON_STEM, getBlockColor(Blocks.MELON_STEM, blockColors)); + resultColors.put(Blocks.PUMPKIN_STEM, getBlockColor(Blocks.PUMPKIN_STEM, blockColors)); + + return resultColors; + } + + private static JsonObject encodeBiomeColorMap(Registry biomeRegistry, Map> colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + for (Biome biome : entry.getValue()) { + Identifier registryKey = biomeRegistry.getKey(biome).orElseThrow().getValue(); + keysArray.add(registryKey.getPath()); + } + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getKey()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeRedstoneColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + keysArray.add(entry.getKey()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeBlocksColorMap(Registry blockRegistry, Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + Identifier registryKey = blockRegistry.getKey(entry.getKey()).orElseThrow().getValue(); + keysArray.add(registryKey.getPath()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + @Override + public String getDataName() { + return "tints"; + } + + @Override + public JsonObject generateDataJson() { + DynamicRegistryManager registryManager = DynamicRegistryManager.BUILTIN.get(); + Registry biomeRegistry = registryManager.get(Registry.BIOME_KEY); + Registry blockRegistry = registryManager.get(Registry.BLOCK_KEY); + + BiomeTintColors biomeTintColors = generateBiomeTintColors(biomeRegistry); + Map redstoneColors = generateRedstoneTintColors(); + Map constantTintColors = generateConstantTintColors(); + + JsonObject resultObject = new JsonObject(); + + resultObject.add("grass", encodeBiomeColorMap(biomeRegistry, biomeTintColors.grassColoursMap)); + resultObject.add("foliage", encodeBiomeColorMap(biomeRegistry, biomeTintColors.foliageColoursMap)); + resultObject.add("water", encodeBiomeColorMap(biomeRegistry, biomeTintColors.waterColourMap)); + + resultObject.add("redstone", encodeRedstoneColorMap(redstoneColors)); + resultObject.add("constant", encodeBlocksColorMap(blockRegistry, constantTintColors)); + + return resultObject; + } + + public static class BiomeTintColors { + final Map> grassColoursMap = new HashMap<>(); + final Map> foliageColoursMap = new HashMap<>(); + final Map> waterColourMap = new HashMap<>(); + } +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java new file mode 100644 index 00000000..0d297e7a --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.server.dedicated.EulaReader; +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; + +@Mixin(EulaReader.class) +public class EULAMixin { + @Inject(method = "isEulaAgreedTo()Z", at = @At("TAIL"), cancellable = true) + public void init(CallbackInfoReturnable cir) { + cir.setReturnValue(true); + } +} diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java similarity index 83% rename from src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java rename to 1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java index 2aa1f983..3e62bf59 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java @@ -1,12 +1,12 @@ -package dev.u9g.minecraftdatagenerator.mixin; - -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityType; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(EntityType.class) -public interface EntityTypeAccessor { - @Accessor("factory") - public EntityType.EntityFactory factory(); -} +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(EntityType.class) +public interface EntityTypeAccessor { + @Accessor("factory") + EntityType.EntityFactory factory(); +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java new file mode 100644 index 00000000..cb7f8a1c --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java @@ -0,0 +1,17 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Block; +import net.minecraft.item.MiningToolItem; +import net.minecraft.tag.TagKey; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(MiningToolItem.class) +public interface MiningToolItemAccessor { + + @Accessor + TagKey getEffectiveBlocks(); + + @Accessor + float getMiningSpeed(); +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java new file mode 100644 index 00000000..6d9415eb --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.generators.DataGenerators; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.MinecraftVersion; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; +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.nio.file.Path; + +@Mixin(MinecraftDedicatedServer.class) +public class ReadyMixin { + @Inject(method = "setupServer()Z", at = @At("TAIL")) + private void init(CallbackInfoReturnable cir) { + Main.LOGGER.info("Starting data generation!"); + String versionName = MinecraftVersion.CURRENT.getName(); + Path serverRootDirectory = DGU.getCurrentlyRunningServer().getRunDirectory().toPath().toAbsolutePath(); + Path dataDumpDirectory = serverRootDirectory.resolve("minecraft-data").resolve(versionName); + DataGenerators.runDataGenerators(dataDumpDirectory); + Main.LOGGER.info("Done data generation!"); + DGU.getCurrentlyRunningServer().stop(false); + } +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/TheEndBiomeDataAccessor.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/TheEndBiomeDataAccessor.java new file mode 100644 index 00000000..2796b2a6 --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mixin/TheEndBiomeDataAccessor.java @@ -0,0 +1,29 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.fabricmc.fabric.impl.biome.TheEndBiomeData; +import net.fabricmc.fabric.impl.biome.WeightedPicker; +import net.minecraft.util.registry.RegistryKey; +import net.minecraft.world.biome.Biome; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + + +@Mixin(TheEndBiomeData.class) +public interface TheEndBiomeDataAccessor { + @Accessor(value = "END_BIOMES_MAP", remap = false) + static Map, WeightedPicker>> END_BIOMES_MAP() { + throw new IllegalStateException("Should never be called."); + } + + @Accessor(value = "END_MIDLANDS_MAP", remap = false) + static Map, WeightedPicker>> END_MIDLANDS_MAP() { + throw new IllegalStateException("Should never be called."); + } + + @Accessor(value = "END_BARRENS_MAP", remap = false) + static Map, WeightedPicker>> END_BARRENS_MAP() { + throw new IllegalStateException("Should never be called."); + } +} diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BiomeColors.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BiomeColors.java new file mode 100644 index 00000000..bb9ae83f --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BiomeColors.java @@ -0,0 +1,35 @@ +package dev.u9g.minecraftdatagenerator.mojangannoyances; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.BlockRenderView; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.level.ColorResolver; + +@Environment(EnvType.CLIENT) +public class BiomeColors { + public static final ColorResolver GRASS_COLOR = Biome::getGrassColorAt; + public static final ColorResolver FOLIAGE_COLOR = (biome, x, z) -> biome.getFoliageColor(); + public static final ColorResolver WATER_COLOR = (biome, x, z) -> biome.getWaterColor(); + + public BiomeColors() { + } + + private static int getColor(BlockRenderView world, BlockPos pos, ColorResolver resolver) { + return world.getColor(pos, resolver); + } + + public static int getGrassColor(BlockRenderView world, BlockPos pos) { + return getColor(world, pos, GRASS_COLOR); + } + + public static int getFoliageColor(BlockRenderView world, BlockPos pos) { + return getColor(world, pos, FOLIAGE_COLOR); + } + + public static int getWaterColor(BlockRenderView world, BlockPos pos) { + return getColor(world, pos, WATER_COLOR); + } +} + diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColorProvider.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColorProvider.java similarity index 97% rename from src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColorProvider.java rename to 1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColorProvider.java index 5ef233df..ac5dc572 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColorProvider.java +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColorProvider.java @@ -1,11 +1,11 @@ -package dev.u9g.minecraftdatagenerator.mojangannoyances; - -import net.minecraft.block.BlockState; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.BlockRenderView; -import org.jetbrains.annotations.Nullable; - -public interface BlockColorProvider { - int getColor(BlockState state, @Nullable BlockRenderView world, @Nullable BlockPos pos, int tintIndex); -} - +package dev.u9g.minecraftdatagenerator.mojangannoyances; + +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.BlockRenderView; +import org.jetbrains.annotations.Nullable; + +public interface BlockColorProvider { + int getColor(BlockState state, @Nullable BlockRenderView world, @Nullable BlockPos pos, int tintIndex); +} + diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColors.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColors.java new file mode 100644 index 00000000..2107780e --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColors.java @@ -0,0 +1,95 @@ +package dev.u9g.minecraftdatagenerator.mojangannoyances; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import net.minecraft.block.*; +import net.minecraft.block.enums.DoubleBlockHalf; +import net.minecraft.client.color.world.FoliageColors; +import net.minecraft.client.color.world.GrassColors; +import net.minecraft.state.property.Property; +import net.minecraft.util.collection.IdList; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.BlockRenderView; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.Set; + +public class BlockColors { + private static final int NO_COLOR = -1; + private final IdList providers = new IdList<>(32); + private final Map>> properties = Maps.newHashMap(); + + public BlockColors() { + } + + public static BlockColors create() { + BlockColors blockColors = new BlockColors(); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getGrassColor(world, state.get(TallPlantBlock.HALF) == DoubleBlockHalf.UPPER ? pos.down() : pos) : -1, Blocks.LARGE_FERN, Blocks.TALL_GRASS); + blockColors.registerColorProperty(TallPlantBlock.HALF, Blocks.LARGE_FERN, Blocks.TALL_GRASS); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getGrassColor(world, pos) : GrassColors.getColor(0.5, 1.0), Blocks.GRASS_BLOCK, Blocks.FERN, Blocks.GRASS, Blocks.POTTED_FERN); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> FoliageColors.getSpruceColor(), Blocks.SPRUCE_LEAVES); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> FoliageColors.getBirchColor(), Blocks.BIRCH_LEAVES); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getFoliageColor(world, pos) : FoliageColors.getDefaultColor(), Blocks.OAK_LEAVES, Blocks.JUNGLE_LEAVES, Blocks.ACACIA_LEAVES, Blocks.DARK_OAK_LEAVES, Blocks.VINE, Blocks.MANGROVE_LEAVES); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getWaterColor(world, pos) : -1, Blocks.WATER, Blocks.BUBBLE_COLUMN, Blocks.WATER_CAULDRON); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> RedstoneWireBlock.getWireColor((Integer) state.get(RedstoneWireBlock.POWER)), Blocks.REDSTONE_WIRE); + blockColors.registerColorProperty(RedstoneWireBlock.POWER, Blocks.REDSTONE_WIRE); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getGrassColor(world, pos) : -1, Blocks.SUGAR_CANE); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> 14731036, Blocks.ATTACHED_MELON_STEM, Blocks.ATTACHED_PUMPKIN_STEM); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> { + int i = (Integer) state.get(StemBlock.AGE); + int j = i * 32; + int k = 255 - i * 8; + int l = i * 4; + return j << 16 | k << 8 | l; + }, Blocks.MELON_STEM, Blocks.PUMPKIN_STEM); + blockColors.registerColorProperty(StemBlock.AGE, Blocks.MELON_STEM, Blocks.PUMPKIN_STEM); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? 2129968 : 7455580, Blocks.LILY_PAD); + return blockColors; + } + + public int getParticleColor(BlockState state, World world, BlockPos pos) { + BlockColorProvider blockColorProvider = (BlockColorProvider) this.providers.get(Registry.BLOCK.getRawId(state.getBlock())); + if (blockColorProvider != null) { + return blockColorProvider.getColor(state, (BlockRenderView) null, (BlockPos) null, 0); + } else { + MapColor mapColor = state.getMapColor(world, pos); + return mapColor != null ? mapColor.color : -1; + } + } + + public int getColor(BlockState state, @Nullable BlockRenderView world, @Nullable BlockPos pos, int tintIndex) { + BlockColorProvider blockColorProvider = (BlockColorProvider) this.providers.get(Registry.BLOCK.getRawId(state.getBlock())); + return blockColorProvider == null ? -1 : blockColorProvider.getColor(state, world, pos, tintIndex); + } + + public void registerColorProvider(BlockColorProvider provider, Block... blocks) { + int var4 = blocks.length; + + for (int var5 = 0; var5 < var4; ++var5) { + Block block = blocks[var5]; + this.providers.set(provider, Registry.BLOCK.getRawId(block)); + } + + } + + private void registerColorProperties(Set> properties, Block... blocks) { + int var4 = blocks.length; + + for (int var5 = 0; var5 < var4; ++var5) { + Block block = blocks[var5]; + this.properties.put(block, properties); + } + + } + + private void registerColorProperty(Property property, Block... blocks) { + this.registerColorProperties(ImmutableSet.of(property), blocks); + } + + public Set> getProperties(Block block) { + return (Set) this.properties.getOrDefault(block, ImmutableSet.of()); + } +} diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java similarity index 93% rename from src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java rename to 1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java index e98112f5..32c2fb65 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java @@ -1,7 +1,5 @@ package dev.u9g.minecraftdatagenerator.util; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.server.MinecraftServer; import net.minecraft.util.Language; @@ -9,6 +7,8 @@ public class DGU { + private static final Language language = Language.getInstance(); + @SuppressWarnings("deprecation") private static MinecraftServer getCurrentlyRunningServerDedicated() { return (MinecraftServer) FabricLoader.getInstance().getGameInstance(); @@ -18,8 +18,6 @@ public static MinecraftServer getCurrentlyRunningServer() { return getCurrentlyRunningServerDedicated(); } - private static final Language language = Language.getInstance(); - private static String translateTextFallback(String translationKey) { return language.get(translationKey); } diff --git a/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java new file mode 100644 index 00000000..1cff92d4 --- /dev/null +++ b/1.19.2/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java @@ -0,0 +1,73 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.fluid.FluidState; +import net.minecraft.fluid.Fluids; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.BlockRenderView; +import net.minecraft.world.LightType; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.BiomeKeys; +import net.minecraft.world.chunk.light.LightingProvider; +import net.minecraft.world.level.ColorResolver; +import org.jetbrains.annotations.Nullable; + +public enum EmptyRenderBlockView implements BlockRenderView { + INSTANCE; + + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + return null; + } + + public BlockState getBlockState(BlockPos pos) { + return Blocks.AIR.getDefaultState(); + } + + public FluidState getFluidState(BlockPos pos) { + return Fluids.EMPTY.getDefaultState(); + } + + public int getBottomY() { + return 0; + } + + public int getHeight() { + return 0; + } + + + @Override + public float getBrightness(Direction direction, boolean shaded) { + return 0.0f; + } + + @Override + public LightingProvider getLightingProvider() { + return null; + } + + @Override + public int getColor(BlockPos pos, ColorResolver colorResolver) { + DynamicRegistryManager registryManager = DynamicRegistryManager.BUILTIN.get(); + Registry biomeRegistry = registryManager.get(Registry.BIOME_KEY); + Biome plainsBiome = biomeRegistry.get(BiomeKeys.PLAINS); + + return colorResolver.getColor(plainsBiome, pos.getX(), pos.getY()); + } + + @Override + public int getLightLevel(LightType type, BlockPos pos) { + return type == LightType.SKY ? getMaxLightLevel() : 0; + } + + @Override + public int getBaseLightLevel(BlockPos pos, int ambientDarkness) { + return ambientDarkness; + } +} diff --git a/src/main/resources/fabric.mod.json b/1.19.2/src/main/resources/fabric.mod.json similarity index 83% rename from src/main/resources/fabric.mod.json rename to 1.19.2/src/main/resources/fabric.mod.json index 3cd71632..c999d2a8 100644 --- a/src/main/resources/fabric.mod.json +++ b/1.19.2/src/main/resources/fabric.mod.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "minecraft-data-generator", "version": "${version}", - "name": "Minecrft Data Generator", + "name": "Minecraft Data Generator", "description": "", "authors": [ "Archengius", @@ -10,7 +10,6 @@ ], "contact": {}, "license": "MIT", - "icon": "assets/minecrft-data-generator/icon.png", "environment": "server", "entrypoints": { "main": [ diff --git a/src/main/resources/minecraft-data-generator.mixins.json b/1.19.2/src/main/resources/minecraft-data-generator.mixins.json similarity index 100% rename from src/main/resources/minecraft-data-generator.mixins.json rename to 1.19.2/src/main/resources/minecraft-data-generator.mixins.json diff --git a/1.19/build.gradle b/1.19/build.gradle new file mode 100644 index 00000000..e85de392 --- /dev/null +++ b/1.19/build.gradle @@ -0,0 +1,31 @@ +plugins { + id 'fabric-loom' +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API. This is technically optional, but you probably want it anyway. + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" +} + +processResources { + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} diff --git a/1.19/gradle.properties b/1.19/gradle.properties new file mode 100644 index 00000000..b4a4ae23 --- /dev/null +++ b/1.19/gradle.properties @@ -0,0 +1,8 @@ +# Fabric Properties +# check these on https://modmuss50.me/fabric.html +minecraft_version=1.19 +yarn_mappings=1.19+build.1 +loader_version=0.14.6 +# Dependencies +# check this on https://modmuss50.me/fabric.html +fabric_version=0.55.2+1.19 diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/Main.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/Main.java new file mode 100644 index 00000000..f8d17372 --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/Main.java @@ -0,0 +1,14 @@ +package dev.u9g.minecraftdatagenerator; + +import net.fabricmc.api.ModInitializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Main implements ModInitializer { + public static final Logger LOGGER = LoggerFactory.getLogger("mc-data-gen-serv"); + + @Override + public void onInitialize() { + + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java new file mode 100644 index 00000000..88200df7 --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java @@ -0,0 +1,315 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.TheEndBiomeDataAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.fabricmc.fabric.api.biome.v1.NetherBiomes; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.RegistryKey; +import net.minecraft.world.biome.Biome; + +import java.util.HashSet; +import java.util.Set; + +public class BiomesDataGenerator implements IDataGenerator { + private static final Set> END_BIOMES = new HashSet<>(); + + static { + END_BIOMES.addAll(TheEndBiomeDataAccessor.END_BIOMES_MAP().keySet()); + END_BIOMES.addAll(TheEndBiomeDataAccessor.END_BARRENS_MAP().keySet()); + END_BIOMES.addAll(TheEndBiomeDataAccessor.END_MIDLANDS_MAP().keySet()); + } + + private static String guessBiomeDimensionFromCategory(Biome biome, String biomeName) { + var key = DynamicRegistryManager.BUILTIN.get().get(Registry.BIOME_KEY).getKey(biome).orElseThrow(); + if (NetherBiomes.canGenerateInNether(key)) { + return "nether"; + } else if (END_BIOMES.contains(key) || biomeName.startsWith("end_")) { + return "end"; + } + return "overworld"; + } + + private static String guessCategoryBasedOnName(String name, String dimension) { + if (dimension.equals("nether")) { + return "nether"; + } else if (dimension.equals("end")) { + return "the_end"; + } + + if (name.contains("end")) { + System.out.println(); + } + + if (name.contains("hills")) { + return "extreme_hills"; + } else if (name.contains("ocean")) { + return "ocean"; + } else if (name.contains("plains")) { + return "plains"; + } else if (name.contains("ice") || name.contains("frozen")) { + return "ice"; + } else if (name.contains("jungle")) { + return "jungle"; + } else if (name.contains("desert")) { + return "desert"; + } else if (name.contains("forest") || name.contains("grove")) { + return "forest"; + } else if (name.contains("taiga")) { + return "taiga"; + } else if (name.contains("swamp")) { + return "swamp"; + } else if (name.contains("river")) { + return "river"; + } else if (name.equals("the_end")) { + return "the_end"; + } else if (name.contains("mushroom")) { + return "mushroom"; + } else if (name.contains("beach") || name.equals("stony_shore")) { + return "beach"; + } else if (name.contains("savanna")) { + return "savanna"; + } else if (name.contains("badlands")) { + return "mesa"; + } else if (name.contains("peaks") || name.equals("snowy_slopes") || name.equals("meadow")) { + return "mountain"; + } else if (name.equals("the_void")) { + return "none"; + } else if (name.contains("cave") || name.equals("deep_dark")) { + return "underground"; + } else { + System.out.println("Unable to find biome category for biome with name: '" + name + "'"); + return "none"; + } + } + + private static int getBiomeColorFor(String biomeName) { + switch (biomeName) { + case "the_void" -> { + return 0; + } + case "plains" -> { + return 9286496; + } + case "sunflower_plains" -> { + return 11918216; + } + case "snowy_plains" -> { + return 16777215; + } + case "ice_spikes" -> { + return 11853020; + } + case "desert" -> { + return 16421912; + } + case "swamp" -> { + return 522674; + } + case "forest" -> { + return 353825; + } + case "flower_forest" -> { + return 2985545; + } + case "birch_forest" -> { + return 3175492; + } + case "dark_forest" -> { + return 4215066; + } + case "old_growth_birch_forest" -> { + return 5807212; + } + case "old_growth_pine_taiga" -> { + return 5858897; + } + case "old_growth_spruce_taiga" -> { + return 8490617; + } + case "taiga" -> { + return 747097; + } + case "snowy_taiga" -> { + return 3233098; + } + case "savanna" -> { + return 12431967; + } + case "savanna_plateau" -> { + return 10984804; + } + case "windswept_hills" -> { + return 6316128; + } + case "windswept_gravelly_hills" -> { + return 8947848; + } + case "windswept_forest" -> { + return 2250012; + } + case "windswept_savanna" -> { + return 15063687; + } + case "jungle" -> { + return 5470985; + } + case "sparse_jungle" -> { + return 6458135; + } + case "bamboo_jungle" -> { + return 7769620; + } + case "badlands" -> { + return 14238997; + } + case "eroded_badlands" -> { + return 16739645; + } + case "wooded_badlands" -> { + return 11573093; + } + case "meadow" -> { + return 9217136; + } + case "grove" -> { + return 14675173; + } + case "snowy_slopes" -> { + return 14348785; + } + case "frozen_peaks" -> { + return 15399931; + } + case "jagged_peaks" -> { + return 14937325; + } + case "stony_peaks" -> { + return 13750737; + } + case "river" -> { + return 255; + } + case "frozen_river" -> { + return 10526975; + } + case "beach" -> { + return 16440917; + } + case "snowy_beach" -> { + return 16445632; + } + case "stony_shore" -> { + return 10658436; + } + case "warm_ocean" -> { + return 172; + } + case "lukewarm_ocean" -> { + return 144; + } + case "deep_lukewarm_ocean" -> { + return 64; + } + case "ocean" -> { + return 112; + } + case "deep_ocean" -> { + return 48; + } + case "cold_ocean" -> { + return 2105456; + } + case "deep_cold_ocean" -> { + return 2105400; + } + case "frozen_ocean" -> { + return 7368918; + } + case "deep_frozen_ocean" -> { + return 4210832; + } + case "mushroom_fields" -> { + return 16711935; + } + case "dripstone_caves" -> { + return 12690831; + } + case "lush_caves" -> { + return 14652980; + } + case "nether_wastes" -> { + return 12532539; + } + case "warped_forest" -> { + return 4821115; + } + case "crimson_forest" -> { + return 14485512; + } + case "soul_sand_valley" -> { + return 6174768; + } + case "basalt_deltas" -> { + return 4208182; + } + case "the_end" -> { + return 8421631; + } + case "end_highlands" -> { + return 12828041; + } + case "end_midlands" -> { + return 15464630; + } + case "small_end_islands" -> { + return 42; + } + case "end_barrens" -> { + return 9474162; + } + } + System.out.println("Don't know the color of biome: '" + biomeName + "'"); + return 0; + } + + public static JsonObject generateBiomeInfo(Registry registry, Biome biome) { + JsonObject biomeDesc = new JsonObject(); + Identifier registryKey = registry.getKey(biome).orElseThrow().getValue(); + String localizationKey = String.format("biome.%s.%s", registryKey.getNamespace(), registryKey.getPath()); + String name = registryKey.getPath(); + biomeDesc.addProperty("id", registry.getRawId(biome)); + biomeDesc.addProperty("name", name); + String dimension = guessBiomeDimensionFromCategory(biome, name); + biomeDesc.addProperty("category", guessCategoryBasedOnName(name, dimension)); + biomeDesc.addProperty("temperature", biome.getTemperature()); + biomeDesc.addProperty("precipitation", biome.getPrecipitation().getName()); + //biomeDesc.addProperty("depth", biome.getDepth()); - Doesn't exist anymore in minecraft source + biomeDesc.addProperty("dimension", dimension); + biomeDesc.addProperty("displayName", DGU.translateText(localizationKey)); + biomeDesc.addProperty("color", getBiomeColorFor(registryKey.getPath())); + biomeDesc.addProperty("rainfall", biome.getDownfall()); + + return biomeDesc; + } + + @Override + public String getDataName() { + return "biomes"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray biomesArray = new JsonArray(); + DynamicRegistryManager registryManager = DynamicRegistryManager.BUILTIN.get(); + Registry biomeRegistry = registryManager.get(Registry.BIOME_KEY); + + biomeRegistry.stream() + .map(biome -> generateBiomeInfo(biomeRegistry, biome)) + .forEach(biomesArray::add); + return biomesArray; + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java new file mode 100644 index 00000000..52c3181d --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java @@ -0,0 +1,111 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.EmptyBlockView; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class BlockCollisionShapesDataGenerator implements IDataGenerator { + + @Override + public String getDataName() { + return "blockCollisionShapes"; + } + + @Override + public JsonObject generateDataJson() { + Registry blockRegistry = Registry.BLOCK; + BlockShapesCache blockShapesCache = new BlockShapesCache(); + + blockRegistry.forEach(blockShapesCache::processBlock); + + JsonObject resultObject = new JsonObject(); + + resultObject.add("blocks", blockShapesCache.dumpBlockShapeIndices(blockRegistry)); + resultObject.add("shapes", blockShapesCache.dumpShapesObject()); + + return resultObject; + } + + private static class BlockShapesCache { + public final Map uniqueBlockShapes = new HashMap<>(); + public final Map> blockCollisionShapes = new HashMap<>(); + private int lastCollisionShapeId = 0; + + public void processBlock(Block block) { + List blockStates = block.getStateManager().getStates(); + List blockCollisionShapes = new ArrayList<>(); + + for (BlockState blockState : blockStates) { + VoxelShape blockShape = blockState.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + Integer blockShapeIndex = uniqueBlockShapes.get(blockShape); + + if (blockShapeIndex == null) { + blockShapeIndex = lastCollisionShapeId++; + uniqueBlockShapes.put(blockShape, blockShapeIndex); + } + blockCollisionShapes.add(blockShapeIndex); + } + + this.blockCollisionShapes.put(block, blockCollisionShapes); + } + + public JsonObject dumpBlockShapeIndices(Registry blockRegistry) { + JsonObject resultObject = new JsonObject(); + + for (var entry : blockCollisionShapes.entrySet()) { + List blockCollisions = entry.getValue(); + long distinctShapesCount = blockCollisions.stream().distinct().count(); + JsonElement blockCollision; + if (distinctShapesCount == 1L) { + blockCollision = new JsonPrimitive(blockCollisions.get(0)); + } else { + blockCollision = new JsonArray(); + for (int collisionId : blockCollisions) { + ((JsonArray) blockCollision).add(collisionId); + } + } + + Identifier registryKey = blockRegistry.getKey(entry.getKey()).orElseThrow().getValue(); + resultObject.add(registryKey.getPath(), blockCollision); + } + + return resultObject; + } + + public JsonObject dumpShapesObject() { + JsonObject shapesObject = new JsonObject(); + + for (var entry : uniqueBlockShapes.entrySet()) { + JsonArray boxesArray = new JsonArray(); + entry.getKey().forEachBox((x1, y1, z1, x2, y2, z2) -> { + JsonArray oneBoxJsonArray = new JsonArray(); + + oneBoxJsonArray.add(x1); + oneBoxJsonArray.add(y1); + oneBoxJsonArray.add(z1); + + oneBoxJsonArray.add(x2); + oneBoxJsonArray.add(y2); + oneBoxJsonArray.add(z2); + + boxesArray.add(oneBoxJsonArray); + }); + shapesObject.add(Integer.toString(entry.getValue()), boxesArray); + } + return shapesObject; + } + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java new file mode 100644 index 00000000..51139655 --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java @@ -0,0 +1,198 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.base.CaseFormat; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.block.AirBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.loot.context.LootContext; +import net.minecraft.loot.context.LootContextParameters; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.state.property.EnumProperty; +import net.minecraft.state.property.IntProperty; +import net.minecraft.state.property.Property; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.EmptyBlockView; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +public class BlocksDataGenerator implements IDataGenerator { + + private static final Logger logger = LoggerFactory.getLogger(BlocksDataGenerator.class); + + private static List getItemsEffectiveForBlock(BlockState blockState) { + return Registry.ITEM.stream() + .filter(item -> item.getDefaultStack().isSuitableFor(blockState)) + .collect(Collectors.toList()); + } + + private static void populateDropsIfPossible(BlockState blockState, Item firstToolItem, List outDrops) { + MinecraftServer minecraftServer = DGU.getCurrentlyRunningServer(); + if (minecraftServer != null) { + //If we have local world context, we can actually evaluate loot tables and determine actual data + ServerWorld serverWorld = minecraftServer.getOverworld(); + LootContext.Builder lootContext = new LootContext.Builder(serverWorld) + .parameter(LootContextParameters.BLOCK_STATE, blockState) + .parameter(LootContextParameters.ORIGIN, Vec3d.ZERO) + .parameter(LootContextParameters.TOOL, firstToolItem.getDefaultStack()) + .random(0L); + outDrops.addAll(blockState.getDroppedStacks(lootContext)); + } else { + //If we're lacking world context to correctly determine drops, assume that default drop is ItemBlock stack in quantity of 1 + Item itemBlock = blockState.getBlock().asItem(); + if (itemBlock != Items.AIR) { + outDrops.add(itemBlock.getDefaultStack()); + } + } + } + + private static String getPropertyTypeName(Property property) { + //Explicitly handle default minecraft properties + if (property instanceof BooleanProperty) { + return "bool"; + } + if (property instanceof IntProperty) { + return "int"; + } + if (property instanceof EnumProperty) { + return "enum"; + } + + //Use simple class name as fallback, this code will give something like + //example_type for ExampleTypeProperty class name + String rawPropertyName = property.getClass().getSimpleName().replace("Property", ""); + return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, rawPropertyName); + } + + private static > JsonObject generateStateProperty(Property property) { + JsonObject propertyObject = new JsonObject(); + Collection propertyValues = property.getValues(); + + propertyObject.addProperty("name", property.getName()); + propertyObject.addProperty("type", getPropertyTypeName(property)); + propertyObject.addProperty("num_values", propertyValues.size()); + + //Do not add values for vanilla boolean properties, they are known by default + if (!(property instanceof BooleanProperty)) { + JsonArray propertyValuesArray = new JsonArray(); + for (T propertyValue : propertyValues) { + propertyValuesArray.add(property.name(propertyValue)); + } + propertyObject.add("values", propertyValuesArray); + } + return propertyObject; + } + + private static String findMatchingBlockMaterial(BlockState blockState, List materials) { + List matchingMaterials = materials.stream() + .filter(material -> material.getPredicate().test(blockState)) + .collect(Collectors.toList()); + + if (matchingMaterials.size() > 1) { + var firstMaterial = matchingMaterials.get(0); + var otherMaterials = matchingMaterials.subList(1, matchingMaterials.size()); + + if (!otherMaterials.stream().allMatch(firstMaterial::includesMaterial)) { + logger.error("Block {} matches multiple materials: {}", blockState.getBlock(), matchingMaterials); + } + } + if (matchingMaterials.isEmpty()) { + return "default"; + } + return matchingMaterials.get(0).getMaterialName(); + } + + public static JsonObject generateBlock(Registry blockRegistry, List materials, Block block) { + JsonObject blockDesc = new JsonObject(); + + List blockStates = block.getStateManager().getStates(); + BlockState defaultState = block.getDefaultState(); + Identifier registryKey = blockRegistry.getKey(block).orElseThrow().getValue(); + String localizationKey = block.getTranslationKey(); + List effectiveTools = getItemsEffectiveForBlock(defaultState); + + blockDesc.addProperty("id", blockRegistry.getRawId(block)); + blockDesc.addProperty("name", registryKey.getPath()); + blockDesc.addProperty("displayName", DGU.translateText(localizationKey)); + + blockDesc.addProperty("hardness", block.getHardness()); + blockDesc.addProperty("resistance", block.getBlastResistance()); + blockDesc.addProperty("stackSize", block.asItem().getMaxCount()); + blockDesc.addProperty("diggable", block.getHardness() != -1.0f && !(block instanceof AirBlock)); +// JsonObject effTools = new JsonObject(); +// effectiveTools.forEach(item -> effTools.addProperty( +// String.valueOf(Registry.ITEM.getRawId(item)), // key +// item.getMiningSpeedMultiplier(item.getDefaultStack(), defaultState) // value +// )); +// blockDesc.add("effectiveTools", effTools); + blockDesc.addProperty("material", findMatchingBlockMaterial(defaultState, materials)); + + blockDesc.addProperty("transparent", !defaultState.isOpaque()); + blockDesc.addProperty("emitLight", defaultState.getLuminance()); + blockDesc.addProperty("filterLight", defaultState.getOpacity(EmptyBlockView.INSTANCE, BlockPos.ORIGIN)); + + blockDesc.addProperty("defaultState", Block.getRawIdFromState(defaultState)); + blockDesc.addProperty("minStateId", Block.getRawIdFromState(blockStates.get(0))); + blockDesc.addProperty("maxStateId", Block.getRawIdFromState(blockStates.get(blockStates.size() - 1))); + + JsonArray stateProperties = new JsonArray(); + for (Property property : block.getStateManager().getProperties()) { + stateProperties.add(generateStateProperty(property)); + } + blockDesc.add("states", stateProperties); + + //Only add harvest tools if tool is required for harvesting this block + if (defaultState.isToolRequired()) { + JsonObject effectiveToolsObject = new JsonObject(); + for (Item effectiveItem : effectiveTools) { + effectiveToolsObject.addProperty(Integer.toString(Item.getRawId(effectiveItem)), true); + } + blockDesc.add("harvestTools", effectiveToolsObject); + } + + List actualBlockDrops = new ArrayList<>(); + populateDropsIfPossible(defaultState, effectiveTools.isEmpty() ? Items.AIR : effectiveTools.get(0), actualBlockDrops); + + JsonArray dropsArray = new JsonArray(); + for (ItemStack dropStack : actualBlockDrops) { + dropsArray.add(Item.getRawId(dropStack.getItem())); + } + blockDesc.add("drops", dropsArray); + + VoxelShape blockCollisionShape = defaultState.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + blockDesc.addProperty("boundingBox", blockCollisionShape.isEmpty() ? "empty" : "block"); + + return blockDesc; + } + + @Override + public String getDataName() { + return "blocks"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultBlocksArray = new JsonArray(); + Registry blockRegistry = Registry.BLOCK; + List availableMaterials = MaterialsDataGenerator.getGlobalMaterialInfo(); + + blockRegistry.forEach(block -> resultBlocksArray.add(generateBlock(blockRegistry, availableMaterials, block))); + return resultBlocksArray; + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java new file mode 100644 index 00000000..c47684ab --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java @@ -0,0 +1,77 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.List; + +public class DataGenerators { + + private static final Logger logger = LoggerFactory.getLogger(DataGenerators.class); + private static final List GENERATORS = new ArrayList<>(); + + static { + register(new BiomesDataGenerator()); + register(new BlockCollisionShapesDataGenerator()); + register(new BlocksDataGenerator()); + register(new EffectsDataGenerator()); + register(new EnchantmentsDataGenerator()); + register(new EntitiesDataGenerator()); + register(new FoodsDataGenerator()); + register(new ItemsDataGenerator()); + register(new ParticlesDataGenerator()); + register(new TintsDataGenerator()); + register(new MaterialsDataGenerator()); +// register(new RecipeDataGenerator()); - On hold until mcdata supports multiple materials for a recipe + register(new LanguageDataGenerator()); + register(new InstrumentsDataGenerator()); + } + + public static void register(IDataGenerator generator) { + GENERATORS.add(generator); + } + + public static boolean runDataGenerators(Path outputDirectory) { + try { + Files.createDirectories(outputDirectory); + } catch (IOException exception) { + logger.error("Failed to create data generator output directory at {}", outputDirectory, exception); + return false; + } + + int generatorsFailed = 0; + logger.info("Running minecraft data generators, output at {}", outputDirectory); + + for (IDataGenerator dataGenerator : GENERATORS) { + logger.info("Running generator {}", dataGenerator.getDataName()); + try { + String outputFileName = String.format("%s.json", dataGenerator.getDataName()); + JsonElement outputElement = dataGenerator.generateDataJson(); + Path outputFilePath = outputDirectory.resolve(outputFileName); + + try (Writer writer = Files.newBufferedWriter(outputFilePath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + JsonWriter jsonWriter = new JsonWriter(writer); + jsonWriter.setIndent(" "); + Streams.write(outputElement, jsonWriter); + } + logger.info("Generator: {} -> {}", dataGenerator.getDataName(), outputFileName); + + } catch (Throwable exception) { + logger.error("Failed to run data generator {}", dataGenerator.getDataName(), exception); + generatorsFailed++; + } + } + + logger.info("Finishing running data generators"); + return generatorsFailed == 0; + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java new file mode 100644 index 00000000..1c9849e6 --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java @@ -0,0 +1,46 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.stream.Collectors; + +public class EffectsDataGenerator implements IDataGenerator { + + public static JsonObject generateEffect(Registry registry, StatusEffect statusEffect) { + JsonObject effectDesc = new JsonObject(); + Identifier registryKey = registry.getKey(statusEffect).orElseThrow().getValue(); + + effectDesc.addProperty("id", registry.getRawId(statusEffect)); + if (statusEffect == StatusEffects.UNLUCK) { + effectDesc.addProperty("name", "BadLuck"); + effectDesc.addProperty("displayName", "Bad Luck"); + } else { + effectDesc.addProperty("name", Arrays.stream(registryKey.getPath().split("_")).map(StringUtils::capitalize).collect(Collectors.joining())); + effectDesc.addProperty("displayName", DGU.translateText(statusEffect.getTranslationKey())); + } + + effectDesc.addProperty("type", statusEffect.isBeneficial() ? "good" : "bad"); + return effectDesc; + } + + @Override + public String getDataName() { + return "effects"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry statusEffectRegistry = Registry.STATUS_EFFECT; + statusEffectRegistry.forEach(effect -> resultsArray.add(generateEffect(statusEffectRegistry, effect))); + return resultsArray; + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java new file mode 100644 index 00000000..a71961c9 --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java @@ -0,0 +1,108 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +public class EnchantmentsDataGenerator implements IDataGenerator { + + private static final ImmutableMap ENCHANTMENT_TARGET_NAMES = ImmutableMap.builder() + .put(EnchantmentTarget.ARMOR, "armor") + .put(EnchantmentTarget.ARMOR_FEET, "armor_feet") + .put(EnchantmentTarget.ARMOR_LEGS, "armor_legs") + .put(EnchantmentTarget.ARMOR_CHEST, "armor_chest") + .put(EnchantmentTarget.ARMOR_HEAD, "armor_head") + .put(EnchantmentTarget.WEAPON, "weapon") + .put(EnchantmentTarget.DIGGER, "digger") + .put(EnchantmentTarget.FISHING_ROD, "fishing_rod") + .put(EnchantmentTarget.TRIDENT, "trident") + .put(EnchantmentTarget.BREAKABLE, "breakable") + .put(EnchantmentTarget.BOW, "bow") + .put(EnchantmentTarget.WEARABLE, "wearable") + .put(EnchantmentTarget.CROSSBOW, "crossbow") + .put(EnchantmentTarget.VANISHABLE, "vanishable") + .build(); + + public static String getEnchantmentTargetName(EnchantmentTarget target) { + return ENCHANTMENT_TARGET_NAMES.getOrDefault(target, target.name().toLowerCase(Locale.ROOT)); + } + + //Equation enchantment costs follow is a * level + b, so we can easily retrieve a and b by passing zero level + private static JsonObject generateEnchantmentMinPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMinPower(0); + int a = enchantment.getMinPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + private static JsonObject generateEnchantmentMaxPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMaxPower(0); + int a = enchantment.getMaxPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + public static JsonObject generateEnchantment(Registry registry, Enchantment enchantment) { + JsonObject enchantmentDesc = new JsonObject(); + Identifier registryKey = registry.getKey(enchantment).orElseThrow().getValue(); + + enchantmentDesc.addProperty("id", registry.getRawId(enchantment)); + enchantmentDesc.addProperty("name", registryKey.getPath()); + enchantmentDesc.addProperty("displayName", DGU.translateText(enchantment.getTranslationKey())); + + enchantmentDesc.addProperty("maxLevel", enchantment.getMaxLevel()); + enchantmentDesc.add("minCost", generateEnchantmentMinPowerCoefficients(enchantment)); + enchantmentDesc.add("maxCost", generateEnchantmentMaxPowerCoefficients(enchantment)); + + enchantmentDesc.addProperty("treasureOnly", enchantment.isTreasure()); + enchantmentDesc.addProperty("curse", enchantment.isCursed()); + + List incompatibleEnchantments = registry.stream() + .filter(other -> !enchantment.canCombine(other)) + .filter(other -> other != enchantment) + .collect(Collectors.toList()); + + JsonArray excludes = new JsonArray(); + for (Enchantment excludedEnchantment : incompatibleEnchantments) { + Identifier otherKey = registry.getKey(excludedEnchantment).orElseThrow().getValue(); + excludes.add(otherKey.getPath()); + } + enchantmentDesc.add("exclude", excludes); + + enchantmentDesc.addProperty("category", getEnchantmentTargetName(enchantment.type)); + enchantmentDesc.addProperty("weight", enchantment.getRarity().getWeight()); + enchantmentDesc.addProperty("tradeable", enchantment.isAvailableForEnchantedBookOffer()); + enchantmentDesc.addProperty("discoverable", enchantment.isAvailableForRandomSelection()); + + return enchantmentDesc; + } + + @Override + public String getDataName() { + return "enchantments"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry enchantmentRegistry = Registry.ENCHANTMENT; + enchantmentRegistry.stream() + .forEach(enchantment -> resultsArray.add(generateEnchantment(enchantmentRegistry, enchantment))); + return resultsArray; + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java new file mode 100644 index 00000000..4eacf202 --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java @@ -0,0 +1,119 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.EntityTypeAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.mob.AmbientEntity; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.mob.WaterCreatureEntity; +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.passive.PassiveEntity; +import net.minecraft.entity.projectile.ProjectileEntity; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +public class EntitiesDataGenerator implements IDataGenerator { + + public static JsonObject generateEntity(Registry> entityRegistry, EntityType entityType) { + JsonObject entityDesc = new JsonObject(); + Identifier registryKey = entityRegistry.getKey(entityType).orElseThrow().getValue(); + int entityRawId = entityRegistry.getRawId(entityType); + + entityDesc.addProperty("id", entityRawId); + entityDesc.addProperty("internalId", entityRawId); + entityDesc.addProperty("name", registryKey.getPath()); + + entityDesc.addProperty("displayName", DGU.translateText(entityType.getTranslationKey())); + entityDesc.addProperty("width", entityType.getDimensions().width); + entityDesc.addProperty("height", entityType.getDimensions().height); + + String entityTypeString = "UNKNOWN"; + MinecraftServer minecraftServer = DGU.getCurrentlyRunningServer(); + + if (minecraftServer != null) { + Entity entityObject = entityType.create(minecraftServer.getOverworld()); + entityTypeString = entityObject != null ? getEntityTypeForClass(entityObject.getClass()) : "player"; + } + entityDesc.addProperty("type", entityTypeString); + entityDesc.addProperty("category", getCategoryFrom(entityType)); + + return entityDesc; + } + + private static String getCategoryFrom(EntityType entityType) { + if (entityType == EntityType.PLAYER) return "UNKNOWN"; // fail early for player entities + /*public T create(World world) { + return this.factory.create(this, world); + }*/ + Entity entity = ((EntityTypeAccessor) entityType).factory().create(entityType, DGU.getWorld()); + if (entity == null) + throw new Error("Entity was null after trying to create a: " + DGU.translateText(entityType.getTranslationKey())); + entity.discard(); + return switch (entity.getClass().getPackageName()) { + case "net.minecraft.entity.decoration", "net.minecraft.entity.decoration.painting" -> "Immobile"; + case "net.minecraft.entity.boss", "net.minecraft.entity.mob", "net.minecraft.entity.boss.dragon" -> + "Hostile mobs"; + case "net.minecraft.entity.projectile", "net.minecraft.entity.projectile.thrown" -> "Projectiles"; + case "net.minecraft.entity.passive" -> "Passive mobs"; + case "net.minecraft.entity.vehicle" -> "Vehicles"; + case "net.minecraft.entity" -> "UNKNOWN"; + default -> throw new Error("Unexpected entity type: " + entity.getClass().getPackageName()); + }; + } + + //Honestly, both "type" and "category" fields in the schema and examples do not contain any useful information + //Since category is optional, I will just leave it out, and for type I will assume general entity classification + //by the Entity class hierarchy (which has some weirdness too by the way) + private static String getEntityTypeForClass(Class entityClass) { + //Top-level classifications + if (WaterCreatureEntity.class.isAssignableFrom(entityClass)) { + return "water_creature"; + } + if (AnimalEntity.class.isAssignableFrom(entityClass)) { + return "animal"; + } + if (HostileEntity.class.isAssignableFrom(entityClass)) { + return "hostile"; + } + if (AmbientEntity.class.isAssignableFrom(entityClass)) { + return "ambient"; + } + + //Second level classifications. PathAwareEntity is not included because it + //doesn't really make much sense to categorize by it + if (PassiveEntity.class.isAssignableFrom(entityClass)) { + return "passive"; + } + if (MobEntity.class.isAssignableFrom(entityClass)) { + return "mob"; + } + + //Other classifications only include living entities and projectiles. everything else is categorized as other + if (LivingEntity.class.isAssignableFrom(entityClass)) { + return "living"; + } + if (ProjectileEntity.class.isAssignableFrom(entityClass)) { + return "projectile"; + } + return "other"; + } + + @Override + public String getDataName() { + return "entities"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry> entityTypeRegistry = Registry.ENTITY_TYPE; + entityTypeRegistry.forEach(entity -> resultArray.add(generateEntity(entityTypeRegistry, entity))); + return resultArray; + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java new file mode 100644 index 00000000..45a0b35f --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java @@ -0,0 +1,51 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.item.FoodComponent; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Objects; + +public class FoodsDataGenerator implements IDataGenerator { + + public static JsonObject generateFoodDescriptor(Registry registry, Item foodItem) { + JsonObject foodDesc = new JsonObject(); + Identifier registryKey = registry.getKey(foodItem).orElseThrow().getValue(); + + foodDesc.addProperty("id", registry.getRawId(foodItem)); + foodDesc.addProperty("name", registryKey.getPath()); + + foodDesc.addProperty("stackSize", foodItem.getMaxCount()); + foodDesc.addProperty("displayName", DGU.translateText(foodItem.getTranslationKey())); + + FoodComponent foodComponent = Objects.requireNonNull(foodItem.getFoodComponent()); + float foodPoints = foodComponent.getHunger(); + float saturationRatio = foodComponent.getSaturationModifier() * 2.0F; + float saturation = foodPoints * saturationRatio; + + foodDesc.addProperty("foodPoints", foodPoints); + foodDesc.addProperty("saturation", saturation); + + foodDesc.addProperty("effectiveQuality", foodPoints + saturation); + foodDesc.addProperty("saturationRatio", saturationRatio); + return foodDesc; + } + + @Override + public String getDataName() { + return "foods"; + } + + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry itemRegistry = Registry.ITEM; + itemRegistry.stream() + .filter(Item::isFood) + .forEach(food -> resultsArray.add(generateFoodDescriptor(itemRegistry, food))); + return resultsArray; + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java new file mode 100644 index 00000000..81fe4e20 --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; + +public interface IDataGenerator { + + String getDataName(); + + JsonElement generateDataJson(); +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java new file mode 100644 index 00000000..e7b9b54f --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java @@ -0,0 +1,25 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.minecraft.block.enums.Instrument; + +public class InstrumentsDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "instruments"; + } + + @Override + public JsonElement generateDataJson() { + JsonArray array = new JsonArray(); + for (Instrument instrument : Instrument.values()) { + JsonObject object = new JsonObject(); + object.addProperty("id", instrument.ordinal()); + object.addProperty("name", instrument.asString()); + array.add(object); + } + return array; + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java new file mode 100644 index 00000000..b0c959dc --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java @@ -0,0 +1,81 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class ItemsDataGenerator implements IDataGenerator { + + private static List calculateItemsToRepairWith(Registry itemRegistry, Item sourceItem) { + ItemStack sourceItemStack = sourceItem.getDefaultStack(); + return itemRegistry.stream() + .filter(otherItem -> sourceItem.canRepair(sourceItemStack, otherItem.getDefaultStack())) + .collect(Collectors.toList()); + } + + private static List getApplicableEnchantmentTargets(Item sourceItem) { + return Arrays.stream(EnchantmentTarget.values()) + .filter(target -> target.isAcceptableItem(sourceItem)) + .collect(Collectors.toList()); + } + + public static JsonObject generateItem(Registry itemRegistry, Item item) { + JsonObject itemDesc = new JsonObject(); + Identifier registryKey = itemRegistry.getKey(item).orElseThrow().getValue(); + + itemDesc.addProperty("id", itemRegistry.getRawId(item)); + itemDesc.addProperty("name", registryKey.getPath()); + + itemDesc.addProperty("displayName", DGU.translateText(item.getTranslationKey())); + itemDesc.addProperty("stackSize", item.getMaxCount()); + + List enchantmentTargets = getApplicableEnchantmentTargets(item); + + JsonArray enchantCategoriesArray = new JsonArray(); + for (EnchantmentTarget target : enchantmentTargets) { + enchantCategoriesArray.add(EnchantmentsDataGenerator.getEnchantmentTargetName(target)); + } + if (enchantCategoriesArray.size() > 0) { + itemDesc.add("enchantCategories", enchantCategoriesArray); + } + + if (item.isDamageable()) { + List repairWithItems = calculateItemsToRepairWith(itemRegistry, item); + + JsonArray fixedWithArray = new JsonArray(); + for (Item repairWithItem : repairWithItems) { + Identifier repairWithName = itemRegistry.getKey(repairWithItem).orElseThrow().getValue(); + fixedWithArray.add(repairWithName.getPath()); + } + if (fixedWithArray.size() > 0) { + itemDesc.add("repairWith", fixedWithArray); + } + + int maxDurability = item.getMaxDamage(); + itemDesc.addProperty("maxDurability", maxDurability); + } + return itemDesc; + } + + @Override + public String getDataName() { + return "items"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry itemRegistry = Registry.ITEM; + itemRegistry.stream().forEach(item -> resultArray.add(generateItem(itemRegistry, item))); + return resultArray; + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java new file mode 100644 index 00000000..7a3c6ced --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +public class LanguageDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "language"; + } + + @Override + public JsonElement generateDataJson() { + try { + InputStream inputStream = Objects.requireNonNull(this.getClass().getResourceAsStream("/assets/minecraft/lang/en_us.json")); + return new Gson().fromJson(new InputStreamReader(inputStream, StandardCharsets.UTF_8), JsonObject.class); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to generate language file"); + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java new file mode 100644 index 00000000..4253ae69 --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java @@ -0,0 +1,197 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.MiningToolItemAccessor; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.Material; +import net.minecraft.item.Item; +import net.minecraft.item.Items; +import net.minecraft.item.MiningToolItem; +import net.minecraft.item.SwordItem; +import net.minecraft.tag.BlockTags; +import net.minecraft.tag.TagKey; +import net.minecraft.util.registry.Registry; + +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +//TODO entire idea of linking materials to tool speeds is obsolete and just wrong now, +//TODO but we kinda have to support it to let old code work for computing digging times, +//TODO so for now we will handle materials as "virtual" ones based on which tools can break blocks +public class MaterialsDataGenerator implements IDataGenerator { + + private static final List> COMPOSITE_MATERIALS = ImmutableList.>builder() + .add(ImmutableList.of("plant", makeMaterialNameForTag(BlockTags.AXE_MINEABLE))) + .add(ImmutableList.of("gourd", makeMaterialNameForTag(BlockTags.AXE_MINEABLE))) + .add(ImmutableList.of(makeMaterialNameForTag(BlockTags.LEAVES), makeMaterialNameForTag(BlockTags.HOE_MINEABLE))) + .add(ImmutableList.of(makeMaterialNameForTag(BlockTags.LEAVES), makeMaterialNameForTag(BlockTags.AXE_MINEABLE), makeMaterialNameForTag(BlockTags.HOE_MINEABLE))) + .add(ImmutableList.of("vine_or_glow_lichen", "plant", makeMaterialNameForTag(BlockTags.AXE_MINEABLE) + )).build(); + + private static String makeMaterialNameForTag(TagKey tag) { + return tag.id().getPath(); + } + + private static void createCompositeMaterialInfo(List allMaterials, List combinedMaterials) { + String compositeMaterialName = Joiner.on(';').join(combinedMaterials); + + List mappedMaterials = combinedMaterials.stream() + .map(otherName -> allMaterials.stream() + .filter(other -> other.getMaterialName().equals(otherName)) + .findFirst().orElseThrow(() -> new RuntimeException("Material not found with name " + otherName))) + .collect(Collectors.toList()); + + Predicate compositePredicate = blockState -> + mappedMaterials.stream().allMatch(it -> it.getPredicate().test(blockState)); + + MaterialInfo materialInfo = new MaterialInfo(compositeMaterialName, compositePredicate).includes(mappedMaterials); + allMaterials.add(0, materialInfo); + } + + private static void createCompositeMaterial(Map> allMaterials, List combinedMaterials) { + String compositeMaterialName = Joiner.on(';').join(combinedMaterials); + + Map resultingToolSpeeds = new HashMap<>(); + combinedMaterials.stream() + .map(allMaterials::get) + .forEach(resultingToolSpeeds::putAll); + allMaterials.put(compositeMaterialName, resultingToolSpeeds); + } + + public static List getGlobalMaterialInfo() { + ArrayList resultList = new ArrayList<>(); + + resultList.add(new MaterialInfo("vine_or_glow_lichen", blockState -> blockState.isOf(Blocks.VINE) || blockState.isOf(Blocks.GLOW_LICHEN))); + resultList.add(new MaterialInfo("coweb", blockState -> blockState.isOf(Blocks.COBWEB))); + + resultList.add(new MaterialInfo("leaves", blockState -> blockState.isIn(BlockTags.LEAVES))); + resultList.add(new MaterialInfo("wool", blockState -> blockState.isIn(BlockTags.WOOL))); + + resultList.add(new MaterialInfo("gourd", blockState -> blockState.getMaterial() == Material.GOURD)); + resultList.add(new MaterialInfo("plant", blockState -> blockState.getMaterial() == Material.PLANT || blockState.getMaterial() == Material.REPLACEABLE_PLANT)); + + HashSet uniqueMaterialNames = new HashSet<>(); + + Registry itemRegistry = Registry.ITEM; + itemRegistry.forEach(item -> { + if (item instanceof MiningToolItem toolItem) { + TagKey effectiveBlocks = ((MiningToolItemAccessor) toolItem).getEffectiveBlocks(); + String materialName = makeMaterialNameForTag(effectiveBlocks); + + if (!uniqueMaterialNames.contains(materialName)) { + uniqueMaterialNames.add(materialName); + resultList.add(new MaterialInfo(materialName, blockState -> blockState.isIn(effectiveBlocks))); + } + } + }); + + COMPOSITE_MATERIALS.forEach(values -> createCompositeMaterialInfo(resultList, values)); + return resultList; + } + + @Override + public String getDataName() { + return "materials"; + } + + @Override + public JsonElement generateDataJson() { + Registry itemRegistry = Registry.ITEM; + + Map> materialMiningSpeeds = new HashMap<>(); + materialMiningSpeeds.put("default", ImmutableMap.of()); + + //Special materials used for shears and swords special mining speed logic + Map leavesMaterialSpeeds = new HashMap<>(); + Map cowebMaterialSpeeds = new HashMap<>(); + Map plantMaterialSpeeds = new HashMap<>(); + Map gourdMaterialSpeeds = new HashMap<>(); + + materialMiningSpeeds.put(makeMaterialNameForTag(BlockTags.LEAVES), leavesMaterialSpeeds); + materialMiningSpeeds.put("coweb", cowebMaterialSpeeds); + materialMiningSpeeds.put("plant", plantMaterialSpeeds); + materialMiningSpeeds.put("gourd", gourdMaterialSpeeds); + + //Shears need special handling because they do not follow normal rules like tools + leavesMaterialSpeeds.put(Items.SHEARS, 15.0f); + cowebMaterialSpeeds.put(Items.SHEARS, 15.0f); + materialMiningSpeeds.put("vine_or_glow_lichen", ImmutableMap.of(Items.SHEARS, 2.0f)); + materialMiningSpeeds.put("wool", ImmutableMap.of(Items.SHEARS, 5.0f)); + + itemRegistry.forEach(item -> { + //Tools are handled rather easily and do not require anything else + if (item instanceof MiningToolItem toolItem) { + TagKey effectiveBlocks = ((MiningToolItemAccessor) toolItem).getEffectiveBlocks(); + String materialName = makeMaterialNameForTag(effectiveBlocks); + + Map materialSpeeds = materialMiningSpeeds.computeIfAbsent(materialName, k -> new HashMap<>()); + float miningSpeed = ((MiningToolItemAccessor) toolItem).getMiningSpeed(); + materialSpeeds.put(item, miningSpeed); + } + + //Swords require special treatment + if (item instanceof SwordItem) { + cowebMaterialSpeeds.put(item, 15.0f); + plantMaterialSpeeds.put(item, 1.5f); + leavesMaterialSpeeds.put(item, 1.5f); + gourdMaterialSpeeds.put(item, 1.5f); + } + }); + + COMPOSITE_MATERIALS.forEach(values -> createCompositeMaterial(materialMiningSpeeds, values)); + + JsonObject resultObject = new JsonObject(); + + for (var entry : materialMiningSpeeds.entrySet()) { + JsonObject toolSpeedsObject = new JsonObject(); + + for (var toolEntry : entry.getValue().entrySet()) { + int rawItemId = itemRegistry.getRawId(toolEntry.getKey()); + toolSpeedsObject.addProperty(Integer.toString(rawItemId), toolEntry.getValue()); + } + resultObject.add(entry.getKey(), toolSpeedsObject); + } + + return resultObject; + } + + public static class MaterialInfo { + private final String materialName; + private final Predicate predicate; + private final List includedMaterials = new ArrayList<>(); + + public MaterialInfo(String materialName, Predicate predicate) { + this.materialName = materialName; + this.predicate = predicate; + } + + protected MaterialInfo includes(List otherMaterials) { + this.includedMaterials.addAll(otherMaterials); + return this; + } + + public String getMaterialName() { + return materialName; + } + + public Predicate getPredicate() { + return predicate; + } + + public boolean includesMaterial(MaterialInfo materialInfo) { + return includedMaterials.contains(materialInfo); + } + + @Override + public String toString() { + return materialName; + } + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java new file mode 100644 index 00000000..5a6b1081 --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java @@ -0,0 +1,32 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.minecraft.particle.ParticleType; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +public class ParticlesDataGenerator implements IDataGenerator { + + public static JsonObject generateParticleType(Registry> registry, ParticleType particleType) { + JsonObject effectDesc = new JsonObject(); + Identifier registryKey = registry.getKey(particleType).orElseThrow().getValue(); + + effectDesc.addProperty("id", registry.getRawId(particleType)); + effectDesc.addProperty("name", registryKey.getPath()); + return effectDesc; + } + + @Override + public String getDataName() { + return "particles"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry> particleTypeRegistry = Registry.PARTICLE_TYPE; + particleTypeRegistry.forEach(particleType -> resultsArray.add(generateParticleType(particleTypeRegistry, particleType))); + return resultsArray; + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java new file mode 100644 index 00000000..389bcc4e --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java @@ -0,0 +1,132 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.item.Item; +import net.minecraft.recipe.Ingredient; +import net.minecraft.recipe.Recipe; +import net.minecraft.recipe.ShapedRecipe; +import net.minecraft.recipe.ShapelessRecipe; +import net.minecraft.util.registry.Registry; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class RecipeDataGenerator implements IDataGenerator { + + private static int getRawIdFor(Item item) { + return Registry.ITEM.getRawId(item); + } + + @Override + public String getDataName() { + return "recipes"; + } + + @Override + public JsonElement generateDataJson() { + JsonObject finalObj = new JsonObject(); + Multimap recipes = ArrayListMultimap.create(); + for (Recipe recipe : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) { + if (recipe instanceof ShapedRecipe sr) { + var ingredients = sr.getIngredients(); + List ingr = new ArrayList<>(); + for (int i = 0; i < 9; i++) { + if (i >= ingredients.size()) { + ingr.add(-1); + continue; + } + var stacks = ingredients.get(i); + var matching = stacks.getMatchingStacks(); + if (matching.length == 0) { + ingr.add(-1); + } else { + ingr.add(getRawIdFor(matching[0].getItem())); + } + } + Lists.reverse(ingr); + + JsonArray inShape = new JsonArray(); + + var iter = ingr.iterator(); + for (int y = 0; y < 3; y++) { + var jsonRow = new JsonArray(); + int one = iter.next(); + int two = iter.next(); + int three = iter.next(); + if (y > 0 && one == -1 && two == -1 && three == -1) continue; + jsonRow.add(one); + jsonRow.add(two); + jsonRow.add(three); + inShape.add(jsonRow); + } + + JsonObject finalRecipe = new JsonObject(); + finalRecipe.add("inShape", inShape); + + var resultObject = new JsonObject(); + resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); + resultObject.addProperty("count", sr.getOutput().getCount()); + finalRecipe.add("result", resultObject); + + String id = ((Integer) getRawIdFor(sr.getOutput().getItem())).toString(); + + if (!finalObj.has(id)) { + finalObj.add(id, new JsonArray()); + } + finalObj.get(id).getAsJsonArray().add(finalRecipe); +// var input = new JsonArray(); +// var ingredients = sr.getIngredients().stream().toList(); +// for (int y = 0; y < sr.getHeight(); y++) { +// var arr = new JsonArray(); +// for (int x = 0; x < sr.getWidth(); x++) { +// if ((y*3)+x >= ingredients.size()) { +// arr.add(JsonNull.INSTANCE); +// continue; +// } +// var ingredient = ingredients.get((y*3)+x).getMatchingStacks(); // FIXME: fix when there are more than one matching stack +// if (ingredient.length == 0) { +// arr.add(JsonNull.INSTANCE); +// } else { +// arr.add(getRawIdFor(ingredient[0].getItem())); +// } +// } +// input.add(arr); +// } +// var rootRecipeObject = new JsonObject(); +// rootRecipeObject.add("inShape", input); +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +// resultObject.addProperty("count", sr.getOutput().getCount()); +// rootRecipeObject.add("result", resultObject); +// recipes.put(getRawIdFor(sr.getOutput().getItem()), rootRecipeObject); + } else if (recipe instanceof ShapelessRecipe sl) { + var ingredients = new JsonArray(); + for (Ingredient ingredient : sl.getIngredients()) { + if (ingredient.isEmpty()) continue; + ingredients.add(getRawIdFor(ingredient.getMatchingStacks()[0].getItem())); + } + var rootRecipeObject = new JsonObject(); + rootRecipeObject.add("ingredients", ingredients); + var resultObject = new JsonObject(); + resultObject.addProperty("id", getRawIdFor(sl.getOutput().getItem())); + resultObject.addProperty("count", sl.getOutput().getCount()); + rootRecipeObject.add("result", resultObject); + recipes.put(getRawIdFor(sl.getOutput().getItem()), rootRecipeObject); + } + } + recipes.forEach((a, b) -> { + if (!finalObj.has(a.toString())) { + finalObj.add(a.toString(), new JsonArray()); + } + finalObj.get(a.toString()).getAsJsonArray().add(b); + }); + return finalObj; + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java new file mode 100644 index 00000000..dda76c54 --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java @@ -0,0 +1,161 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mojangannoyances.BlockColors; +import dev.u9g.minecraftdatagenerator.util.EmptyRenderBlockView; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.block.RedstoneWireBlock; +import net.minecraft.client.color.world.FoliageColors; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TintsDataGenerator implements IDataGenerator { + + public static BiomeTintColors generateBiomeTintColors(Registry biomeRegistry) { + BiomeTintColors colors = new BiomeTintColors(); + + biomeRegistry.forEach(biome -> { + int biomeGrassColor = biome.getGrassColorAt(0.0, 0.0); + int biomeFoliageColor = biome.getFoliageColor(); + int biomeWaterColor = biome.getWaterColor(); + + colors.grassColoursMap.computeIfAbsent(biomeGrassColor, k -> new ArrayList<>()).add(biome); + colors.foliageColoursMap.computeIfAbsent(biomeFoliageColor, k -> new ArrayList<>()).add(biome); + colors.waterColourMap.computeIfAbsent(biomeWaterColor, k -> new ArrayList<>()).add(biome); + }); + return colors; + } + + public static Map generateRedstoneTintColors() { + Map resultColors = new HashMap<>(); + + for (int redstoneLevel : RedstoneWireBlock.POWER.getValues()) { + int color = RedstoneWireBlock.getWireColor(redstoneLevel); + resultColors.put(redstoneLevel, color); + } + return resultColors; + } + + private static int getBlockColor(Block block, BlockColors blockColors) { + return blockColors.getColor(block.getDefaultState(), EmptyRenderBlockView.INSTANCE, BlockPos.ORIGIN, 0xFFFFFF); + } + + public static Map generateConstantTintColors() { + Map resultColors = new HashMap<>(); + BlockColors blockColors = BlockColors.create(); + + resultColors.put(Blocks.BIRCH_LEAVES, FoliageColors.getBirchColor()); + resultColors.put(Blocks.SPRUCE_LEAVES, FoliageColors.getSpruceColor()); + + resultColors.put(Blocks.LILY_PAD, getBlockColor(Blocks.LILY_PAD, blockColors)); + resultColors.put(Blocks.ATTACHED_MELON_STEM, getBlockColor(Blocks.ATTACHED_MELON_STEM, blockColors)); + resultColors.put(Blocks.ATTACHED_PUMPKIN_STEM, getBlockColor(Blocks.ATTACHED_PUMPKIN_STEM, blockColors)); + + //not really constant, depend on the block age, but kinda have to be handled since textures are literally white without them + resultColors.put(Blocks.MELON_STEM, getBlockColor(Blocks.MELON_STEM, blockColors)); + resultColors.put(Blocks.PUMPKIN_STEM, getBlockColor(Blocks.PUMPKIN_STEM, blockColors)); + + return resultColors; + } + + private static JsonObject encodeBiomeColorMap(Registry biomeRegistry, Map> colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + for (Biome biome : entry.getValue()) { + Identifier registryKey = biomeRegistry.getKey(biome).orElseThrow().getValue(); + keysArray.add(registryKey.getPath()); + } + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getKey()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeRedstoneColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + keysArray.add(entry.getKey()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeBlocksColorMap(Registry blockRegistry, Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + Identifier registryKey = blockRegistry.getKey(entry.getKey()).orElseThrow().getValue(); + keysArray.add(registryKey.getPath()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + @Override + public String getDataName() { + return "tints"; + } + + @Override + public JsonObject generateDataJson() { + DynamicRegistryManager registryManager = DynamicRegistryManager.BUILTIN.get(); + Registry biomeRegistry = registryManager.get(Registry.BIOME_KEY); + Registry blockRegistry = registryManager.get(Registry.BLOCK_KEY); + + BiomeTintColors biomeTintColors = generateBiomeTintColors(biomeRegistry); + Map redstoneColors = generateRedstoneTintColors(); + Map constantTintColors = generateConstantTintColors(); + + JsonObject resultObject = new JsonObject(); + + resultObject.add("grass", encodeBiomeColorMap(biomeRegistry, biomeTintColors.grassColoursMap)); + resultObject.add("foliage", encodeBiomeColorMap(biomeRegistry, biomeTintColors.foliageColoursMap)); + resultObject.add("water", encodeBiomeColorMap(biomeRegistry, biomeTintColors.waterColourMap)); + + resultObject.add("redstone", encodeRedstoneColorMap(redstoneColors)); + resultObject.add("constant", encodeBlocksColorMap(blockRegistry, constantTintColors)); + + return resultObject; + } + + public static class BiomeTintColors { + final Map> grassColoursMap = new HashMap<>(); + final Map> foliageColoursMap = new HashMap<>(); + final Map> waterColourMap = new HashMap<>(); + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java new file mode 100644 index 00000000..0d297e7a --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.server.dedicated.EulaReader; +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; + +@Mixin(EulaReader.class) +public class EULAMixin { + @Inject(method = "isEulaAgreedTo()Z", at = @At("TAIL"), cancellable = true) + public void init(CallbackInfoReturnable cir) { + cir.setReturnValue(true); + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java new file mode 100644 index 00000000..3e62bf59 --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java @@ -0,0 +1,12 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(EntityType.class) +public interface EntityTypeAccessor { + @Accessor("factory") + EntityType.EntityFactory factory(); +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java new file mode 100644 index 00000000..cb7f8a1c --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java @@ -0,0 +1,17 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Block; +import net.minecraft.item.MiningToolItem; +import net.minecraft.tag.TagKey; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(MiningToolItem.class) +public interface MiningToolItemAccessor { + + @Accessor + TagKey getEffectiveBlocks(); + + @Accessor + float getMiningSpeed(); +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java new file mode 100644 index 00000000..6d9415eb --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.generators.DataGenerators; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.MinecraftVersion; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; +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.nio.file.Path; + +@Mixin(MinecraftDedicatedServer.class) +public class ReadyMixin { + @Inject(method = "setupServer()Z", at = @At("TAIL")) + private void init(CallbackInfoReturnable cir) { + Main.LOGGER.info("Starting data generation!"); + String versionName = MinecraftVersion.CURRENT.getName(); + Path serverRootDirectory = DGU.getCurrentlyRunningServer().getRunDirectory().toPath().toAbsolutePath(); + Path dataDumpDirectory = serverRootDirectory.resolve("minecraft-data").resolve(versionName); + DataGenerators.runDataGenerators(dataDumpDirectory); + Main.LOGGER.info("Done data generation!"); + DGU.getCurrentlyRunningServer().stop(false); + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mixin/TheEndBiomeDataAccessor.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mixin/TheEndBiomeDataAccessor.java new file mode 100644 index 00000000..b50d1a93 --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mixin/TheEndBiomeDataAccessor.java @@ -0,0 +1,29 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.fabricmc.fabric.impl.biome.TheEndBiomeData; +import net.fabricmc.fabric.impl.biome.WeightedPicker; +import net.minecraft.util.registry.RegistryKey; +import net.minecraft.world.biome.Biome; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + + +@Mixin(TheEndBiomeData.class) +public interface TheEndBiomeDataAccessor { + @Accessor("END_BIOMES_MAP") + static Map, WeightedPicker>> END_BIOMES_MAP() { + throw new IllegalStateException("Should never be called."); + } + + @Accessor("END_MIDLANDS_MAP") + static Map, WeightedPicker>> END_MIDLANDS_MAP() { + throw new IllegalStateException("Should never be called."); + } + + @Accessor("END_BARRENS_MAP") + static Map, WeightedPicker>> END_BARRENS_MAP() { + throw new IllegalStateException("Should never be called."); + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BiomeColors.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BiomeColors.java new file mode 100644 index 00000000..bb9ae83f --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BiomeColors.java @@ -0,0 +1,35 @@ +package dev.u9g.minecraftdatagenerator.mojangannoyances; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.BlockRenderView; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.level.ColorResolver; + +@Environment(EnvType.CLIENT) +public class BiomeColors { + public static final ColorResolver GRASS_COLOR = Biome::getGrassColorAt; + public static final ColorResolver FOLIAGE_COLOR = (biome, x, z) -> biome.getFoliageColor(); + public static final ColorResolver WATER_COLOR = (biome, x, z) -> biome.getWaterColor(); + + public BiomeColors() { + } + + private static int getColor(BlockRenderView world, BlockPos pos, ColorResolver resolver) { + return world.getColor(pos, resolver); + } + + public static int getGrassColor(BlockRenderView world, BlockPos pos) { + return getColor(world, pos, GRASS_COLOR); + } + + public static int getFoliageColor(BlockRenderView world, BlockPos pos) { + return getColor(world, pos, FOLIAGE_COLOR); + } + + public static int getWaterColor(BlockRenderView world, BlockPos pos) { + return getColor(world, pos, WATER_COLOR); + } +} + diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColorProvider.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColorProvider.java new file mode 100644 index 00000000..ac5dc572 --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColorProvider.java @@ -0,0 +1,11 @@ +package dev.u9g.minecraftdatagenerator.mojangannoyances; + +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.BlockRenderView; +import org.jetbrains.annotations.Nullable; + +public interface BlockColorProvider { + int getColor(BlockState state, @Nullable BlockRenderView world, @Nullable BlockPos pos, int tintIndex); +} + diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColors.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColors.java new file mode 100644 index 00000000..2107780e --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColors.java @@ -0,0 +1,95 @@ +package dev.u9g.minecraftdatagenerator.mojangannoyances; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import net.minecraft.block.*; +import net.minecraft.block.enums.DoubleBlockHalf; +import net.minecraft.client.color.world.FoliageColors; +import net.minecraft.client.color.world.GrassColors; +import net.minecraft.state.property.Property; +import net.minecraft.util.collection.IdList; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.BlockRenderView; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.Set; + +public class BlockColors { + private static final int NO_COLOR = -1; + private final IdList providers = new IdList<>(32); + private final Map>> properties = Maps.newHashMap(); + + public BlockColors() { + } + + public static BlockColors create() { + BlockColors blockColors = new BlockColors(); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getGrassColor(world, state.get(TallPlantBlock.HALF) == DoubleBlockHalf.UPPER ? pos.down() : pos) : -1, Blocks.LARGE_FERN, Blocks.TALL_GRASS); + blockColors.registerColorProperty(TallPlantBlock.HALF, Blocks.LARGE_FERN, Blocks.TALL_GRASS); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getGrassColor(world, pos) : GrassColors.getColor(0.5, 1.0), Blocks.GRASS_BLOCK, Blocks.FERN, Blocks.GRASS, Blocks.POTTED_FERN); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> FoliageColors.getSpruceColor(), Blocks.SPRUCE_LEAVES); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> FoliageColors.getBirchColor(), Blocks.BIRCH_LEAVES); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getFoliageColor(world, pos) : FoliageColors.getDefaultColor(), Blocks.OAK_LEAVES, Blocks.JUNGLE_LEAVES, Blocks.ACACIA_LEAVES, Blocks.DARK_OAK_LEAVES, Blocks.VINE, Blocks.MANGROVE_LEAVES); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getWaterColor(world, pos) : -1, Blocks.WATER, Blocks.BUBBLE_COLUMN, Blocks.WATER_CAULDRON); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> RedstoneWireBlock.getWireColor((Integer) state.get(RedstoneWireBlock.POWER)), Blocks.REDSTONE_WIRE); + blockColors.registerColorProperty(RedstoneWireBlock.POWER, Blocks.REDSTONE_WIRE); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getGrassColor(world, pos) : -1, Blocks.SUGAR_CANE); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> 14731036, Blocks.ATTACHED_MELON_STEM, Blocks.ATTACHED_PUMPKIN_STEM); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> { + int i = (Integer) state.get(StemBlock.AGE); + int j = i * 32; + int k = 255 - i * 8; + int l = i * 4; + return j << 16 | k << 8 | l; + }, Blocks.MELON_STEM, Blocks.PUMPKIN_STEM); + blockColors.registerColorProperty(StemBlock.AGE, Blocks.MELON_STEM, Blocks.PUMPKIN_STEM); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? 2129968 : 7455580, Blocks.LILY_PAD); + return blockColors; + } + + public int getParticleColor(BlockState state, World world, BlockPos pos) { + BlockColorProvider blockColorProvider = (BlockColorProvider) this.providers.get(Registry.BLOCK.getRawId(state.getBlock())); + if (blockColorProvider != null) { + return blockColorProvider.getColor(state, (BlockRenderView) null, (BlockPos) null, 0); + } else { + MapColor mapColor = state.getMapColor(world, pos); + return mapColor != null ? mapColor.color : -1; + } + } + + public int getColor(BlockState state, @Nullable BlockRenderView world, @Nullable BlockPos pos, int tintIndex) { + BlockColorProvider blockColorProvider = (BlockColorProvider) this.providers.get(Registry.BLOCK.getRawId(state.getBlock())); + return blockColorProvider == null ? -1 : blockColorProvider.getColor(state, world, pos, tintIndex); + } + + public void registerColorProvider(BlockColorProvider provider, Block... blocks) { + int var4 = blocks.length; + + for (int var5 = 0; var5 < var4; ++var5) { + Block block = blocks[var5]; + this.providers.set(provider, Registry.BLOCK.getRawId(block)); + } + + } + + private void registerColorProperties(Set> properties, Block... blocks) { + int var4 = blocks.length; + + for (int var5 = 0; var5 < var4; ++var5) { + Block block = blocks[var5]; + this.properties.put(block, properties); + } + + } + + private void registerColorProperty(Property property, Block... blocks) { + this.registerColorProperties(ImmutableSet.of(property), blocks); + } + + public Set> getProperties(Block block) { + return (Set) this.properties.getOrDefault(block, ImmutableSet.of()); + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java new file mode 100644 index 00000000..32c2fb65 --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java @@ -0,0 +1,32 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Language; +import net.minecraft.world.World; + +public class DGU { + + private static final Language language = Language.getInstance(); + + @SuppressWarnings("deprecation") + private static MinecraftServer getCurrentlyRunningServerDedicated() { + return (MinecraftServer) FabricLoader.getInstance().getGameInstance(); + } + + public static MinecraftServer getCurrentlyRunningServer() { + return getCurrentlyRunningServerDedicated(); + } + + private static String translateTextFallback(String translationKey) { + return language.get(translationKey); + } + + public static String translateText(String translationKey) { + return translateTextFallback(translationKey); + } + + public static World getWorld() { + return getCurrentlyRunningServer().getOverworld(); + } +} diff --git a/1.19/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java new file mode 100644 index 00000000..1cff92d4 --- /dev/null +++ b/1.19/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java @@ -0,0 +1,73 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.fluid.FluidState; +import net.minecraft.fluid.Fluids; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.BlockRenderView; +import net.minecraft.world.LightType; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.BiomeKeys; +import net.minecraft.world.chunk.light.LightingProvider; +import net.minecraft.world.level.ColorResolver; +import org.jetbrains.annotations.Nullable; + +public enum EmptyRenderBlockView implements BlockRenderView { + INSTANCE; + + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + return null; + } + + public BlockState getBlockState(BlockPos pos) { + return Blocks.AIR.getDefaultState(); + } + + public FluidState getFluidState(BlockPos pos) { + return Fluids.EMPTY.getDefaultState(); + } + + public int getBottomY() { + return 0; + } + + public int getHeight() { + return 0; + } + + + @Override + public float getBrightness(Direction direction, boolean shaded) { + return 0.0f; + } + + @Override + public LightingProvider getLightingProvider() { + return null; + } + + @Override + public int getColor(BlockPos pos, ColorResolver colorResolver) { + DynamicRegistryManager registryManager = DynamicRegistryManager.BUILTIN.get(); + Registry biomeRegistry = registryManager.get(Registry.BIOME_KEY); + Biome plainsBiome = biomeRegistry.get(BiomeKeys.PLAINS); + + return colorResolver.getColor(plainsBiome, pos.getX(), pos.getY()); + } + + @Override + public int getLightLevel(LightType type, BlockPos pos) { + return type == LightType.SKY ? getMaxLightLevel() : 0; + } + + @Override + public int getBaseLightLevel(BlockPos pos, int ambientDarkness) { + return ambientDarkness; + } +} diff --git a/1.19/src/main/resources/fabric.mod.json b/1.19/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..1d6fbb58 --- /dev/null +++ b/1.19/src/main/resources/fabric.mod.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "id": "minecraft-data-generator", + "version": "${version}", + "name": "Minecraft Data Generator", + "description": "", + "authors": [ + "Archengius", + "U9G" + ], + "contact": {}, + "license": "MIT", + "environment": "server", + "entrypoints": { + "main": [ + "dev.u9g.minecraftdatagenerator.Main" + ] + }, + "mixins": [ + "minecraft-data-generator.mixins.json" + ], + "depends": { + "fabricloader": ">=0.14.4", + "fabric": "*", + "minecraft": "1.19" + } +} diff --git a/1.19/src/main/resources/minecraft-data-generator.mixins.json b/1.19/src/main/resources/minecraft-data-generator.mixins.json new file mode 100644 index 00000000..f3afe061 --- /dev/null +++ b/1.19/src/main/resources/minecraft-data-generator.mixins.json @@ -0,0 +1,18 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "dev.u9g.minecraftdatagenerator.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "EntityTypeAccessor", + "EULAMixin", + "MiningToolItemAccessor", + "ReadyMixin", + "TheEndBiomeDataAccessor" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/1.20/build.gradle b/1.20/build.gradle new file mode 100644 index 00000000..e85de392 --- /dev/null +++ b/1.20/build.gradle @@ -0,0 +1,31 @@ +plugins { + id 'fabric-loom' +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API. This is technically optional, but you probably want it anyway. + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" +} + +processResources { + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} diff --git a/1.20/gradle.properties b/1.20/gradle.properties new file mode 100644 index 00000000..1818770d --- /dev/null +++ b/1.20/gradle.properties @@ -0,0 +1,8 @@ +# Fabric Properties +# check these on https://modmuss50.me/fabric.html +minecraft_version=1.20.2 +yarn_mappings=1.20.2+build.4 +loader_version=0.14.24 +# Dependencies +# check this on https://modmuss50.me/fabric.html +fabric_version=0.90.4+1.20.2 diff --git a/1.20/src/main/java/dev/u9g/minecraftdatagenerator/Main.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/Main.java new file mode 100644 index 00000000..f8d17372 --- /dev/null +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/Main.java @@ -0,0 +1,14 @@ +package dev.u9g.minecraftdatagenerator; + +import net.fabricmc.api.ModInitializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Main implements ModInitializer { + public static final Logger LOGGER = LoggerFactory.getLogger("mc-data-gen-serv"); + + @Override + public void onInitialize() { + + } +} diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java similarity index 50% rename from src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java rename to 1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java index c61eb4d9..bda5f967 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java @@ -18,6 +18,12 @@ public class BiomesDataGenerator implements IDataGenerator { private static final Set> END_BIOMES = new HashSet<>(); + static { + END_BIOMES.addAll(TheEndBiomeDataAccessor.END_BIOMES_MAP().keySet()); + END_BIOMES.addAll(TheEndBiomeDataAccessor.END_BARRENS_MAP().keySet()); + END_BIOMES.addAll(TheEndBiomeDataAccessor.END_MIDLANDS_MAP().keySet()); + } + private static String guessBiomeDimensionFromCategory(Biome biome, String biomeName) { var key = DGU.getWorld().getRegistryManager().get(RegistryKeys.BIOME).getKey(biome).orElseThrow(); if (NetherBiomes.canGenerateInNether(key)) { @@ -76,74 +82,198 @@ private static String guessCategoryBasedOnName(String name, String dimension) { } else if (name.contains("cave") || name.equals("deep_dark")) { return "underground"; } else { - System.out.println("Unable to find biome category for biome with name: '"+name+"'"); + System.out.println("Unable to find biome category for biome with name: '" + name + "'"); return "none"; } } private static int getBiomeColorFor(String biomeName) { - if (biomeName.equals("the_void")) return 0; - else if (biomeName.equals("plains")) return 9286496; - else if (biomeName.equals("sunflower_plains")) return 11918216; - else if (biomeName.equals("snowy_plains")) return 16777215; - else if (biomeName.equals("ice_spikes")) return 11853020; - else if (biomeName.equals("desert")) return 16421912; - else if (biomeName.equals("swamp")) return 522674; - else if (biomeName.equals("forest")) return 353825; - else if (biomeName.equals("flower_forest")) return 2985545; - else if (biomeName.equals("birch_forest")) return 3175492; - else if (biomeName.equals("dark_forest")) return 4215066; - else if (biomeName.equals("old_growth_birch_forest")) return 5807212; - else if (biomeName.equals("old_growth_pine_taiga")) return 5858897; - else if (biomeName.equals("old_growth_spruce_taiga")) return 8490617; - else if (biomeName.equals("taiga")) return 747097; - else if (biomeName.equals("snowy_taiga")) return 3233098; - else if (biomeName.equals("savanna")) return 12431967; - else if (biomeName.equals("savanna_plateau")) return 10984804; - else if (biomeName.equals("windswept_hills")) return 6316128; - else if (biomeName.equals("windswept_gravelly_hills")) return 8947848; - else if (biomeName.equals("windswept_forest")) return 2250012; - else if (biomeName.equals("windswept_savanna")) return 15063687; - else if (biomeName.equals("jungle")) return 5470985; - else if (biomeName.equals("sparse_jungle")) return 6458135; - else if (biomeName.equals("bamboo_jungle")) return 7769620; - else if (biomeName.equals("badlands")) return 14238997; - else if (biomeName.equals("eroded_badlands")) return 16739645; - else if (biomeName.equals("wooded_badlands")) return 11573093; - else if (biomeName.equals("meadow")) return 9217136; - else if (biomeName.equals("grove")) return 14675173; - else if (biomeName.equals("snowy_slopes")) return 14348785; - else if (biomeName.equals("frozen_peaks")) return 15399931; - else if (biomeName.equals("jagged_peaks")) return 14937325; - else if (biomeName.equals("stony_peaks")) return 13750737; - else if (biomeName.equals("river")) return 255; - else if (biomeName.equals("frozen_river")) return 10526975; - else if (biomeName.equals("beach")) return 16440917; - else if (biomeName.equals("snowy_beach")) return 16445632; - else if (biomeName.equals("stony_shore")) return 10658436; - else if (biomeName.equals("warm_ocean")) return 172; - else if (biomeName.equals("lukewarm_ocean")) return 144; - else if (biomeName.equals("deep_lukewarm_ocean")) return 64; - else if (biomeName.equals("ocean")) return 112; - else if (biomeName.equals("deep_ocean")) return 48; - else if (biomeName.equals("cold_ocean")) return 2105456; - else if (biomeName.equals("deep_cold_ocean")) return 2105400; - else if (biomeName.equals("frozen_ocean")) return 7368918; - else if (biomeName.equals("deep_frozen_ocean")) return 4210832; - else if (biomeName.equals("mushroom_fields")) return 16711935; - else if (biomeName.equals("dripstone_caves")) return 12690831; - else if (biomeName.equals("lush_caves")) return 14652980; - else if (biomeName.equals("nether_wastes")) return 12532539; - else if (biomeName.equals("warped_forest")) return 4821115; - else if (biomeName.equals("crimson_forest")) return 14485512; - else if (biomeName.equals("soul_sand_valley")) return 6174768; - else if (biomeName.equals("basalt_deltas")) return 4208182; - else if (biomeName.equals("the_end")) return 8421631; - else if (biomeName.equals("end_highlands")) return 12828041; - else if (biomeName.equals("end_midlands")) return 15464630; - else if (biomeName.equals("small_end_islands")) return 42; - else if (biomeName.equals("end_barrens")) return 9474162; - System.out.println("Don't know the color of biome: '"+biomeName+"'"); + switch (biomeName) { + case "the_void" -> { + return 0; + } + case "plains" -> { + return 9286496; + } + case "sunflower_plains" -> { + return 11918216; + } + case "snowy_plains" -> { + return 16777215; + } + case "ice_spikes" -> { + return 11853020; + } + case "desert" -> { + return 16421912; + } + case "swamp" -> { + return 522674; + } + case "forest" -> { + return 353825; + } + case "flower_forest" -> { + return 2985545; + } + case "birch_forest" -> { + return 3175492; + } + case "dark_forest" -> { + return 4215066; + } + case "old_growth_birch_forest" -> { + return 5807212; + } + case "old_growth_pine_taiga" -> { + return 5858897; + } + case "old_growth_spruce_taiga" -> { + return 8490617; + } + case "taiga" -> { + return 747097; + } + case "snowy_taiga" -> { + return 3233098; + } + case "savanna" -> { + return 12431967; + } + case "savanna_plateau" -> { + return 10984804; + } + case "windswept_hills" -> { + return 6316128; + } + case "windswept_gravelly_hills" -> { + return 8947848; + } + case "windswept_forest" -> { + return 2250012; + } + case "windswept_savanna" -> { + return 15063687; + } + case "jungle" -> { + return 5470985; + } + case "sparse_jungle" -> { + return 6458135; + } + case "bamboo_jungle" -> { + return 7769620; + } + case "badlands" -> { + return 14238997; + } + case "eroded_badlands" -> { + return 16739645; + } + case "wooded_badlands" -> { + return 11573093; + } + case "meadow" -> { + return 9217136; + } + case "grove" -> { + return 14675173; + } + case "snowy_slopes" -> { + return 14348785; + } + case "frozen_peaks" -> { + return 15399931; + } + case "jagged_peaks" -> { + return 14937325; + } + case "stony_peaks" -> { + return 13750737; + } + case "river" -> { + return 255; + } + case "frozen_river" -> { + return 10526975; + } + case "beach" -> { + return 16440917; + } + case "snowy_beach" -> { + return 16445632; + } + case "stony_shore" -> { + return 10658436; + } + case "warm_ocean" -> { + return 172; + } + case "lukewarm_ocean" -> { + return 144; + } + case "deep_lukewarm_ocean" -> { + return 64; + } + case "ocean" -> { + return 112; + } + case "deep_ocean" -> { + return 48; + } + case "cold_ocean" -> { + return 2105456; + } + case "deep_cold_ocean" -> { + return 2105400; + } + case "frozen_ocean" -> { + return 7368918; + } + case "deep_frozen_ocean" -> { + return 4210832; + } + case "mushroom_fields" -> { + return 16711935; + } + case "dripstone_caves" -> { + return 12690831; + } + case "lush_caves" -> { + return 14652980; + } + case "nether_wastes" -> { + return 12532539; + } + case "warped_forest" -> { + return 4821115; + } + case "crimson_forest" -> { + return 14485512; + } + case "soul_sand_valley" -> { + return 6174768; + } + case "basalt_deltas" -> { + return 4208182; + } + case "the_end" -> { + return 8421631; + } + case "end_highlands" -> { + return 12828041; + } + case "end_midlands" -> { + return 15464630; + } + case "small_end_islands" -> { + return 42; + } + case "end_barrens" -> { + return 9474162; + } + } + System.out.println("Don't know the color of biome: '" + biomeName + "'"); return 0; } @@ -184,10 +314,4 @@ public JsonArray generateDataJson() { .forEach(biomesArray::add); return biomesArray; } - - static { - END_BIOMES.addAll(TheEndBiomeDataAccessor.END_BIOMES_MAP().keySet()); - END_BIOMES.addAll(TheEndBiomeDataAccessor.END_BARRENS_MAP().keySet()); - END_BIOMES.addAll(TheEndBiomeDataAccessor.END_MIDLANDS_MAP().keySet()); - } } diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java similarity index 96% rename from src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java rename to 1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java index 04f07803..8b9d0e06 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java @@ -21,9 +21,29 @@ public class BlockCollisionShapesDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "blockCollisionShapes"; + } + + @Override + public JsonObject generateDataJson() { + Registry blockRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.BLOCK); + BlockShapesCache blockShapesCache = new BlockShapesCache(); + + blockRegistry.forEach(blockShapesCache::processBlock); + + JsonObject resultObject = new JsonObject(); + + resultObject.add("blocks", blockShapesCache.dumpBlockShapeIndices(blockRegistry)); + resultObject.add("shapes", blockShapesCache.dumpShapesObject()); + + return resultObject; + } + private static class BlockShapesCache { - public Map uniqueBlockShapes = new HashMap<>(); - public Map> blockCollisionShapes = new HashMap<>(); + public final Map uniqueBlockShapes = new HashMap<>(); + public final Map> blockCollisionShapes = new HashMap<>(); private int lastCollisionShapeId = 0; public void processBlock(Block block) { @@ -90,24 +110,4 @@ public JsonObject dumpShapesObject() { return shapesObject; } } - - @Override - public String getDataName() { - return "blockCollisionShapes"; - } - - @Override - public JsonObject generateDataJson() { - Registry blockRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.BLOCK); - BlockShapesCache blockShapesCache = new BlockShapesCache(); - - blockRegistry.forEach(blockShapesCache::processBlock); - - JsonObject resultObject = new JsonObject(); - - resultObject.add("blocks", blockShapesCache.dumpBlockShapeIndices(blockRegistry)); - resultObject.add("shapes", blockShapesCache.dumpShapesObject()); - - return resultObject; - } } diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java similarity index 99% rename from src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java rename to 1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java index 4307fd6d..44e8e29c 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java @@ -35,7 +35,7 @@ public class BlocksDataGenerator implements IDataGenerator { - private static Logger logger = LoggerFactory.getLogger(BlocksDataGenerator.class); + private static final Logger logger = LoggerFactory.getLogger(BlocksDataGenerator.class); private static List getItemsEffectiveForBlock(BlockState blockState) { return DGU.getWorld().getRegistryManager().get(RegistryKeys.ITEM).stream() @@ -99,21 +99,6 @@ private static > JsonObject generateStateProperty(Proper return propertyObject; } - @Override - public String getDataName() { - return "blocks"; - } - - @Override - public JsonArray generateDataJson() { - JsonArray resultBlocksArray = new JsonArray(); - Registry blockRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.BLOCK); - List availableMaterials = MaterialsDataGenerator.getGlobalMaterialInfo(); - - blockRegistry.forEach(block -> resultBlocksArray.add(generateBlock(blockRegistry, availableMaterials, block))); - return resultBlocksArray; - } - private static String findMatchingBlockMaterial(BlockState blockState, List materials) { List matchingMaterials = materials.stream() .filter(material -> material.getPredicate().test(blockState)) @@ -195,4 +180,19 @@ public static JsonObject generateBlock(Registry blockRegistry, List blockRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.BLOCK); + List availableMaterials = MaterialsDataGenerator.getGlobalMaterialInfo(); + + blockRegistry.forEach(block -> resultBlocksArray.add(generateBlock(blockRegistry, availableMaterials, block))); + return resultBlocksArray; + } } diff --git a/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java new file mode 100644 index 00000000..c47684ab --- /dev/null +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java @@ -0,0 +1,77 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.List; + +public class DataGenerators { + + private static final Logger logger = LoggerFactory.getLogger(DataGenerators.class); + private static final List GENERATORS = new ArrayList<>(); + + static { + register(new BiomesDataGenerator()); + register(new BlockCollisionShapesDataGenerator()); + register(new BlocksDataGenerator()); + register(new EffectsDataGenerator()); + register(new EnchantmentsDataGenerator()); + register(new EntitiesDataGenerator()); + register(new FoodsDataGenerator()); + register(new ItemsDataGenerator()); + register(new ParticlesDataGenerator()); + register(new TintsDataGenerator()); + register(new MaterialsDataGenerator()); +// register(new RecipeDataGenerator()); - On hold until mcdata supports multiple materials for a recipe + register(new LanguageDataGenerator()); + register(new InstrumentsDataGenerator()); + } + + public static void register(IDataGenerator generator) { + GENERATORS.add(generator); + } + + public static boolean runDataGenerators(Path outputDirectory) { + try { + Files.createDirectories(outputDirectory); + } catch (IOException exception) { + logger.error("Failed to create data generator output directory at {}", outputDirectory, exception); + return false; + } + + int generatorsFailed = 0; + logger.info("Running minecraft data generators, output at {}", outputDirectory); + + for (IDataGenerator dataGenerator : GENERATORS) { + logger.info("Running generator {}", dataGenerator.getDataName()); + try { + String outputFileName = String.format("%s.json", dataGenerator.getDataName()); + JsonElement outputElement = dataGenerator.generateDataJson(); + Path outputFilePath = outputDirectory.resolve(outputFileName); + + try (Writer writer = Files.newBufferedWriter(outputFilePath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + JsonWriter jsonWriter = new JsonWriter(writer); + jsonWriter.setIndent(" "); + Streams.write(outputElement, jsonWriter); + } + logger.info("Generator: {} -> {}", dataGenerator.getDataName(), outputFileName); + + } catch (Throwable exception) { + logger.error("Failed to run data generator {}", dataGenerator.getDataName(), exception); + generatorsFailed++; + } + } + + logger.info("Finishing running data generators"); + return generatorsFailed == 0; + } +} diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java similarity index 100% rename from src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java rename to 1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java index 03f8cbf5..3c619c36 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java @@ -15,19 +15,6 @@ public class EffectsDataGenerator implements IDataGenerator { - @Override - public String getDataName() { - return "effects"; - } - - @Override - public JsonArray generateDataJson() { - JsonArray resultsArray = new JsonArray(); - Registry statusEffectRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.STATUS_EFFECT); - statusEffectRegistry.forEach(effect -> resultsArray.add(generateEffect(statusEffectRegistry, effect))); - return resultsArray; - } - public static JsonObject generateEffect(Registry registry, StatusEffect statusEffect) { JsonObject effectDesc = new JsonObject(); Identifier registryKey = registry.getKey(statusEffect).orElseThrow().getValue(); @@ -44,4 +31,17 @@ public static JsonObject generateEffect(Registry registry, StatusE effectDesc.addProperty("type", statusEffect.isBeneficial() ? "good" : "bad"); return effectDesc; } + + @Override + public String getDataName() { + return "effects"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry statusEffectRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.STATUS_EFFECT); + statusEffectRegistry.forEach(effect -> resultsArray.add(generateEffect(statusEffectRegistry, effect))); + return resultsArray; + } } diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java similarity index 100% rename from src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java rename to 1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java index 00955451..a015cc4f 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java @@ -58,20 +58,6 @@ private static JsonObject generateEnchantmentMaxPowerCoefficients(Enchantment en return resultObject; } - @Override - public String getDataName() { - return "enchantments"; - } - - @Override - public JsonArray generateDataJson() { - JsonArray resultsArray = new JsonArray(); - Registry enchantmentRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.ENCHANTMENT); - enchantmentRegistry.stream() - .forEach(enchantment -> resultsArray.add(generateEnchantment(enchantmentRegistry, enchantment))); - return resultsArray; - } - public static JsonObject generateEnchantment(Registry registry, Enchantment enchantment) { JsonObject enchantmentDesc = new JsonObject(); Identifier registryKey = registry.getKey(enchantment).orElseThrow().getValue(); @@ -106,4 +92,18 @@ public static JsonObject generateEnchantment(Registry registry, Enc return enchantmentDesc; } + + @Override + public String getDataName() { + return "enchantments"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry enchantmentRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.ENCHANTMENT); + enchantmentRegistry.stream() + .forEach(enchantment -> resultsArray.add(generateEnchantment(enchantmentRegistry, enchantment))); + return resultsArray; + } } diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java similarity index 93% rename from src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java rename to 1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java index 02c15c6b..37d4fb1b 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java @@ -21,19 +21,6 @@ public class EntitiesDataGenerator implements IDataGenerator { - @Override - public String getDataName() { - return "entities"; - } - - @Override - public JsonArray generateDataJson() { - JsonArray resultArray = new JsonArray(); - Registry> entityTypeRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.ENTITY_TYPE); - entityTypeRegistry.forEach(entity -> resultArray.add(generateEntity(entityTypeRegistry, entity))); - return resultArray; - } - public static JsonObject generateEntity(Registry> entityRegistry, EntityType entityType) { JsonObject entityDesc = new JsonObject(); Identifier registryKey = entityRegistry.getKey(entityType).orElseThrow().getValue(); @@ -65,12 +52,14 @@ private static String getCategoryFrom(EntityType entityType) { /*public T create(World world) { return this.factory.create(this, world); }*/ - Entity entity = ((EntityTypeAccessor)entityType).factory().create(entityType, DGU.getWorld()); - if (entity == null) throw new Error("Entity was null after trying to create a: " + DGU.translateText(entityType.getTranslationKey())); + Entity entity = ((EntityTypeAccessor) entityType).factory().create(entityType, DGU.getWorld()); + if (entity == null) + throw new Error("Entity was null after trying to create a: " + DGU.translateText(entityType.getTranslationKey())); entity.discard(); return switch (entity.getClass().getPackageName()) { case "net.minecraft.entity.decoration", "net.minecraft.entity.decoration.painting" -> "Immobile"; - case "net.minecraft.entity.boss", "net.minecraft.entity.mob", "net.minecraft.entity.boss.dragon" -> "Hostile mobs"; + case "net.minecraft.entity.boss", "net.minecraft.entity.mob", "net.minecraft.entity.boss.dragon" -> + "Hostile mobs"; case "net.minecraft.entity.projectile", "net.minecraft.entity.projectile.thrown" -> "Projectiles"; case "net.minecraft.entity.passive" -> "Passive mobs"; case "net.minecraft.entity.vehicle" -> "Vehicles"; @@ -115,4 +104,17 @@ private static String getEntityTypeForClass(Class entityClass) } return "other"; } + + @Override + public String getDataName() { + return "entities"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry> entityTypeRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.ENTITY_TYPE); + entityTypeRegistry.forEach(entity -> resultArray.add(generateEntity(entityTypeRegistry, entity))); + return resultArray; + } } diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java similarity index 100% rename from src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java rename to 1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java index 84133fdb..a5b164f1 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java @@ -13,20 +13,6 @@ public class FoodsDataGenerator implements IDataGenerator { - @Override - public String getDataName() { - return "foods"; - } - - public JsonArray generateDataJson() { - JsonArray resultsArray = new JsonArray(); - Registry itemRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.ITEM); - itemRegistry.stream() - .filter(Item::isFood) - .forEach(food -> resultsArray.add(generateFoodDescriptor(itemRegistry, food))); - return resultsArray; - } - public static JsonObject generateFoodDescriptor(Registry registry, Item foodItem) { JsonObject foodDesc = new JsonObject(); Identifier registryKey = registry.getKey(foodItem).orElseThrow().getValue(); @@ -49,4 +35,18 @@ public static JsonObject generateFoodDescriptor(Registry registry, Item fo foodDesc.addProperty("saturationRatio", saturationRatio); return foodDesc; } + + @Override + public String getDataName() { + return "foods"; + } + + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry itemRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.ITEM); + itemRegistry.stream() + .filter(Item::isFood) + .forEach(food -> resultsArray.add(generateFoodDescriptor(itemRegistry, food))); + return resultsArray; + } } diff --git a/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java new file mode 100644 index 00000000..81fe4e20 --- /dev/null +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; + +public interface IDataGenerator { + + String getDataName(); + + JsonElement generateDataJson(); +} diff --git a/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java new file mode 100644 index 00000000..e7b9b54f --- /dev/null +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java @@ -0,0 +1,25 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.minecraft.block.enums.Instrument; + +public class InstrumentsDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "instruments"; + } + + @Override + public JsonElement generateDataJson() { + JsonArray array = new JsonArray(); + for (Instrument instrument : Instrument.values()) { + JsonObject object = new JsonObject(); + object.addProperty("id", instrument.ordinal()); + object.addProperty("name", instrument.asString()); + array.add(object); + } + return array; + } +} diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java similarity index 100% rename from src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java rename to 1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java index 09ed4890..54020489 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java @@ -29,19 +29,6 @@ private static List getApplicableEnchantmentTargets(Item sour .collect(Collectors.toList()); } - @Override - public String getDataName() { - return "items"; - } - - @Override - public JsonArray generateDataJson() { - JsonArray resultArray = new JsonArray(); - Registry itemRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.ITEM); - itemRegistry.stream().forEach(item -> resultArray.add(generateItem(itemRegistry, item))); - return resultArray; - } - public static JsonObject generateItem(Registry itemRegistry, Item item) { JsonObject itemDesc = new JsonObject(); Identifier registryKey = itemRegistry.getKey(item).orElseThrow().getValue(); @@ -79,4 +66,17 @@ public static JsonObject generateItem(Registry itemRegistry, Item item) { } return itemDesc; } + + @Override + public String getDataName() { + return "items"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry itemRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.ITEM); + itemRegistry.stream().forEach(item -> resultArray.add(generateItem(itemRegistry, item))); + return resultArray; + } } diff --git a/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java new file mode 100644 index 00000000..7a3c6ced --- /dev/null +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +public class LanguageDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "language"; + } + + @Override + public JsonElement generateDataJson() { + try { + InputStream inputStream = Objects.requireNonNull(this.getClass().getResourceAsStream("/assets/minecraft/lang/en_us.json")); + return new Gson().fromJson(new InputStreamReader(inputStream, StandardCharsets.UTF_8), JsonObject.class); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to generate language file"); + } +} diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java similarity index 100% rename from src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java rename to 1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java index 7f6bb98a..9a5fac15 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java @@ -36,48 +36,10 @@ public class MaterialsDataGenerator implements IDataGenerator { .add(ImmutableList.of("vine_or_glow_lichen", "plant", makeMaterialNameForTag(BlockTags.AXE_MINEABLE) )).build(); - @Override - public String getDataName() { - return "materials"; - } - private static String makeMaterialNameForTag(TagKey tag) { return tag.id().getPath(); } - public static class MaterialInfo { - private final String materialName; - private final Predicate predicate; - private final List includedMaterials = new ArrayList<>(); - - public MaterialInfo(String materialName, Predicate predicate) { - this.materialName = materialName; - this.predicate = predicate; - } - - protected MaterialInfo includes(List otherMaterials) { - this.includedMaterials.addAll(otherMaterials); - return this; - } - - public String getMaterialName() { - return materialName; - } - - public Predicate getPredicate() { - return predicate; - } - - public boolean includesMaterial(MaterialInfo materialInfo) { - return includedMaterials.contains(materialInfo); - } - - @Override - public String toString() { - return materialName; - } - } - private static void createCompositeMaterialInfo(List allMaterials, List combinedMaterials) { String compositeMaterialName = Joiner.on(';').join(combinedMaterials); @@ -137,6 +99,11 @@ public static List getGlobalMaterialInfo() { return resultList; } + @Override + public String getDataName() { + return "materials"; + } + @Override public JsonElement generateDataJson() { Registry itemRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.ITEM); @@ -197,4 +164,37 @@ public JsonElement generateDataJson() { return resultObject; } + + public static class MaterialInfo { + private final String materialName; + private final Predicate predicate; + private final List includedMaterials = new ArrayList<>(); + + public MaterialInfo(String materialName, Predicate predicate) { + this.materialName = materialName; + this.predicate = predicate; + } + + protected MaterialInfo includes(List otherMaterials) { + this.includedMaterials.addAll(otherMaterials); + return this; + } + + public String getMaterialName() { + return materialName; + } + + public Predicate getPredicate() { + return predicate; + } + + public boolean includesMaterial(MaterialInfo materialInfo) { + return includedMaterials.contains(materialInfo); + } + + @Override + public String toString() { + return materialName; + } + } } diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java similarity index 90% rename from src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java rename to 1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java index b4161ff1..e446a50b 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java @@ -10,6 +10,15 @@ public class ParticlesDataGenerator implements IDataGenerator { + public static JsonObject generateParticleType(Registry> registry, ParticleType particleType) { + JsonObject effectDesc = new JsonObject(); + Identifier registryKey = registry.getKey(particleType).orElseThrow().getValue(); + + effectDesc.addProperty("id", registry.getRawId(particleType)); + effectDesc.addProperty("name", registryKey.getPath()); + return effectDesc; + } + @Override public String getDataName() { return "particles"; @@ -18,17 +27,8 @@ public String getDataName() { @Override public JsonArray generateDataJson() { JsonArray resultsArray = new JsonArray(); - Registry>particleTypeRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.PARTICLE_TYPE); + Registry> particleTypeRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.PARTICLE_TYPE); particleTypeRegistry.forEach(particleType -> resultsArray.add(generateParticleType(particleTypeRegistry, particleType))); return resultsArray; } - - public static JsonObject generateParticleType(Registry> registry, ParticleType particleType) { - JsonObject effectDesc = new JsonObject(); - Identifier registryKey = registry.getKey(particleType).orElseThrow().getValue(); - - effectDesc.addProperty("id", registry.getRawId(particleType)); - effectDesc.addProperty("name", registryKey.getPath()); - return effectDesc; - } } diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java similarity index 91% rename from src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java rename to 1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java index e703da07..ea489d11 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java @@ -1,136 +1,128 @@ -package dev.u9g.minecraftdatagenerator.generators; - -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import dev.u9g.minecraftdatagenerator.util.DGU; -import net.minecraft.item.Item; -import net.minecraft.item.Items; -import net.minecraft.recipe.Ingredient; -import net.minecraft.recipe.Recipe; -import net.minecraft.recipe.RecipeEntry; -import net.minecraft.recipe.ShapedRecipe; -import net.minecraft.recipe.ShapelessRecipe; -import net.minecraft.registry.DynamicRegistryManager; -import net.minecraft.registry.RegistryKeys; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -public class RecipeDataGenerator implements IDataGenerator { - - @Override - public String getDataName() { - return "recipes"; - } - - - - @Override - public JsonElement generateDataJson() { - DynamicRegistryManager registryManager = DGU.getWorld().getRegistryManager(); - JsonObject finalObj = new JsonObject(); - Multimap recipes = ArrayListMultimap.create(); - for (RecipeEntry recipeE : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) { - Recipe recipe = recipeE.value(); - if (recipe instanceof ShapedRecipe sr) { - var ingredients = sr.getIngredients(); - List ingr = new ArrayList<>(); - for (int i = 0; i < 9; i++) { - if (i >= ingredients.size()) { - ingr.add(-1); - continue; - } - var stacks = ingredients.get(i); - var matching = stacks.getMatchingStacks(); - if (matching.length == 0) { - ingr.add(-1); - } else { - ingr.add(getRawIdFor(matching[0].getItem())); - } - } - //Lists.reverse(ingr); - - JsonArray inShape = new JsonArray(); - - - var iter = ingr.iterator(); - for (int y = 0; y < sr.getHeight(); y++) { - var jsonRow = new JsonArray(); - for (int z = 0; z < sr.getWidth();z++) { - jsonRow.add(iter.next()); - } - inShape.add(jsonRow); - } - - JsonObject finalRecipe = new JsonObject(); - finalRecipe.add("inShape", inShape); - - var resultObject = new JsonObject(); - resultObject.addProperty("id", getRawIdFor(sr.getResult(registryManager).getItem())); - resultObject.addProperty("count", sr.getResult(registryManager).getCount()); - finalRecipe.add("result", resultObject); - - String id = ((Integer) getRawIdFor(sr.getResult(registryManager).getItem())).toString(); - - if (!finalObj.has(id)) { - finalObj.add(id, new JsonArray()); - } - finalObj.get(id).getAsJsonArray().add(finalRecipe); -// var input = new JsonArray(); -// var ingredients = sr.getIngredients().stream().toList(); -// for (int y = 0; y < sr.getHeight(); y++) { -// var arr = new JsonArray(); -// for (int x = 0; x < sr.getWidth(); x++) { -// if ((y*3)+x >= ingredients.size()) { -// arr.add(JsonNull.INSTANCE); -// continue; -// } -// var ingredient = ingredients.get((y*3)+x).getMatchingStacks(); // FIXME: fix when there are more than one matching stack -// if (ingredient.length == 0) { -// arr.add(JsonNull.INSTANCE); -// } else { -// arr.add(getRawIdFor(ingredient[0].getItem())); -// } -// } -// input.add(arr); -// } -// var rootRecipeObject = new JsonObject(); -// rootRecipeObject.add("inShape", input); -// var resultObject = new JsonObject(); -// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); -// resultObject.addProperty("count", sr.getOutput().getCount()); -// rootRecipeObject.add("result", resultObject); -// recipes.put(getRawIdFor(sr.getOutput().getItem()), rootRecipeObject); - } else if (recipe instanceof ShapelessRecipe sl) { - var ingredients = new JsonArray(); - for (Ingredient ingredient : sl.getIngredients()) { - if (ingredient.isEmpty()) continue; - ingredients.add(getRawIdFor(ingredient.getMatchingStacks()[0].getItem())); - } - var rootRecipeObject = new JsonObject(); - rootRecipeObject.add("ingredients", ingredients); - var resultObject = new JsonObject(); - resultObject.addProperty("id", getRawIdFor(sl.getResult(registryManager).getItem())); - resultObject.addProperty("count", sl.getResult(registryManager).getCount()); - rootRecipeObject.add("result", resultObject); - recipes.put(getRawIdFor(sl.getResult(registryManager).getItem()), rootRecipeObject); - } - } - recipes.forEach((a, b) -> { - if (!finalObj.has(a.toString())) { - finalObj.add(a.toString(), new JsonArray()); - } - finalObj.get(a.toString()).getAsJsonArray().add(b); - }); - return finalObj; - } - - private static int getRawIdFor (Item item) { - return DGU.getWorld().getRegistryManager().get(RegistryKeys.ITEM).getRawId(item); - } -} +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.item.Item; +import net.minecraft.recipe.*; +import net.minecraft.registry.DynamicRegistryManager; +import net.minecraft.registry.RegistryKeys; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class RecipeDataGenerator implements IDataGenerator { + + private static int getRawIdFor(Item item) { + return DGU.getWorld().getRegistryManager().get(RegistryKeys.ITEM).getRawId(item); + } + + @Override + public String getDataName() { + return "recipes"; + } + + @Override + public JsonElement generateDataJson() { + DynamicRegistryManager registryManager = DGU.getWorld().getRegistryManager(); + JsonObject finalObj = new JsonObject(); + Multimap recipes = ArrayListMultimap.create(); + for (RecipeEntry recipeE : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) { + Recipe recipe = recipeE.value(); + if (recipe instanceof ShapedRecipe sr) { + var ingredients = sr.getIngredients(); + List ingr = new ArrayList<>(); + for (int i = 0; i < 9; i++) { + if (i >= ingredients.size()) { + ingr.add(-1); + continue; + } + var stacks = ingredients.get(i); + var matching = stacks.getMatchingStacks(); + if (matching.length == 0) { + ingr.add(-1); + } else { + ingr.add(getRawIdFor(matching[0].getItem())); + } + } + //Lists.reverse(ingr); + + JsonArray inShape = new JsonArray(); + + + var iter = ingr.iterator(); + for (int y = 0; y < sr.getHeight(); y++) { + var jsonRow = new JsonArray(); + for (int z = 0; z < sr.getWidth(); z++) { + jsonRow.add(iter.next()); + } + inShape.add(jsonRow); + } + + JsonObject finalRecipe = new JsonObject(); + finalRecipe.add("inShape", inShape); + + var resultObject = new JsonObject(); + resultObject.addProperty("id", getRawIdFor(sr.getResult(registryManager).getItem())); + resultObject.addProperty("count", sr.getResult(registryManager).getCount()); + finalRecipe.add("result", resultObject); + + String id = ((Integer) getRawIdFor(sr.getResult(registryManager).getItem())).toString(); + + if (!finalObj.has(id)) { + finalObj.add(id, new JsonArray()); + } + finalObj.get(id).getAsJsonArray().add(finalRecipe); +// var input = new JsonArray(); +// var ingredients = sr.getIngredients().stream().toList(); +// for (int y = 0; y < sr.getHeight(); y++) { +// var arr = new JsonArray(); +// for (int x = 0; x < sr.getWidth(); x++) { +// if ((y*3)+x >= ingredients.size()) { +// arr.add(JsonNull.INSTANCE); +// continue; +// } +// var ingredient = ingredients.get((y*3)+x).getMatchingStacks(); // FIXME: fix when there are more than one matching stack +// if (ingredient.length == 0) { +// arr.add(JsonNull.INSTANCE); +// } else { +// arr.add(getRawIdFor(ingredient[0].getItem())); +// } +// } +// input.add(arr); +// } +// var rootRecipeObject = new JsonObject(); +// rootRecipeObject.add("inShape", input); +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +// resultObject.addProperty("count", sr.getOutput().getCount()); +// rootRecipeObject.add("result", resultObject); +// recipes.put(getRawIdFor(sr.getOutput().getItem()), rootRecipeObject); + } else if (recipe instanceof ShapelessRecipe sl) { + var ingredients = new JsonArray(); + for (Ingredient ingredient : sl.getIngredients()) { + if (ingredient.isEmpty()) continue; + ingredients.add(getRawIdFor(ingredient.getMatchingStacks()[0].getItem())); + } + var rootRecipeObject = new JsonObject(); + rootRecipeObject.add("ingredients", ingredients); + var resultObject = new JsonObject(); + resultObject.addProperty("id", getRawIdFor(sl.getResult(registryManager).getItem())); + resultObject.addProperty("count", sl.getResult(registryManager).getCount()); + rootRecipeObject.add("result", resultObject); + recipes.put(getRawIdFor(sl.getResult(registryManager).getItem()), rootRecipeObject); + } + } + recipes.forEach((a, b) -> { + if (!finalObj.has(a.toString())) { + finalObj.add(a.toString(), new JsonArray()); + } + finalObj.get(a.toString()).getAsJsonArray().add(b); + }); + return finalObj; + } +} diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java similarity index 96% rename from src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java rename to 1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java index af71f6ac..1f4f96b0 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java @@ -23,12 +23,6 @@ public class TintsDataGenerator implements IDataGenerator { - public static class BiomeTintColors { - Map> grassColoursMap = new HashMap<>(); - Map> foliageColoursMap = new HashMap<>(); - Map> waterColourMap = new HashMap<>(); - } - public static BiomeTintColors generateBiomeTintColors(Registry biomeRegistry) { BiomeTintColors colors = new BiomeTintColors(); @@ -160,4 +154,10 @@ public JsonObject generateDataJson() { return resultObject; } + + public static class BiomeTintColors { + final Map> grassColoursMap = new HashMap<>(); + final Map> foliageColoursMap = new HashMap<>(); + final Map> waterColourMap = new HashMap<>(); + } } diff --git a/1.20/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java new file mode 100644 index 00000000..0d297e7a --- /dev/null +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.server.dedicated.EulaReader; +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; + +@Mixin(EulaReader.class) +public class EULAMixin { + @Inject(method = "isEulaAgreedTo()Z", at = @At("TAIL"), cancellable = true) + public void init(CallbackInfoReturnable cir) { + cir.setReturnValue(true); + } +} diff --git a/1.20/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java new file mode 100644 index 00000000..3e62bf59 --- /dev/null +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java @@ -0,0 +1,12 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(EntityType.class) +public interface EntityTypeAccessor { + @Accessor("factory") + EntityType.EntityFactory factory(); +} diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java similarity index 100% rename from src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java rename to 1.20/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java diff --git a/1.20/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java new file mode 100644 index 00000000..6d9415eb --- /dev/null +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.generators.DataGenerators; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.MinecraftVersion; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; +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.nio.file.Path; + +@Mixin(MinecraftDedicatedServer.class) +public class ReadyMixin { + @Inject(method = "setupServer()Z", at = @At("TAIL")) + private void init(CallbackInfoReturnable cir) { + Main.LOGGER.info("Starting data generation!"); + String versionName = MinecraftVersion.CURRENT.getName(); + Path serverRootDirectory = DGU.getCurrentlyRunningServer().getRunDirectory().toPath().toAbsolutePath(); + Path dataDumpDirectory = serverRootDirectory.resolve("minecraft-data").resolve(versionName); + DataGenerators.runDataGenerators(dataDumpDirectory); + Main.LOGGER.info("Done data generation!"); + DGU.getCurrentlyRunningServer().stop(false); + } +} diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/TheEndBiomeDataAccessor.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/mixin/TheEndBiomeDataAccessor.java similarity index 71% rename from src/main/java/dev/u9g/minecraftdatagenerator/mixin/TheEndBiomeDataAccessor.java rename to 1.20/src/main/java/dev/u9g/minecraftdatagenerator/mixin/TheEndBiomeDataAccessor.java index f39000f4..dc35e0b3 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/TheEndBiomeDataAccessor.java +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/mixin/TheEndBiomeDataAccessor.java @@ -1,27 +1,29 @@ -package dev.u9g.minecraftdatagenerator.mixin; - -import net.fabricmc.fabric.impl.biome.TheEndBiomeData; -import net.fabricmc.fabric.impl.biome.WeightedPicker; -import net.minecraft.registry.RegistryKey; -import net.minecraft.world.biome.Biome; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -import java.util.Map; - - -@Mixin(TheEndBiomeData.class) -public interface TheEndBiomeDataAccessor { - @Accessor(value = "END_BIOMES_MAP", remap = false) - public static Map, WeightedPicker>> END_BIOMES_MAP() { - throw new IllegalStateException("Should never be called."); - } - @Accessor(value = "END_MIDLANDS_MAP", remap = false) - public static Map, WeightedPicker>> END_MIDLANDS_MAP() { - throw new IllegalStateException("Should never be called."); - } - @Accessor(value = "END_BARRENS_MAP", remap = false) - public static Map, WeightedPicker>> END_BARRENS_MAP() { - throw new IllegalStateException("Should never be called."); - } -} \ No newline at end of file +package dev.u9g.minecraftdatagenerator.mixin; + +import net.fabricmc.fabric.impl.biome.TheEndBiomeData; +import net.fabricmc.fabric.impl.biome.WeightedPicker; +import net.minecraft.registry.RegistryKey; +import net.minecraft.world.biome.Biome; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + + +@Mixin(TheEndBiomeData.class) +public interface TheEndBiomeDataAccessor { + @Accessor(value = "END_BIOMES_MAP", remap = false) + static Map, WeightedPicker>> END_BIOMES_MAP() { + throw new IllegalStateException("Should never be called."); + } + + @Accessor(value = "END_MIDLANDS_MAP", remap = false) + static Map, WeightedPicker>> END_MIDLANDS_MAP() { + throw new IllegalStateException("Should never be called."); + } + + @Accessor(value = "END_BARRENS_MAP", remap = false) + static Map, WeightedPicker>> END_BARRENS_MAP() { + throw new IllegalStateException("Should never be called."); + } +} diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BiomeColors.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BiomeColors.java similarity index 88% rename from src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BiomeColors.java rename to 1.20/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BiomeColors.java index 41c7ec3a..d374b11a 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BiomeColors.java +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BiomeColors.java @@ -1,39 +1,35 @@ -package dev.u9g.minecraftdatagenerator.mojangannoyances; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.BlockRenderView; -import net.minecraft.world.biome.Biome; -import net.minecraft.world.biome.ColorResolver; - -@Environment(EnvType.CLIENT) -public class BiomeColors { - public static final ColorResolver GRASS_COLOR = Biome::getGrassColorAt; - public static final ColorResolver FOLIAGE_COLOR = (biome, x, z) -> { - return biome.getFoliageColor(); - }; - public static final ColorResolver WATER_COLOR = (biome, x, z) -> { - return biome.getWaterColor(); - }; - - public BiomeColors() { - } - - private static int getColor(BlockRenderView world, BlockPos pos, ColorResolver resolver) { - return world.getColor(pos, resolver); - } - - public static int getGrassColor(BlockRenderView world, BlockPos pos) { - return getColor(world, pos, GRASS_COLOR); - } - - public static int getFoliageColor(BlockRenderView world, BlockPos pos) { - return getColor(world, pos, FOLIAGE_COLOR); - } - - public static int getWaterColor(BlockRenderView world, BlockPos pos) { - return getColor(world, pos, WATER_COLOR); - } -} - +package dev.u9g.minecraftdatagenerator.mojangannoyances; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.BlockRenderView; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.ColorResolver; + +@Environment(EnvType.CLIENT) +public class BiomeColors { + public static final ColorResolver GRASS_COLOR = Biome::getGrassColorAt; + public static final ColorResolver FOLIAGE_COLOR = (biome, x, z) -> biome.getFoliageColor(); + public static final ColorResolver WATER_COLOR = (biome, x, z) -> biome.getWaterColor(); + + public BiomeColors() { + } + + private static int getColor(BlockRenderView world, BlockPos pos, ColorResolver resolver) { + return world.getColor(pos, resolver); + } + + public static int getGrassColor(BlockRenderView world, BlockPos pos) { + return getColor(world, pos, GRASS_COLOR); + } + + public static int getFoliageColor(BlockRenderView world, BlockPos pos) { + return getColor(world, pos, FOLIAGE_COLOR); + } + + public static int getWaterColor(BlockRenderView world, BlockPos pos) { + return getColor(world, pos, WATER_COLOR); + } +} + diff --git a/1.20/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColorProvider.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColorProvider.java new file mode 100644 index 00000000..ac5dc572 --- /dev/null +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColorProvider.java @@ -0,0 +1,11 @@ +package dev.u9g.minecraftdatagenerator.mojangannoyances; + +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.BlockRenderView; +import org.jetbrains.annotations.Nullable; + +public interface BlockColorProvider { + int getColor(BlockState state, @Nullable BlockRenderView world, @Nullable BlockPos pos, int tintIndex); +} + diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColors.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColors.java similarity index 60% rename from src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColors.java rename to 1.20/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColors.java index dc31c870..368da7f4 100644 --- a/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColors.java +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/mojangannoyances/BlockColors.java @@ -1,118 +1,96 @@ -package dev.u9g.minecraftdatagenerator.mojangannoyances; - -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Maps; -import dev.u9g.minecraftdatagenerator.util.DGU; -import net.minecraft.block.*; -import net.minecraft.block.enums.DoubleBlockHalf; -import net.minecraft.client.color.world.FoliageColors; -import net.minecraft.client.color.world.GrassColors; -import net.minecraft.registry.RegistryKeys; -import net.minecraft.state.property.Property; -import net.minecraft.util.collection.IdList; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.BlockRenderView; -import net.minecraft.world.World; -import org.jetbrains.annotations.Nullable; - -import java.util.Map; -import java.util.Set; - -public class BlockColors { - private static final int NO_COLOR = -1; - private final IdList providers = new IdList(32); - private final Map>> properties = Maps.newHashMap(); - - public BlockColors() { - } - - public static BlockColors create() { - BlockColors blockColors = new BlockColors(); - blockColors.registerColorProvider((state, world, pos, tintIndex) -> { - return world != null && pos != null ? BiomeColors.getGrassColor(world, state.get(TallPlantBlock.HALF) == DoubleBlockHalf.UPPER ? pos.down() : pos) : -1; - }, Blocks.LARGE_FERN, Blocks.TALL_GRASS); - blockColors.registerColorProperty(TallPlantBlock.HALF, Blocks.LARGE_FERN, Blocks.TALL_GRASS); - blockColors.registerColorProvider((state, world, pos, tintIndex) -> { - return world != null && pos != null ? BiomeColors.getGrassColor(world, pos) : GrassColors.getColor(0.5, 1.0); - }, Blocks.GRASS_BLOCK, Blocks.FERN, Blocks.GRASS, Blocks.POTTED_FERN); - blockColors.registerColorProvider((state, world, pos, tintIndex) -> { - return FoliageColors.getSpruceColor(); - }, Blocks.SPRUCE_LEAVES); - blockColors.registerColorProvider((state, world, pos, tintIndex) -> { - return FoliageColors.getBirchColor(); - }, Blocks.BIRCH_LEAVES); - blockColors.registerColorProvider((state, world, pos, tintIndex) -> { - return world != null && pos != null ? BiomeColors.getFoliageColor(world, pos) : FoliageColors.getDefaultColor(); - }, Blocks.OAK_LEAVES, Blocks.JUNGLE_LEAVES, Blocks.ACACIA_LEAVES, Blocks.DARK_OAK_LEAVES, Blocks.VINE, Blocks.MANGROVE_LEAVES); - blockColors.registerColorProvider((state, world, pos, tintIndex) -> { - return world != null && pos != null ? BiomeColors.getWaterColor(world, pos) : -1; - }, Blocks.WATER, Blocks.BUBBLE_COLUMN, Blocks.WATER_CAULDRON); - blockColors.registerColorProvider((state, world, pos, tintIndex) -> { - return RedstoneWireBlock.getWireColor((Integer)state.get(RedstoneWireBlock.POWER)); - }, Blocks.REDSTONE_WIRE); - blockColors.registerColorProperty(RedstoneWireBlock.POWER, Blocks.REDSTONE_WIRE); - blockColors.registerColorProvider((state, world, pos, tintIndex) -> { - return world != null && pos != null ? BiomeColors.getGrassColor(world, pos) : -1; - }, Blocks.SUGAR_CANE); - blockColors.registerColorProvider((state, world, pos, tintIndex) -> { - return 14731036; - }, Blocks.ATTACHED_MELON_STEM, Blocks.ATTACHED_PUMPKIN_STEM); - blockColors.registerColorProvider((state, world, pos, tintIndex) -> { - int i = (Integer)state.get(StemBlock.AGE); - int j = i * 32; - int k = 255 - i * 8; - int l = i * 4; - return j << 16 | k << 8 | l; - }, Blocks.MELON_STEM, Blocks.PUMPKIN_STEM); - blockColors.registerColorProperty(StemBlock.AGE, Blocks.MELON_STEM, Blocks.PUMPKIN_STEM); - blockColors.registerColorProvider((state, world, pos, tintIndex) -> { - return world != null && pos != null ? 2129968 : 7455580; - }, Blocks.LILY_PAD); - return blockColors; - } - - public int getParticleColor(BlockState state, World world, BlockPos pos) { - BlockColorProvider blockColorProvider = (BlockColorProvider)this.providers.get(DGU.getWorld().getRegistryManager().get(RegistryKeys.BLOCK).getRawId(state.getBlock())); - if (blockColorProvider != null) { - return blockColorProvider.getColor(state, (BlockRenderView)null, (BlockPos)null, 0); - } else { - MapColor mapColor = state.getMapColor(world, pos); - return mapColor != null ? mapColor.color : -1; - } - } - - public int getColor(BlockState state, @Nullable BlockRenderView world, @Nullable BlockPos pos, int tintIndex) { - BlockColorProvider blockColorProvider = (BlockColorProvider)this.providers.get(DGU.getWorld().getRegistryManager().get(RegistryKeys.BLOCK).getRawId(state.getBlock())); - return blockColorProvider == null ? -1 : blockColorProvider.getColor(state, world, pos, tintIndex); - } - - public void registerColorProvider(BlockColorProvider provider, Block... blocks) { - Block[] var3 = blocks; - int var4 = blocks.length; - - for(int var5 = 0; var5 < var4; ++var5) { - Block block = var3[var5]; - this.providers.set(provider, DGU.getWorld().getRegistryManager().get(RegistryKeys.BLOCK).getRawId(block)); - } - - } - - private void registerColorProperties(Set> properties, Block... blocks) { - Block[] var3 = blocks; - int var4 = blocks.length; - - for(int var5 = 0; var5 < var4; ++var5) { - Block block = var3[var5]; - this.properties.put(block, properties); - } - - } - - private void registerColorProperty(Property property, Block... blocks) { - this.registerColorProperties(ImmutableSet.of(property), blocks); - } - - public Set> getProperties(Block block) { - return (Set)this.properties.getOrDefault(block, ImmutableSet.of()); - } -} +package dev.u9g.minecraftdatagenerator.mojangannoyances; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.block.*; +import net.minecraft.block.enums.DoubleBlockHalf; +import net.minecraft.client.color.world.FoliageColors; +import net.minecraft.client.color.world.GrassColors; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.state.property.Property; +import net.minecraft.util.collection.IdList; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.BlockRenderView; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.Set; + +public class BlockColors { + private static final int NO_COLOR = -1; + private final IdList providers = new IdList<>(32); + private final Map>> properties = Maps.newHashMap(); + + public BlockColors() { + } + + public static BlockColors create() { + BlockColors blockColors = new BlockColors(); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getGrassColor(world, state.get(TallPlantBlock.HALF) == DoubleBlockHalf.UPPER ? pos.down() : pos) : -1, Blocks.LARGE_FERN, Blocks.TALL_GRASS); + blockColors.registerColorProperty(TallPlantBlock.HALF, Blocks.LARGE_FERN, Blocks.TALL_GRASS); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getGrassColor(world, pos) : GrassColors.getColor(0.5, 1.0), Blocks.GRASS_BLOCK, Blocks.FERN, Blocks.GRASS, Blocks.POTTED_FERN); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> FoliageColors.getSpruceColor(), Blocks.SPRUCE_LEAVES); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> FoliageColors.getBirchColor(), Blocks.BIRCH_LEAVES); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getFoliageColor(world, pos) : FoliageColors.getDefaultColor(), Blocks.OAK_LEAVES, Blocks.JUNGLE_LEAVES, Blocks.ACACIA_LEAVES, Blocks.DARK_OAK_LEAVES, Blocks.VINE, Blocks.MANGROVE_LEAVES); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getWaterColor(world, pos) : -1, Blocks.WATER, Blocks.BUBBLE_COLUMN, Blocks.WATER_CAULDRON); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> RedstoneWireBlock.getWireColor((Integer) state.get(RedstoneWireBlock.POWER)), Blocks.REDSTONE_WIRE); + blockColors.registerColorProperty(RedstoneWireBlock.POWER, Blocks.REDSTONE_WIRE); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getGrassColor(world, pos) : -1, Blocks.SUGAR_CANE); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> 14731036, Blocks.ATTACHED_MELON_STEM, Blocks.ATTACHED_PUMPKIN_STEM); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> { + int i = (Integer) state.get(StemBlock.AGE); + int j = i * 32; + int k = 255 - i * 8; + int l = i * 4; + return j << 16 | k << 8 | l; + }, Blocks.MELON_STEM, Blocks.PUMPKIN_STEM); + blockColors.registerColorProperty(StemBlock.AGE, Blocks.MELON_STEM, Blocks.PUMPKIN_STEM); + blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? 2129968 : 7455580, Blocks.LILY_PAD); + return blockColors; + } + + public int getParticleColor(BlockState state, World world, BlockPos pos) { + BlockColorProvider blockColorProvider = (BlockColorProvider) this.providers.get(DGU.getWorld().getRegistryManager().get(RegistryKeys.BLOCK).getRawId(state.getBlock())); + if (blockColorProvider != null) { + return blockColorProvider.getColor(state, (BlockRenderView) null, (BlockPos) null, 0); + } else { + MapColor mapColor = state.getMapColor(world, pos); + return mapColor != null ? mapColor.color : -1; + } + } + + public int getColor(BlockState state, @Nullable BlockRenderView world, @Nullable BlockPos pos, int tintIndex) { + BlockColorProvider blockColorProvider = (BlockColorProvider) this.providers.get(DGU.getWorld().getRegistryManager().get(RegistryKeys.BLOCK).getRawId(state.getBlock())); + return blockColorProvider == null ? -1 : blockColorProvider.getColor(state, world, pos, tintIndex); + } + + public void registerColorProvider(BlockColorProvider provider, Block... blocks) { + int var4 = blocks.length; + + for (int var5 = 0; var5 < var4; ++var5) { + Block block = blocks[var5]; + this.providers.set(provider, DGU.getWorld().getRegistryManager().get(RegistryKeys.BLOCK).getRawId(block)); + } + + } + + private void registerColorProperties(Set> properties, Block... blocks) { + int var4 = blocks.length; + + for (int var5 = 0; var5 < var4; ++var5) { + Block block = blocks[var5]; + this.properties.put(block, properties); + } + + } + + private void registerColorProperty(Property property, Block... blocks) { + this.registerColorProperties(ImmutableSet.of(property), blocks); + } + + public Set> getProperties(Block block) { + return (Set) this.properties.getOrDefault(block, ImmutableSet.of()); + } +} diff --git a/1.20/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java new file mode 100644 index 00000000..32c2fb65 --- /dev/null +++ b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java @@ -0,0 +1,32 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Language; +import net.minecraft.world.World; + +public class DGU { + + private static final Language language = Language.getInstance(); + + @SuppressWarnings("deprecation") + private static MinecraftServer getCurrentlyRunningServerDedicated() { + return (MinecraftServer) FabricLoader.getInstance().getGameInstance(); + } + + public static MinecraftServer getCurrentlyRunningServer() { + return getCurrentlyRunningServerDedicated(); + } + + private static String translateTextFallback(String translationKey) { + return language.get(translationKey); + } + + public static String translateText(String translationKey) { + return translateTextFallback(translationKey); + } + + public static World getWorld() { + return getCurrentlyRunningServer().getOverworld(); + } +} diff --git a/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java b/1.20/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java similarity index 100% rename from src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java rename to 1.20/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java diff --git a/1.20/src/main/resources/fabric.mod.json b/1.20/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..c999d2a8 --- /dev/null +++ b/1.20/src/main/resources/fabric.mod.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "id": "minecraft-data-generator", + "version": "${version}", + "name": "Minecraft Data Generator", + "description": "", + "authors": [ + "Archengius", + "U9G" + ], + "contact": {}, + "license": "MIT", + "environment": "server", + "entrypoints": { + "main": [ + "dev.u9g.minecraftdatagenerator.Main" + ] + }, + "mixins": [ + "minecraft-data-generator.mixins.json" + ], + "depends": { + "fabricloader": ">=0.14.4", + "fabric": "*", + "minecraft": "*" + } +} diff --git a/1.20/src/main/resources/minecraft-data-generator.mixins.json b/1.20/src/main/resources/minecraft-data-generator.mixins.json new file mode 100644 index 00000000..f3afe061 --- /dev/null +++ b/1.20/src/main/resources/minecraft-data-generator.mixins.json @@ -0,0 +1,18 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "dev.u9g.minecraftdatagenerator.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "EntityTypeAccessor", + "EULAMixin", + "MiningToolItemAccessor", + "ReadyMixin", + "TheEndBiomeDataAccessor" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/1.7/build.gradle b/1.7/build.gradle new file mode 100644 index 00000000..224e88a1 --- /dev/null +++ b/1.7/build.gradle @@ -0,0 +1,70 @@ +plugins { + id 'fabric-loom' +} + +repositories { + maven { + name = "u9g" + url = "https://maven.u9g.dev" + } +} + +loom { + setIntermediaryUrl('https://maven.legacyfabric.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar'); + customMinecraftManifest.set("https://legacyfabric.u9g.dev/v2/manifest/${minecraft_version}") + + // Required by 1.7.x + runs { + client { + programArgs "--userProperties", "{}" + } + } +} + +dependencies { + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API provides hooks for events, item registration, and more. As most mods will need this, it's included by default. + // If you know for a fact you don't, it's not required and can be safely removed. +// modImplementation ("net.legacyfabric.legacy-fabric-api:legacy-fabric-api:${fabric_version}") { +// exclude module: "legacy-fabric-entity-events-v1" +// } + + if (System.getProperty("os.name").toLowerCase().contains("mac")) { + implementation 'org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209' + implementation 'org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209' + implementation 'org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209' + } +} + +if (System.getProperty("os.name").toLowerCase().contains("mac")) { + configurations.configureEach { + resolutionStrategy { + dependencySubstitution { + substitute module('org.lwjgl.lwjgl:lwjgl_util:2.9.2-nightly-201408222') with module('org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209') + substitute module('org.lwjgl.lwjgl:lwjgl:2.9.2-nightly-201408222') with module('org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209') + } + force 'org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209' + } + } +} + +processResources { + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} diff --git a/1.7/gradle.properties b/1.7/gradle.properties new file mode 100644 index 00000000..8212c17c --- /dev/null +++ b/1.7/gradle.properties @@ -0,0 +1,7 @@ +# Fabric Properties +# More versions available at: https://grayray75.github.io/LegacyFabric-Versions/ +minecraft_version=1.7 +yarn_mappings=1.7+build.202206071953 +loader_version=0.14.3 +# Dependencies +# More versions available at: https://grayray75.github.io/LegacyFabric-Versions/ diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeBlockColors.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeBlockColors.java new file mode 100644 index 00000000..7ea5ca90 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeBlockColors.java @@ -0,0 +1,80 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.block.*; +import net.minecraft.world.biome.Biome; + + +public class BiomeBlockColors { + // generated manually from each Biome's Biome#getBlockColor + public static int getBlockColor(Block block, Biome biome, int blockData) { + if (block instanceof LilyPadBlock) { + return 2129968; + } else if (block instanceof AbstractFluidBlock && block.getMaterial() == Material.WATER) { + int n = 0; + int n2 = 0; + int n3 = 0; + for (int i = -1; i <= 1; ++i) { + for (int j = -1; j <= 1; ++j) { + int n4 = biome.waterColor; + n += (n4 & 0xFF0000) >> 16; + n2 += (n4 & 0xFF00) >> 8; + n3 += n4 & 0xFF; + } + } + return (n / 9 & 0xFF) << 16 | (n2 / 9 & 0xFF) << 8 | n3 / 9 & 0xFF; + } else if (block instanceof AttachedStemBlock) { + int n = blockData * 32; + int n2 = 255 - blockData * 8; + int n3 = blockData * 4; + return n << 16 | n2 << 8 | n3; + } else if (block instanceof RedstoneWireBlock) { + return 0x800000; + } else if (block instanceof DoublePlantBlock) { + int n = ((DoublePlantBlock) block).method_6478(null, 0, 0, 0); + if (n == 2 || n == 3) { + return GrassColors.getGrassColor(biome); + } + } else if (block instanceof GrassBlock) { + int n = 0; + int n2 = 0; + int n3 = 0; + for (int i = -1; i <= 1; ++i) { + for (int j = -1; j <= 1; ++j) { + int n4 = GrassColors.getGrassColor(biome); + n += (n4 & 0xFF0000) >> 16; + n2 += (n4 & 0xFF00) >> 8; + n3 += n4 & 0xFF; + } + } + return (n / 9 & 0xFF) << 16 | (n2 / 9 & 0xFF) << 8 | n3 / 9 & 0xFF; + } else if (block instanceof Leaves1Block) { + if ((blockData & 3) == 1) { + return FoliageColors.getSpruceColor(); + } + if ((blockData & 3) == 2) { + return FoliageColors.getBirchColor(); + } + } else if (block instanceof LeavesBlock) { + int n = 0; + int n2 = 0; + int n3 = 0; + for (int i = -1; i <= 1; ++i) { + for (int j = -1; j <= 1; ++j) { + int n4 = FoliageColors.getColor(biome); + n += (n4 & 0xFF0000) >> 16; + n2 += (n4 & 0xFF00) >> 8; + n3 += n4 & 0xFF; + } + } + return (n / 9 & 0xFF) << 16 | (n2 / 9 & 0xFF) << 8 | n3 / 9 & 0xFF; + } else if (block instanceof SugarCaneBlock) { + return GrassColors.getGrassColor(biome); + } else if (block instanceof TallPlantBlock & blockData != 0) { + return GrassColors.getGrassColor(biome); + } else if (block instanceof VineBlock) { + return FoliageColors.getColor(biome); + } + + return 0xFFFFFF; + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java new file mode 100644 index 00000000..bb71f227 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java @@ -0,0 +1,38 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +public class FoliageColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] pixels) { + colorMap = pixels; + } + + public static int getColor(double temperature, double humidity) { + humidity *= temperature; + int i = (int) ((1.0D - temperature) * 255.0D); + int j = (int) ((1.0D - humidity) * 255.0D); + return colorMap[j << 8 | i]; + } + + public static int getSpruceColor() { + return 6396257; + } + + public static int getBirchColor() { + return 8431445; + } + + public static int getDefaultColor() { + return 4764952; + } + + public static int getColor(Biome biome) { + double d = MathHelper.clamp(biome.temperature, 0.0f, 1.0f); + double e = MathHelper.clamp(biome.downfall, 0.0f, 1.0f); + return FoliageColors.getColor(d, e); + } +} + diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java new file mode 100644 index 00000000..9841e737 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +public class GrassColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] map) { + colorMap = map; + } + + public static int getColor(double temperature, double humidity) { + int n = (int) ((1.0 - temperature) * 255.0); + int n2 = (int) ((1.0 - (humidity *= temperature)) * 255.0); + return colorMap[n2 << 8 | n]; + } + + public static int getGrassColor(Biome biome) { + double d = MathHelper.clamp(biome.temperature, 0.0f, 1.0f); + double e = MathHelper.clamp(biome.downfall, 0.0f, 1.0f); + return GrassColors.getColor(d, e); + } +} + + + diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java new file mode 100644 index 00000000..1572a082 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java @@ -0,0 +1,28 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; + +public class ServerSideRedstoneWireBlock { + public static int getWireColor(int powerLevel) { + float f = (float) powerLevel / 15.0F; + float g = f * 0.6F + 0.4F; + if (powerLevel == 0) { + g = 0.3F; + } + + float h = f * f * 0.7F - 0.5F; + float j = f * f * 0.6F - 0.7F; + if (h < 0.0F) { + h = 0.0F; + } + + if (j < 0.0F) { + j = 0.0F; + } + + int k = MathHelper.clamp((int) (g * 255.0F), 0, 255); + int l = MathHelper.clamp((int) (h * 255.0F), 0, 255); + int m = MathHelper.clamp((int) (j * 255.0F), 0, 255); + return -16777216 | k << 16 | l << 8 | m; + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/Main.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/Main.java new file mode 100644 index 00000000..d23c5ba2 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/Main.java @@ -0,0 +1,13 @@ +package dev.u9g.minecraftdatagenerator; + +import net.fabricmc.api.ModInitializer; + +import java.util.logging.Logger; + +public class Main implements ModInitializer { + public static final Logger LOGGER = Logger.getLogger("mc-data-gen-serv"); + + @Override + public void onInitialize() { + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java new file mode 100644 index 00000000..b4cd2c02 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java @@ -0,0 +1,33 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.accessor.LanguageAccessor; +import net.minecraft.util.Language; + +import java.util.Map; + +public class AttributesDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "attributes"; + } + + @Override + public JsonElement generateDataJson() { + Language lang = new Language(); + Map translations = ((LanguageAccessor) lang).translations(); + JsonArray arr = new JsonArray(); + for (Map.Entry translation : translations.entrySet()) { + String key = translation.getKey(); + if (!key.startsWith("attribute.name.")) continue; + JsonObject obj = new JsonObject(); + key = key.replace("attribute.name.", ""); + obj.addProperty("name", key.split("\\.")[1]); + obj.addProperty("resource", key); + arr.add(obj); + } + return arr; + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java new file mode 100644 index 00000000..3c61ebcd --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java @@ -0,0 +1,192 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.accessor.BiomeAccessor; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.world.biome.*; +import org.apache.commons.lang3.StringUtils; + +import java.util.Locale; +import java.util.SortedMap; +import java.util.TreeMap; + +public class BiomesDataGenerator implements IDataGenerator { + + private static String guessBiomeDimensionFromCategory(Biome biome) { + if (biome instanceof NetherBiome) { + return "nether"; + } else if (biome instanceof EndBiome) { + return "end"; + } + return "overworld"; + } + + private static int getBiomeColorFor(String biomeDisplayNamed) { + if (biomeDisplayNamed.equals("Redwood Taiga Hills M")) { + biomeDisplayNamed = "MegaTaigaHills"; + } + String biomeDisplayName = StringUtils.join(biomeDisplayNamed.split(" "), ""); + return switch (biomeDisplayName) { + case "Ocean" -> 112; + case "Plains" -> 9286496; + case "Desert" -> 16421912; + case "ExtremeHills", "Extreme Hills" -> 6316128; + case "Forest" -> 353825; + case "Taiga" -> 747097; + case "Swampland" -> 522674; + case "River" -> 255; + case "Hell" -> 16711680; + case "TheEnd", "The End" -> 8421631; + case "FrozenOcean", "Frozen Ocean" -> 7368918; + case "FrozenRiver", "Frozen River" -> 10526975; + case "IcePlains", "Ice Plains" -> 16777215; + case "IceMountains", "Ice Mountains" -> 10526880; + case "MushroomIsland", "Mushroom Island" -> 16711935; + case "MushroomIslandShore", "Mushroom Island Shore" -> 10486015; + case "Beach" -> 16440917; + case "DesertHills", "Desert Hills" -> 13786898; + case "ForestHills", "Forest Hills" -> 2250012; + case "TaigaHills", "Taiga Hills" -> 1456435; + case "ExtremeHillsEdge", "Extreme Hills Edge" -> 7501978; + case "Jungle" -> 5470985; + case "JungleHills", "Jungle Hills" -> 2900485; + case "JungleEdge", "Jungle Edge" -> 6458135; + case "DeepOcean", "Deep Ocean" -> 48; + case "StoneBeach", "Stone Beach" -> 10658436; + case "ColdBeach", "Cold Beach" -> 16445632; + case "BirchForest", "Birch Forest" -> 3175492; + case "BirchForestHills", "Birch Forest Hills" -> 2055986; + case "RoofedForest", "Roofed Forest" -> 4215066; + case "ColdTaiga", "Cold Taiga" -> 3233098; + case "ColdTaigaHills", "Cold Taiga Hills" -> 2375478; + case "MegaTaiga", "Mega Taiga" -> 5858897; + case "MegaTaigaHills", "Mega Taiga Hills" -> 4542270; + case "ExtremeHills+", "Extreme Hills+" -> 5271632; + case "Savanna" -> 12431967; + case "SavannaPlateau", "Savanna Plateau" -> 10984804; + case "Mesa" -> 14238997; + case "MesaPlateauF", "Mesa Plateau F" -> 11573093; + case "MesaPlateau", "Mesa Plateau" -> 13274213; + case "TheEnd-Floatingislands", "The End - Floating islands" -> 8421631; + case "TheEnd-Mediumisland", "The End - Medium island" -> 8421631; + case "TheEnd-Highisland", "The End - High island" -> 8421631; + case "TheEnd-Barrenisland", "The End - Barren island" -> 8421631; + case "WarmOcean", "Warm Ocean" -> 172; + case "LukewarmOcean", "Lukewarm Ocean" -> 144; + case "ColdOcean", "Cold Ocean" -> 2105456; + case "WarmDeepOcean", "Warm Deep Ocean" -> 80; + case "LukewarmDeepOcean", "Lukewarm Deep Ocean" -> 64; + case "ColdDeepOcean", "Cold Deep Ocean" -> 2105400; + case "FrozenDeepOcean", "Frozen Deep Ocean" -> 4210832; + case "TheVoid", "The Void" -> 0; + case "SunflowerPlains", "Sunflower Plains" -> 11918216; + case "DesertM", "Desert M" -> 16759872; + case "ExtremeHillsM", "Extreme Hills M" -> 8947848; + case "FlowerForest", "Flower Forest" -> 2985545; + case "TaigaM", "Taiga M" -> 3378817; + case "SwamplandM", "Swampland M" -> 3145690; + case "IcePlainsSpikes", "Ice Plains Spikes" -> 11853020; + case "JungleM", "Jungle M" -> 8102705; + case "JungleEdgeM", "Jungle Edge M" -> 9089855; + case "BirchForestM", "Birch Forest M" -> 5807212; + case "BirchForestHillsM", "Birch Forest Hills M" -> 4687706; + case "RoofedForestM", "Roofed Forest M" -> 6846786; + case "ColdTaigaM", "Cold Taiga M" -> 5864818; + case "MegaSpruceTaiga", "Mega Spruce Taiga" -> 8490617; + case "MegaSpruceTaiga(Hills)", "Mega Spruce Taiga (Hills)" -> 7173990; + case "ExtremeHills+M", "Extreme Hills+ M" -> 7903352; + case "SavannaM", "Savanna M" -> 15063687; + case "SavannaPlateauM", "Savanna Plateau M" -> 13616524; + case "Mesa(Bryce)", "Mesa (Bryce)" -> 16739645; + case "MesaPlateauFM", "Mesa Plateau F M" -> 14204813; + case "MesaPlateauM", "Mesa Plateau M" -> 15905933; + case "BambooJungle", "Bamboo Jungle" -> 7769620; + case "BambooJungleHills", "Bamboo Jungle Hills" -> 3884810; + default -> throw new Error("Unexpected biome, with name: '" + biomeDisplayName + "'"); + }; + } + + public static JsonObject generateBiomeInfo(Biome biome) { + JsonObject biomeDesc = new JsonObject(); + + biomeDesc.addProperty("id", Registries.BIOMES.getRawId(biome)); + biomeDesc.addProperty("name", String.join("_", ((BiomeAccessor) biome).name().toLowerCase(Locale.ENGLISH).split(" "))); + biomeDesc.addProperty("category", category(biome)); + biomeDesc.addProperty("temperature", biome.temperature); + biomeDesc.addProperty("precipitation", precipitation(biome)); + biomeDesc.addProperty("depth", biome.depth); + biomeDesc.addProperty("dimension", guessBiomeDimensionFromCategory(biome)); + biomeDesc.addProperty("displayName", ((BiomeAccessor) biome).name()); + biomeDesc.addProperty("color", getBiomeColorFor(((BiomeAccessor) biome).name())); + biomeDesc.addProperty("rainfall", biome.downfall); + + return biomeDesc; + } + + private static String category(Biome biome) { + if (biome instanceof ForestBiome) { + return "forest"; + } else if (biome instanceof OceanBiome) { + return "ocean"; + } else if (biome instanceof PlainsBiome) { + return "plains"; + } else if (biome instanceof DesertBiome) { + return "desert"; + } else if (biome instanceof ExtremeHillsBiome) { + return "extreme_hills"; + } else if (biome instanceof TaigaBiome) { + return "taiga"; + } else if (biome instanceof SwampBiome) { + return "swamp"; + } else if (biome instanceof RiverBiome) { + return "river"; + } else if (biome instanceof NetherBiome) { + return "nether"; + } else if (biome instanceof EndBiome) { + return "the_end"; + } else if (biome instanceof IceBiome) { + return "icy"; + } else if (biome instanceof MushroomBiome) { + return "mushroom"; + } else if (biome instanceof BeachBiome) { + return "beach"; + } else if (biome instanceof JungleBiome) { + return "jungle"; + } else if (biome instanceof SavannaBiome) { + return "savanna"; + } else if (biome instanceof MesaBiome) { + return "mesa"; + } else if (biome instanceof StoneBeachBiome) { + return "none"; // Should StoneBeachBiome be beach too? this is how it is now in mcdata + } + throw new Error("Unable to find biome category for " + biome.getClass().getName()); + } + + private static String precipitation(Biome biome) { + float rainfall = biome.downfall; + float temperature = biome.temperature; + if (rainfall == 0) { + return "none"; + } else if (temperature < 0.2f) { + return "snow"; + } + return "rain"; + } + + @Override + public String getDataName() { + return "biomes"; + } + + @Override + public JsonArray generateDataJson() { + SortedMap biomes = new TreeMap<>(); + for (Biome biome : Registries.BIOMES) { + biomes.put(biome.id, generateBiomeInfo(biome)); + } + JsonArray biomesArray = new JsonArray(); + biomes.values().forEach(biomesArray::add); + return biomesArray; + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java new file mode 100644 index 00000000..8cbef351 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java @@ -0,0 +1,141 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.block.Block; +import net.minecraft.client.Texture; +import net.minecraft.util.math.Box; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class BlockCollisionShapesDataGenerator implements IDataGenerator { + private static final Box ENTITY_BOX = Box.of(0.0D, 0.0D, 0.0D, 1.0D, 2.0D, 1.0D); + + private static String nameOf(Block block) { + return Objects.requireNonNull(Registries.BLOCKS.getId(block)); + } + + private static JsonArray jsonOf(Box box) { + JsonArray arr = new JsonArray(); + if (box == null) return arr; + arr.add(new JsonPrimitive(box.minX)); + arr.add(new JsonPrimitive(box.minY)); + arr.add(new JsonPrimitive(box.minZ)); + arr.add(new JsonPrimitive(box.maxX)); + arr.add(new JsonPrimitive(box.maxY)); + arr.add(new JsonPrimitive(box.maxZ)); + return arr; + } + + @Override + public String getDataName() { + return "blockCollisionShapes"; + } + + @Override + public JsonObject generateDataJson() { + ShapeCache shapeCache = new ShapeCache(); + JsonObject blocksObject = new JsonObject(); + + for (Block block : Registries.BLOCKS) { + Object val = shapeCache.addShapesFrom(block); + if (val instanceof JsonArray) { + blocksObject.add(nameOf(block), (JsonElement) val); + } else { + blocksObject.addProperty(nameOf(block), (Integer) val); + } + } + JsonObject resultObject = new JsonObject(); + resultObject.add("blocks", blocksObject); + resultObject.add("shapes", shapeCache.toJSON()); + return resultObject; + } + + public static class ShapeCache { + private final ArrayList shapesCache = new ArrayList<>(); + + public Object addShapesFrom(Block block) { + List indexesOfBoxesInTheShapesCache = new ArrayList<>(); + for (Field field : block.getClass().getDeclaredFields()) { + if (!field.getType().getName().equals("[Lnet.minecraft.client.ItemIcon;")) { // ItemIcon[] + continue; + } + try { + field.setAccessible(true); + Texture[] icons = (Texture[]) field.get(block); + return icons.length; + } catch (Exception err) { + err.printStackTrace(); + } + } + // old way +// new Throwable("find block damages based on ItemIcon[]").printStackTrace(); +// for (BlockState state : block.getStateManager().getBlockStates().reverse()) { +// List boxes = new ArrayList<>(); +// try { +// block.appendCollisionBoxes(DGU.getWorld(), BlockPos.ORIGIN, state, ENTITY_BOX, boxes, null); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// Shapes thisBlockStateShapes = new Shapes(boxes); +// int indexOfThisBlockStatesShapes = shapesCache.indexOf(thisBlockStateShapes); +// if (indexOfThisBlockStatesShapes != -1) { +// indexesOfBoxesInTheShapesCache.add(indexOfThisBlockStatesShapes); +// } else { +// shapesCache.add(thisBlockStateShapes); +// indexesOfBoxesInTheShapesCache.add(shapesCache.size() - 1); +// } +// } +// if (indexesOfBoxesInTheShapesCache.stream().distinct().count() < 2) { +// return indexesOfBoxesInTheShapesCache.get(0); +// } else { +// JsonArray shapeIndexes = new JsonArray(); +// indexesOfBoxesInTheShapesCache.forEach(shapeIndex -> shapeIndexes.add(new JsonPrimitive(shapeIndex))); +// return shapeIndexes; +// } + return 1; + } + + public JsonObject toJSON() { + JsonObject shapes = new JsonObject(); + int i = 0; + for (Shapes s : shapesCache) { + shapes.add(String.valueOf(i++), s.toJSON()); + } + return shapes; + } + + private static class Shapes { + final List boxes; + + public Shapes(List boxes) { + this.boxes = boxes; + } + + public JsonArray toJSON() { + JsonArray arr = new JsonArray(); + boxes.forEach(box -> arr.add(jsonOf(box))); + return arr; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Shapes shapes = (Shapes) o; + return Objects.equals(boxes, shapes.boxes); + } + + @Override + public int hashCode() { + return boxes != null ? boxes.hashCode() : 0; + } + } + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java new file mode 100644 index 00000000..feaed25b --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java @@ -0,0 +1,96 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.mixin.accessor.BlockAccessor; +import dev.u9g.minecraftdatagenerator.mixin.accessor.MiningToolItemAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.block.AirBlock; +import net.minecraft.block.Block; +import net.minecraft.block.TransparentBlock; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ToolItem; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.logging.Logger; + +public class BlocksDataGenerator implements IDataGenerator { + + private static final Logger logger = Main.LOGGER; + + private static List getItemsEffectiveForBlock(Block block) { + List items = new ArrayList<>(); + for (Item item : Registries.ITEMS) { + if (item instanceof ToolItem && ((MiningToolItemAccessor) item).getEffectiveBlocks().contains(block)) { + items.add(item); + } + } + return items; + } + + private static List populateDropsIfPossible(Item firstToolItem) { + return new ArrayList<>(); + } + + public static JsonObject generateBlock(Block block) { + JsonObject blockDesc = new JsonObject(); + + String registryKey = Registries.BLOCKS.getId(block); + List effectiveTools = getItemsEffectiveForBlock(block); + + blockDesc.addProperty("id", Registries.BLOCKS.getRawId(block)); + blockDesc.addProperty("name", Objects.requireNonNull(registryKey)); + if (!block.getTranslatedName().startsWith("tile.")) { + blockDesc.addProperty("displayName", block.getTranslatedName()); + } + + float hardness = block.getHardness(null, 0, 0, 0); + + blockDesc.addProperty("hardness", hardness); + blockDesc.addProperty("resistance", ((BlockAccessor) block).getBlastResistance()); + blockDesc.addProperty("diggable", hardness != -1.0f && !(block instanceof AirBlock)); + JsonObject effTools = new JsonObject(); + effectiveTools.forEach(item -> effTools.addProperty( + String.valueOf(Registries.ITEMS.getRawId(item)), // key + item.getMiningSpeedMultiplier(DGU.stackFor(item), block) // value + )); + blockDesc.add("effectiveTools", effTools); + blockDesc.addProperty("transparent", block instanceof TransparentBlock); + blockDesc.addProperty("emitLight", block.getLightLevel()); + blockDesc.addProperty("filterLight", block.getOpacity()); + + blockDesc.addProperty("boundingBox", boundingBox(block)); + + return blockDesc; + } + + private static String boundingBox(Block block) { + if (block.getCollisionBox(DGU.getWorld(), 0, 0, 0) == null) { + return "empty"; + } + return "block"; + } + + private static Item getItemFromBlock(Block block) { + return Registries.ITEMS.get(Registries.BLOCKS.getId(block)); + } + + @Override + public String getDataName() { + return "blocks"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultBlocksArray = new JsonArray(); + for (Block block : Registries.BLOCKS) { + resultBlocksArray.add(generateBlock(block)); + } + return resultBlocksArray; + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java new file mode 100644 index 00000000..d8986bd9 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java @@ -0,0 +1,80 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonWriter; +import dev.u9g.minecraftdatagenerator.Main; + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class DataGenerators { + + private static final List GENERATORS = new ArrayList<>(); + private static final Logger logger = Main.LOGGER; + + static { + register(new BiomesDataGenerator()); +// register(new BlockCollisionShapesDataGenerator()); - Needs to be finished + register(new BlocksDataGenerator()); + register(new EffectsDataGenerator()); + register(new EnchantmentsDataGenerator()); + register(new EntitiesDataGenerator()); + register(new FoodsDataGenerator()); + register(new ItemsDataGenerator()); +// register(new ParticlesDataGenerator()); - See WorldRenderer#spawnParticle + register(new TintsDataGenerator()); + register(new LanguageDataGenerator()); + register(new InstrumentsDataGenerator()); + register(new AttributesDataGenerator()); + } + + public static void register(IDataGenerator generator) { + GENERATORS.add(generator); + } + + public static boolean runDataGenerators(Path outputDirectory) { + try { + Files.createDirectories(outputDirectory); + } catch (IOException exception) { + logger.log(Level.INFO, "Failed to create data generator output directory at " + outputDirectory); + exception.printStackTrace(); + return false; + } + + int generatorsFailed = 0; + logger.log(Level.INFO, MessageFormat.format("Running minecraft data generators, output at {0}", outputDirectory)); + + for (IDataGenerator dataGenerator : GENERATORS) { + logger.log(Level.INFO, MessageFormat.format("Running generator {0}", dataGenerator.getDataName())); + try { + String outputFileName = String.format("%s.json", dataGenerator.getDataName()); + JsonElement outputElement = dataGenerator.generateDataJson(); + Path outputFilePath = outputDirectory.resolve(outputFileName); + + try (Writer writer = Files.newBufferedWriter(outputFilePath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + JsonWriter jsonWriter = new JsonWriter(writer); + jsonWriter.setIndent(" "); + Streams.write(outputElement, jsonWriter); + } + logger.log(Level.INFO, MessageFormat.format("Generator: {0} -> {1}", dataGenerator.getDataName(), outputFileName)); + + } catch (Throwable exception) { + logger.log(Level.INFO, MessageFormat.format("Failed to run data generator {0}", dataGenerator.getDataName())); + exception.printStackTrace(); + generatorsFailed++; + } + } + + logger.log(Level.INFO, "Finishing running data generators"); + return generatorsFailed == 0; + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java new file mode 100644 index 00000000..66f54d2f --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java @@ -0,0 +1,40 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.accessor.StatusEffectAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.entity.effect.StatusEffect; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +public class EffectsDataGenerator implements IDataGenerator { + + public static JsonObject generateEffect(StatusEffect statusEffect) { + JsonObject effectDesc = new JsonObject(); + @NotNull String name = Objects.requireNonNull(Registries.STATUS_EFFECTS.getId(statusEffect)); + + effectDesc.addProperty("id", Registries.STATUS_EFFECTS.getRawId(statusEffect)); + effectDesc.addProperty("name", name); + effectDesc.addProperty("displayName", DGU.translateText(statusEffect.getTranslationKey())); + + effectDesc.addProperty("type", !((StatusEffectAccessor) statusEffect).negative() ? "good" : "bad"); + return effectDesc; + } + + @Override + public String getDataName() { + return "effects"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + for (StatusEffect effect : Registries.STATUS_EFFECTS) { + resultsArray.add(generateEffect(effect)); + } + return resultsArray; + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java new file mode 100644 index 00000000..0ea6d1c6 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java @@ -0,0 +1,107 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentTarget; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +public class EnchantmentsDataGenerator implements IDataGenerator { + + private static final ImmutableMap ENCHANTMENT_TARGET_NAMES = ImmutableMap.builder() + .put(EnchantmentTarget.ARMOR, "armor") + .put(EnchantmentTarget.ARMOR_FEET, "armor_feet") + .put(EnchantmentTarget.ARMOR_LEGS, "armor_legs") + .put(EnchantmentTarget.ARMOR_TORSO, "armor_chest") + .put(EnchantmentTarget.ARMOR_HEAD, "armor_head") + .put(EnchantmentTarget.WEAPON, "weapon") + .put(EnchantmentTarget.DIGGER, "digger") + .put(EnchantmentTarget.FISHING_ROD, "fishing_rod") + .put(EnchantmentTarget.BREAKABLE, "breakable") + .put(EnchantmentTarget.BOW, "bow") + .build(); + + public static String getEnchantmentTargetName(EnchantmentTarget target) { + return ENCHANTMENT_TARGET_NAMES.getOrDefault(target, target.name().toLowerCase(Locale.ROOT)); + } + + // Equation enchantment costs follow is a * level + b, so we can easily retrieve a and b by passing zero level + private static JsonObject generateEnchantmentMinPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMinimumPower(0); + int a = enchantment.getMinimumPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + private static JsonObject generateEnchantmentMaxPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMaximumPower(0); + int a = enchantment.getMaximumPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + public static JsonObject generateEnchantment(Enchantment enchantment) { + JsonObject enchantmentDesc = new JsonObject(); + String registryKey = Registries.ENCHANTMENTS.getId(enchantment); + + enchantmentDesc.addProperty("id", Registries.ENCHANTMENTS.getRawId(enchantment)); + enchantmentDesc.addProperty("name", Objects.requireNonNull(registryKey)); + enchantmentDesc.addProperty("displayName", DGU.translateText(enchantment.getTranslationKey())); + + enchantmentDesc.addProperty("maxLevel", enchantment.getMaximumLevel()); + enchantmentDesc.add("minCost", generateEnchantmentMinPowerCoefficients(enchantment)); + enchantmentDesc.add("maxCost", generateEnchantmentMaxPowerCoefficients(enchantment)); + + enchantmentDesc.addProperty("treasureOnly", false); // 1.9 added treasure enchants + enchantmentDesc.addProperty("curse", false); // 1.10 added curse enchants + + List incompatibleEnchantments = new ArrayList<>(); + for (Enchantment other : Registries.ENCHANTMENTS) { + if (!enchantment.differs(other) && !other.differs(enchantment) && other != enchantment) { + incompatibleEnchantments.add(other); + } + } + + JsonArray excludes = new JsonArray(); + for (Enchantment excludedEnchantment : incompatibleEnchantments) { + String otherKey = Registries.ENCHANTMENTS.getId(excludedEnchantment); + excludes.add(new JsonPrimitive(Objects.requireNonNull(otherKey))); + } + enchantmentDesc.add("exclude", excludes); + + enchantmentDesc.addProperty("category", getEnchantmentTargetName(enchantment.target)); + enchantmentDesc.addProperty("weight", enchantment.getEnchantmentType()); // see AnvilScreenhandler L209 + enchantmentDesc.addProperty("tradeable", true); // the first non-tradeable enchant came in 1.16, soul speed + enchantmentDesc.addProperty("discoverable", true); // the first non-enchantable enchant came in 1.16, soul speed + + return enchantmentDesc; + } + + @Override + public String getDataName() { + return "enchantments"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + for (Enchantment enchantment : Registries.ENCHANTMENTS) { + resultsArray.add(generateEnchantment(enchantment)); + } + return resultsArray; + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java new file mode 100644 index 00000000..6db70ce8 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java @@ -0,0 +1,137 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.accessor.EntityTypeAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.entity.*; +import net.minecraft.entity.mob.AmbientEntity; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.mob.WaterCreatureEntity; +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.projectile.FishingBobberEntity; +import net.minecraft.entity.projectile.Projectile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +public class EntitiesDataGenerator implements IDataGenerator { + + public static JsonObject generateEntity(Class entityClass) { + JsonObject entityDesc = new JsonObject(); + String registryKey = Registries.ENTITY_TYPES.getId(entityClass); + int entityRawId = Registries.ENTITY_TYPES.getRawId(entityClass); + @Nullable Entity entity = makeEntity(entityClass); + // FIXME: ENTITY ID IS WRONG + int id = entityId(entity); + entityDesc.addProperty("id", id); + entityDesc.addProperty("internalId", id); + entityDesc.addProperty("name", Objects.requireNonNull(registryKey)); + String displayName = entity != null ? entity.getTranslatedName() : null; + if (displayName != null && !displayName.startsWith("entity.")) { + entityDesc.addProperty("displayName", displayName); + } + entityDesc.addProperty("width", entity == null ? 0 : entity.width); + entityDesc.addProperty("height", entity == null ? 0 : entity.height); + + String entityTypeString = "UNKNOWN"; + entityTypeString = getEntityTypeForClass(entityClass); + entityDesc.addProperty("type", entityTypeString); + entityDesc.addProperty("category", getCategoryFrom(entityClass)); + + return entityDesc; + } + + private static Entity makeEntity(Class type) { + String name = EntityTypeAccessor.CLASS_NAME_MAP().get(type); + return EntityType.createInstanceFromName(name, DGU.getWorld()); + } + + private static String getCategoryFrom(@NotNull Class entityClass) { + if (entityClass == PlayerEntity.class) return "other"; // fail early for player entities + String packageName = entityClass.getPackage().getName(); + String category = switch (packageName) { + case "net.minecraft.entity.decoration", "net.minecraft.entity.decoration.painting" -> "Immobile"; + case "net.minecraft.entity.boss", "net.minecraft.entity.mob", "net.minecraft.entity.boss.dragon" -> + "Hostile mobs"; + case "net.minecraft.entity.projectile", "net.minecraft.entity.thrown" -> "Projectiles"; + case "net.minecraft.entity.passive" -> "Passive mobs"; + case "net.minecraft.entity.vehicle" -> "Vehicles"; + case "net.minecraft.entity" -> "other"; + default -> throw new Error("Unexpected entity type: " + packageName); + }; + return category; + } + + //Honestly, both "type" and "category" fields in the schema and examples do not contain any useful information + //Since category is optional, I will just leave it out, and for type I will assume general entity classification + //by the Entity class hierarchy (which has some weirdness too by the way) + private static String getEntityTypeForClass(Class entityClass) { + //Top-level classifications + if (WaterCreatureEntity.class.isAssignableFrom(entityClass)) { + return "water_creature"; + } + if (AnimalEntity.class.isAssignableFrom(entityClass)) { + return "animal"; + } + if (HostileEntity.class.isAssignableFrom(entityClass)) { + return "hostile"; + } + if (AmbientEntity.class.isAssignableFrom(entityClass)) { + return "ambient"; + } + + //Second level classifications. PathAwareEntity is not included because it + //doesn't really make much sense to categorize by it + if (PathAwareEntity.class.isAssignableFrom(entityClass)) { + return "passive"; + } + if (MobEntity.class.isAssignableFrom(entityClass)) { + return "mob"; + } + + //Other classifications only include living entities and projectiles. everything else is categorized as other + if (LivingEntity.class.isAssignableFrom(entityClass)) { + return "living"; + } + if (Projectile.class.isAssignableFrom(entityClass)) { + return "projectile"; + } + return "other"; + } + + private static int entityId(Entity entity) { + if (!DGU.getCurrentlyRunningServer().getVersion().equals("1.7")) { + throw new Error("These ids were gotten manually for 1.7, remake for " + DGU.getCurrentlyRunningServer().getVersion()); + } + int rawId = Registries.ENTITY_TYPES.getRawId(entity.getClass()); + if (rawId == -1) { // see TrackedEntityInstance + if (entity instanceof ItemEntity) { + return 2; + } else if (entity instanceof FishingBobberEntity) { + return 90; + } else { + throw new Error("unable to find rawId for entity: " + entity.getClass().getName()); + } + } + return rawId; + } + + @Override + public String getDataName() { + return "entities"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + for (Class entityType : Registries.ENTITY_TYPES) { + resultArray.add(generateEntity(entityType)); + } + return resultArray; + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java new file mode 100644 index 00000000..b93cec8f --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java @@ -0,0 +1,49 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.item.FoodItem; +import net.minecraft.item.Item; + +import java.util.Objects; + +public class FoodsDataGenerator implements IDataGenerator { + + public static JsonObject generateFoodDescriptor(FoodItem foodItem) { + JsonObject foodDesc = new JsonObject(); + String registryKey = Registries.ITEMS.getId(foodItem); + + foodDesc.addProperty("id", Registries.ITEMS.getRawId(foodItem)); + foodDesc.addProperty("name", Objects.requireNonNull(registryKey).replace("minecraft:", "")); + + foodDesc.addProperty("stackSize", foodItem.getMaxCount()); + foodDesc.addProperty("displayName", foodItem.getDisplayName(DGU.stackFor(foodItem))); + float foodPoints = foodItem.getHungerPoints(DGU.stackFor(foodItem)); + float saturationRatio = foodItem.getSaturation(DGU.stackFor(foodItem)) * 2.0F; + float saturation = foodPoints * saturationRatio; + + foodDesc.addProperty("foodPoints", foodPoints); + foodDesc.addProperty("saturation", saturation); + + foodDesc.addProperty("effectiveQuality", foodPoints + saturation); + foodDesc.addProperty("saturationRatio", saturationRatio); + return foodDesc; + } + + @Override + public String getDataName() { + return "foods"; + } + + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + for (Item item : Registries.ITEMS) { + if (item instanceof FoodItem) { + resultsArray.add(generateFoodDescriptor((FoodItem) item)); + } + } + return resultsArray; + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java new file mode 100644 index 00000000..81fe4e20 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; + +public interface IDataGenerator { + + String getDataName(); + + JsonElement generateDataJson(); +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java new file mode 100644 index 00000000..1054ac87 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java @@ -0,0 +1,36 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import net.minecraft.block.Material; + +public class InstrumentsDataGenerator implements IDataGenerator { + public static Material currentMaterial = null; + public static int LAST_WORLD_BLOCKDATA = -1; + + @Override + public String getDataName() { + return "instruments"; + } + + @Override + public JsonElement generateDataJson() { + JsonArray array = new JsonArray(); + // copied from NoteBlock#syncedBlockAction + array.add(makeInstrumentObj(0, "harp")); + array.add(makeInstrumentObj(1, "bd")); + array.add(makeInstrumentObj(2, "snare")); + array.add(makeInstrumentObj(3, "hat")); + array.add(makeInstrumentObj(4, "bassattack")); + return array; + } + + private JsonObject makeInstrumentObj(int i, String s) { + JsonObject obj = new JsonObject(); + obj.add("id", new JsonPrimitive(i)); + obj.add("name", new JsonPrimitive(s)); + return obj; + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java new file mode 100644 index 00000000..13211150 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java @@ -0,0 +1,110 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.mixin.VariantBlockItemAccessor; +import dev.u9g.minecraftdatagenerator.mixin.accessor.ItemAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.VariantBlockItem; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class ItemsDataGenerator implements IDataGenerator { + + private static List calculateItemsToRepairWith(Item sourceItem) { + List items = new ArrayList<>(); + for (Item otherItem : Registries.ITEMS) { + if (sourceItem.canRepair(DGU.stackFor(sourceItem), DGU.stackFor(otherItem))) { + items.add(otherItem); + } + } + return items; + } + + private static List getApplicableEnchantmentTargets(Item sourceItem) { + List targets = new ArrayList<>(); + for (EnchantmentTarget target : EnchantmentTarget.values()) { + if (!target.isCompatible(sourceItem)) continue; + targets.add(target); + } + return targets; + } + + public static JsonObject generateItem(Item item) { + JsonObject itemDesc = new JsonObject(); + + itemDesc.addProperty("id", Registries.ITEMS.getRawId(item)); + + String name = Registries.ITEMS.getId(item); + itemDesc.addProperty("name", name == null ? ((ItemAccessor) item).translationKey() : name.replace("minecraft:", "")); + + itemDesc.addProperty("displayName", item.getDisplayName(DGU.stackFor(item))); + itemDesc.addProperty("stackSize", item.getMaxCount()); + + List enchantmentTargets = getApplicableEnchantmentTargets(item); + + if (item.isDamageable()) { + JsonArray enchantCategoriesArray = new JsonArray(); + for (EnchantmentTarget target : enchantmentTargets) { + enchantCategoriesArray.add(new JsonPrimitive(EnchantmentsDataGenerator.getEnchantmentTargetName(target))); + } + if (enchantCategoriesArray.size() > 0) { + itemDesc.add("enchantCategories", enchantCategoriesArray); + } + } + + if (item.isDamageable()) { + List repairWithItems = calculateItemsToRepairWith(item); + + JsonArray fixedWithArray = new JsonArray(); + for (Item repairWithItem : repairWithItems) { + String repairWithName = Registries.ITEMS.getId(repairWithItem); + fixedWithArray.add(new JsonPrimitive(Objects.requireNonNull(repairWithName))); + } + if (fixedWithArray.size() > 0) { + itemDesc.add("repairWith", fixedWithArray); + } + + int maxDurability = item.getMaxDamage(); + itemDesc.addProperty("maxDurability", maxDurability); + } + + if (item instanceof VariantBlockItem it) { + JsonArray variations = new JsonArray(); + int i = 0; + JsonObject obj = new JsonObject(); + for (String variant : ((VariantBlockItemAccessor) it).variants()) { + ItemStack stack = new ItemStack(item, 1, i); + obj.add("id", new JsonPrimitive(i)); + obj.add("name", new JsonPrimitive(variant)); + obj.add("displayName", new JsonPrimitive(DGU.translateText(it.getTranslationKey(stack) + ".name"))); + variations.add(obj); + i++; + } + itemDesc.add("variations", variations); + } + + return itemDesc; + } + + @Override + public String getDataName() { + return "items"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + for (Item item : Registries.ITEMS) { + resultArray.add(generateItem(item)); + } + return resultArray; + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java new file mode 100644 index 00000000..866f9d1f --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java @@ -0,0 +1,29 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.accessor.LanguageAccessor; +import dev.u9g.minecraftdatagenerator.util.Registries; + +import java.util.Map; + +public class LanguageDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "language"; + } + + @Override + public JsonElement generateDataJson() { + try { + JsonObject obj = new JsonObject(); + Map translations = ((LanguageAccessor) Registries.LANGUAGE).translations(); + for (Map.Entry entry : translations.entrySet()) { + obj.addProperty(entry.getKey(), entry.getValue()); + } + return obj; + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to generate language file"); + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java new file mode 100644 index 00000000..32498dfa --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java @@ -0,0 +1,119 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; + +public class RecipeDataGenerator implements IDataGenerator { + + @Override + public String getDataName() { + return "recipes"; + } + + @Override + public JsonElement generateDataJson() { +// JsonObject finalObj = new JsonObject(); +// Multimap recipes = ArrayListMultimap.create(); +// for (Recipe recipe : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) { +// if (recipe instanceof ShapedRecipe sr) { +// var ingredients = sr.getIngredients(); +// List ingr = new ArrayList<>(); +// for (int i = 0; i < 9; i++) { +// if (i >= ingredients.size()) { +// ingr.add(-1); +// continue; +// } +// var stacks = ingredients.get(i); +//// var matching = stacks.getMatchingStacks(); +//// if (matching.length == 0) { +//// ingr.add(-1); +//// } else { +//// ingr.add(getRawIdFor(matching[0].getItem())); +//// } +// } +// Lists.reverse(ingr); +// +// JsonArray inShape = new JsonArray(); +// +// var iter = ingr.iterator(); +// for (int y = 0; y < 3; y++) { +// var jsonRow = new JsonArray(); +// int one = iter.next(); +// int two = iter.next(); +// int three = iter.next(); +// if (y > 0 && one == -1 && two == -1 && three == -1) continue; +// jsonRow.add(one); +// jsonRow.add(two); +// jsonRow.add(three); +// inShape.add(jsonRow); +// } +// +// JsonObject finalRecipe = new JsonObject(); +// finalRecipe.add("inShape", inShape); +// +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +// resultObject.addProperty("count", sr.getOutput().getCount()); +// finalRecipe.add("result", resultObject); +// +// String id = ((Integer) getRawIdFor(sr.getOutput().getItem())).toString(); +// +// if (!finalObj.has(id)) { +// finalObj.add(id, new JsonArray()); +// } +// finalObj.get(id).getAsJsonArray().add(finalRecipe); +//// var input = new JsonArray(); +//// var ingredients = sr.getIngredients().stream().toList(); +//// for (int y = 0; y < sr.getHeight(); y++) { +//// var arr = new JsonArray(); +//// for (int x = 0; x < sr.getWidth(); x++) { +//// if ((y*3)+x >= ingredients.size()) { +//// arr.add(JsonNull.INSTANCE); +//// continue; +//// } +//// var ingredient = ingredients.get((y*3)+x).getMatchingStacks(); // FIXME: fix when there are more than one matching stack +//// if (ingredient.length == 0) { +//// arr.add(JsonNull.INSTANCE); +//// } else { +//// arr.add(getRawIdFor(ingredient[0].getItem())); +//// } +//// } +//// input.add(arr); +//// } +//// var rootRecipeObject = new JsonObject(); +//// rootRecipeObject.add("inShape", input); +//// var resultObject = new JsonObject(); +//// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +//// resultObject.addProperty("count", sr.getOutput().getCount()); +//// rootRecipeObject.add("result", resultObject); +//// recipes.put(getRawIdFor(sr.getOutput().getItem()), rootRecipeObject); +// } else if (recipe instanceof ShapelessRecipe sl) { + +// var ingredients = new JsonArray(); +// for (Ingredient ingredient : sl.getIngredients()) { +// if (ingredient.isEmpty()) continue; +//// ingredients.add(getRawIdFor(ingredient.getMatchingStacks()[0].getItem())); +// } +// var rootRecipeObject = new JsonObject(); +// rootRecipeObject.add("ingredients", ingredients); +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sl.getOutput().getItem())); +// resultObject.addProperty("count", sl.getOutput().getCount()); +// rootRecipeObject.add("result", resultObject); +// recipes.put(getRawIdFor(sl.getOutput().getItem()), rootRecipeObject); +// } +// } +// recipes.forEach((a, b) -> { +// if (!finalObj.has(a.toString())) { +// finalObj.add(a.toString(), new JsonArray()); +// } +// finalObj.get(a.toString()).getAsJsonArray().add(b); +// }); +// return finalObj; + return JsonNull.INSTANCE; + } +// +// private static int getRawIdFor (Item item) { +// return Registry.ITEM.getRawId(item); +// } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java new file mode 100644 index 00000000..fdb342fb --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java @@ -0,0 +1,145 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.BiomeBlockColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.FoliageColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.GrassColors; +import dev.u9g.minecraftdatagenerator.mixin.accessor.BiomeAccessor; +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.world.biome.Biome; + +import java.util.*; + +public class TintsDataGenerator implements IDataGenerator { + + public static BiomeTintColors generateBiomeTintColors() { + BiomeTintColors colors = new BiomeTintColors(); + + for (Biome biome : Registries.BIOMES) { + EmptyBlockView bv = new EmptyBlockView() { + @Override + public Biome getBiome(int x, int z) { + return biome; + } + }; + int biomeGrassColor = GrassColors.getGrassColor(bv.getBiome(0, 0)); + int biomeFoliageColor = FoliageColors.getColor(bv.getBiome(0, 0)); + int biomeWaterColor = ((BiomeAccessor) biome).waterColor(); + + colors.grassColoursMap.computeIfAbsent(biomeGrassColor, k -> new ArrayList<>()).add(biome); + colors.foliageColoursMap.computeIfAbsent(biomeFoliageColor, k -> new ArrayList<>()).add(biome); + colors.waterColourMap.computeIfAbsent(biomeWaterColor, k -> new ArrayList<>()).add(biome); + } + return colors; + } + + private static int getBlockColor(Block block) { + return BiomeBlockColors.getBlockColor(block, EmptyBlockView.INSTANCE.getBiome(0, 0), 0); + } + + public static Map generateConstantTintColors() { + Map resultColors = new HashMap<>(); + // FIXME: ? + // resultColors.put(Blocks.BIRCH_LEAVES, FoliageColors.getBirchColor()); + // resultColors.put(Blocks.SPRUCE_LEAVES, FoliageColors.getSpruceColor()); + + resultColors.put(Registries.BLOCKS.get("waterlily"), getBlockColor(Blocks.LILY_PAD)); + // FIXME: ? + // resultColors.put(Blocks.ATTACHED_MELON_STEM, getBlockColor(Blocks.ATTACHED_MELON_STEM, blockColors)); + // resultColors.put(Blocks.ATTACHED_PUMPKIN_STEM, getBlockColor(Blocks.ATTACHED_PUMPKIN_STEM, blockColors)); + + //not really constant, depend on the block age, but kinda have to be handled since textures are literally white without them + resultColors.put(Registries.BLOCKS.get("melon_stem"), getBlockColor(Blocks.MELON_STEM)); + resultColors.put(Registries.BLOCKS.get("pumpkin_stem"), getBlockColor(Blocks.PUMPKIN_STEM)); + + return resultColors; + } + + private static JsonObject encodeBiomeColorMap(Map> colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry> entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + for (Biome biome : entry.getValue()) { + keysArray.add(new JsonPrimitive(biome.name)); + } + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getKey()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeRedstoneColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + keysArray.add(new JsonPrimitive(entry.getKey())); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeBlocksColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + JsonArray keysArray = new JsonArray(); + String registryKey = Registries.BLOCKS.getId(entry.getKey()); + keysArray.add(new JsonPrimitive(Objects.requireNonNull(registryKey).replace("minecraft:", ""))); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + @Override + public String getDataName() { + return "tints"; + } + + @Override + public JsonObject generateDataJson() { + BiomeTintColors biomeTintColors = generateBiomeTintColors(); + Map constantTintColors = generateConstantTintColors(); + + JsonObject resultObject = new JsonObject(); + + resultObject.add("grass", encodeBiomeColorMap(biomeTintColors.grassColoursMap)); + resultObject.add("foliage", encodeBiomeColorMap(biomeTintColors.foliageColoursMap)); + resultObject.add("water", encodeBiomeColorMap(biomeTintColors.waterColourMap)); + + resultObject.add("constant", encodeBlocksColorMap(constantTintColors)); + + return resultObject; + } + + public static class BiomeTintColors { + final Map> grassColoursMap = new HashMap<>(); + final Map> foliageColoursMap = new HashMap<>(); + final Map> waterColourMap = new HashMap<>(); + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BlocksMixin.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BlocksMixin.java new file mode 100644 index 00000000..5a032e42 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BlocksMixin.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Blocks; +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(Blocks.class) +public class BlocksMixin { + @Inject(method = "", at = @At("HEAD")) + private static void doBlocksStaticCtor(CallbackInfo ci) { +// Block.setup(); + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java new file mode 100644 index 00000000..1e9185fb --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java @@ -0,0 +1,30 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.generators.DataGenerators; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; +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.io.File; +import java.nio.file.Path; +import java.util.logging.Level; + +@Mixin(MinecraftDedicatedServer.class) +public class ReadyMixin { + @Inject(method = "setupServer()Z", at = @At("TAIL")) + private void init(CallbackInfoReturnable cir) { + Registries.init(); + Main.LOGGER.log(Level.INFO, "Starting data generation!"); + String versionName = DGU.getCurrentlyRunningServer().getVersion(); + Path serverRootDirectory = (new File(".")).toPath().toAbsolutePath(); + Path dataDumpDirectory = serverRootDirectory.resolve("minecraft-data").resolve(versionName); + DataGenerators.runDataGenerators(dataDumpDirectory); + Main.LOGGER.log(Level.INFO, "Done data generation!"); + System.exit(0); + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/VariantBlockItemAccessor.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/VariantBlockItemAccessor.java new file mode 100644 index 00000000..6568fd86 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/VariantBlockItemAccessor.java @@ -0,0 +1,11 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.item.VariantBlockItem; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(VariantBlockItem.class) +public interface VariantBlockItemAccessor { + @Accessor("field_5448") + String[] variants(); +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/BiomeAccessor.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/BiomeAccessor.java new file mode 100644 index 00000000..04a1a7ff --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/BiomeAccessor.java @@ -0,0 +1,21 @@ +package dev.u9g.minecraftdatagenerator.mixin.accessor; + +import net.minecraft.world.biome.Biome; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Set; + +@Mixin(Biome.class) +public interface BiomeAccessor { + @Accessor("BIOMESET") + static Set BIOMESET() { + throw new Error(); + } + + @Accessor("waterColor") + int waterColor(); + + @Accessor("name") + String name(); +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/BlockAccessor.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/BlockAccessor.java new file mode 100644 index 00000000..b2568970 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/BlockAccessor.java @@ -0,0 +1,11 @@ +package dev.u9g.minecraftdatagenerator.mixin.accessor; + +import net.minecraft.block.Block; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Block.class) +public interface BlockAccessor { + @Accessor("blastResistance") + float getBlastResistance(); +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/EnchantmentAccessor.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/EnchantmentAccessor.java new file mode 100644 index 00000000..d69c22e3 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/EnchantmentAccessor.java @@ -0,0 +1,13 @@ +package dev.u9g.minecraftdatagenerator.mixin.accessor; + +import net.minecraft.enchantment.Enchantment; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Enchantment.class) +public interface EnchantmentAccessor { + @Accessor("ALL_ENCHANTMENTS") + static Enchantment[] ALL_ENCHANTMENTS() { + throw new Error(); + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/EntityTypeAccessor.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/EntityTypeAccessor.java new file mode 100644 index 00000000..17b42f40 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/EntityTypeAccessor.java @@ -0,0 +1,31 @@ +package dev.u9g.minecraftdatagenerator.mixin.accessor; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(EntityType.class) +public interface EntityTypeAccessor { + @Accessor("NAME_CLASS_MAP") + static Map> NAME_CLASS_MAP() { + throw new Error(); + } + + @Accessor("CLASS_NAME_MAP") + static Map, String> CLASS_NAME_MAP() { + throw new Error(); + } + + @Accessor("ID_CLASS_MAP") + static Map> ID_CLASS_MAP() { + throw new Error(); + } + + @Accessor("CLASS_ID_MAP") + static Map, Integer> CLASS_ID_MAP() { + throw new Error(); + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/ItemAccessor.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/ItemAccessor.java new file mode 100644 index 00000000..61529d53 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/ItemAccessor.java @@ -0,0 +1,11 @@ +package dev.u9g.minecraftdatagenerator.mixin.accessor; + +import net.minecraft.item.Item; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Item.class) +public interface ItemAccessor { + @Accessor("translationKey") + String translationKey(); +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/LanguageAccessor.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/LanguageAccessor.java new file mode 100644 index 00000000..4eaff0a3 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/LanguageAccessor.java @@ -0,0 +1,13 @@ +package dev.u9g.minecraftdatagenerator.mixin.accessor; + +import net.minecraft.util.Language; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(Language.class) +public interface LanguageAccessor { + @Accessor("translations") + Map translations(); +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/MiningToolItemAccessor.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/MiningToolItemAccessor.java new file mode 100644 index 00000000..cf288c0a --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/MiningToolItemAccessor.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin.accessor; + +import net.minecraft.block.Block; +import net.minecraft.item.ToolItem; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Set; + +@Mixin(ToolItem.class) +public interface MiningToolItemAccessor { + + @Accessor + Set getEffectiveBlocks(); +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/StatusEffectAccessor.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/StatusEffectAccessor.java new file mode 100644 index 00000000..debed94c --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/accessor/StatusEffectAccessor.java @@ -0,0 +1,16 @@ +package dev.u9g.minecraftdatagenerator.mixin.accessor; + +import net.minecraft.entity.effect.StatusEffect; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(StatusEffect.class) +public interface StatusEffectAccessor { + @Accessor("STATUS_EFFECTS") + static StatusEffect[] STATUS_EFFECTS() { + throw new Error(); + } + + @Accessor("negative") + boolean negative(); +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/overwrite/ItemEntityOverwrite.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/overwrite/ItemEntityOverwrite.java new file mode 100644 index 00000000..4aa5ec67 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/mixin/overwrite/ItemEntityOverwrite.java @@ -0,0 +1,24 @@ +package dev.u9g.minecraftdatagenerator.mixin.overwrite; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +@Mixin(ItemEntity.class) +public abstract class ItemEntityOverwrite extends Entity { + + public ItemEntityOverwrite(World world) { + super(world); + } + + /** + * @author a + * @reason a + */ + @Overwrite + public String getTranslatedName() { + return super.getTranslatedName(); + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/registryview/RegistryBackedRegistryView.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/registryview/RegistryBackedRegistryView.java new file mode 100644 index 00000000..95bfe0cf --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/registryview/RegistryBackedRegistryView.java @@ -0,0 +1,46 @@ +package dev.u9g.minecraftdatagenerator.registryview; + +import net.minecraft.util.registry.SimpleRegistry; +import org.jetbrains.annotations.NotNull; + +import java.util.Iterator; +import java.util.function.Consumer; + +public class RegistryBackedRegistryView implements RegistryView { + private final SimpleRegistry registry; + + public RegistryBackedRegistryView(SimpleRegistry registry) { + this.registry = registry; + } + + @Override + public int getRawId(V value) { + return registry.getRawId(value); + } + + @Override + public String getId(V value) { + return registry.getId(value); + } + + @Override + public V getByRawId(int rawId) { + return (V) registry.getByRawId(rawId); + } + + @Override + public V get(String id) { + return (V) registry.get(id); + } + + @NotNull + @Override + public Iterator iterator() { + return (Iterator) registry.iterator(); + } + + @Override + public void forEach(Consumer action) { + registry.forEach(value -> action.accept((V) value)); + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/registryview/RegistryView.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/registryview/RegistryView.java new file mode 100644 index 00000000..6b3de0d0 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/registryview/RegistryView.java @@ -0,0 +1,11 @@ +package dev.u9g.minecraftdatagenerator.registryview; + +public interface RegistryView extends Iterable { + int getRawId(V value); + + K getId(V value); + + V getByRawId(int rawId); + + V get(K id); +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/registryview/TableBackedRegistryView.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/registryview/TableBackedRegistryView.java new file mode 100644 index 00000000..a89014de --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/registryview/TableBackedRegistryView.java @@ -0,0 +1,75 @@ +package dev.u9g.minecraftdatagenerator.registryview; + +import com.google.common.collect.HashBasedTable; +import org.jetbrains.annotations.NotNull; + +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; + +public class TableBackedRegistryView implements RegistryView { + private final HashBasedTable table; + + protected TableBackedRegistryView(HashBasedTable table) { + this.table = table; + } + + @Override + public int getRawId(V value) { + for (Map.Entry> row : table.rowMap().entrySet()) { + Map.Entry entry = row.getValue().entrySet().iterator().next(); + if (Objects.equals(entry.getValue(), value)) { + return entry.getKey(); + } + } + throw new Error("value not in registry."); + } + + @Override + public K getId(V value) { + for (Map.Entry> row : table.rowMap().entrySet()) { + Map.Entry entry = row.getValue().entrySet().iterator().next(); + if (Objects.equals(entry.getValue(), value)) { + return row.getKey(); + } + } + throw new Error("value not in registry."); + } + + @Override + public V getByRawId(int rawId) { + return table.columnMap().get(rawId).entrySet().iterator().next().getValue(); + } + + @Override + public V get(K id) { + return table.row(id).entrySet().iterator().next().getValue(); + } + + @NotNull + @Override + public Iterator iterator() { + return table.values().iterator(); + } + + @Override + public void forEach(Consumer action) { + for (V v : this) { + action.accept(v); + } + } + + public static class Builder { + private final HashBasedTable table = HashBasedTable.create(); + + public Builder add(K key, int rawId, V value) { + table.put(key, rawId, value); + return this; + } + + public TableBackedRegistryView build() { + return new TableBackedRegistryView<>(table); + } + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java new file mode 100644 index 00000000..9f1bf0b7 --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java @@ -0,0 +1,48 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.World; +import org.jetbrains.annotations.NotNull; + +public class DGU { + public static MinecraftServer getCurrentlyRunningServer() { + return MinecraftServer.getServer(); + } + + @Environment(EnvType.CLIENT) + private static String translateTextClient(String translationKey) { + return I18n.translate(translationKey); + } + + private static String translateTextFallback(String translationKey) { + try { + return Registries.LANGUAGE.translate(translationKey); + } catch (Exception err) { + err.printStackTrace(); + } + throw new RuntimeException("Failed to translate: '" + translationKey + "'"); + } + + public static String translateText(String translationKey) { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return translateTextClient(translationKey); + } + return translateTextFallback(translationKey); + } + + @NotNull + public static World getWorld() { + return getCurrentlyRunningServer().getWorld(); + } + + public static ItemStack stackFor(Item ic) { + return new ItemStack(ic); + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java new file mode 100644 index 00000000..6439917b --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java @@ -0,0 +1,61 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.minecraft.block.Block; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.class_5093; +import net.minecraft.world.BlockView; +import net.minecraft.world.biome.Biome; + +public class EmptyBlockView implements BlockView { + public static final EmptyBlockView INSTANCE = new EmptyBlockView(); + + @Override + public Block getBlock(int x, int y, int z) { + return null; + } + + @Override + public BlockEntity getBlockEntity(int x, int y, int z) { + return null; + } + + @Override + public int getBlockData(int x, int y, int z) { + return 0; + } + + @Override + public boolean isAir(int x, int y, int z) { + return false; + } + + @Override + public Biome getBiome(int x, int z) { + return null; + } + + @Override + public int method_3771() { + return 0; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public class_5093 method_22399() { + return null; + } + + @Override + public int method_3719(int i, int j, int k, int l) { + return 0; + } + + @Override + public int method_3778(int i, int j, int k, int l) { + return 0; + } +} diff --git a/1.7/src/main/java/dev/u9g/minecraftdatagenerator/util/Registries.java b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/util/Registries.java new file mode 100644 index 00000000..d345d91e --- /dev/null +++ b/1.7/src/main/java/dev/u9g/minecraftdatagenerator/util/Registries.java @@ -0,0 +1,81 @@ +package dev.u9g.minecraftdatagenerator.util; + +import dev.u9g.minecraftdatagenerator.mixin.accessor.BiomeAccessor; +import dev.u9g.minecraftdatagenerator.mixin.accessor.EnchantmentAccessor; +import dev.u9g.minecraftdatagenerator.mixin.accessor.EntityTypeAccessor; +import dev.u9g.minecraftdatagenerator.mixin.accessor.StatusEffectAccessor; +import dev.u9g.minecraftdatagenerator.registryview.RegistryBackedRegistryView; +import dev.u9g.minecraftdatagenerator.registryview.RegistryView; +import dev.u9g.minecraftdatagenerator.registryview.TableBackedRegistryView; +import net.minecraft.block.Block; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.entity.Entity; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.item.Item; +import net.minecraft.util.Language; +import net.minecraft.world.biome.Biome; +import org.apache.commons.lang3.StringUtils; + +import java.util.Locale; +import java.util.Map; + +public class Registries { + public static Language LANGUAGE; + public static RegistryView BIOMES; + public static RegistryView BLOCKS; + public static RegistryView ITEMS; + public static RegistryView STATUS_EFFECTS; + public static RegistryView ENCHANTMENTS; + public static RegistryView> ENTITY_TYPES; + + private static RegistryView> setupEntityTypesRegistry() { + TableBackedRegistryView.Builder> registry = new TableBackedRegistryView.Builder<>(); + for (Map.Entry> entry : EntityTypeAccessor.ID_CLASS_MAP().entrySet()) { + String name = EntityTypeAccessor.CLASS_NAME_MAP().get(entry.getValue()); + if (name.equals("Mob") || name.equals("Monster")) { + continue; + } + registry.add(name, entry.getKey(), entry.getValue()); + } + + return registry.build(); + } + + private static RegistryView setupEnchantmentRegistry() { + TableBackedRegistryView.Builder registry = new TableBackedRegistryView.Builder<>(); + for (Enchantment enchantment : EnchantmentAccessor.ALL_ENCHANTMENTS()) { + if (enchantment == null) continue; + String translatedName = Registries.LANGUAGE.translate(enchantment.getTranslationKey()); + registry.add(String.join("", translatedName.toLowerCase(Locale.ENGLISH).split(" ")), enchantment.id, enchantment); + } + return registry.build(); + } + + private static RegistryView setupBiomeRegistry() { + TableBackedRegistryView.Builder builder = new TableBackedRegistryView.Builder<>(); + for (Biome biome : BiomeAccessor.BIOMESET()) { + builder.add(biome.name, biome.id, biome); + } + return builder.build(); + } + + private static RegistryView setupStatusEffectRegistry() { + TableBackedRegistryView.Builder builder = new TableBackedRegistryView.Builder<>(); + for (StatusEffect effect : StatusEffectAccessor.STATUS_EFFECTS()) { + if (effect == null) continue; + String[] words = Registries.LANGUAGE.translate(effect.getTranslationKey()).split(" "); + builder.add(StringUtils.join(words, ""), effect.id, effect); + } + return builder.build(); + } + + public static void init() { + LANGUAGE = new Language(); + BIOMES = setupBiomeRegistry(); + BLOCKS = new RegistryBackedRegistryView<>(Block.REGISTRY); + ITEMS = new RegistryBackedRegistryView<>(Item.REGISTRY); + STATUS_EFFECTS = setupStatusEffectRegistry(); + ENCHANTMENTS = setupEnchantmentRegistry(); + ENTITY_TYPES = setupEntityTypesRegistry(); + } +} diff --git a/1.7/src/main/resources/fabric.mod.json b/1.7/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..2e49539c --- /dev/null +++ b/1.7/src/main/resources/fabric.mod.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": 1, + "id": "minecraft-data-generator", + "version": "${version}", + "name": "Minecraft Data Generator", + "description": "", + "authors": [ + "Archengius", + "U9G" + ], + "contact": {}, + "license": "MIT", + "environment": "server", + "entrypoints": { + "main": [ + "dev.u9g.minecraftdatagenerator.Main" + ] + }, + "mixins": [ + "minecraft-data-generator.mixins.json" + ], + "depends": { + "fabricloader": ">=0.13.3", + "minecraft": ">=1.7" + } +} diff --git a/1.7/src/main/resources/minecraft-data-generator.mixins.json b/1.7/src/main/resources/minecraft-data-generator.mixins.json new file mode 100644 index 00000000..804329be --- /dev/null +++ b/1.7/src/main/resources/minecraft-data-generator.mixins.json @@ -0,0 +1,25 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "dev.u9g.minecraftdatagenerator.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "BlocksMixin", + "ReadyMixin", + "VariantBlockItemAccessor", + "accessor.BiomeAccessor", + "accessor.BlockAccessor", + "accessor.EnchantmentAccessor", + "accessor.EntityTypeAccessor", + "accessor.ItemAccessor", + "accessor.LanguageAccessor", + "accessor.MiningToolItemAccessor", + "accessor.StatusEffectAccessor", + "overwrite.ItemEntityOverwrite" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/1.8.9/build.gradle b/1.8.9/build.gradle new file mode 100644 index 00000000..057916c6 --- /dev/null +++ b/1.8.9/build.gradle @@ -0,0 +1,63 @@ +plugins { + id 'fabric-loom' +} + +repositories { + maven { + name = "legacy-fabric" + url = "https://maven.legacyfabric.net" + } +} + +loom { + setIntermediaryUrl('https://maven.legacyfabric.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar'); + customMinecraftManifest.set("https://meta.legacyfabric.net/v2/manifest/${minecraft_version}") +} + +dependencies { + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API provides hooks for events, item registration, and more. As most mods will need this, it's included by default. + // If you know for a fact you don't, it's not required and can be safely removed. +// modImplementation ("net.legacyfabric.legacy-fabric-api:legacy-fabric-api:${fabric_version}") { +// exclude module: "legacy-fabric-entity-events-v1" +// } + + if (System.getProperty("os.name").toLowerCase().contains("mac")) { + implementation 'org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209' + implementation 'org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209' + implementation 'org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209' + } +} + +if (System.getProperty("os.name").toLowerCase().contains("mac")) { + configurations.configureEach { + resolutionStrategy { + dependencySubstitution { + substitute module('org.lwjgl.lwjgl:lwjgl_util:2.9.2-nightly-201408222') with module('org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209') + substitute module('org.lwjgl.lwjgl:lwjgl:2.9.2-nightly-201408222') with module('org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209') + } + force 'org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209' + } + } +} + +processResources { + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} diff --git a/1.8.9/gradle.properties b/1.8.9/gradle.properties new file mode 100644 index 00000000..7cbf4cd4 --- /dev/null +++ b/1.8.9/gradle.properties @@ -0,0 +1,7 @@ +# Fabric Properties +# More versions available at: https://grayray75.github.io/LegacyFabric-Versions/ +minecraft_version=1.8.9 +yarn_mappings=1.8.9+build.202206020145 +loader_version=0.14.3 +# Dependencies +# More versions available at: https://grayray75.github.io/LegacyFabric-Versions/ diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeBlockColors.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeBlockColors.java new file mode 100644 index 00000000..e860bb9d --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeBlockColors.java @@ -0,0 +1,81 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import net.minecraft.block.*; +import net.minecraft.client.color.world.BiomeColors; +import net.minecraft.client.color.world.FoliageColors; +import net.minecraft.util.math.BlockPos; + +import static net.minecraft.block.RedstoneWireBlock.POWER; + +public class BiomeBlockColors { + // generated manually from each Biome's Biome#getBlockColor + public static int getBlockColor(Block block, BlockState state) { + if (block instanceof AbstractFluidBlock) { + return BiomeColors.getWaterColor(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + } else if (block instanceof AttachedStemBlock) { + return getAttachedStemColor(state, block); + } else if (block instanceof DoublePlantBlock) { + EmptyBlockView bv = new EmptyBlockView() { + @Override + public BlockState getBlockState(BlockPos pos) { + return state; + } + }; + DoublePlantBlock.DoublePlantType doublePlantType = ((DoublePlantBlock) block).getVariant(bv, BlockPos.ORIGIN); + if (doublePlantType == DoublePlantBlock.DoublePlantType.GRASS || doublePlantType == DoublePlantBlock.DoublePlantType.FERN) { + return BiomeColors.getGrassColor(bv, BlockPos.ORIGIN); + } + } else if (block instanceof FlowerPotBlock) { + // FIXME: Yeah, flower pot block's color depends on the block inside of it, since we dont compute that, let's just not do anything +// Item item; +// BlockEntity blockEntity = view.getBlockEntity(pos); +// if (blockEntity instanceof FlowerPotBlockEntity && (item = ((FlowerPotBlockEntity)blockEntity).getItem()) instanceof BlockItem) { +// return Block.getBlockFromItem(item).getBlockColor(view, pos, id); +// } + } else if (block instanceof GrassBlock) { + return BiomeColors.getGrassColor(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + } else if (block instanceof Leaves1Block) { + return getLeaves1Color(state, block); + } else if (block instanceof LeavesBlock) { + return getLeavesColor(); + } else if (block instanceof RedstoneWireBlock) { + if (state.getBlock() == block) { + return ServerSideRedstoneWireBlock.getWireColor(state.get(POWER)); + } + } else if (block instanceof SugarCaneBlock || block instanceof TallPlantBlock) { + return GrassColors.getGrassColor(EmptyBlockView.INSTANCE.getBiome(BlockPos.ORIGIN)); + } else if (block instanceof VineBlock) { + return dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.FoliageColors.getFoliageColor(EmptyBlockView.INSTANCE.getBiome(BlockPos.ORIGIN)); + } + return 0xFFFFFF; + } + + private static int getLeaves1Color(BlockState blockState, Block block) { + if (blockState.getBlock() == block) { + PlanksBlock.WoodType woodType = blockState.get(Leaves1Block.VARIANT); + if (woodType == PlanksBlock.WoodType.SPRUCE) { + return net.minecraft.client.color.world.FoliageColors.getSpruceColor(); + } + if (woodType == PlanksBlock.WoodType.BIRCH) { + return FoliageColors.getBirchColor(); + } + } + return getLeavesColor(); + } + + private static int getLeavesColor() { + return FoliageColors.getColor(0.5, 1.0); + } + + private static int getAttachedStemColor(BlockState state, Block block) { + if (state.getBlock() != block) { + return 0xFFFFFF; + } + int i = state.get(AttachedStemBlock.AGE); + int j = i * 32; + int k = 255 - i * 8; + int l = i * 4; + return j << 16 | k << 8 | l; + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeColors.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeColors.java new file mode 100644 index 00000000..3b020082 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeColors.java @@ -0,0 +1,46 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.BlockView; +import net.minecraft.world.biome.Biome; + +import java.util.Iterator; + +public class BiomeColors { + private static final ColorProvider GRASS_COLOR = (biome, pos) -> biome.getGrassColor(pos); + private static final ColorProvider FOLIAGE_COLOR = (biome, pos) -> biome.getFoliageColor(pos); + private static final ColorProvider WATER_COLOR = (biome, pos) -> biome.waterColor; + + private static int getColor(BlockView view, BlockPos pos, ColorProvider provider) { + int i = 0; + int j = 0; + int k = 0; + + int l; + for (Iterator iterator = BlockPos.mutableIterate(pos.add(-1, 0, -1), pos.add(1, 0, 1)).iterator(); iterator.hasNext(); k += l & 255) { + BlockPos.Mutable mutable = (BlockPos.Mutable) iterator.next(); + l = provider.getColorAtPos(view.getBiome(mutable), mutable); + i += (l & 16711680) >> 16; + j += (l & '\uff00') >> 8; + } + + return (i / 9 & 255) << 16 | (j / 9 & 255) << 8 | k / 9 & 255; + } + + public static int getGrassColor(BlockView view, BlockPos pos) { + return getColor(view, pos, GRASS_COLOR); + } + + public static int getFoliageColor(BlockView view, BlockPos pos) { + return getColor(view, pos, FOLIAGE_COLOR); + } + + public static int getWaterColor(BlockView view, BlockPos pos) { + return getColor(view, pos, WATER_COLOR); + } + + interface ColorProvider { + int getColorAtPos(Biome biome, BlockPos pos); + } +} + diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java new file mode 100644 index 00000000..c137d32d --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java @@ -0,0 +1,38 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +public class FoliageColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] pixels) { + colorMap = pixels; + } + + public static int getColor(double temperature, double humidity) { + humidity *= temperature; + int i = (int) ((1.0D - temperature) * 255.0D); + int j = (int) ((1.0D - humidity) * 255.0D); + return colorMap[j << 8 | i]; + } + + public static int getSpruceColor() { + return 6396257; + } + + public static int getBirchColor() { + return 8431445; + } + + public static int getDefaultColor() { + return 4764952; + } + + public static int getFoliageColor(Biome biome) { + double d = MathHelper.clamp(biome.temperature, 0.0f, 1.0f); + double e = MathHelper.clamp(biome.downfall, 0.0f, 1.0f); + return FoliageColors.getColor(d, e); + } +} + diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java new file mode 100644 index 00000000..112d816c --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java @@ -0,0 +1,29 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +public class GrassColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] map) { + colorMap = map; + } + + public static int getColor(double temperature, double humidity) { + humidity *= temperature; + int i = (int) ((1.0D - temperature) * 255.0D); + int j = (int) ((1.0D - humidity) * 255.0D); + int k = j << 8 | i; + return k > colorMap.length ? -65281 : colorMap[k]; + } + + public static int getGrassColor(Biome biome) { + double d = MathHelper.clamp(biome.temperature, 0.0f, 1.0f); + double e = MathHelper.clamp(biome.downfall, 0.0f, 1.0f); + return GrassColors.getColor(d, e); + } +} + + + diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java new file mode 100644 index 00000000..1572a082 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java @@ -0,0 +1,28 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; + +public class ServerSideRedstoneWireBlock { + public static int getWireColor(int powerLevel) { + float f = (float) powerLevel / 15.0F; + float g = f * 0.6F + 0.4F; + if (powerLevel == 0) { + g = 0.3F; + } + + float h = f * f * 0.7F - 0.5F; + float j = f * f * 0.6F - 0.7F; + if (h < 0.0F) { + h = 0.0F; + } + + if (j < 0.0F) { + j = 0.0F; + } + + int k = MathHelper.clamp((int) (g * 255.0F), 0, 255); + int l = MathHelper.clamp((int) (h * 255.0F), 0, 255); + int m = MathHelper.clamp((int) (j * 255.0F), 0, 255); + return -16777216 | k << 16 | l << 8 | m; + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/Main.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/Main.java new file mode 100644 index 00000000..d23c5ba2 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/Main.java @@ -0,0 +1,13 @@ +package dev.u9g.minecraftdatagenerator; + +import net.fabricmc.api.ModInitializer; + +import java.util.logging.Logger; + +public class Main implements ModInitializer { + public static final Logger LOGGER = Logger.getLogger("mc-data-gen-serv"); + + @Override + public void onInitialize() { + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java new file mode 100644 index 00000000..2b1079ff --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java @@ -0,0 +1,33 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.LanguageAccessor; +import net.minecraft.util.Language; + +import java.util.Map; + +public class AttributesDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "attributes"; + } + + @Override + public JsonElement generateDataJson() { + Language lang = new Language(); + Map translations = ((LanguageAccessor) lang).translations(); + JsonArray arr = new JsonArray(); + for (Map.Entry translation : translations.entrySet()) { + String key = translation.getKey(); + if (!key.startsWith("attribute.name.")) continue; + JsonObject obj = new JsonObject(); + key = key.replace("attribute.name.", ""); + obj.addProperty("name", key.split("\\.")[1]); + obj.addProperty("resource", key); + arr.add(obj); + } + return arr; + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java new file mode 100644 index 00000000..675e449f --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java @@ -0,0 +1,190 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.BiomeAccessor; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.world.biome.*; +import org.apache.commons.lang3.StringUtils; + +import java.util.Locale; + +public class BiomesDataGenerator implements IDataGenerator { + + private static String guessBiomeDimensionFromCategory(Biome biome) { + if (biome instanceof NetherBiome) { + return "nether"; + } else if (biome instanceof EndBiome) { + return "end"; + } + return "overworld"; + } + + private static int getBiomeColorFor(String biomeDisplayNamed) { + if (biomeDisplayNamed.equals("Redwood Taiga Hills M")) { + biomeDisplayNamed = "MegaTaigaHills"; + } + String biomeDisplayName = StringUtils.join(biomeDisplayNamed.split(" "), ""); + return switch (biomeDisplayName) { + case "Ocean" -> 112; + case "Plains" -> 9286496; + case "Desert" -> 16421912; + case "ExtremeHills", "Extreme Hills" -> 6316128; + case "Forest" -> 353825; + case "Taiga" -> 747097; + case "Swampland" -> 522674; + case "River" -> 255; + case "Hell" -> 16711680; + case "TheEnd", "The End" -> 8421631; + case "FrozenOcean", "Frozen Ocean" -> 7368918; + case "FrozenRiver", "Frozen River" -> 10526975; + case "IcePlains", "Ice Plains" -> 16777215; + case "IceMountains", "Ice Mountains" -> 10526880; + case "MushroomIsland", "Mushroom Island" -> 16711935; + case "MushroomIslandShore", "Mushroom Island Shore" -> 10486015; + case "Beach" -> 16440917; + case "DesertHills", "Desert Hills" -> 13786898; + case "ForestHills", "Forest Hills" -> 2250012; + case "TaigaHills", "Taiga Hills" -> 1456435; + case "ExtremeHillsEdge", "Extreme Hills Edge" -> 7501978; + case "Jungle" -> 5470985; + case "JungleHills", "Jungle Hills" -> 2900485; + case "JungleEdge", "Jungle Edge" -> 6458135; + case "DeepOcean", "Deep Ocean" -> 48; + case "StoneBeach", "Stone Beach" -> 10658436; + case "ColdBeach", "Cold Beach" -> 16445632; + case "BirchForest", "Birch Forest" -> 3175492; + case "BirchForestHills", "Birch Forest Hills" -> 2055986; + case "RoofedForest", "Roofed Forest" -> 4215066; + case "ColdTaiga", "Cold Taiga" -> 3233098; + case "ColdTaigaHills", "Cold Taiga Hills" -> 2375478; + case "MegaTaiga", "Mega Taiga" -> 5858897; + case "MegaTaigaHills", "Mega Taiga Hills" -> 4542270; + case "ExtremeHills+", "Extreme Hills+" -> 5271632; + case "Savanna" -> 12431967; + case "SavannaPlateau", "Savanna Plateau" -> 10984804; + case "Mesa" -> 14238997; + case "MesaPlateauF", "Mesa Plateau F" -> 11573093; + case "MesaPlateau", "Mesa Plateau" -> 13274213; + case "TheEnd-Floatingislands", "The End - Floating islands" -> 8421631; + case "TheEnd-Mediumisland", "The End - Medium island" -> 8421631; + case "TheEnd-Highisland", "The End - High island" -> 8421631; + case "TheEnd-Barrenisland", "The End - Barren island" -> 8421631; + case "WarmOcean", "Warm Ocean" -> 172; + case "LukewarmOcean", "Lukewarm Ocean" -> 144; + case "ColdOcean", "Cold Ocean" -> 2105456; + case "WarmDeepOcean", "Warm Deep Ocean" -> 80; + case "LukewarmDeepOcean", "Lukewarm Deep Ocean" -> 64; + case "ColdDeepOcean", "Cold Deep Ocean" -> 2105400; + case "FrozenDeepOcean", "Frozen Deep Ocean" -> 4210832; + case "TheVoid", "The Void" -> 0; + case "SunflowerPlains", "Sunflower Plains" -> 11918216; + case "DesertM", "Desert M" -> 16759872; + case "ExtremeHillsM", "Extreme Hills M" -> 8947848; + case "FlowerForest", "Flower Forest" -> 2985545; + case "TaigaM", "Taiga M" -> 3378817; + case "SwamplandM", "Swampland M" -> 3145690; + case "IcePlainsSpikes", "Ice Plains Spikes" -> 11853020; + case "JungleM", "Jungle M" -> 8102705; + case "JungleEdgeM", "Jungle Edge M" -> 9089855; + case "BirchForestM", "Birch Forest M" -> 5807212; + case "BirchForestHillsM", "Birch Forest Hills M" -> 4687706; + case "RoofedForestM", "Roofed Forest M" -> 6846786; + case "ColdTaigaM", "Cold Taiga M" -> 5864818; + case "MegaSpruceTaiga", "Mega Spruce Taiga" -> 8490617; + case "MegaSpruceTaiga(Hills)", "Mega Spruce Taiga (Hills)" -> 7173990; + case "ExtremeHills+M", "Extreme Hills+ M" -> 7903352; + case "SavannaM", "Savanna M" -> 15063687; + case "SavannaPlateauM", "Savanna Plateau M" -> 13616524; + case "Mesa(Bryce)", "Mesa (Bryce)" -> 16739645; + case "MesaPlateauFM", "Mesa Plateau F M" -> 14204813; + case "MesaPlateauM", "Mesa Plateau M" -> 15905933; + case "BambooJungle", "Bamboo Jungle" -> 7769620; + case "BambooJungleHills", "Bamboo Jungle Hills" -> 3884810; + default -> throw new Error("Unexpected biome, with name: '" + biomeDisplayName + "'"); + }; + } + + public static JsonObject generateBiomeInfo(Biome biome) { + JsonObject biomeDesc = new JsonObject(); +// Identifier registryKey = registry.getIdentifier(biome); + + biomeDesc.addProperty("id", Registries.BIOMES.getRawId(biome)); + biomeDesc.addProperty("name", String.join("_", ((BiomeAccessor) biome).name().toLowerCase(Locale.ENGLISH).split(" "))); + biomeDesc.addProperty("category", category(biome)); + biomeDesc.addProperty("temperature", biome.temperature); + biomeDesc.addProperty("precipitation", precipitation(biome)); + biomeDesc.addProperty("depth", biome.depth); + biomeDesc.addProperty("dimension", guessBiomeDimensionFromCategory(biome)); + biomeDesc.addProperty("displayName", ((BiomeAccessor) biome).name()); + biomeDesc.addProperty("color", getBiomeColorFor(((BiomeAccessor) biome).name())); + biomeDesc.addProperty("rainfall", biome.downfall); + + return biomeDesc; + } + + private static String category(Biome biome) { + if (biome instanceof ForestBiome) { + return "forest"; + } else if (biome instanceof OceanBiome) { + return "ocean"; + } else if (biome instanceof PlainsBiome) { + return "plains"; + } else if (biome instanceof DesertBiome) { + return "desert"; + } else if (biome instanceof ExtremeHillsBiome) { + return "extreme_hills"; + } else if (biome instanceof TaigaBiome) { + return "taiga"; + } else if (biome instanceof SwampBiome) { + return "swamp"; + } else if (biome instanceof RiverBiome) { + return "river"; + } else if (biome instanceof NetherBiome) { + return "nether"; + } else if (biome instanceof EndBiome) { + return "the_end"; + } else if (biome instanceof IceBiome) { + return "icy"; + } else if (biome instanceof MushroomBiome) { + return "mushroom"; + } else if (biome instanceof BeachBiome) { + return "beach"; + } else if (biome instanceof JungleBiome) { + return "jungle"; + } else if (biome instanceof SavannaBiome) { + return "savanna"; + } else if (biome instanceof MesaBiome) { + return "mesa"; + } else if (biome instanceof StoneBeachBiome) { + return "none"; // Should StoneBeachBiome be beach too? this is how it is now in mcdata + } + throw new Error("Unable to find biome category for " + biome.getClass().getName()); + } + + private static String precipitation(Biome biome) { + float rainfall = biome.downfall; + float temperature = biome.temperature; + if (rainfall == 0) { + return "none"; + } else if (temperature < 0.2f) { + return "snow"; + } + return "rain"; + } + + @Override + public String getDataName() { + return "biomes"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray biomesArray = new JsonArray(); + + for (Biome biome : Registries.BIOMES) { + biomesArray.add(generateBiomeInfo(biome)); + } + return biomesArray; + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java new file mode 100644 index 00000000..6d025605 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java @@ -0,0 +1,126 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class BlockCollisionShapesDataGenerator implements IDataGenerator { + private static final Box ENTITY_BOX = new Box(0.0D, 0.0D, 0.0D, 1.0D, 2.0D, 1.0D); + + private static String nameOf(Block block) { + return Objects.requireNonNull(Registries.BLOCKS.getIdentifier(block)).getPath(); + } + + private static JsonArray jsonOf(Box box) { + JsonArray arr = new JsonArray(); + if (box == null) return arr; + arr.add(new JsonPrimitive(box.minX)); + arr.add(new JsonPrimitive(box.minY)); + arr.add(new JsonPrimitive(box.minZ)); + arr.add(new JsonPrimitive(box.maxX)); + arr.add(new JsonPrimitive(box.maxY)); + arr.add(new JsonPrimitive(box.maxZ)); + return arr; + } + + @Override + public String getDataName() { + return "blockCollisionShapes"; + } + + @Override + public JsonObject generateDataJson() { + ShapeCache shapeCache = new ShapeCache(); + JsonObject blocksObject = new JsonObject(); + for (Block block : Registries.BLOCKS) { + Object val = shapeCache.addShapesFrom(block); + if (val instanceof JsonArray) { + blocksObject.add(nameOf(block), (JsonElement) val); + } else { + blocksObject.addProperty(nameOf(block), (Integer) val); + } + } + JsonObject resultObject = new JsonObject(); + resultObject.add("blocks", blocksObject); + resultObject.add("shapes", shapeCache.toJSON()); + return resultObject; + } + + public static class ShapeCache { + private final ArrayList shapesCache = new ArrayList<>(); + + public Object addShapesFrom(Block block) { + List indexesOfBoxesInTheShapesCache = new ArrayList<>(); + for (BlockState state : block.getStateManager().getBlockStates().reverse()) { + List boxes = new ArrayList<>(); + try { + block.appendCollisionBoxes(DGU.getWorld(), BlockPos.ORIGIN, state, ENTITY_BOX, boxes, null); + } catch (Exception e) { + e.printStackTrace(); + } + Shapes thisBlockStateShapes = new Shapes(boxes); + int indexOfThisBlockStatesShapes = shapesCache.indexOf(thisBlockStateShapes); + if (indexOfThisBlockStatesShapes != -1) { + indexesOfBoxesInTheShapesCache.add(indexOfThisBlockStatesShapes); + } else { + shapesCache.add(thisBlockStateShapes); + indexesOfBoxesInTheShapesCache.add(shapesCache.size() - 1); + } + } + if (indexesOfBoxesInTheShapesCache.stream().distinct().count() < 2) { + return indexesOfBoxesInTheShapesCache.get(0); + } else { + JsonArray shapeIndexes = new JsonArray(); + indexesOfBoxesInTheShapesCache.forEach(shapeIndex -> shapeIndexes.add(new JsonPrimitive(shapeIndex))); + return shapeIndexes; + } + } + + public JsonObject toJSON() { + JsonObject shapes = new JsonObject(); + int i = 0; + for (Shapes s : shapesCache) { + shapes.add(String.valueOf(i++), s.toJSON()); + } + return shapes; + } + + private static class Shapes { + final List boxes; + + public Shapes(List boxes) { + this.boxes = boxes; + } + + public JsonArray toJSON() { + JsonArray arr = new JsonArray(); + boxes.forEach(box -> arr.add(jsonOf(box))); + return arr; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Shapes shapes = (Shapes) o; + return Objects.equals(boxes, shapes.boxes); + } + + @Override + public int hashCode() { + return boxes != null ? boxes.hashCode() : 0; + } + } + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java new file mode 100644 index 00000000..5431a483 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java @@ -0,0 +1,151 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.base.CaseFormat; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.mixin.BlockAccessor; +import dev.u9g.minecraftdatagenerator.mixin.MiningToolItemAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.block.AirBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.TransparentBlock; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ToolItem; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.state.property.EnumProperty; +import net.minecraft.state.property.IntProperty; +import net.minecraft.state.property.Property; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.logging.Logger; + +public class BlocksDataGenerator implements IDataGenerator { + + private static final Logger logger = Main.LOGGER; + + private static List getItemsEffectiveForBlock(Block block) { + List items = new ArrayList<>(); + for (Item item : Registries.ITEMS) { + if (item instanceof ToolItem && ((MiningToolItemAccessor) item).getEffectiveBlocks().contains(block)) { + items.add(item); + } + } + return items; + } + + private static List populateDropsIfPossible(BlockState blockState, Item firstToolItem) { + return new ArrayList<>(); + } + + private static String getPropertyTypeName(Property property) { + //Explicitly handle default minecraft properties + if (property instanceof BooleanProperty) { + return "bool"; + } + if (property instanceof IntProperty) { + return "int"; + } + if (property instanceof EnumProperty) { + return "enum"; + } + + //Use simple class name as fallback, this code will give something like + //example_type for ExampleTypeProperty class name + String rawPropertyName = property.getClass().getSimpleName().replace("Property", ""); + return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, rawPropertyName); + } + + private static > JsonObject generateStateProperty(Property property) { + JsonObject propertyObject = new JsonObject(); + Collection propertyValues = property.getValues(); + + propertyObject.addProperty("name", property.getName()); + propertyObject.addProperty("type", getPropertyTypeName(property)); + propertyObject.addProperty("num_values", propertyValues.size()); + + //Do not add values for vanilla boolean properties, they are known by default + if (!(property instanceof BooleanProperty)) { + JsonArray propertyValuesArray = new JsonArray(); + for (T propertyValue : propertyValues) { + propertyValuesArray.add(new JsonPrimitive(property.name(propertyValue))); + } + propertyObject.add("values", propertyValuesArray); + } + return propertyObject; + } + + public static JsonObject generateBlock(Block block) { + JsonObject blockDesc = new JsonObject(); + + List blockStates = block.getStateManager().getBlockStates(); + BlockState defaultState = block.getDefaultState(); + Identifier registryKey = Registries.BLOCKS.getIdentifier(block); + List effectiveTools = getItemsEffectiveForBlock(block); + + blockDesc.addProperty("id", Registries.BLOCKS.getRawId(block)); + blockDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + if (!block.getTranslatedName().startsWith("tile.")) { + blockDesc.addProperty("displayName", block.getTranslatedName()); + } + + float hardness = block.getStrength(null, null); + + blockDesc.addProperty("hardness", hardness); + blockDesc.addProperty("resistance", ((BlockAccessor) block).getBlastResistance()); + blockDesc.addProperty("diggable", hardness != -1.0f && !(block instanceof AirBlock)); + JsonObject effTools = new JsonObject(); + effectiveTools.forEach(item -> effTools.addProperty( + String.valueOf(Registries.ITEMS.getRawId(item)), // key + item.getMiningSpeedMultiplier(DGU.stackFor(item), block) // value + )); + blockDesc.add("effectiveTools", effTools); + blockDesc.addProperty("transparent", block instanceof TransparentBlock); + blockDesc.addProperty("emitLight", block.getLightLevel()); + blockDesc.addProperty("filterLight", block.getOpacity()); + + JsonArray stateProperties = new JsonArray(); + for (Property property : block.getStateManager().getProperties()) { + stateProperties.add(generateStateProperty(property)); + } + blockDesc.add("states", stateProperties); + blockDesc.add("drops", new JsonArray()); + blockDesc.addProperty("boundingBox", boundingBox(block, defaultState)); + + return blockDesc; + } + + private static String boundingBox(Block block, BlockState state) { + if (block.getCollisionBox(DGU.getWorld(), BlockPos.ORIGIN, state) == null) { + return "empty"; + } + return "block"; + } + + private static Item getItemFromBlock(Block block) { + return Registries.ITEMS.get(Registries.BLOCKS.getIdentifier(block)); + } + + @Override + public String getDataName() { + return "blocks"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultBlocksArray = new JsonArray(); + for (Block block : Registries.BLOCKS) { + resultBlocksArray.add(generateBlock(block)); + } + return resultBlocksArray; + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java new file mode 100644 index 00000000..273d67ca --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java @@ -0,0 +1,80 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonWriter; +import dev.u9g.minecraftdatagenerator.Main; + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class DataGenerators { + + private static final List GENERATORS = new ArrayList<>(); + private static final Logger logger = Main.LOGGER; + + static { + register(new BiomesDataGenerator()); + register(new BlockCollisionShapesDataGenerator()); + register(new BlocksDataGenerator()); + register(new EffectsDataGenerator()); + register(new EnchantmentsDataGenerator()); + register(new EntitiesDataGenerator()); + register(new FoodsDataGenerator()); + register(new ItemsDataGenerator()); + register(new ParticlesDataGenerator()); + register(new TintsDataGenerator()); + register(new LanguageDataGenerator()); + register(new InstrumentsDataGenerator()); + register(new AttributesDataGenerator()); + } + + public static void register(IDataGenerator generator) { + GENERATORS.add(generator); + } + + public static boolean runDataGenerators(Path outputDirectory) { + try { + Files.createDirectories(outputDirectory); + } catch (IOException exception) { + logger.log(Level.INFO, "Failed to create data generator output directory at " + outputDirectory); + exception.printStackTrace(); + return false; + } + + int generatorsFailed = 0; + logger.log(Level.INFO, MessageFormat.format("Running minecraft data generators, output at {0}", outputDirectory)); + + for (IDataGenerator dataGenerator : GENERATORS) { + logger.log(Level.INFO, MessageFormat.format("Running generator {0}", dataGenerator.getDataName())); + try { + String outputFileName = String.format("%s.json", dataGenerator.getDataName()); + JsonElement outputElement = dataGenerator.generateDataJson(); + Path outputFilePath = outputDirectory.resolve(outputFileName); + + try (Writer writer = Files.newBufferedWriter(outputFilePath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + JsonWriter jsonWriter = new JsonWriter(writer); + jsonWriter.setIndent(" "); + Streams.write(outputElement, jsonWriter); + } + logger.log(Level.INFO, MessageFormat.format("Generator: {0} -> {1}", dataGenerator.getDataName(), outputFileName)); + + } catch (Throwable exception) { + logger.log(Level.INFO, MessageFormat.format("Failed to run data generator {0}", dataGenerator.getDataName())); + exception.printStackTrace(); + generatorsFailed++; + } + } + + logger.log(Level.INFO, "Finishing running data generators"); + return generatorsFailed == 0; + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java new file mode 100644 index 00000000..08524dc0 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java @@ -0,0 +1,44 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.StatusEffectAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.util.Identifier; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.Collectors; + +public class EffectsDataGenerator implements IDataGenerator { + + public static JsonObject generateEffect(StatusEffect statusEffect) { + JsonObject effectDesc = new JsonObject(); + @NotNull Identifier registryKey = Objects.requireNonNull(Registries.STATUS_EFFECTS.getIdentifier(statusEffect)); + + effectDesc.addProperty("id", Registries.STATUS_EFFECTS.getRawId(statusEffect)); + effectDesc.addProperty("name", Arrays.stream(registryKey.getPath().split("_")).map(StringUtils::capitalize).collect(Collectors.joining())); + effectDesc.addProperty("displayName", DGU.translateText(statusEffect.getTranslationKey())); + + effectDesc.addProperty("type", !((StatusEffectAccessor) statusEffect).negative() ? "good" : "bad"); + return effectDesc; + } + + @Override + public String getDataName() { + return "effects"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + for (StatusEffect effect : Registries.STATUS_EFFECTS) { + resultsArray.add(generateEffect(effect)); + } + return resultsArray; + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java new file mode 100644 index 00000000..0c352ebd --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java @@ -0,0 +1,108 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.util.Identifier; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +public class EnchantmentsDataGenerator implements IDataGenerator { + + private static final ImmutableMap ENCHANTMENT_TARGET_NAMES = ImmutableMap.builder() + .put(EnchantmentTarget.ALL_ARMOR, "armor") + .put(EnchantmentTarget.FEET, "armor_feet") + .put(EnchantmentTarget.LEGS, "armor_legs") + .put(EnchantmentTarget.TORSO, "armor_chest") + .put(EnchantmentTarget.HEAD, "armor_head") + .put(EnchantmentTarget.WEAPON, "weapon") + .put(EnchantmentTarget.DIGGER, "digger") + .put(EnchantmentTarget.FISHING_ROD, "fishing_rod") + .put(EnchantmentTarget.BREAKABLE, "breakable") + .put(EnchantmentTarget.BOW, "bow") + .build(); + + public static String getEnchantmentTargetName(EnchantmentTarget target) { + return ENCHANTMENT_TARGET_NAMES.getOrDefault(target, target.name().toLowerCase(Locale.ROOT)); + } + + // Equation enchantment costs follow is a * level + b, so we can easily retrieve a and b by passing zero level + private static JsonObject generateEnchantmentMinPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMinimumPower(0); + int a = enchantment.getMinimumPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + private static JsonObject generateEnchantmentMaxPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMaximumPower(0); + int a = enchantment.getMaximumPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + public static JsonObject generateEnchantment(Enchantment enchantment) { + JsonObject enchantmentDesc = new JsonObject(); + Identifier registryKey = Registries.ENCHANTMENTS.getIdentifier(enchantment); + + enchantmentDesc.addProperty("id", Registries.ENCHANTMENTS.getRawId(enchantment)); + enchantmentDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + enchantmentDesc.addProperty("displayName", DGU.translateText(enchantment.getTranslationKey())); + + enchantmentDesc.addProperty("maxLevel", enchantment.getMaximumLevel()); + enchantmentDesc.add("minCost", generateEnchantmentMinPowerCoefficients(enchantment)); + enchantmentDesc.add("maxCost", generateEnchantmentMaxPowerCoefficients(enchantment)); + + enchantmentDesc.addProperty("treasureOnly", false); // 1.9 added treasure enchants + enchantmentDesc.addProperty("curse", false); // 1.10 added curse enchants + + List incompatibleEnchantments = new ArrayList<>(); + for (Enchantment other : Registries.ENCHANTMENTS) { + if (!enchantment.differs(other) && !other.differs(enchantment) && other != enchantment) { + incompatibleEnchantments.add(other); + } + } + + JsonArray excludes = new JsonArray(); + for (Enchantment excludedEnchantment : incompatibleEnchantments) { + Identifier otherKey = Registries.ENCHANTMENTS.getIdentifier(excludedEnchantment); + excludes.add(new JsonPrimitive(Objects.requireNonNull(otherKey).getPath())); + } + enchantmentDesc.add("exclude", excludes); + + enchantmentDesc.addProperty("category", getEnchantmentTargetName(enchantment.target)); + enchantmentDesc.addProperty("weight", enchantment.getEnchantmentType()); // see AnvilScreenhandler L209 + enchantmentDesc.addProperty("tradeable", true); // the first non-tradeable enchant came in 1.16, soul speed + enchantmentDesc.addProperty("discoverable", true); // the first non-enchantable enchant came in 1.16, soul speed + + return enchantmentDesc; + } + + @Override + public String getDataName() { + return "enchantments"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + for (Enchantment enchantment : Registries.ENCHANTMENTS) { + resultsArray.add(generateEnchantment(enchantment)); + } + return resultsArray; + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java new file mode 100644 index 00000000..7ade8308 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java @@ -0,0 +1,138 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.EntityTypeAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.entity.*; +import net.minecraft.entity.mob.AmbientEntity; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.mob.WaterCreatureEntity; +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.projectile.FishingBobberEntity; +import net.minecraft.entity.projectile.Projectile; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +public class EntitiesDataGenerator implements IDataGenerator { + + public static JsonObject generateEntity(Class entityClass) { + JsonObject entityDesc = new JsonObject(); + Identifier registryKey = Registries.ENTITY_TYPES.getIdentifier(entityClass); + int entityRawId = Registries.ENTITY_TYPES.getRawId(entityClass); + @Nullable Entity entity = makeEntity(entityClass); + // FIXME: ENTITY ID IS WRONG + int id = entityId(entity); + entityDesc.addProperty("id", id); + entityDesc.addProperty("internalId", id); + entityDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + String displayName = entity != null ? entity.getTranslationKey() : null; + if (displayName != null && !displayName.startsWith("entity.")) { + entityDesc.addProperty("displayName", displayName); + } + entityDesc.addProperty("width", entity == null ? 0 : entity.width); + entityDesc.addProperty("height", entity == null ? 0 : entity.height); + + String entityTypeString = "UNKNOWN"; + entityTypeString = getEntityTypeForClass(entityClass); + entityDesc.addProperty("type", entityTypeString); + entityDesc.addProperty("category", getCategoryFrom(entityClass)); + + return entityDesc; + } + + private static Entity makeEntity(Class type) { + String name = EntityTypeAccessor.CLASS_NAME_MAP().get(type); + return EntityType.createInstanceFromName(name, DGU.getWorld()); + } + + private static String getCategoryFrom(@NotNull Class entityClass) { + if (entityClass == PlayerEntity.class) return "other"; // fail early for player entities + String packageName = entityClass.getPackage().getName(); + String category = switch (packageName) { + case "net.minecraft.entity.decoration", "net.minecraft.entity.decoration.painting" -> "Immobile"; + case "net.minecraft.entity.boss", "net.minecraft.entity.mob", "net.minecraft.entity.boss.dragon" -> + "Hostile mobs"; + case "net.minecraft.entity.projectile", "net.minecraft.entity.thrown" -> "Projectiles"; + case "net.minecraft.entity.passive" -> "Passive mobs"; + case "net.minecraft.entity.vehicle" -> "Vehicles"; + case "net.minecraft.entity" -> "other"; + default -> throw new Error("Unexpected entity type: " + packageName); + }; + return category; + } + + //Honestly, both "type" and "category" fields in the schema and examples do not contain any useful information + //Since category is optional, I will just leave it out, and for type I will assume general entity classification + //by the Entity class hierarchy (which has some weirdness too by the way) + private static String getEntityTypeForClass(Class entityClass) { + //Top-level classifications + if (WaterCreatureEntity.class.isAssignableFrom(entityClass)) { + return "water_creature"; + } + if (AnimalEntity.class.isAssignableFrom(entityClass)) { + return "animal"; + } + if (HostileEntity.class.isAssignableFrom(entityClass)) { + return "hostile"; + } + if (AmbientEntity.class.isAssignableFrom(entityClass)) { + return "ambient"; + } + + //Second level classifications. PathAwareEntity is not included because it + //doesn't really make much sense to categorize by it + if (PathAwareEntity.class.isAssignableFrom(entityClass)) { + return "passive"; + } + if (MobEntity.class.isAssignableFrom(entityClass)) { + return "mob"; + } + + //Other classifications only include living entities and projectiles. everything else is categorized as other + if (LivingEntity.class.isAssignableFrom(entityClass)) { + return "living"; + } + if (Projectile.class.isAssignableFrom(entityClass)) { + return "projectile"; + } + return "other"; + } + + private static int entityId(Entity entity) { + if (!DGU.getCurrentlyRunningServer().getVersion().equals("1.8.9")) { + throw new Error("These ids were gotten manually for 1.8.9, remake for " + DGU.getCurrentlyRunningServer().getVersion()); + } + int rawId = Registries.ENTITY_TYPES.getRawId(entity.getClass()); + if (rawId == -1) { // see TrackedEntityInstance + if (entity instanceof ItemEntity) { + return 2; + } else if (entity instanceof FishingBobberEntity) { + return 90; + } else { + throw new Error("unable to find rawId for entity: " + entity.getEntity().getClass().getName()); + } + } + return rawId; + } + + @Override + public String getDataName() { + return "entities"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + for (Class entityType : Registries.ENTITY_TYPES) { + resultArray.add(generateEntity(entityType)); + } + return resultArray; + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java new file mode 100644 index 00000000..fd68583a --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java @@ -0,0 +1,50 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.item.FoodItem; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; + +import java.util.Objects; + +public class FoodsDataGenerator implements IDataGenerator { + + public static JsonObject generateFoodDescriptor(FoodItem foodItem) { + JsonObject foodDesc = new JsonObject(); + Identifier registryKey = Registries.ITEMS.getIdentifier(foodItem); + + foodDesc.addProperty("id", Registries.ITEMS.getRawId(foodItem)); + foodDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + foodDesc.addProperty("stackSize", foodItem.getMaxCount()); + foodDesc.addProperty("displayName", foodItem.getDisplayName(DGU.stackFor(foodItem))); + float foodPoints = foodItem.getHungerPoints(DGU.stackFor(foodItem)); + float saturationRatio = foodItem.getSaturation(DGU.stackFor(foodItem)) * 2.0F; + float saturation = foodPoints * saturationRatio; + + foodDesc.addProperty("foodPoints", foodPoints); + foodDesc.addProperty("saturation", saturation); + + foodDesc.addProperty("effectiveQuality", foodPoints + saturation); + foodDesc.addProperty("saturationRatio", saturationRatio); + return foodDesc; + } + + @Override + public String getDataName() { + return "foods"; + } + + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + for (Item item : Registries.ITEMS) { + if (item instanceof FoodItem) { + resultsArray.add(generateFoodDescriptor((FoodItem) item)); + } + } + return resultsArray; + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java new file mode 100644 index 00000000..81fe4e20 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; + +public interface IDataGenerator { + + String getDataName(); + + JsonElement generateDataJson(); +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java new file mode 100644 index 00000000..5ca25753 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java @@ -0,0 +1,28 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.NoteBlockAccessor; + +import java.util.Objects; + +public class InstrumentsDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "instruments"; + } + + @Override + public JsonElement generateDataJson() { + JsonArray array = new JsonArray(); + int i = 0; + for (String soundName : Objects.requireNonNull(NoteBlockAccessor.TUNES())) { + JsonObject object = new JsonObject(); + object.addProperty("id", i++); + object.addProperty("name", soundName); + array.add(object); + } + return array; + } +} \ No newline at end of file diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java new file mode 100644 index 00000000..4b84f478 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java @@ -0,0 +1,90 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class ItemsDataGenerator implements IDataGenerator { + + private static List calculateItemsToRepairWith(Item sourceItem) { + List items = new ArrayList<>(); + for (Item otherItem : Registries.ITEMS) { + if (sourceItem.canRepair(DGU.stackFor(sourceItem), DGU.stackFor(otherItem))) { + items.add(otherItem); + } + } + return items; + } + + private static List getApplicableEnchantmentTargets(Item sourceItem) { + List targets = new ArrayList<>(); + for (EnchantmentTarget target : EnchantmentTarget.values()) { + if (!target.isCompatible(sourceItem)) continue; + targets.add(target); + } + return targets; + } + + public static JsonObject generateItem(Item item) { + JsonObject itemDesc = new JsonObject(); + Identifier registryKey = Registries.ITEMS.getIdentifier(item); + + itemDesc.addProperty("id", Registries.ITEMS.getRawId(item)); + itemDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + itemDesc.addProperty("displayName", item.getDisplayName(DGU.stackFor(item))); + itemDesc.addProperty("stackSize", item.getMaxCount()); + + List enchantmentTargets = getApplicableEnchantmentTargets(item); + + if (item.isDamageable()) { + JsonArray enchantCategoriesArray = new JsonArray(); + for (EnchantmentTarget target : enchantmentTargets) { + enchantCategoriesArray.add(new JsonPrimitive(EnchantmentsDataGenerator.getEnchantmentTargetName(target))); + } + if (enchantCategoriesArray.size() > 0) { + itemDesc.add("enchantCategories", enchantCategoriesArray); + } + } + + if (item.isDamageable()) { + List repairWithItems = calculateItemsToRepairWith(item); + + JsonArray fixedWithArray = new JsonArray(); + for (Item repairWithItem : repairWithItems) { + Identifier repairWithName = Registries.ITEMS.getIdentifier(repairWithItem); + fixedWithArray.add(new JsonPrimitive(Objects.requireNonNull(repairWithName).getPath())); + } + if (fixedWithArray.size() > 0) { + itemDesc.add("repairWith", fixedWithArray); + } + + int maxDurability = item.getMaxDamage(); + itemDesc.addProperty("maxDurability", maxDurability); + } + return itemDesc; + } + + @Override + public String getDataName() { + return "items"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + for (Item item : Registries.ITEMS) { + resultArray.add(generateItem(item)); + } + return resultArray; + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java new file mode 100644 index 00000000..88e8d2fb --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java @@ -0,0 +1,29 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.LanguageAccessor; +import dev.u9g.minecraftdatagenerator.util.Registries; + +import java.util.Map; + +public class LanguageDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "language"; + } + + @Override + public JsonElement generateDataJson() { + try { + JsonObject obj = new JsonObject(); + Map translations = ((LanguageAccessor) Registries.LANGUAGE).translations(); + for (Map.Entry entry : translations.entrySet()) { + obj.addProperty(entry.getKey(), entry.getValue()); + } + return obj; + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to generate language file"); + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java new file mode 100644 index 00000000..2b1a5085 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java @@ -0,0 +1,31 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.minecraft.client.particle.ParticleType; + +public class ParticlesDataGenerator implements IDataGenerator { + + public static JsonObject generateParticleType(int id, ParticleType particleType) { + JsonObject effectDesc = new JsonObject(); + + effectDesc.addProperty("id", id); + effectDesc.addProperty("name", particleType.getName()); + return effectDesc; + } + + @Override + public String getDataName() { + return "particles"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + int i = 0; + for (ParticleType particleType : ParticleType.values()) { + resultsArray.add(generateParticleType(i++, particleType)); + } + return resultsArray; + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java new file mode 100644 index 00000000..32498dfa --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java @@ -0,0 +1,119 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; + +public class RecipeDataGenerator implements IDataGenerator { + + @Override + public String getDataName() { + return "recipes"; + } + + @Override + public JsonElement generateDataJson() { +// JsonObject finalObj = new JsonObject(); +// Multimap recipes = ArrayListMultimap.create(); +// for (Recipe recipe : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) { +// if (recipe instanceof ShapedRecipe sr) { +// var ingredients = sr.getIngredients(); +// List ingr = new ArrayList<>(); +// for (int i = 0; i < 9; i++) { +// if (i >= ingredients.size()) { +// ingr.add(-1); +// continue; +// } +// var stacks = ingredients.get(i); +//// var matching = stacks.getMatchingStacks(); +//// if (matching.length == 0) { +//// ingr.add(-1); +//// } else { +//// ingr.add(getRawIdFor(matching[0].getItem())); +//// } +// } +// Lists.reverse(ingr); +// +// JsonArray inShape = new JsonArray(); +// +// var iter = ingr.iterator(); +// for (int y = 0; y < 3; y++) { +// var jsonRow = new JsonArray(); +// int one = iter.next(); +// int two = iter.next(); +// int three = iter.next(); +// if (y > 0 && one == -1 && two == -1 && three == -1) continue; +// jsonRow.add(one); +// jsonRow.add(two); +// jsonRow.add(three); +// inShape.add(jsonRow); +// } +// +// JsonObject finalRecipe = new JsonObject(); +// finalRecipe.add("inShape", inShape); +// +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +// resultObject.addProperty("count", sr.getOutput().getCount()); +// finalRecipe.add("result", resultObject); +// +// String id = ((Integer) getRawIdFor(sr.getOutput().getItem())).toString(); +// +// if (!finalObj.has(id)) { +// finalObj.add(id, new JsonArray()); +// } +// finalObj.get(id).getAsJsonArray().add(finalRecipe); +//// var input = new JsonArray(); +//// var ingredients = sr.getIngredients().stream().toList(); +//// for (int y = 0; y < sr.getHeight(); y++) { +//// var arr = new JsonArray(); +//// for (int x = 0; x < sr.getWidth(); x++) { +//// if ((y*3)+x >= ingredients.size()) { +//// arr.add(JsonNull.INSTANCE); +//// continue; +//// } +//// var ingredient = ingredients.get((y*3)+x).getMatchingStacks(); // FIXME: fix when there are more than one matching stack +//// if (ingredient.length == 0) { +//// arr.add(JsonNull.INSTANCE); +//// } else { +//// arr.add(getRawIdFor(ingredient[0].getItem())); +//// } +//// } +//// input.add(arr); +//// } +//// var rootRecipeObject = new JsonObject(); +//// rootRecipeObject.add("inShape", input); +//// var resultObject = new JsonObject(); +//// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +//// resultObject.addProperty("count", sr.getOutput().getCount()); +//// rootRecipeObject.add("result", resultObject); +//// recipes.put(getRawIdFor(sr.getOutput().getItem()), rootRecipeObject); +// } else if (recipe instanceof ShapelessRecipe sl) { + +// var ingredients = new JsonArray(); +// for (Ingredient ingredient : sl.getIngredients()) { +// if (ingredient.isEmpty()) continue; +//// ingredients.add(getRawIdFor(ingredient.getMatchingStacks()[0].getItem())); +// } +// var rootRecipeObject = new JsonObject(); +// rootRecipeObject.add("ingredients", ingredients); +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sl.getOutput().getItem())); +// resultObject.addProperty("count", sl.getOutput().getCount()); +// rootRecipeObject.add("result", resultObject); +// recipes.put(getRawIdFor(sl.getOutput().getItem()), rootRecipeObject); +// } +// } +// recipes.forEach((a, b) -> { +// if (!finalObj.has(a.toString())) { +// finalObj.add(a.toString(), new JsonArray()); +// } +// finalObj.get(a.toString()).getAsJsonArray().add(b); +// }); +// return finalObj; + return JsonNull.INSTANCE; + } +// +// private static int getRawIdFor (Item item) { +// return Registry.ITEM.getRawId(item); +// } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java new file mode 100644 index 00000000..c69b3cb6 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java @@ -0,0 +1,161 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.BiomeBlockColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.FoliageColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.GrassColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.ServerSideRedstoneWireBlock; +import dev.u9g.minecraftdatagenerator.mixin.BiomeAccessor; +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.block.RedstoneWireBlock; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.biome.Biome; + +import java.util.*; + +public class TintsDataGenerator implements IDataGenerator { + + public static BiomeTintColors generateBiomeTintColors() { + BiomeTintColors colors = new BiomeTintColors(); + + for (Biome biome : Registries.BIOMES) { + EmptyBlockView bv = new EmptyBlockView() { + @Override + public Biome getBiome(BlockPos pos) { + return biome; + } + }; + int biomeGrassColor = GrassColors.getGrassColor(bv.getBiome(BlockPos.ORIGIN)); + int biomeFoliageColor = FoliageColors.getFoliageColor(bv.getBiome(BlockPos.ORIGIN)); + int biomeWaterColor = ((BiomeAccessor) biome).waterColor(); + + colors.grassColoursMap.computeIfAbsent(biomeGrassColor, k -> new ArrayList<>()).add(biome); + colors.foliageColoursMap.computeIfAbsent(biomeFoliageColor, k -> new ArrayList<>()).add(biome); + colors.waterColourMap.computeIfAbsent(biomeWaterColor, k -> new ArrayList<>()).add(biome); + } + return colors; + } + + public static Map generateRedstoneTintColors() { + Map resultColors = new HashMap<>(); + + for (int redstoneLevel : RedstoneWireBlock.POWER.getValues()) { + int color = ServerSideRedstoneWireBlock.getWireColor(redstoneLevel); + resultColors.put(redstoneLevel, color); + } + return resultColors; + } + + private static int getBlockColor(Block block) { + return BiomeBlockColors.getBlockColor(block, block.getDefaultState()); + } + + public static Map generateConstantTintColors() { + Map resultColors = new HashMap<>(); + // FIXME: ? + // resultColors.put(Blocks.BIRCH_LEAVES, FoliageColors.getBirchColor()); + // resultColors.put(Blocks.SPRUCE_LEAVES, FoliageColors.getSpruceColor()); + + resultColors.put(Blocks.LILY_PAD, getBlockColor(Blocks.LILY_PAD)); + // FIXME: ? + // resultColors.put(Blocks.ATTACHED_MELON_STEM, getBlockColor(Blocks.ATTACHED_MELON_STEM, blockColors)); + // resultColors.put(Blocks.ATTACHED_PUMPKIN_STEM, getBlockColor(Blocks.ATTACHED_PUMPKIN_STEM, blockColors)); + + //not really constant, depend on the block age, but kinda have to be handled since textures are literally white without them + resultColors.put(Blocks.MELON_STEM, getBlockColor(Blocks.MELON_STEM)); + resultColors.put(Blocks.PUMPKIN_STEM, getBlockColor(Blocks.PUMPKIN_STEM)); + + return resultColors; + } + + private static JsonObject encodeBiomeColorMap(Map> colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry> entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + for (Biome biome : entry.getValue()) { + keysArray.add(new JsonPrimitive(biome.name)); + } + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getKey()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeRedstoneColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + keysArray.add(new JsonPrimitive(entry.getKey())); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeBlocksColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + JsonArray keysArray = new JsonArray(); + Identifier registryKey = Registries.BLOCKS.getIdentifier(entry.getKey()); + keysArray.add(new JsonPrimitive(Objects.requireNonNull(registryKey).getPath())); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + @Override + public String getDataName() { + return "tints"; + } + + @Override + public JsonObject generateDataJson() { + BiomeTintColors biomeTintColors = generateBiomeTintColors(); + Map redstoneColors = generateRedstoneTintColors(); + Map constantTintColors = generateConstantTintColors(); + + JsonObject resultObject = new JsonObject(); + + resultObject.add("grass", encodeBiomeColorMap(biomeTintColors.grassColoursMap)); + resultObject.add("foliage", encodeBiomeColorMap(biomeTintColors.foliageColoursMap)); + resultObject.add("water", encodeBiomeColorMap(biomeTintColors.waterColourMap)); + + resultObject.add("redstone", encodeRedstoneColorMap(redstoneColors)); + resultObject.add("constant", encodeBlocksColorMap(constantTintColors)); + + return resultObject; + } + + public static class BiomeTintColors { + final Map> grassColoursMap = new HashMap<>(); + final Map> foliageColoursMap = new HashMap<>(); + final Map> waterColourMap = new HashMap<>(); + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BiomeAccessor.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BiomeAccessor.java new file mode 100644 index 00000000..dd813619 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BiomeAccessor.java @@ -0,0 +1,21 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.world.biome.Biome; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Set; + +@Mixin(Biome.class) +public interface BiomeAccessor { + @Accessor("BIOMESET") + static Set BIOMESET() { + throw new Error(); + } + + @Accessor("waterColor") + int waterColor(); + + @Accessor("name") + String name(); +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BlockAccessor.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BlockAccessor.java new file mode 100644 index 00000000..1c25c003 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BlockAccessor.java @@ -0,0 +1,11 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Block; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Block.class) +public interface BlockAccessor { + @Accessor("blastResistance") + float getBlastResistance(); +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java new file mode 100644 index 00000000..0d297e7a --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.server.dedicated.EulaReader; +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; + +@Mixin(EulaReader.class) +public class EULAMixin { + @Inject(method = "isEulaAgreedTo()Z", at = @At("TAIL"), cancellable = true) + public void init(CallbackInfoReturnable cir) { + cir.setReturnValue(true); + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EnchantmentAccessor.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EnchantmentAccessor.java new file mode 100644 index 00000000..1daf881c --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EnchantmentAccessor.java @@ -0,0 +1,16 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(Enchantment.class) +public interface EnchantmentAccessor { + @Accessor("ENCHANTMENT_MAP") + static Map ENCHANTMENT_MAP() { + throw new Error(); + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java new file mode 100644 index 00000000..60386ba8 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java @@ -0,0 +1,36 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(EntityType.class) +public interface EntityTypeAccessor { + @Accessor("NAME_CLASS_MAP") + static Map> NAME_CLASS_MAP() { + throw new Error(); + } + + @Accessor("CLASS_NAME_MAP") + static Map, String> CLASS_NAME_MAP() { + throw new Error(); + } + + @Accessor("ID_CLASS_MAP") + static Map> ID_CLASS_MAP() { + throw new Error(); + } + + @Accessor("CLASS_ID_MAP") + static Map, Integer> CLASS_ID_MAP() { + throw new Error(); + } + + @Accessor("NAME_ID_MAP") + static Map NAME_ID_MAP() { + throw new Error(); + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/LanguageAccessor.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/LanguageAccessor.java new file mode 100644 index 00000000..7bfd95fc --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/LanguageAccessor.java @@ -0,0 +1,13 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.util.Language; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(Language.class) +public interface LanguageAccessor { + @Accessor("translations") + Map translations(); +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java new file mode 100644 index 00000000..53b8cdb5 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Block; +import net.minecraft.item.ToolItem; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Set; + +@Mixin(ToolItem.class) +public interface MiningToolItemAccessor { + + @Accessor + Set getEffectiveBlocks(); +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/NoteBlockAccessor.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/NoteBlockAccessor.java new file mode 100644 index 00000000..f21bf744 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/NoteBlockAccessor.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.NoteBlock; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + +@Mixin(NoteBlock.class) +public interface NoteBlockAccessor { + @Accessor("TUNES") + static List TUNES() { + return null; + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java new file mode 100644 index 00000000..32871cc3 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.generators.DataGenerators; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; +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.nio.file.Path; +import java.util.logging.Level; + +@Mixin(MinecraftDedicatedServer.class) +public class ReadyMixin { + @Inject(method = "setupServer()Z", at = @At("TAIL")) + private void init(CallbackInfoReturnable cir) { + Main.LOGGER.log(Level.INFO, "Starting data generation!"); + String versionName = DGU.getCurrentlyRunningServer().getVersion(); + Path serverRootDirectory = DGU.getCurrentlyRunningServer().getRunDirectory().toPath().toAbsolutePath(); + Path dataDumpDirectory = serverRootDirectory.resolve("minecraft-data").resolve(versionName); + DataGenerators.runDataGenerators(dataDumpDirectory); + Main.LOGGER.log(Level.INFO, "Done data generation!"); + System.exit(0); + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java new file mode 100644 index 00000000..09985fe0 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java @@ -0,0 +1,19 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(StatusEffect.class) +public interface StatusEffectAccessor { + @Accessor("STATUS_EFFECTS_BY_ID") + static Map STATUS_EFFECTS_BY_ID() { + throw new Error(); + } + + @Accessor("negative") + boolean negative(); +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/overwrite/CocoaBlockOverwrite.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/overwrite/CocoaBlockOverwrite.java new file mode 100644 index 00000000..8290f2b2 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/overwrite/CocoaBlockOverwrite.java @@ -0,0 +1,41 @@ +package dev.u9g.minecraftdatagenerator.mixin.overwrite; + +import net.minecraft.block.BlockState; +import net.minecraft.block.CocoaBlock; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.Direction; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +@Mixin(CocoaBlock.class) +public class CocoaBlockOverwrite { + private static Box collisionBox(BlockState state) { + Direction direction = state.get(CocoaBlock.FACING); + int i = state.get(CocoaBlock.AGE); + int j = 4 + i * 2; + int k = 5 + i * 2; + float f = (float) j / 2.0f; + return switch (direction) { + case SOUTH -> + new Box((8.0f - f) / 16.0f, (12.0f - (float) k) / 16.0f, (15.0f - (float) j) / 16.0f, (8.0f + f) / 16.0f, 0.75f, 0.9375f); + case NORTH -> + new Box((8.0f - f) / 16.0f, (12.0f - (float) k) / 16.0f, 0.0625f, (8.0f + f) / 16.0f, 0.75f, (1.0f + (float) j) / 16.0f); + case WEST -> + new Box(0.0625f, (12.0f - (float) k) / 16.0f, (8.0f - f) / 16.0f, (1.0f + (float) j) / 16.0f, 0.75f, (8.0f + f) / 16.0f); + case EAST -> + new Box((15.0f - (float) j) / 16.0f, (12.0f - (float) k) / 16.0f, (8.0f - f) / 16.0f, 0.9375f, 0.75f, (8.0f + f) / 16.0f); + default -> throw new Error("Should never hit here"); + }; + } + + /** + * @author a + * @reason a + */ + @Overwrite + public Box getCollisionBox(World world, BlockPos pos, BlockState state) { + return collisionBox(state); + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/overwrite/EndPortalFrameBlockOverwrite.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/overwrite/EndPortalFrameBlockOverwrite.java new file mode 100644 index 00000000..f4ac01bf --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/overwrite/EndPortalFrameBlockOverwrite.java @@ -0,0 +1,34 @@ +package dev.u9g.minecraftdatagenerator.mixin.overwrite; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.EndPortalFrameBlock; +import net.minecraft.block.Material; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +import java.util.List; + +@Mixin(EndPortalFrameBlock.class) +public class EndPortalFrameBlockOverwrite extends Block { + protected EndPortalFrameBlockOverwrite(Material material) { + super(material); + } + + /** + * @author a + * @reason a + */ + @Overwrite() + public void appendCollisionBoxes(World world, BlockPos pos, BlockState state, Box box, List list, Entity entity) { + this.setBoundingBox(0.0F, 0.0F, 0.0F, 1.0F, 0.8125F, 1.0F); + super.appendCollisionBoxes(world, pos, state, box, list, entity); + this.setBoundingBox(0.3125F, 0.8125F, 0.3125F, 0.6875F, 1.0F, 0.6875F); + super.appendCollisionBoxes(world, pos, state, box, list, entity); + this.setBlockItemBounds(); + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/overwrite/ItemEntityOverwrite.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/overwrite/ItemEntityOverwrite.java new file mode 100644 index 00000000..e2f9a197 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/overwrite/ItemEntityOverwrite.java @@ -0,0 +1,24 @@ +package dev.u9g.minecraftdatagenerator.mixin.overwrite; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +@Mixin(ItemEntity.class) +public abstract class ItemEntityOverwrite extends Entity { + + public ItemEntityOverwrite(World world) { + super(world); + } + + /** + * @author a + * @reason a + */ + @Overwrite + public String getTranslationKey() { + return super.getTranslationKey(); + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/overwrite/SkeletonSkullBlockOverwrite.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/overwrite/SkeletonSkullBlockOverwrite.java new file mode 100644 index 00000000..538eba50 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/overwrite/SkeletonSkullBlockOverwrite.java @@ -0,0 +1,25 @@ +package dev.u9g.minecraftdatagenerator.mixin.overwrite; + +import net.minecraft.block.BlockState; +import net.minecraft.block.SkeletonSkullBlock; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(SkeletonSkullBlock.class) +public class SkeletonSkullBlockOverwrite { + private static Box boundingBox(BlockState state) { + return switch (state.get(SkeletonSkullBlock.FACING)) { + default -> new Box(0.25f, 0.0f, 0.25f, 0.75f, 0.5f, 0.75f); + case NORTH -> new Box(0.25f, 0.25f, 0.5f, 0.75f, 0.75f, 1.0f); + case SOUTH -> new Box(0.25f, 0.25f, 0.0f, 0.75f, 0.75f, 0.5f); + case WEST -> new Box(0.5f, 0.25f, 0.25f, 1.0f, 0.75f, 0.75f); + case EAST -> new Box(0.0f, 0.25f, 0.25f, 0.5f, 0.75f, 0.75f); + }; + } + + public Box getCollisionBox(World world, BlockPos pos, BlockState state) { + return boundingBox(state); + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/overwrite/StairBlockOverwrite.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/overwrite/StairBlockOverwrite.java new file mode 100644 index 00000000..bf64f53e --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/mixin/overwrite/StairBlockOverwrite.java @@ -0,0 +1,150 @@ +package dev.u9g.minecraftdatagenerator.mixin.overwrite; + +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Material; +import net.minecraft.block.StairsBlock; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.Direction; +import net.minecraft.world.BlockView; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.List; + +import static net.minecraft.block.StairsBlock.HALF; + +@Mixin(StairsBlock.class) +public abstract class StairBlockOverwrite extends Block { + protected StairBlockOverwrite(Material material) { + super(material); + } + + @Shadow + public abstract void setBoundingBox(BlockView view, BlockPos pos); + + @Shadow + public abstract boolean method_8911(BlockView blockView, BlockPos blockPos); + + /** + * @author a + * @reason a + */ + @Overwrite + public void appendCollisionBoxes(World world, BlockPos pos, BlockState state, Box box, List list, Entity entity) { + list.add(someFuncThatIRemade(state)); + super.appendCollisionBoxes(world, pos, state, box, list, entity); + EmptyBlockView bv = new EmptyBlockView() { + @Override + public BlockState getBlockState(BlockPos pos) { + return state; + } + }; + boolean bl = this.method_8911(bv, pos); + list.add(this.getCollisionBox(world, pos, state)); + if (bl && calculateWhichTypeOfStairThisIsButDoesntWorkBecauseWeDontHaveAdjBlocks(state)) { + list.add(this.getCollisionBox(world, pos, state)); + } + this.setBoundingBox(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f); + } + + public Box someFuncThatIRemade(BlockState state) { + if (state.get(HALF) == StairsBlock.Half.TOP) { + return new Box(0.0f, 0.5f, 0.0f, 1.0f, 1.0f, 1.0f); + } else { + return new Box(0.0f, 0.0f, 0.0f, 1.0f, 0.5f, 1.0f); + } + } + + public boolean calculateWhichTypeOfStairThisIsButDoesntWorkBecauseWeDontHaveAdjBlocks(BlockState state) { + EmptyBlockView blockView = new EmptyBlockView() { + @Override + public BlockState getBlockState(BlockPos pos) { + return state; + } + }; + BlockState blockState2; + Block block; + BlockState blockState = blockView.getBlockState(BlockPos.ORIGIN); + Direction direction = blockState.get(StairsBlock.FACING); + StairsBlock.Half half = blockState.get(HALF); + boolean bl = half == StairsBlock.Half.TOP; + float f = 0.5f; + float g = 1.0f; + if (bl) { + f = 0.0f; + g = 0.5f; + } + float h = 0.0f; + float i = 0.5f; + float j = 0.5f; + float k = 1.0f; + boolean bl2 = false; + if (direction == Direction.EAST) { + BlockState blockState22 = blockView.getBlockState(BlockPos.ORIGIN); + Block block2 = blockState22.getBlock(); + if (StairsBlock.method_6513(block2) && half == blockState22.get(HALF)) { + Direction direction2 = blockState22.get(StairsBlock.FACING); + if (direction2 == Direction.NORTH && !StairsBlock.method_8907(blockView, BlockPos.ORIGIN, blockState)) { + j = 0.0f; + k = 0.5f; + bl2 = true; + } else if (direction2 == Direction.SOUTH && !StairsBlock.method_8907(blockView, BlockPos.ORIGIN, blockState)) { + j = 0.5f; + k = 1.0f; + bl2 = true; + } + } + } else if (direction == Direction.WEST) { + BlockState blockState23 = blockView.getBlockState(BlockPos.ORIGIN); + Block block3 = blockState23.getBlock(); + if (StairsBlock.method_6513(block3) && half == blockState23.get(HALF)) { + h = 0.5f; + i = 1.0f; + Direction direction2 = blockState23.get(StairsBlock.FACING); + if (direction2 == Direction.NORTH && !StairsBlock.method_8907(blockView, BlockPos.ORIGIN, blockState)) { + j = 0.0f; + k = 0.5f; + bl2 = true; + } else if (direction2 == Direction.SOUTH && !StairsBlock.method_8907(blockView, BlockPos.ORIGIN, blockState)) { + j = 0.5f; + k = 1.0f; + bl2 = true; + } + } + } else if (direction == Direction.SOUTH) { + BlockState blockState24 = blockView.getBlockState(BlockPos.ORIGIN); + Block block4 = blockState24.getBlock(); + if (StairsBlock.method_6513(block4) && half == blockState24.get(HALF)) { + j = 0.0f; + k = 0.5f; + Direction direction2 = blockState24.get(StairsBlock.FACING); + if (direction2 == Direction.WEST && !StairsBlock.method_8907(blockView, BlockPos.ORIGIN, blockState)) { + bl2 = true; + } else if (direction2 == Direction.EAST && !StairsBlock.method_8907(blockView, BlockPos.ORIGIN, blockState)) { + h = 0.5f; + i = 1.0f; + bl2 = true; + } + } + } else if (direction == Direction.NORTH && StairsBlock.method_6513(block = (blockState2 = blockView.getBlockState(BlockPos.ORIGIN)).getBlock()) && half == blockState2.get(HALF)) { + Direction direction2 = blockState2.get(StairsBlock.FACING); + if (direction2 == Direction.WEST && !StairsBlock.method_8907(blockView, BlockPos.ORIGIN, blockState)) { + bl2 = true; + } else if (direction2 == Direction.EAST && !StairsBlock.method_8907(blockView, BlockPos.ORIGIN, blockState)) { + h = 0.5f; + i = 1.0f; + bl2 = true; + } + } + if (bl2) { + this.setBoundingBox(h, f, j, i, g, k); + } + return bl2; + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java new file mode 100644 index 00000000..5f77e529 --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java @@ -0,0 +1,47 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.World; +import org.jetbrains.annotations.NotNull; + +public class DGU { + public static MinecraftServer getCurrentlyRunningServer() { + return (MinecraftServer) FabricLoader.getInstance().getGameInstance(); + } + + @Environment(EnvType.CLIENT) + private static String translateTextClient(String translationKey) { + return I18n.translate(translationKey); + } + + private static String translateTextFallback(String translationKey) { + try { + return Registries.LANGUAGE.translate(translationKey); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to translate: '" + translationKey + "'"); + } + + public static String translateText(String translationKey) { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return translateTextClient(translationKey); + } + return translateTextFallback(translationKey); + } + + @NotNull + public static World getWorld() { + return getCurrentlyRunningServer().getWorld(); + } + + public static ItemStack stackFor(Item ic) { + return new ItemStack(ic); + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java new file mode 100644 index 00000000..2fbb489b --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java @@ -0,0 +1,54 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.BlockView; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.level.LevelGeneratorType; +import org.jetbrains.annotations.Nullable; + +public class EmptyBlockView implements BlockView { + public static final EmptyBlockView INSTANCE = new EmptyBlockView(); + + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + return null; + } + + @Override + public int getLight(BlockPos pos, int minBlockLight) { + return 0; + } + + public BlockState getBlockState(BlockPos pos) { + return Blocks.AIR.getDefaultState(); + } + + @Override + public boolean isAir(BlockPos pos) { + return false; + } + + @Override + public Biome getBiome(BlockPos pos) { + return null; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public int getStrongRedstonePower(BlockPos pos, Direction direction) { + return 0; + } + + @Override + public LevelGeneratorType getGeneratorType() { + return null; + } +} diff --git a/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/util/Registries.java b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/util/Registries.java new file mode 100644 index 00000000..5039f75c --- /dev/null +++ b/1.8.9/src/main/java/dev/u9g/minecraftdatagenerator/util/Registries.java @@ -0,0 +1,64 @@ +package dev.u9g.minecraftdatagenerator.util; + +import dev.u9g.minecraftdatagenerator.mixin.BiomeAccessor; +import dev.u9g.minecraftdatagenerator.mixin.EnchantmentAccessor; +import dev.u9g.minecraftdatagenerator.mixin.EntityTypeAccessor; +import dev.u9g.minecraftdatagenerator.mixin.StatusEffectAccessor; +import net.minecraft.block.Block; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.entity.Entity; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; +import net.minecraft.util.Language; +import net.minecraft.util.registry.SimpleRegistry; +import net.minecraft.world.biome.Biome; + +import java.util.Map; + +public class Registries { + public static final SimpleRegistry BIOMES = setupBiomeRegistry(); + public static final SimpleRegistry BLOCKS = Block.REGISTRY; + public static final SimpleRegistry ITEMS = Item.REGISTRY; + public static final SimpleRegistry STATUS_EFFECTS = setupStatusEffectRegistry(); + public static final SimpleRegistry ENCHANTMENTS = setupEnchantmentRegistry(); + public static final SimpleRegistry> ENTITY_TYPES = setupEntityTypesRegistry(); + public static final Language LANGUAGE = new Language(); + + private static SimpleRegistry> setupEntityTypesRegistry() { + SimpleRegistry> registry = new SimpleRegistry<>(); + for (Map.Entry> entry : EntityTypeAccessor.ID_CLASS_MAP().entrySet()) { + String name = EntityTypeAccessor.CLASS_NAME_MAP().get(entry.getValue()); + if (name.equals("Mob") || name.equals("Monster")) { + continue; + } + registry.add(entry.getKey(), new Identifier(name), entry.getValue()); + } + + return registry; + } + + private static SimpleRegistry setupEnchantmentRegistry() { + SimpleRegistry registry = new SimpleRegistry<>(); + for (Map.Entry entry : EnchantmentAccessor.ENCHANTMENT_MAP().entrySet()) { + registry.add(entry.getValue().id, entry.getKey(), entry.getValue()); + } + return registry; + } + + private static SimpleRegistry setupBiomeRegistry() { + SimpleRegistry registry = new SimpleRegistry<>(); + for (Biome biome : BiomeAccessor.BIOMESET()) { + registry.add(biome.id, biome.name, biome); + } + return registry; + } + + private static SimpleRegistry setupStatusEffectRegistry() { + SimpleRegistry registry = new SimpleRegistry<>(); + for (Map.Entry entry : StatusEffectAccessor.STATUS_EFFECTS_BY_ID().entrySet()) { + registry.add(entry.getValue().id, entry.getKey(), entry.getValue()); + } + return registry; + } +} diff --git a/1.8.9/src/main/resources/fabric.mod.json b/1.8.9/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..96b7e403 --- /dev/null +++ b/1.8.9/src/main/resources/fabric.mod.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": 1, + "id": "minecraft-data-generator", + "version": "${version}", + "name": "Minecraft Data Generator", + "description": "", + "authors": [ + "Archengius", + "U9G" + ], + "contact": {}, + "license": "MIT", + "environment": "server", + "entrypoints": { + "main": [ + "dev.u9g.minecraftdatagenerator.Main" + ] + }, + "mixins": [ + "minecraft-data-generator.mixins.json" + ], + "depends": { + "fabricloader": ">=0.13.3", + "minecraft": ">=1.8.9" + } +} diff --git a/1.8.9/src/main/resources/minecraft-data-generator.mixins.json b/1.8.9/src/main/resources/minecraft-data-generator.mixins.json new file mode 100644 index 00000000..90389267 --- /dev/null +++ b/1.8.9/src/main/resources/minecraft-data-generator.mixins.json @@ -0,0 +1,30 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "dev.u9g.minecraftdatagenerator.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "BiomeAccessor", + "BlockAccessor", + "EnchantmentAccessor", + "EntityTypeAccessor", + "LanguageAccessor", + "MiningToolItemAccessor", + "NoteBlockAccessor", + "ReadyMixin", + "StatusEffectAccessor", + "overwrite.CocoaBlockOverwrite", + "overwrite.EndPortalFrameBlockOverwrite", + "overwrite.ItemEntityOverwrite", + "overwrite.SkeletonSkullBlockOverwrite", + "overwrite.StairBlockOverwrite" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "server": [ + "EULAMixin" + ] +} diff --git a/1.9.4/build.gradle b/1.9.4/build.gradle new file mode 100644 index 00000000..057916c6 --- /dev/null +++ b/1.9.4/build.gradle @@ -0,0 +1,63 @@ +plugins { + id 'fabric-loom' +} + +repositories { + maven { + name = "legacy-fabric" + url = "https://maven.legacyfabric.net" + } +} + +loom { + setIntermediaryUrl('https://maven.legacyfabric.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar'); + customMinecraftManifest.set("https://meta.legacyfabric.net/v2/manifest/${minecraft_version}") +} + +dependencies { + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API provides hooks for events, item registration, and more. As most mods will need this, it's included by default. + // If you know for a fact you don't, it's not required and can be safely removed. +// modImplementation ("net.legacyfabric.legacy-fabric-api:legacy-fabric-api:${fabric_version}") { +// exclude module: "legacy-fabric-entity-events-v1" +// } + + if (System.getProperty("os.name").toLowerCase().contains("mac")) { + implementation 'org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209' + implementation 'org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209' + implementation 'org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209' + } +} + +if (System.getProperty("os.name").toLowerCase().contains("mac")) { + configurations.configureEach { + resolutionStrategy { + dependencySubstitution { + substitute module('org.lwjgl.lwjgl:lwjgl_util:2.9.2-nightly-201408222') with module('org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209') + substitute module('org.lwjgl.lwjgl:lwjgl:2.9.2-nightly-201408222') with module('org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209') + } + force 'org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209' + } + } +} + +processResources { + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} diff --git a/1.9.4/gradle.properties b/1.9.4/gradle.properties new file mode 100644 index 00000000..fc6b90df --- /dev/null +++ b/1.9.4/gradle.properties @@ -0,0 +1,7 @@ +# Fabric Properties +# More versions available at: https://grayray75.github.io/LegacyFabric-Versions/ +minecraft_version=1.9.4 +yarn_mappings=1.9.4+build.202206020145 +loader_version=0.14.3 +# Dependencies +# More versions available at: https://grayray75.github.io/LegacyFabric-Versions/ diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeColors.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeColors.java new file mode 100644 index 00000000..257db1a9 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BiomeColors.java @@ -0,0 +1,47 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.biome.Biome; + +import java.util.Iterator; + +public class BiomeColors { + private static final ColorProvider GRASS_COLOR = (biome, pos) -> biome.getGrassColor(pos); + private static final ColorProvider FOLIAGE_COLOR = (biome, pos) -> biome.getFoliageColor(pos); + private static final ColorProvider WATER_COLOR = (biome, pos) -> biome.getWaterColor(); + + private static int getColor(EmptyBlockView view, BlockPos pos, ColorProvider provider) { + int i = 0; + int j = 0; + int k = 0; + + int l; + for (Iterator iterator = BlockPos.mutableIterate(pos.add(-1, 0, -1), pos.add(1, 0, 1)).iterator(); iterator.hasNext(); k += l & 255) { + BlockPos.Mutable mutable = (BlockPos.Mutable) iterator.next(); + l = provider.getColorAtPos(view.getBiome(mutable), mutable); + i += (l & 16711680) >> 16; + j += (l & '\uff00') >> 8; + } + + return (i / 9 & 255) << 16 | (j / 9 & 255) << 8 | k / 9 & 255; + } + + public static int getGrassColor(EmptyBlockView view, BlockPos pos) { + return getColor(view, pos, GRASS_COLOR); + } + + public static int getFoliageColor(EmptyBlockView view, BlockPos pos) { + return getColor(view, pos, FOLIAGE_COLOR); + } + + public static int getWaterColor(EmptyBlockView view, BlockPos pos) { + return getColor(view, pos, WATER_COLOR); + } + + interface ColorProvider { + int getColorAtPos(Biome biome, BlockPos pos); + } +} + + diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColorable.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColorable.java new file mode 100644 index 00000000..3e3e044c --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColorable.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import org.jetbrains.annotations.Nullable; + +public interface BlockColorable { + int method_12155(BlockState blockState, @Nullable EmptyBlockView blockView, @Nullable BlockPos blockPos, int i); +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColors.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColors.java new file mode 100644 index 00000000..4df7551c --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/BlockColors.java @@ -0,0 +1,97 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import net.minecraft.block.*; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.FlowerPotBlockEntity; +import net.minecraft.item.BlockItem; +import net.minecraft.item.Item; +import net.minecraft.util.collection.IdList; +import net.minecraft.util.math.BlockPos; +import org.jetbrains.annotations.Nullable; + +public class BlockColors { + private final IdList BlockColor2Id = new IdList<>(32); + + public BlockColors() { + } + + public static BlockColors create() { + final BlockColors blockColors = new BlockColors(); + blockColors.method_12158((blockState, blockView, blockPos, i) -> { + DoublePlantBlock.DoublePlantType doublePlantType = (DoublePlantBlock.DoublePlantType) blockState.get(DoublePlantBlock.VARIANT); + return blockView == null || blockPos == null || doublePlantType != DoublePlantBlock.DoublePlantType.GRASS && doublePlantType != DoublePlantBlock.DoublePlantType.FERN ? -1 : BiomeColors.getGrassColor(blockView, blockPos); + }, Blocks.DOUBLE_PLANT); + blockColors.method_12158((blockState, blockView, blockPos, i) -> { + if (blockView != null && blockPos != null) { + BlockEntity blockEntity = blockView.getBlockEntity(blockPos); + if (blockEntity instanceof FlowerPotBlockEntity) { + Item item = ((FlowerPotBlockEntity) blockEntity).getItem(); + if (item instanceof BlockItem) { + BlockState blockState2 = Block.getBlockFromItem(item).getDefaultState(); + return blockColors.method_12157(blockState2, blockView, blockPos, i); + } + } + } + + return -1; + }, Blocks.FLOWER_POT); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getGrassColor(blockView, blockPos) : GrassColors.getColor(0.5D, 1.0D), Blocks.GRASS); + blockColors.method_12158((blockState, blockView, blockPos, i) -> { + PlanksBlock.WoodType woodType = (PlanksBlock.WoodType) blockState.get(Leaves1Block.VARIANT); + if (woodType == PlanksBlock.WoodType.SPRUCE) { + return FoliageColors.getSpruceColor(); + } else if (woodType == PlanksBlock.WoodType.BIRCH) { + return FoliageColors.getBirchColor(); + } else { + return blockView != null && blockPos != null ? BiomeColors.getFoliageColor(blockView, blockPos) : FoliageColors.getDefaultColor(); + } + }, Blocks.LEAVES); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getFoliageColor(blockView, blockPos) : FoliageColors.getDefaultColor(), Blocks.LEAVES2); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getWaterColor(blockView, blockPos) : -1, Blocks.WATER, Blocks.FLOWING_WATER); + blockColors.method_12158((blockState, blockView, blockPos, i) -> RedstoneWireBlock.method_8877((Integer) blockState.get(RedstoneWireBlock.POWER)), Blocks.REDSTONE_WIRE); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getGrassColor(blockView, blockPos) : -1, Blocks.SUGARCANE); + blockColors.method_12158((blockState, blockView, blockPos, i) -> { + int j = (Integer) blockState.get(AttachedStemBlock.AGE); + int k = j * 32; + int l = 255 - j * 8; + int m = j * 4; + return k << 16 | l << 8 | m; + }, Blocks.MELON_STEM, Blocks.PUMPKIN_STEM); + blockColors.method_12158((blockState, blockView, blockPos, i) -> { + if (blockView != null && blockPos != null) { + return BiomeColors.getGrassColor(blockView, blockPos); + } else { + return blockState.get(TallPlantBlock.TYPE) == TallPlantBlock.GrassType.DEAD_BUSH ? 16777215 : GrassColors.getColor(0.5D, 1.0D); + } + }, Blocks.TALLGRASS); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? BiomeColors.getFoliageColor(blockView, blockPos) : FoliageColors.getDefaultColor(), Blocks.VINE); + blockColors.method_12158((blockState, blockView, blockPos, i) -> blockView != null && blockPos != null ? 2129968 : 7455580, Blocks.LILY_PAD); + return blockColors; + } + + public int method_13410(BlockState blockState) { + BlockColorable blockColorable = (BlockColorable) this.BlockColor2Id.fromId(Block.getIdByBlock(blockState.getBlock())); + if (blockColorable != null) { + return blockColorable.method_12155(blockState, (EmptyBlockView) null, (BlockPos) null, 0); + } else { + MaterialColor materialColor = blockState.getMaterialColor(); + return materialColor != null ? materialColor.color : -1; + } + } + + public int method_12157(BlockState blockState, @Nullable EmptyBlockView blockView, @Nullable BlockPos blockPos, int i) { + BlockColorable blockColorable = (BlockColorable) this.BlockColor2Id.fromId(Block.getIdByBlock(blockState.getBlock())); + return blockColorable == null ? -1 : blockColorable.method_12155(blockState, blockView, blockPos, i); + } + + public void method_12158(BlockColorable blockColorable, Block... blocks) { + int i = blocks.length; + + for (int j = 0; j < i; ++j) { + Block block = blocks[j]; + this.BlockColor2Id.set(blockColorable, Block.getIdByBlock(block)); + } + + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java new file mode 100644 index 00000000..8c32f48f --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/FoliageColors.java @@ -0,0 +1,37 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +public class FoliageColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] pixels) { + colorMap = pixels; + } + + public static int getColor(double temperature, double humidity) { + humidity *= temperature; + int i = (int) ((1.0D - temperature) * 255.0D); + int j = (int) ((1.0D - humidity) * 255.0D); + return colorMap[j << 8 | i]; + } + + public static int getSpruceColor() { + return 6396257; + } + + public static int getBirchColor() { + return 8431445; + } + + public static int getDefaultColor() { + return 4764952; + } + + public static int getFoliageColor(Biome biome) { + double d = (double) MathHelper.clamp(biome.getTemperature(), 0.0F, 1.0F); + double e = (double) MathHelper.clamp(biome.getRainfall(), 0.0F, 1.0F); + return FoliageColors.getColor(d, e); + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java new file mode 100644 index 00000000..88687071 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/GrassColors.java @@ -0,0 +1,29 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.biome.Biome; + +public class GrassColors { + private static int[] colorMap = new int[65536]; + + public static void setColorMap(int[] map) { + colorMap = map; + } + + public static int getColor(double temperature, double humidity) { + humidity *= temperature; + int i = (int) ((1.0D - temperature) * 255.0D); + int j = (int) ((1.0D - humidity) * 255.0D); + int k = j << 8 | i; + return k > colorMap.length ? -65281 : colorMap[k]; + } + + public static int getGrassColor(Biome biome) { + double d = (double) MathHelper.clamp(biome.getTemperature(), 0.0F, 1.0F); + double e = (double) MathHelper.clamp(biome.getRainfall(), 0.0F, 1.0F); + return GrassColors.getColor(d, e); + } +} + + + diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java new file mode 100644 index 00000000..1572a082 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/ClientSideAnnoyances/ServerSideRedstoneWireBlock.java @@ -0,0 +1,28 @@ +package dev.u9g.minecraftdatagenerator.ClientSideAnnoyances; + +import net.minecraft.util.math.MathHelper; + +public class ServerSideRedstoneWireBlock { + public static int getWireColor(int powerLevel) { + float f = (float) powerLevel / 15.0F; + float g = f * 0.6F + 0.4F; + if (powerLevel == 0) { + g = 0.3F; + } + + float h = f * f * 0.7F - 0.5F; + float j = f * f * 0.6F - 0.7F; + if (h < 0.0F) { + h = 0.0F; + } + + if (j < 0.0F) { + j = 0.0F; + } + + int k = MathHelper.clamp((int) (g * 255.0F), 0, 255); + int l = MathHelper.clamp((int) (h * 255.0F), 0, 255); + int m = MathHelper.clamp((int) (j * 255.0F), 0, 255); + return -16777216 | k << 16 | l << 8 | m; + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/Main.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/Main.java new file mode 100644 index 00000000..d23c5ba2 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/Main.java @@ -0,0 +1,13 @@ +package dev.u9g.minecraftdatagenerator; + +import net.fabricmc.api.ModInitializer; + +import java.util.logging.Logger; + +public class Main implements ModInitializer { + public static final Logger LOGGER = Logger.getLogger("mc-data-gen-serv"); + + @Override + public void onInitialize() { + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java new file mode 100644 index 00000000..1689c8b5 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java @@ -0,0 +1,32 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.LanguageAccessor; +import dev.u9g.minecraftdatagenerator.util.Registries; + +import java.util.Map; + +public class AttributesDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "attributes"; + } + + @Override + public JsonElement generateDataJson() { + Map translations = ((LanguageAccessor) Registries.LANGUAGE).translations(); + JsonArray arr = new JsonArray(); + for (Map.Entry translation : translations.entrySet()) { + String key = translation.getKey(); + if (!key.startsWith("attribute.name.")) continue; + JsonObject obj = new JsonObject(); + key = key.replace("attribute.name.", ""); + obj.addProperty("name", key.split("\\.")[1]); + obj.addProperty("resource", key); + arr.add(obj); + } + return arr; + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java new file mode 100644 index 00000000..79299efe --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java @@ -0,0 +1,192 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.BiomeAccessor; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.SimpleRegistry; +import net.minecraft.world.biome.*; +import org.apache.commons.lang3.StringUtils; + +import java.util.Locale; + +public class BiomesDataGenerator implements IDataGenerator { + + private static String guessBiomeDimensionFromCategory(Biome biome) { + if (biome instanceof NetherBiome) { + return "nether"; + } else if (biome instanceof EndBiome) { + return "end"; + } + return "overworld"; + } + + private static int getBiomeColorFor(String biomeDisplayNamed) { + if (biomeDisplayNamed.equals("Redwood Taiga Hills M")) { + biomeDisplayNamed = "MegaTaigaHills"; + } + String biomeDisplayName = StringUtils.join(biomeDisplayNamed.split(" "), ""); + return switch (biomeDisplayName) { + case "Ocean" -> 112; + case "Plains" -> 9286496; + case "Desert" -> 16421912; + case "ExtremeHills", "Extreme Hills" -> 6316128; + case "Forest" -> 353825; + case "Taiga" -> 747097; + case "Swampland" -> 522674; + case "River" -> 255; + case "Hell" -> 16711680; + case "TheEnd", "The End" -> 8421631; + case "FrozenOcean", "Frozen Ocean" -> 7368918; + case "FrozenRiver", "Frozen River" -> 10526975; + case "IcePlains", "Ice Plains" -> 16777215; + case "IceMountains", "Ice Mountains" -> 10526880; + case "MushroomIsland", "Mushroom Island" -> 16711935; + case "MushroomIslandShore", "Mushroom Island Shore" -> 10486015; + case "Beach" -> 16440917; + case "DesertHills", "Desert Hills" -> 13786898; + case "ForestHills", "Forest Hills" -> 2250012; + case "TaigaHills", "Taiga Hills" -> 1456435; + case "ExtremeHillsEdge", "Extreme Hills Edge" -> 7501978; + case "Jungle" -> 5470985; + case "JungleHills", "Jungle Hills" -> 2900485; + case "JungleEdge", "Jungle Edge" -> 6458135; + case "DeepOcean", "Deep Ocean" -> 48; + case "StoneBeach", "Stone Beach" -> 10658436; + case "ColdBeach", "Cold Beach" -> 16445632; + case "BirchForest", "Birch Forest" -> 3175492; + case "BirchForestHills", "Birch Forest Hills" -> 2055986; + case "RoofedForest", "Roofed Forest" -> 4215066; + case "ColdTaiga", "Cold Taiga" -> 3233098; + case "ColdTaigaHills", "Cold Taiga Hills" -> 2375478; + case "MegaTaiga", "Mega Taiga" -> 5858897; + case "MegaTaigaHills", "Mega Taiga Hills" -> 4542270; + case "ExtremeHills+", "Extreme Hills+" -> 5271632; + case "Savanna" -> 12431967; + case "SavannaPlateau", "Savanna Plateau" -> 10984804; + case "Mesa" -> 14238997; + case "MesaPlateauF", "Mesa Plateau F" -> 11573093; + case "MesaPlateau", "Mesa Plateau" -> 13274213; + case "TheEnd-Floatingislands", "The End - Floating islands" -> 8421631; + case "TheEnd-Mediumisland", "The End - Medium island" -> 8421631; + case "TheEnd-Highisland", "The End - High island" -> 8421631; + case "TheEnd-Barrenisland", "The End - Barren island" -> 8421631; + case "WarmOcean", "Warm Ocean" -> 172; + case "LukewarmOcean", "Lukewarm Ocean" -> 144; + case "ColdOcean", "Cold Ocean" -> 2105456; + case "WarmDeepOcean", "Warm Deep Ocean" -> 80; + case "LukewarmDeepOcean", "Lukewarm Deep Ocean" -> 64; + case "ColdDeepOcean", "Cold Deep Ocean" -> 2105400; + case "FrozenDeepOcean", "Frozen Deep Ocean" -> 4210832; + case "TheVoid", "The Void" -> 0; + case "SunflowerPlains", "Sunflower Plains" -> 11918216; + case "DesertM", "Desert M" -> 16759872; + case "ExtremeHillsM", "Extreme Hills M" -> 8947848; + case "FlowerForest", "Flower Forest" -> 2985545; + case "TaigaM", "Taiga M" -> 3378817; + case "SwamplandM", "Swampland M" -> 3145690; + case "IcePlainsSpikes", "Ice Plains Spikes" -> 11853020; + case "JungleM", "Jungle M" -> 8102705; + case "JungleEdgeM", "Jungle Edge M" -> 9089855; + case "BirchForestM", "Birch Forest M" -> 5807212; + case "BirchForestHillsM", "Birch Forest Hills M" -> 4687706; + case "RoofedForestM", "Roofed Forest M" -> 6846786; + case "ColdTaigaM", "Cold Taiga M" -> 5864818; + case "MegaSpruceTaiga", "Mega Spruce Taiga" -> 8490617; + case "MegaSpruceTaiga(Hills)", "Mega Spruce Taiga (Hills)" -> 7173990; + case "ExtremeHills+M", "Extreme Hills+ M" -> 7903352; + case "SavannaM", "Savanna M" -> 15063687; + case "SavannaPlateauM", "Savanna Plateau M" -> 13616524; + case "Mesa(Bryce)", "Mesa (Bryce)" -> 16739645; + case "MesaPlateauFM", "Mesa Plateau F M" -> 14204813; + case "MesaPlateauM", "Mesa Plateau M" -> 15905933; + case "BambooJungle", "Bamboo Jungle" -> 7769620; + case "BambooJungleHills", "Bamboo Jungle Hills" -> 3884810; + default -> throw new Error("Unexpected biome, with name: '" + biomeDisplayName + "'"); + }; + } + + public static JsonObject generateBiomeInfo(SimpleRegistry registry, Biome biome) { + JsonObject biomeDesc = new JsonObject(); + Identifier registryKey = registry.getIdentifier(biome); + + biomeDesc.addProperty("id", registry.getRawId(biome)); + biomeDesc.addProperty("name", String.join("_", ((BiomeAccessor) biome).name().toLowerCase(Locale.ENGLISH).split(" "))); + biomeDesc.addProperty("category", category(biome)); + biomeDesc.addProperty("temperature", biome.getTemperature()); + biomeDesc.addProperty("precipitation", precipitation(biome)); + biomeDesc.addProperty("depth", biome.getDepth()); + biomeDesc.addProperty("dimension", guessBiomeDimensionFromCategory(biome)); + biomeDesc.addProperty("displayName", ((BiomeAccessor) biome).name()); + biomeDesc.addProperty("color", getBiomeColorFor(((BiomeAccessor) biome).name())); + biomeDesc.addProperty("rainfall", biome.getRainfall()); + + return biomeDesc; + } + + private static String category(Biome biome) { + if (biome instanceof ForestBiome) { + return "forest"; + } else if (biome instanceof OceanBiome) { + return "ocean"; + } else if (biome instanceof PlainsBiome) { + return "plains"; + } else if (biome instanceof DesertBiome) { + return "desert"; + } else if (biome instanceof ExtremeHillsBiome) { + return "extreme_hills"; + } else if (biome instanceof TaigaBiome) { + return "taiga"; + } else if (biome instanceof SwampBiome) { + return "swamp"; + } else if (biome instanceof RiverBiome) { + return "river"; + } else if (biome instanceof NetherBiome) { + return "nether"; + } else if (biome instanceof EndBiome) { + return "the_end"; + } else if (biome instanceof IceBiome) { + return "icy"; + } else if (biome instanceof MushroomBiome) { + return "mushroom"; + } else if (biome instanceof BeachBiome) { + return "beach"; + } else if (biome instanceof JungleBiome) { + return "jungle"; + } else if (biome instanceof SavannaBiome) { + return "savanna"; + } else if (biome instanceof MesaBiome) { + return "mesa"; + } else if (biome instanceof StoneBeachBiome || biome instanceof VoidBiome) { + return "none"; // Should StoneBeachBiome be beach too? this is how it is now in mcdata + } + throw new Error("Unable to find biome category for " + biome.getClass().getName()); + } + + private static String precipitation(Biome biome) { + float rainfall = biome.getRainfall(); + float temperature = biome.getTemperature(); + if (rainfall == 0) { + return "none"; + } else if (temperature < 0.2f) { + return "snow"; + } + return "rain"; + } + + @Override + public String getDataName() { + return "biomes"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray biomesArray = new JsonArray(); + SimpleRegistry biomeRegistry = Biome.REGISTRY; + + for (Biome biome : biomeRegistry) { + biomesArray.add(generateBiomeInfo(biomeRegistry, biome)); + } + return biomesArray; + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java new file mode 100644 index 00000000..b2789fd0 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java @@ -0,0 +1,126 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class BlockCollisionShapesDataGenerator implements IDataGenerator { + private static final Box ENTITY_BOX = new Box(0.0D, 0.0D, 0.0D, 1.0D, 2.0D, 1.0D); + + private static String nameOf(Block block) { + return Objects.requireNonNull(Registries.BLOCKS.getIdentifier(block)).getPath(); + } + + private static JsonArray jsonOf(Box box) { + JsonArray arr = new JsonArray(); + if (box == null) return arr; + arr.add(new JsonPrimitive(box.minX)); + arr.add(new JsonPrimitive(box.minY)); + arr.add(new JsonPrimitive(box.minZ)); + arr.add(new JsonPrimitive(box.maxX)); + arr.add(new JsonPrimitive(box.maxY)); + arr.add(new JsonPrimitive(box.maxZ)); + return arr; + } + + @Override + public String getDataName() { + return "blockCollisionShapes"; + } + + @Override + public JsonObject generateDataJson() { + ShapeCache shapeCache = new ShapeCache(); + JsonObject blocksObject = new JsonObject(); + for (Block block : Registries.BLOCKS) { + Object val = shapeCache.addShapesFrom(block); + if (val instanceof JsonArray) { + blocksObject.add(nameOf(block), (JsonElement) val); + } else { + blocksObject.addProperty(nameOf(block), (Integer) val); + } + } + JsonObject resultObject = new JsonObject(); + resultObject.add("blocks", blocksObject); + resultObject.add("shapes", shapeCache.toJSON()); + return resultObject; + } + + public static class ShapeCache { + private final ArrayList shapesCache = new ArrayList<>(); + + public Object addShapesFrom(Block block) { + List indexesOfBoxesInTheShapesCache = new ArrayList<>(); + for (BlockState state : block.getStateManager().getBlockStates().reverse()) { + List boxes = new ArrayList<>(); + try { + state.addCollisionBoxesToList(DGU.getWorld(), BlockPos.ORIGIN, ENTITY_BOX, boxes, null); + } catch (Exception e) { + e.printStackTrace(); + } + Shapes thisBlockStateShapes = new Shapes(boxes); + int indexOfThisBlockStatesShapes = shapesCache.indexOf(thisBlockStateShapes); + if (indexOfThisBlockStatesShapes != -1) { + indexesOfBoxesInTheShapesCache.add(indexOfThisBlockStatesShapes); + } else { + shapesCache.add(thisBlockStateShapes); + indexesOfBoxesInTheShapesCache.add(shapesCache.size() - 1); + } + } + if (indexesOfBoxesInTheShapesCache.stream().distinct().count() < 2) { + return indexesOfBoxesInTheShapesCache.get(0); + } else { + JsonArray shapeIndexes = new JsonArray(); + indexesOfBoxesInTheShapesCache.forEach(shapeIndex -> shapeIndexes.add(new JsonPrimitive(shapeIndex))); + return shapeIndexes; + } + } + + public JsonObject toJSON() { + JsonObject shapes = new JsonObject(); + int i = 0; + for (Shapes s : shapesCache) { + shapes.add(String.valueOf(i++), s.toJSON()); + } + return shapes; + } + + private static class Shapes { + final List boxes; + + public Shapes(List boxes) { + this.boxes = boxes; + } + + public JsonArray toJSON() { + JsonArray arr = new JsonArray(); + boxes.forEach(box -> arr.add(jsonOf(box))); + return arr; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Shapes shapes = (Shapes) o; + return Objects.equals(boxes, shapes.boxes); + } + + @Override + public int hashCode() { + return boxes != null ? boxes.hashCode() : 0; + } + } + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java new file mode 100644 index 00000000..dbea9f6e --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java @@ -0,0 +1,152 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.base.CaseFormat; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.mixin.BlockAccessor; +import dev.u9g.minecraftdatagenerator.mixin.MiningToolItemAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.block.AirBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.TransparentBlock; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ToolItem; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.state.property.EnumProperty; +import net.minecraft.state.property.IntProperty; +import net.minecraft.state.property.Property; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.logging.Logger; + +public class BlocksDataGenerator implements IDataGenerator { + + private static final Logger logger = Main.LOGGER; + + private static List getItemsEffectiveForBlock(Block block) { + List items = new ArrayList<>(); + for (Item item : Registries.ITEMS) { + if (item instanceof ToolItem && ((MiningToolItemAccessor) item).getEffectiveBlocks().contains(block)) { + items.add(item); + } + } + return items; + } + + private static List populateDropsIfPossible(BlockState blockState, Item firstToolItem) { + return new ArrayList<>(); + } + + private static String getPropertyTypeName(Property property) { + //Explicitly handle default minecraft properties + if (property instanceof BooleanProperty) { + return "bool"; + } + if (property instanceof IntProperty) { + return "int"; + } + if (property instanceof EnumProperty) { + return "enum"; + } + + //Use simple class name as fallback, this code will give something like + //example_type for ExampleTypeProperty class name + String rawPropertyName = property.getClass().getSimpleName().replace("Property", ""); + return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, rawPropertyName); + } + + private static > JsonObject generateStateProperty(Property property) { + JsonObject propertyObject = new JsonObject(); + Collection propertyValues = property.getValues(); + + propertyObject.addProperty("name", property.getName()); + propertyObject.addProperty("type", getPropertyTypeName(property)); + propertyObject.addProperty("num_values", propertyValues.size()); + + //Do not add values for vanilla boolean properties, they are known by default + if (!(property instanceof BooleanProperty)) { + JsonArray propertyValuesArray = new JsonArray(); + for (T propertyValue : propertyValues) { + propertyValuesArray.add(new JsonPrimitive(property.name(propertyValue))); + } + propertyObject.add("values", propertyValuesArray); + } + return propertyObject; + } + + public static JsonObject generateBlock(Block block) { + JsonObject blockDesc = new JsonObject(); + + List blockStates = block.getStateManager().getBlockStates(); + BlockState defaultState = block.getDefaultState(); + Identifier registryKey = Registries.BLOCKS.getIdentifier(block); + List effectiveTools = getItemsEffectiveForBlock(block); + + blockDesc.addProperty("id", Registries.BLOCKS.getRawId(block)); + blockDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + if (!block.getTranslatedName().startsWith("tile.")) { + blockDesc.addProperty("displayName", block.getTranslatedName()); + } + + float hardness = block.getDefaultState().getHardness(null, null); + + blockDesc.addProperty("hardness", hardness); + blockDesc.addProperty("resistance", ((BlockAccessor) block).getBlastResistance()); + blockDesc.addProperty("diggable", hardness != -1.0f && !(block instanceof AirBlock)); + JsonObject effTools = new JsonObject(); + effectiveTools.forEach(item -> effTools.addProperty( + String.valueOf(Registries.ITEMS.getRawId(item)), // key + item.getBlockBreakingSpeed(DGU.stackFor(item), defaultState) // value + )); + blockDesc.add("effectiveTools", effTools); + blockDesc.addProperty("transparent", block instanceof TransparentBlock); + blockDesc.addProperty("emitLight", defaultState.getLuminance()); + blockDesc.addProperty("filterLight", block.getDefaultState().getOpacity()); + + JsonArray stateProperties = new JsonArray(); + for (Property property : block.getStateManager().getProperties()) { + stateProperties.add(generateStateProperty(property)); + } + blockDesc.add("states", stateProperties); + blockDesc.add("drops", new JsonArray()); + blockDesc.addProperty("boundingBox", boundingBox(block, defaultState)); + + return blockDesc; + } + + private static String boundingBox(Block block, BlockState state) { + if (block.getDefaultState().getCollisionBox(EmptyBlockView.INSTANCE, BlockPos.ORIGIN) == Block.EMPTY_BOX) { + return "empty"; + } + return "block"; + } + + private static Item getItemFromBlock(Block block) { + return Registries.ITEMS.get(Registries.BLOCKS.getIdentifier(block)); + } + + @Override + public String getDataName() { + return "blocks"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultBlocksArray = new JsonArray(); + for (Block block : Registries.BLOCKS) { + resultBlocksArray.add(generateBlock(block)); + } + return resultBlocksArray; + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java new file mode 100644 index 00000000..273d67ca --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java @@ -0,0 +1,80 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonWriter; +import dev.u9g.minecraftdatagenerator.Main; + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class DataGenerators { + + private static final List GENERATORS = new ArrayList<>(); + private static final Logger logger = Main.LOGGER; + + static { + register(new BiomesDataGenerator()); + register(new BlockCollisionShapesDataGenerator()); + register(new BlocksDataGenerator()); + register(new EffectsDataGenerator()); + register(new EnchantmentsDataGenerator()); + register(new EntitiesDataGenerator()); + register(new FoodsDataGenerator()); + register(new ItemsDataGenerator()); + register(new ParticlesDataGenerator()); + register(new TintsDataGenerator()); + register(new LanguageDataGenerator()); + register(new InstrumentsDataGenerator()); + register(new AttributesDataGenerator()); + } + + public static void register(IDataGenerator generator) { + GENERATORS.add(generator); + } + + public static boolean runDataGenerators(Path outputDirectory) { + try { + Files.createDirectories(outputDirectory); + } catch (IOException exception) { + logger.log(Level.INFO, "Failed to create data generator output directory at " + outputDirectory); + exception.printStackTrace(); + return false; + } + + int generatorsFailed = 0; + logger.log(Level.INFO, MessageFormat.format("Running minecraft data generators, output at {0}", outputDirectory)); + + for (IDataGenerator dataGenerator : GENERATORS) { + logger.log(Level.INFO, MessageFormat.format("Running generator {0}", dataGenerator.getDataName())); + try { + String outputFileName = String.format("%s.json", dataGenerator.getDataName()); + JsonElement outputElement = dataGenerator.generateDataJson(); + Path outputFilePath = outputDirectory.resolve(outputFileName); + + try (Writer writer = Files.newBufferedWriter(outputFilePath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + JsonWriter jsonWriter = new JsonWriter(writer); + jsonWriter.setIndent(" "); + Streams.write(outputElement, jsonWriter); + } + logger.log(Level.INFO, MessageFormat.format("Generator: {0} -> {1}", dataGenerator.getDataName(), outputFileName)); + + } catch (Throwable exception) { + logger.log(Level.INFO, MessageFormat.format("Failed to run data generator {0}", dataGenerator.getDataName())); + exception.printStackTrace(); + generatorsFailed++; + } + } + + logger.log(Level.INFO, "Finishing running data generators"); + return generatorsFailed == 0; + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java new file mode 100644 index 00000000..6aba51e0 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java @@ -0,0 +1,50 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.StatusEffectAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.util.Identifier; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.Collectors; + +public class EffectsDataGenerator implements IDataGenerator { + + public static JsonObject generateEffect(StatusEffect statusEffect) { + JsonObject effectDesc = new JsonObject(); + @NotNull Identifier registryKey = Objects.requireNonNull(Registries.STATUS_EFFECTS.getIdentifier(statusEffect)); + + effectDesc.addProperty("id", Registries.STATUS_EFFECTS.getRawId(statusEffect)); + if (statusEffect == StatusEffects.UNLUCK) { + effectDesc.addProperty("name", "BadLuck"); + effectDesc.addProperty("displayName", "Bad Luck"); + } else { + effectDesc.addProperty("name", Arrays.stream(registryKey.getPath().split("_")).map(StringUtils::capitalize).collect(Collectors.joining())); + effectDesc.addProperty("displayName", DGU.translateText(statusEffect.getTranslationKey())); + } + + effectDesc.addProperty("type", !((StatusEffectAccessor) statusEffect).negative() ? "good" : "bad"); + return effectDesc; + } + + @Override + public String getDataName() { + return "effects"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + for (StatusEffect effect : Registries.STATUS_EFFECTS) { + resultsArray.add(generateEffect(effect)); + } + return resultsArray; + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java new file mode 100644 index 00000000..1f1685d8 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java @@ -0,0 +1,108 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.util.Identifier; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +public class EnchantmentsDataGenerator implements IDataGenerator { + + private static final ImmutableMap ENCHANTMENT_TARGET_NAMES = ImmutableMap.builder() + .put(EnchantmentTarget.ALL_ARMOR, "armor") + .put(EnchantmentTarget.FEET, "armor_feet") + .put(EnchantmentTarget.LEGS, "armor_legs") + .put(EnchantmentTarget.ARMOR_CHEST, "armor_chest") + .put(EnchantmentTarget.HEAD, "armor_head") + .put(EnchantmentTarget.WEAPON, "weapon") + .put(EnchantmentTarget.DIGGER, "digger") + .put(EnchantmentTarget.FISHING_ROD, "fishing_rod") + .put(EnchantmentTarget.BREAKABLE, "breakable") + .put(EnchantmentTarget.BOW, "bow") + .build(); + + public static String getEnchantmentTargetName(EnchantmentTarget target) { + return ENCHANTMENT_TARGET_NAMES.getOrDefault(target, target.name().toLowerCase(Locale.ROOT)); + } + + // Equation enchantment costs follow is a * level + b, so we can easily retrieve a and b by passing zero level + private static JsonObject generateEnchantmentMinPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMinimumPower(0); + int a = enchantment.getMinimumPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + private static JsonObject generateEnchantmentMaxPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMaximumPower(0); + int a = enchantment.getMaximumPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + public static JsonObject generateEnchantment(Enchantment enchantment) { + JsonObject enchantmentDesc = new JsonObject(); + Identifier registryKey = Registries.ENCHANTMENTS.getIdentifier(enchantment); + + enchantmentDesc.addProperty("id", Registries.ENCHANTMENTS.getRawId(enchantment)); + enchantmentDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + enchantmentDesc.addProperty("displayName", DGU.translateText(enchantment.getTranslationKey())); + + enchantmentDesc.addProperty("maxLevel", enchantment.getMaximumLevel()); + enchantmentDesc.add("minCost", generateEnchantmentMinPowerCoefficients(enchantment)); + enchantmentDesc.add("maxCost", generateEnchantmentMaxPowerCoefficients(enchantment)); + + enchantmentDesc.addProperty("treasureOnly", enchantment.isTreasure()); + enchantmentDesc.addProperty("curse", false); // 1.10 added curse enchants + + List incompatibleEnchantments = new ArrayList<>(); + for (Enchantment other : Registries.ENCHANTMENTS) { + if (!enchantment.differs(other) && !other.differs(enchantment) && other != enchantment) { + incompatibleEnchantments.add(other); + } + } + + JsonArray excludes = new JsonArray(); + for (Enchantment excludedEnchantment : incompatibleEnchantments) { + Identifier otherKey = Registries.ENCHANTMENTS.getIdentifier(excludedEnchantment); + excludes.add(new JsonPrimitive(Objects.requireNonNull(otherKey).getPath())); + } + enchantmentDesc.add("exclude", excludes); + + enchantmentDesc.addProperty("category", getEnchantmentTargetName(enchantment.target)); + enchantmentDesc.addProperty("weight", enchantment.getRarity().getChance()); + enchantmentDesc.addProperty("tradeable", true); // the first non-tradeable enchant came in 1.16, soul speed + enchantmentDesc.addProperty("discoverable", true); // the first non-enchantable enchant came in 1.16, soul speed + + return enchantmentDesc; + } + + @Override + public String getDataName() { + return "enchantments"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + for (Enchantment enchantment : Registries.ENCHANTMENTS) { + resultsArray.add(generateEnchantment(enchantment)); + } + return resultsArray; + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java new file mode 100644 index 00000000..6a33b00d --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java @@ -0,0 +1,138 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.EntityTypeAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.entity.*; +import net.minecraft.entity.mob.AmbientEntity; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.mob.WaterCreatureEntity; +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.projectile.FishingBobberEntity; +import net.minecraft.entity.projectile.Projectile; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +public class EntitiesDataGenerator implements IDataGenerator { + + public static JsonObject generateEntity(Class entityClass) { + JsonObject entityDesc = new JsonObject(); + Identifier registryKey = Registries.ENTITY_TYPES.getIdentifier(entityClass); + int entityRawId = Registries.ENTITY_TYPES.getRawId(entityClass); + @Nullable Entity entity = makeEntity(entityClass); + // FIXME: ENTITY ID IS WRONG + int id = entityId(entity); + entityDesc.addProperty("id", id); + entityDesc.addProperty("internalId", id); + entityDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + String displayName = entity != null ? DGU.translateText(entity.getTranslationKey()) : null; + if (displayName != null && !displayName.startsWith("entity.")) { + entityDesc.addProperty("displayName", displayName); + } + entityDesc.addProperty("width", entity == null ? 0 : entity.width); + entityDesc.addProperty("height", entity == null ? 0 : entity.height); + + String entityTypeString = "UNKNOWN"; + entityTypeString = getEntityTypeForClass(entityClass); + entityDesc.addProperty("type", entityTypeString); + entityDesc.addProperty("category", getCategoryFrom(entityClass)); + + return entityDesc; + } + + private static Entity makeEntity(Class type) { + String name = EntityTypeAccessor.CLASS_NAME_MAP().get(type); + return EntityType.createInstanceFromName(name, DGU.getWorld()); + } + + private static String getCategoryFrom(@NotNull Class entityClass) { + if (entityClass == PlayerEntity.class) return "other"; // fail early for player entities + String packageName = entityClass.getPackage().getName(); + String category = switch (packageName) { + case "net.minecraft.entity.decoration", "net.minecraft.entity.decoration.painting" -> "Immobile"; + case "net.minecraft.entity.boss", "net.minecraft.entity.mob", "net.minecraft.entity.boss.dragon" -> + "Hostile mobs"; + case "net.minecraft.entity.projectile", "net.minecraft.entity.thrown" -> "Projectiles"; + case "net.minecraft.entity.passive" -> "Passive mobs"; + case "net.minecraft.entity.vehicle" -> "Vehicles"; + case "net.minecraft.entity" -> "other"; + default -> throw new Error("Unexpected entity type: " + packageName); + }; + return category; + } + + //Honestly, both "type" and "category" fields in the schema and examples do not contain any useful information + //Since category is optional, I will just leave it out, and for type I will assume general entity classification + //by the Entity class hierarchy (which has some weirdness too by the way) + private static String getEntityTypeForClass(Class entityClass) { + //Top-level classifications + if (WaterCreatureEntity.class.isAssignableFrom(entityClass)) { + return "water_creature"; + } + if (AnimalEntity.class.isAssignableFrom(entityClass)) { + return "animal"; + } + if (HostileEntity.class.isAssignableFrom(entityClass)) { + return "hostile"; + } + if (AmbientEntity.class.isAssignableFrom(entityClass)) { + return "ambient"; + } + + //Second level classifications. PathAwareEntity is not included because it + //doesn't really make much sense to categorize by it + if (PathAwareEntity.class.isAssignableFrom(entityClass)) { + return "passive"; + } + if (MobEntity.class.isAssignableFrom(entityClass)) { + return "mob"; + } + + //Other classifications only include living entities and projectiles. everything else is categorized as other + if (LivingEntity.class.isAssignableFrom(entityClass)) { + return "living"; + } + if (Projectile.class.isAssignableFrom(entityClass)) { + return "projectile"; + } + return "other"; + } + + private static int entityId(Entity entity) { + if (!DGU.getCurrentlyRunningServer().getVersion().equals("1.9.4")) { + throw new Error("These ids were gotten manually for 1.9.4, remake for " + DGU.getCurrentlyRunningServer().getVersion()); + } + int rawId = Registries.ENTITY_TYPES.getRawId(entity.getClass()); + if (rawId == -1) { // see TrackedEntityInstance + if (entity instanceof ItemEntity) { + return 2; + } else if (entity instanceof FishingBobberEntity) { + return 90; + } else { + throw new Error("unable to find rawId for entity: " + entity.getEntityName()); + } + } + return rawId; + } + + @Override + public String getDataName() { + return "entities"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + for (Class entityType : Registries.ENTITY_TYPES) { + resultArray.add(generateEntity(entityType)); + } + return resultArray; + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java new file mode 100644 index 00000000..fd68583a --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java @@ -0,0 +1,50 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.item.FoodItem; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; + +import java.util.Objects; + +public class FoodsDataGenerator implements IDataGenerator { + + public static JsonObject generateFoodDescriptor(FoodItem foodItem) { + JsonObject foodDesc = new JsonObject(); + Identifier registryKey = Registries.ITEMS.getIdentifier(foodItem); + + foodDesc.addProperty("id", Registries.ITEMS.getRawId(foodItem)); + foodDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + foodDesc.addProperty("stackSize", foodItem.getMaxCount()); + foodDesc.addProperty("displayName", foodItem.getDisplayName(DGU.stackFor(foodItem))); + float foodPoints = foodItem.getHungerPoints(DGU.stackFor(foodItem)); + float saturationRatio = foodItem.getSaturation(DGU.stackFor(foodItem)) * 2.0F; + float saturation = foodPoints * saturationRatio; + + foodDesc.addProperty("foodPoints", foodPoints); + foodDesc.addProperty("saturation", saturation); + + foodDesc.addProperty("effectiveQuality", foodPoints + saturation); + foodDesc.addProperty("saturationRatio", saturationRatio); + return foodDesc; + } + + @Override + public String getDataName() { + return "foods"; + } + + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + for (Item item : Registries.ITEMS) { + if (item instanceof FoodItem) { + resultsArray.add(generateFoodDescriptor((FoodItem) item)); + } + } + return resultsArray; + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java new file mode 100644 index 00000000..81fe4e20 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; + +public interface IDataGenerator { + + String getDataName(); + + JsonElement generateDataJson(); +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java new file mode 100644 index 00000000..3aa51c41 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java @@ -0,0 +1,30 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.NoteBlockAccessor; +import dev.u9g.minecraftdatagenerator.mixin.SoundAccessor; +import net.minecraft.client.sound.SoundEvent; + +import java.util.Objects; + +public class InstrumentsDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "instruments"; + } + + @Override + public JsonElement generateDataJson() { + JsonArray array = new JsonArray(); + int i = 0; + for (SoundEvent sound : Objects.requireNonNull(NoteBlockAccessor.TUNES())) { + JsonObject object = new JsonObject(); + object.addProperty("id", i++); + object.addProperty("name", ((SoundAccessor) sound).id().getPath().split("\\.")[2]); + array.add(object); + } + return array; + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java new file mode 100644 index 00000000..4b84f478 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java @@ -0,0 +1,90 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.util.DGU; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class ItemsDataGenerator implements IDataGenerator { + + private static List calculateItemsToRepairWith(Item sourceItem) { + List items = new ArrayList<>(); + for (Item otherItem : Registries.ITEMS) { + if (sourceItem.canRepair(DGU.stackFor(sourceItem), DGU.stackFor(otherItem))) { + items.add(otherItem); + } + } + return items; + } + + private static List getApplicableEnchantmentTargets(Item sourceItem) { + List targets = new ArrayList<>(); + for (EnchantmentTarget target : EnchantmentTarget.values()) { + if (!target.isCompatible(sourceItem)) continue; + targets.add(target); + } + return targets; + } + + public static JsonObject generateItem(Item item) { + JsonObject itemDesc = new JsonObject(); + Identifier registryKey = Registries.ITEMS.getIdentifier(item); + + itemDesc.addProperty("id", Registries.ITEMS.getRawId(item)); + itemDesc.addProperty("name", Objects.requireNonNull(registryKey).getPath()); + + itemDesc.addProperty("displayName", item.getDisplayName(DGU.stackFor(item))); + itemDesc.addProperty("stackSize", item.getMaxCount()); + + List enchantmentTargets = getApplicableEnchantmentTargets(item); + + if (item.isDamageable()) { + JsonArray enchantCategoriesArray = new JsonArray(); + for (EnchantmentTarget target : enchantmentTargets) { + enchantCategoriesArray.add(new JsonPrimitive(EnchantmentsDataGenerator.getEnchantmentTargetName(target))); + } + if (enchantCategoriesArray.size() > 0) { + itemDesc.add("enchantCategories", enchantCategoriesArray); + } + } + + if (item.isDamageable()) { + List repairWithItems = calculateItemsToRepairWith(item); + + JsonArray fixedWithArray = new JsonArray(); + for (Item repairWithItem : repairWithItems) { + Identifier repairWithName = Registries.ITEMS.getIdentifier(repairWithItem); + fixedWithArray.add(new JsonPrimitive(Objects.requireNonNull(repairWithName).getPath())); + } + if (fixedWithArray.size() > 0) { + itemDesc.add("repairWith", fixedWithArray); + } + + int maxDurability = item.getMaxDamage(); + itemDesc.addProperty("maxDurability", maxDurability); + } + return itemDesc; + } + + @Override + public String getDataName() { + return "items"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + for (Item item : Registries.ITEMS) { + resultArray.add(generateItem(item)); + } + return resultArray; + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java new file mode 100644 index 00000000..88e8d2fb --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java @@ -0,0 +1,29 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.LanguageAccessor; +import dev.u9g.minecraftdatagenerator.util.Registries; + +import java.util.Map; + +public class LanguageDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "language"; + } + + @Override + public JsonElement generateDataJson() { + try { + JsonObject obj = new JsonObject(); + Map translations = ((LanguageAccessor) Registries.LANGUAGE).translations(); + for (Map.Entry entry : translations.entrySet()) { + obj.addProperty(entry.getKey(), entry.getValue()); + } + return obj; + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to generate language file"); + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java new file mode 100644 index 00000000..2b1a5085 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java @@ -0,0 +1,31 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.minecraft.client.particle.ParticleType; + +public class ParticlesDataGenerator implements IDataGenerator { + + public static JsonObject generateParticleType(int id, ParticleType particleType) { + JsonObject effectDesc = new JsonObject(); + + effectDesc.addProperty("id", id); + effectDesc.addProperty("name", particleType.getName()); + return effectDesc; + } + + @Override + public String getDataName() { + return "particles"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + int i = 0; + for (ParticleType particleType : ParticleType.values()) { + resultsArray.add(generateParticleType(i++, particleType)); + } + return resultsArray; + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java new file mode 100644 index 00000000..32498dfa --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java @@ -0,0 +1,119 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; + +public class RecipeDataGenerator implements IDataGenerator { + + @Override + public String getDataName() { + return "recipes"; + } + + @Override + public JsonElement generateDataJson() { +// JsonObject finalObj = new JsonObject(); +// Multimap recipes = ArrayListMultimap.create(); +// for (Recipe recipe : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) { +// if (recipe instanceof ShapedRecipe sr) { +// var ingredients = sr.getIngredients(); +// List ingr = new ArrayList<>(); +// for (int i = 0; i < 9; i++) { +// if (i >= ingredients.size()) { +// ingr.add(-1); +// continue; +// } +// var stacks = ingredients.get(i); +//// var matching = stacks.getMatchingStacks(); +//// if (matching.length == 0) { +//// ingr.add(-1); +//// } else { +//// ingr.add(getRawIdFor(matching[0].getItem())); +//// } +// } +// Lists.reverse(ingr); +// +// JsonArray inShape = new JsonArray(); +// +// var iter = ingr.iterator(); +// for (int y = 0; y < 3; y++) { +// var jsonRow = new JsonArray(); +// int one = iter.next(); +// int two = iter.next(); +// int three = iter.next(); +// if (y > 0 && one == -1 && two == -1 && three == -1) continue; +// jsonRow.add(one); +// jsonRow.add(two); +// jsonRow.add(three); +// inShape.add(jsonRow); +// } +// +// JsonObject finalRecipe = new JsonObject(); +// finalRecipe.add("inShape", inShape); +// +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +// resultObject.addProperty("count", sr.getOutput().getCount()); +// finalRecipe.add("result", resultObject); +// +// String id = ((Integer) getRawIdFor(sr.getOutput().getItem())).toString(); +// +// if (!finalObj.has(id)) { +// finalObj.add(id, new JsonArray()); +// } +// finalObj.get(id).getAsJsonArray().add(finalRecipe); +//// var input = new JsonArray(); +//// var ingredients = sr.getIngredients().stream().toList(); +//// for (int y = 0; y < sr.getHeight(); y++) { +//// var arr = new JsonArray(); +//// for (int x = 0; x < sr.getWidth(); x++) { +//// if ((y*3)+x >= ingredients.size()) { +//// arr.add(JsonNull.INSTANCE); +//// continue; +//// } +//// var ingredient = ingredients.get((y*3)+x).getMatchingStacks(); // FIXME: fix when there are more than one matching stack +//// if (ingredient.length == 0) { +//// arr.add(JsonNull.INSTANCE); +//// } else { +//// arr.add(getRawIdFor(ingredient[0].getItem())); +//// } +//// } +//// input.add(arr); +//// } +//// var rootRecipeObject = new JsonObject(); +//// rootRecipeObject.add("inShape", input); +//// var resultObject = new JsonObject(); +//// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +//// resultObject.addProperty("count", sr.getOutput().getCount()); +//// rootRecipeObject.add("result", resultObject); +//// recipes.put(getRawIdFor(sr.getOutput().getItem()), rootRecipeObject); +// } else if (recipe instanceof ShapelessRecipe sl) { + +// var ingredients = new JsonArray(); +// for (Ingredient ingredient : sl.getIngredients()) { +// if (ingredient.isEmpty()) continue; +//// ingredients.add(getRawIdFor(ingredient.getMatchingStacks()[0].getItem())); +// } +// var rootRecipeObject = new JsonObject(); +// rootRecipeObject.add("ingredients", ingredients); +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sl.getOutput().getItem())); +// resultObject.addProperty("count", sl.getOutput().getCount()); +// rootRecipeObject.add("result", resultObject); +// recipes.put(getRawIdFor(sl.getOutput().getItem()), rootRecipeObject); +// } +// } +// recipes.forEach((a, b) -> { +// if (!finalObj.has(a.toString())) { +// finalObj.add(a.toString(), new JsonArray()); +// } +// finalObj.get(a.toString()).getAsJsonArray().add(b); +// }); +// return finalObj; + return JsonNull.INSTANCE; + } +// +// private static int getRawIdFor (Item item) { +// return Registry.ITEM.getRawId(item); +// } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java new file mode 100644 index 00000000..a4f970c9 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java @@ -0,0 +1,165 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.BlockColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.FoliageColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.GrassColors; +import dev.u9g.minecraftdatagenerator.ClientSideAnnoyances.ServerSideRedstoneWireBlock; +import dev.u9g.minecraftdatagenerator.mixin.BiomeAccessor; +import dev.u9g.minecraftdatagenerator.util.EmptyBlockView; +import dev.u9g.minecraftdatagenerator.util.Registries; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.block.RedstoneWireBlock; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.biome.Biome; + +import java.util.*; + +public class TintsDataGenerator implements IDataGenerator { + + private static final BlockColors blockColors = BlockColors.create(); + + public static BiomeTintColors generateBiomeTintColors() { + BiomeTintColors colors = new BiomeTintColors(); + + for (Biome biome : Registries.BIOMES) { + EmptyBlockView bv = new EmptyBlockView() { + @Override + public Biome getBiome(BlockPos pos) { + return biome; + } + }; + int biomeGrassColor = GrassColors.getGrassColor(bv.getBiome(BlockPos.ORIGIN)); + int biomeFoliageColor = FoliageColors.getFoliageColor(bv.getBiome(BlockPos.ORIGIN)); + int biomeWaterColor = ((BiomeAccessor) biome).waterColor(); + + colors.grassColoursMap.computeIfAbsent(biomeGrassColor, k -> new ArrayList<>()).add(biome); + colors.foliageColoursMap.computeIfAbsent(biomeFoliageColor, k -> new ArrayList<>()).add(biome); + colors.waterColourMap.computeIfAbsent(biomeWaterColor, k -> new ArrayList<>()).add(biome); + } + return colors; + } + + public static Map generateRedstoneTintColors() { + Map resultColors = new HashMap<>(); + + for (int redstoneLevel : RedstoneWireBlock.POWER.getValues()) { + int color = ServerSideRedstoneWireBlock.getWireColor(redstoneLevel); + resultColors.put(redstoneLevel, color); + } + return resultColors; + } + + private static int getBlockColor(Block block) { + return blockColors.method_13410(block.getDefaultState()); + } + + public static Map generateConstantTintColors() { + Map resultColors = new HashMap<>(); + BlockColors blockColors = BlockColors.create(); + // FIXME: ? + // resultColors.put(Blocks.BIRCH_LEAVES, FoliageColors.getBirchColor()); + // resultColors.put(Blocks.SPRUCE_LEAVES, FoliageColors.getSpruceColor()); + + resultColors.put(Blocks.LILY_PAD, getBlockColor(Blocks.LILY_PAD)); + // FIXME: ? + // resultColors.put(Blocks.ATTACHED_MELON_STEM, getBlockColor(Blocks.ATTACHED_MELON_STEM, blockColors)); + // resultColors.put(Blocks.ATTACHED_PUMPKIN_STEM, getBlockColor(Blocks.ATTACHED_PUMPKIN_STEM, blockColors)); + + //not really constant, depend on the block age, but kinda have to be handled since textures are literally white without them + resultColors.put(Blocks.MELON_STEM, getBlockColor(Blocks.MELON_STEM)); + resultColors.put(Blocks.PUMPKIN_STEM, getBlockColor(Blocks.PUMPKIN_STEM)); + + return resultColors; + } + + private static JsonObject encodeBiomeColorMap(Map> colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry> entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + for (Biome biome : entry.getValue()) { + Identifier registryKey = Registries.BIOMES.getIdentifier(biome); + keysArray.add(new JsonPrimitive(Objects.requireNonNull(registryKey).getPath())); + } + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getKey()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeRedstoneColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + keysArray.add(new JsonPrimitive(entry.getKey())); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeBlocksColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (Map.Entry entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + JsonArray keysArray = new JsonArray(); + Identifier registryKey = Registries.BLOCKS.getIdentifier(entry.getKey()); + keysArray.add(new JsonPrimitive(Objects.requireNonNull(registryKey).getPath())); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + @Override + public String getDataName() { + return "tints"; + } + + @Override + public JsonObject generateDataJson() { + BiomeTintColors biomeTintColors = generateBiomeTintColors(); + Map redstoneColors = generateRedstoneTintColors(); + Map constantTintColors = generateConstantTintColors(); + + JsonObject resultObject = new JsonObject(); + + resultObject.add("grass", encodeBiomeColorMap(biomeTintColors.grassColoursMap)); + resultObject.add("foliage", encodeBiomeColorMap(biomeTintColors.foliageColoursMap)); + resultObject.add("water", encodeBiomeColorMap(biomeTintColors.waterColourMap)); + + resultObject.add("redstone", encodeRedstoneColorMap(redstoneColors)); + resultObject.add("constant", encodeBlocksColorMap(constantTintColors)); + + return resultObject; + } + + public static class BiomeTintColors { + final Map> grassColoursMap = new HashMap<>(); + final Map> foliageColoursMap = new HashMap<>(); + final Map> waterColourMap = new HashMap<>(); + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BiomeAccessor.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BiomeAccessor.java new file mode 100644 index 00000000..1feed6b7 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BiomeAccessor.java @@ -0,0 +1,14 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.world.biome.Biome; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Biome.class) +public interface BiomeAccessor { + @Accessor("waterColor") + int waterColor(); + + @Accessor("name") + String name(); +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BlockAccessor.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BlockAccessor.java new file mode 100644 index 00000000..1c25c003 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/BlockAccessor.java @@ -0,0 +1,11 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Block; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Block.class) +public interface BlockAccessor { + @Accessor("blastResistance") + float getBlastResistance(); +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java new file mode 100644 index 00000000..0d297e7a --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.server.dedicated.EulaReader; +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; + +@Mixin(EulaReader.class) +public class EULAMixin { + @Inject(method = "isEulaAgreedTo()Z", at = @At("TAIL"), cancellable = true) + public void init(CallbackInfoReturnable cir) { + cir.setReturnValue(true); + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockAccessor.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockAccessor.java new file mode 100644 index 00000000..5bec8213 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockAccessor.java @@ -0,0 +1,19 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.EndPortalFrameBlock; +import net.minecraft.util.math.Box; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(EndPortalFrameBlock.class) +public interface EndPortalFrameBlockAccessor { + @Accessor("PORTAL_FRAME") + static Box portalFrame() { + throw new IllegalStateException(); + } + + @Accessor("PORTAL_EYE") + static Box eye() { + throw new IllegalStateException(); + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockOverwrite.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockOverwrite.java new file mode 100644 index 00000000..4da63091 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EndPortalFrameBlockOverwrite.java @@ -0,0 +1,26 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.BlockState; +import net.minecraft.block.EndPortalFrameBlock; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +import java.util.List; + +@Mixin(EndPortalFrameBlock.class) +public class EndPortalFrameBlockOverwrite { + /** + * @author a + * @reason a + */ + @Overwrite() + public void appendCollisionBoxes(BlockState state, World world, BlockPos pos, Box entityBox, List boxes, @Nullable Entity entity) { + boxes.add(EndPortalFrameBlockAccessor.portalFrame()); + boxes.add(EndPortalFrameBlockAccessor.eye()); + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java new file mode 100644 index 00000000..60386ba8 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EntityTypeAccessor.java @@ -0,0 +1,36 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(EntityType.class) +public interface EntityTypeAccessor { + @Accessor("NAME_CLASS_MAP") + static Map> NAME_CLASS_MAP() { + throw new Error(); + } + + @Accessor("CLASS_NAME_MAP") + static Map, String> CLASS_NAME_MAP() { + throw new Error(); + } + + @Accessor("ID_CLASS_MAP") + static Map> ID_CLASS_MAP() { + throw new Error(); + } + + @Accessor("CLASS_ID_MAP") + static Map, Integer> CLASS_ID_MAP() { + throw new Error(); + } + + @Accessor("NAME_ID_MAP") + static Map NAME_ID_MAP() { + throw new Error(); + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ItemEntityOverwrite.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ItemEntityOverwrite.java new file mode 100644 index 00000000..4b29afb3 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ItemEntityOverwrite.java @@ -0,0 +1,24 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +@Mixin(ItemEntity.class) +public abstract class ItemEntityOverwrite extends Entity { + + public ItemEntityOverwrite(World world) { + super(world); + } + + /** + * @author a + * @reason a + */ + @Overwrite + public String getTranslationKey() { + return super.getTranslationKey(); + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/LanguageAccessor.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/LanguageAccessor.java new file mode 100644 index 00000000..7bfd95fc --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/LanguageAccessor.java @@ -0,0 +1,13 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.util.Language; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(Language.class) +public interface LanguageAccessor { + @Accessor("translations") + Map translations(); +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java new file mode 100644 index 00000000..53b8cdb5 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Block; +import net.minecraft.item.ToolItem; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Set; + +@Mixin(ToolItem.class) +public interface MiningToolItemAccessor { + + @Accessor + Set getEffectiveBlocks(); +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/NoteBlockAccessor.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/NoteBlockAccessor.java new file mode 100644 index 00000000..4bc3bcb9 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/NoteBlockAccessor.java @@ -0,0 +1,16 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.NoteBlock; +import net.minecraft.client.sound.SoundEvent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + +@Mixin(NoteBlock.class) +public interface NoteBlockAccessor { + @Accessor("TUNES") + static List TUNES() { + return null; + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java new file mode 100644 index 00000000..32871cc3 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.generators.DataGenerators; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; +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.nio.file.Path; +import java.util.logging.Level; + +@Mixin(MinecraftDedicatedServer.class) +public class ReadyMixin { + @Inject(method = "setupServer()Z", at = @At("TAIL")) + private void init(CallbackInfoReturnable cir) { + Main.LOGGER.log(Level.INFO, "Starting data generation!"); + String versionName = DGU.getCurrentlyRunningServer().getVersion(); + Path serverRootDirectory = DGU.getCurrentlyRunningServer().getRunDirectory().toPath().toAbsolutePath(); + Path dataDumpDirectory = serverRootDirectory.resolve("minecraft-data").resolve(versionName); + DataGenerators.runDataGenerators(dataDumpDirectory); + Main.LOGGER.log(Level.INFO, "Done data generation!"); + System.exit(0); + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/SoundAccessor.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/SoundAccessor.java new file mode 100644 index 00000000..fe505910 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/SoundAccessor.java @@ -0,0 +1,12 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.client.sound.SoundEvent; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(SoundEvent.class) +public interface SoundAccessor { + @Accessor("id") + Identifier id(); +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java new file mode 100644 index 00000000..33d4cfcd --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/mixin/StatusEffectAccessor.java @@ -0,0 +1,11 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.entity.effect.StatusEffect; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(StatusEffect.class) +public interface StatusEffectAccessor { + @Accessor("negative") + boolean negative(); +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java new file mode 100644 index 00000000..8edd9a23 --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java @@ -0,0 +1,83 @@ +package dev.u9g.minecraftdatagenerator.util; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapter; +import com.google.gson.internal.bind.TypeAdapters; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.World; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; + +public class DGU { + +// @Environment(EnvType.CLIENT) +// private static MinecraftServer getCurrentlyRunningServerClient() { +// return MinecraftClient.getInstance().getServer(); +// } +// +// @SuppressWarnings("deprecation") +// private static MinecraftServer getCurrentlyRunningServerDedicated() { +// return (MinecraftServer) FabricLoader.getInstance().getGameInstance(); +// } + + public static Gson gson = new GsonBuilder().registerTypeAdapterFactory(TypeAdapters.newFactory(double.class, Double.class, new TypeAdapter() { + @Override + public Number read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + return in.nextDouble(); + } + + @Override + public void write(JsonWriter out, Number value) throws IOException { + out.value(value); + } + })).create(); + + public static MinecraftServer getCurrentlyRunningServer() { + return (MinecraftServer) FabricLoader.getInstance().getGameInstance(); + } + + @Environment(EnvType.CLIENT) + private static String translateTextClient(String translationKey) { + return I18n.translate(translationKey); + } + + private static String translateTextFallback(String translationKey) { + try { + return Registries.LANGUAGE.translate(translationKey); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to translate: '" + translationKey + "'"); + } + + public static String translateText(String translationKey) { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return translateTextClient(translationKey); + } + return translateTextFallback(translationKey); + } + + @NotNull + public static World getWorld() { + return getCurrentlyRunningServer().getWorld(); + } + + public static ItemStack stackFor(Item ic) { + return new ItemStack(ic); + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java new file mode 100644 index 00000000..2fbb489b --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyBlockView.java @@ -0,0 +1,54 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.BlockView; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.level.LevelGeneratorType; +import org.jetbrains.annotations.Nullable; + +public class EmptyBlockView implements BlockView { + public static final EmptyBlockView INSTANCE = new EmptyBlockView(); + + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + return null; + } + + @Override + public int getLight(BlockPos pos, int minBlockLight) { + return 0; + } + + public BlockState getBlockState(BlockPos pos) { + return Blocks.AIR.getDefaultState(); + } + + @Override + public boolean isAir(BlockPos pos) { + return false; + } + + @Override + public Biome getBiome(BlockPos pos) { + return null; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public int getStrongRedstonePower(BlockPos pos, Direction direction) { + return 0; + } + + @Override + public LevelGeneratorType getGeneratorType() { + return null; + } +} diff --git a/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/util/Registries.java b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/util/Registries.java new file mode 100644 index 00000000..7f5b00ae --- /dev/null +++ b/1.9.4/src/main/java/dev/u9g/minecraftdatagenerator/util/Registries.java @@ -0,0 +1,34 @@ +package dev.u9g.minecraftdatagenerator.util; + +import dev.u9g.minecraftdatagenerator.mixin.EntityTypeAccessor; +import net.minecraft.block.Block; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.entity.Entity; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; +import net.minecraft.util.Language; +import net.minecraft.util.registry.SimpleRegistry; +import net.minecraft.world.biome.Biome; + +import java.util.Map; + +public class Registries { + public static final SimpleRegistry BIOMES = Biome.REGISTRY; + public static final SimpleRegistry BLOCKS = Block.REGISTRY; + public static final SimpleRegistry ITEMS = Item.REGISTRY; + public static final SimpleRegistry STATUS_EFFECTS = StatusEffect.REGISTRY; + public static final SimpleRegistry ENCHANTMENTS = Enchantment.REGISTRY; + public static final SimpleRegistry> ENTITY_TYPES = new SimpleRegistry<>(); + public static final Language LANGUAGE = new Language(); + + static { + for (Map.Entry> entry : EntityTypeAccessor.ID_CLASS_MAP().entrySet()) { + String name = EntityTypeAccessor.CLASS_NAME_MAP().get(entry.getValue()); + if (name.equals("Mob") || name.equals("Monster")) { + continue; + } + ENTITY_TYPES.add(entry.getKey(), new Identifier(name), entry.getValue()); + } + } +} diff --git a/1.9.4/src/main/resources/fabric.mod.json b/1.9.4/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..ad1562f2 --- /dev/null +++ b/1.9.4/src/main/resources/fabric.mod.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": 1, + "id": "minecraft-data-generator", + "version": "${version}", + "name": "Minecraft Data Generator", + "description": "", + "authors": [ + "Archengius", + "U9G" + ], + "contact": {}, + "license": "MIT", + "environment": "server", + "entrypoints": { + "main": [ + "dev.u9g.minecraftdatagenerator.Main" + ] + }, + "mixins": [ + "minecraft-data-generator.mixins.json" + ], + "depends": { + "fabricloader": ">=0.13.3", + "minecraft": ">=1.9" + } +} diff --git a/1.9.4/src/main/resources/minecraft-data-generator.mixins.json b/1.9.4/src/main/resources/minecraft-data-generator.mixins.json new file mode 100644 index 00000000..4dd13158 --- /dev/null +++ b/1.9.4/src/main/resources/minecraft-data-generator.mixins.json @@ -0,0 +1,28 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "dev.u9g.minecraftdatagenerator.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "BiomeAccessor", + "BlockAccessor", + "EndPortalFrameBlockAccessor", + "EndPortalFrameBlockOverwrite", + "EntityTypeAccessor", + "ItemEntityOverwrite", + "LanguageAccessor", + "MiningToolItemAccessor", + "NoteBlockAccessor", + "ReadyMixin", + "SoundAccessor", + "StatusEffectAccessor" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "server": [ + "EULAMixin" + ] +} diff --git a/22w19a/build.gradle b/22w19a/build.gradle new file mode 100644 index 00000000..e85de392 --- /dev/null +++ b/22w19a/build.gradle @@ -0,0 +1,31 @@ +plugins { + id 'fabric-loom' +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API. This is technically optional, but you probably want it anyway. + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" +} + +processResources { + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} diff --git a/22w19a/gradle.properties b/22w19a/gradle.properties new file mode 100644 index 00000000..d6cfd057 --- /dev/null +++ b/22w19a/gradle.properties @@ -0,0 +1,8 @@ +# Fabric Properties +# check these on https://modmuss50.me/fabric.html +minecraft_version=22w19a +yarn_mappings=22w19a+build.1 +loader_version=0.14.5 +# Dependencies +# check this on https://modmuss50.me/fabric.html +fabric_version=0.52.2+1.19 diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/Main.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/Main.java new file mode 100644 index 00000000..f8d17372 --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/Main.java @@ -0,0 +1,14 @@ +package dev.u9g.minecraftdatagenerator; + +import net.fabricmc.api.ModInitializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Main implements ModInitializer { + public static final Logger LOGGER = LoggerFactory.getLogger("mc-data-gen-serv"); + + @Override + public void onInitialize() { + + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java new file mode 100644 index 00000000..1d365498 --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java @@ -0,0 +1,63 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.TheEndBiomeDataAccessor; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.fabricmc.fabric.api.biome.v1.NetherBiomes; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; + +public class BiomesDataGenerator implements IDataGenerator { + + private static String guessBiomeDimensionFromCategory(Biome biome) { + // FIND A WAY TO FIND WHAT CATEGORY THE BIOME IS + var key = DynamicRegistryManager.BUILTIN.get().get(Registry.BIOME_KEY).getKey(biome).orElseThrow(); + System.out.println(DynamicRegistryManager.BUILTIN.get().get(Registry.BIOME_KEY).getKey(biome).orElseThrow()); + if (NetherBiomes.canGenerateInNether(key)) { + return "nether"; + } else if (TheEndBiomeDataAccessor.END_BARRENS_MAP().containsKey(key) || TheEndBiomeDataAccessor.END_BIOMES_MAP().containsKey(key) || TheEndBiomeDataAccessor.END_MIDLANDS_MAP().containsKey(key)) { + return "end"; + } + return "overworld"; + } + + public static JsonObject generateBiomeInfo(Registry registry, Biome biome) { + JsonObject biomeDesc = new JsonObject(); + Identifier registryKey = registry.getKey(biome).orElseThrow().getValue(); + String localizationKey = String.format("biome.%s.%s", registryKey.getNamespace(), registryKey.getPath()); + + biomeDesc.addProperty("id", registry.getRawId(biome)); + biomeDesc.addProperty("name", registryKey.getPath()); + + //FIXME: this... + biomeDesc.addProperty("category", ""); + biomeDesc.addProperty("temperature", biome.getTemperature()); + biomeDesc.addProperty("precipitation", biome.getPrecipitation().getName()); + //biomeDesc.addProperty("depth", biome.getDepth()); - Doesn't exist anymore in minecraft source + biomeDesc.addProperty("dimension", guessBiomeDimensionFromCategory(biome)); + biomeDesc.addProperty("displayName", DGU.translateText(localizationKey)); + biomeDesc.addProperty("color", biome.getSkyColor()); + biomeDesc.addProperty("rainfall", biome.getDownfall()); + + return biomeDesc; + } + + @Override + public String getDataName() { + return "biomes"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray biomesArray = new JsonArray(); + Registry biomeRegistry = DynamicRegistryManager.BUILTIN.get().get(Registry.BIOME_KEY); + + biomeRegistry.stream() + .map(biome -> generateBiomeInfo(biomeRegistry, biome)) + .forEach(biomesArray::add); + return biomesArray; + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java new file mode 100644 index 00000000..52c3181d --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java @@ -0,0 +1,111 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.EmptyBlockView; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class BlockCollisionShapesDataGenerator implements IDataGenerator { + + @Override + public String getDataName() { + return "blockCollisionShapes"; + } + + @Override + public JsonObject generateDataJson() { + Registry blockRegistry = Registry.BLOCK; + BlockShapesCache blockShapesCache = new BlockShapesCache(); + + blockRegistry.forEach(blockShapesCache::processBlock); + + JsonObject resultObject = new JsonObject(); + + resultObject.add("blocks", blockShapesCache.dumpBlockShapeIndices(blockRegistry)); + resultObject.add("shapes", blockShapesCache.dumpShapesObject()); + + return resultObject; + } + + private static class BlockShapesCache { + public final Map uniqueBlockShapes = new HashMap<>(); + public final Map> blockCollisionShapes = new HashMap<>(); + private int lastCollisionShapeId = 0; + + public void processBlock(Block block) { + List blockStates = block.getStateManager().getStates(); + List blockCollisionShapes = new ArrayList<>(); + + for (BlockState blockState : blockStates) { + VoxelShape blockShape = blockState.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + Integer blockShapeIndex = uniqueBlockShapes.get(blockShape); + + if (blockShapeIndex == null) { + blockShapeIndex = lastCollisionShapeId++; + uniqueBlockShapes.put(blockShape, blockShapeIndex); + } + blockCollisionShapes.add(blockShapeIndex); + } + + this.blockCollisionShapes.put(block, blockCollisionShapes); + } + + public JsonObject dumpBlockShapeIndices(Registry blockRegistry) { + JsonObject resultObject = new JsonObject(); + + for (var entry : blockCollisionShapes.entrySet()) { + List blockCollisions = entry.getValue(); + long distinctShapesCount = blockCollisions.stream().distinct().count(); + JsonElement blockCollision; + if (distinctShapesCount == 1L) { + blockCollision = new JsonPrimitive(blockCollisions.get(0)); + } else { + blockCollision = new JsonArray(); + for (int collisionId : blockCollisions) { + ((JsonArray) blockCollision).add(collisionId); + } + } + + Identifier registryKey = blockRegistry.getKey(entry.getKey()).orElseThrow().getValue(); + resultObject.add(registryKey.getPath(), blockCollision); + } + + return resultObject; + } + + public JsonObject dumpShapesObject() { + JsonObject shapesObject = new JsonObject(); + + for (var entry : uniqueBlockShapes.entrySet()) { + JsonArray boxesArray = new JsonArray(); + entry.getKey().forEachBox((x1, y1, z1, x2, y2, z2) -> { + JsonArray oneBoxJsonArray = new JsonArray(); + + oneBoxJsonArray.add(x1); + oneBoxJsonArray.add(y1); + oneBoxJsonArray.add(z1); + + oneBoxJsonArray.add(x2); + oneBoxJsonArray.add(y2); + oneBoxJsonArray.add(z2); + + boxesArray.add(oneBoxJsonArray); + }); + shapesObject.add(Integer.toString(entry.getValue()), boxesArray); + } + return shapesObject; + } + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java new file mode 100644 index 00000000..51139655 --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java @@ -0,0 +1,198 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.base.CaseFormat; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.block.AirBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.loot.context.LootContext; +import net.minecraft.loot.context.LootContextParameters; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.state.property.EnumProperty; +import net.minecraft.state.property.IntProperty; +import net.minecraft.state.property.Property; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.EmptyBlockView; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +public class BlocksDataGenerator implements IDataGenerator { + + private static final Logger logger = LoggerFactory.getLogger(BlocksDataGenerator.class); + + private static List getItemsEffectiveForBlock(BlockState blockState) { + return Registry.ITEM.stream() + .filter(item -> item.getDefaultStack().isSuitableFor(blockState)) + .collect(Collectors.toList()); + } + + private static void populateDropsIfPossible(BlockState blockState, Item firstToolItem, List outDrops) { + MinecraftServer minecraftServer = DGU.getCurrentlyRunningServer(); + if (minecraftServer != null) { + //If we have local world context, we can actually evaluate loot tables and determine actual data + ServerWorld serverWorld = minecraftServer.getOverworld(); + LootContext.Builder lootContext = new LootContext.Builder(serverWorld) + .parameter(LootContextParameters.BLOCK_STATE, blockState) + .parameter(LootContextParameters.ORIGIN, Vec3d.ZERO) + .parameter(LootContextParameters.TOOL, firstToolItem.getDefaultStack()) + .random(0L); + outDrops.addAll(blockState.getDroppedStacks(lootContext)); + } else { + //If we're lacking world context to correctly determine drops, assume that default drop is ItemBlock stack in quantity of 1 + Item itemBlock = blockState.getBlock().asItem(); + if (itemBlock != Items.AIR) { + outDrops.add(itemBlock.getDefaultStack()); + } + } + } + + private static String getPropertyTypeName(Property property) { + //Explicitly handle default minecraft properties + if (property instanceof BooleanProperty) { + return "bool"; + } + if (property instanceof IntProperty) { + return "int"; + } + if (property instanceof EnumProperty) { + return "enum"; + } + + //Use simple class name as fallback, this code will give something like + //example_type for ExampleTypeProperty class name + String rawPropertyName = property.getClass().getSimpleName().replace("Property", ""); + return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, rawPropertyName); + } + + private static > JsonObject generateStateProperty(Property property) { + JsonObject propertyObject = new JsonObject(); + Collection propertyValues = property.getValues(); + + propertyObject.addProperty("name", property.getName()); + propertyObject.addProperty("type", getPropertyTypeName(property)); + propertyObject.addProperty("num_values", propertyValues.size()); + + //Do not add values for vanilla boolean properties, they are known by default + if (!(property instanceof BooleanProperty)) { + JsonArray propertyValuesArray = new JsonArray(); + for (T propertyValue : propertyValues) { + propertyValuesArray.add(property.name(propertyValue)); + } + propertyObject.add("values", propertyValuesArray); + } + return propertyObject; + } + + private static String findMatchingBlockMaterial(BlockState blockState, List materials) { + List matchingMaterials = materials.stream() + .filter(material -> material.getPredicate().test(blockState)) + .collect(Collectors.toList()); + + if (matchingMaterials.size() > 1) { + var firstMaterial = matchingMaterials.get(0); + var otherMaterials = matchingMaterials.subList(1, matchingMaterials.size()); + + if (!otherMaterials.stream().allMatch(firstMaterial::includesMaterial)) { + logger.error("Block {} matches multiple materials: {}", blockState.getBlock(), matchingMaterials); + } + } + if (matchingMaterials.isEmpty()) { + return "default"; + } + return matchingMaterials.get(0).getMaterialName(); + } + + public static JsonObject generateBlock(Registry blockRegistry, List materials, Block block) { + JsonObject blockDesc = new JsonObject(); + + List blockStates = block.getStateManager().getStates(); + BlockState defaultState = block.getDefaultState(); + Identifier registryKey = blockRegistry.getKey(block).orElseThrow().getValue(); + String localizationKey = block.getTranslationKey(); + List effectiveTools = getItemsEffectiveForBlock(defaultState); + + blockDesc.addProperty("id", blockRegistry.getRawId(block)); + blockDesc.addProperty("name", registryKey.getPath()); + blockDesc.addProperty("displayName", DGU.translateText(localizationKey)); + + blockDesc.addProperty("hardness", block.getHardness()); + blockDesc.addProperty("resistance", block.getBlastResistance()); + blockDesc.addProperty("stackSize", block.asItem().getMaxCount()); + blockDesc.addProperty("diggable", block.getHardness() != -1.0f && !(block instanceof AirBlock)); +// JsonObject effTools = new JsonObject(); +// effectiveTools.forEach(item -> effTools.addProperty( +// String.valueOf(Registry.ITEM.getRawId(item)), // key +// item.getMiningSpeedMultiplier(item.getDefaultStack(), defaultState) // value +// )); +// blockDesc.add("effectiveTools", effTools); + blockDesc.addProperty("material", findMatchingBlockMaterial(defaultState, materials)); + + blockDesc.addProperty("transparent", !defaultState.isOpaque()); + blockDesc.addProperty("emitLight", defaultState.getLuminance()); + blockDesc.addProperty("filterLight", defaultState.getOpacity(EmptyBlockView.INSTANCE, BlockPos.ORIGIN)); + + blockDesc.addProperty("defaultState", Block.getRawIdFromState(defaultState)); + blockDesc.addProperty("minStateId", Block.getRawIdFromState(blockStates.get(0))); + blockDesc.addProperty("maxStateId", Block.getRawIdFromState(blockStates.get(blockStates.size() - 1))); + + JsonArray stateProperties = new JsonArray(); + for (Property property : block.getStateManager().getProperties()) { + stateProperties.add(generateStateProperty(property)); + } + blockDesc.add("states", stateProperties); + + //Only add harvest tools if tool is required for harvesting this block + if (defaultState.isToolRequired()) { + JsonObject effectiveToolsObject = new JsonObject(); + for (Item effectiveItem : effectiveTools) { + effectiveToolsObject.addProperty(Integer.toString(Item.getRawId(effectiveItem)), true); + } + blockDesc.add("harvestTools", effectiveToolsObject); + } + + List actualBlockDrops = new ArrayList<>(); + populateDropsIfPossible(defaultState, effectiveTools.isEmpty() ? Items.AIR : effectiveTools.get(0), actualBlockDrops); + + JsonArray dropsArray = new JsonArray(); + for (ItemStack dropStack : actualBlockDrops) { + dropsArray.add(Item.getRawId(dropStack.getItem())); + } + blockDesc.add("drops", dropsArray); + + VoxelShape blockCollisionShape = defaultState.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + blockDesc.addProperty("boundingBox", blockCollisionShape.isEmpty() ? "empty" : "block"); + + return blockDesc; + } + + @Override + public String getDataName() { + return "blocks"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultBlocksArray = new JsonArray(); + Registry blockRegistry = Registry.BLOCK; + List availableMaterials = MaterialsDataGenerator.getGlobalMaterialInfo(); + + blockRegistry.forEach(block -> resultBlocksArray.add(generateBlock(blockRegistry, availableMaterials, block))); + return resultBlocksArray; + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java new file mode 100644 index 00000000..937d7ada --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/DataGenerators.java @@ -0,0 +1,77 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.List; + +public class DataGenerators { + + private static final Logger logger = LoggerFactory.getLogger(DataGenerators.class); + private static final List GENERATORS = new ArrayList<>(); + + static { + register(new BiomesDataGenerator()); + register(new BlockCollisionShapesDataGenerator()); + register(new BlocksDataGenerator()); + register(new EffectsDataGenerator()); + register(new EnchantmentsDataGenerator()); + register(new EntitiesDataGenerator()); + register(new FoodsDataGenerator()); + register(new ItemsDataGenerator()); + register(new ParticlesDataGenerator()); + register(new TintsDataGenerator()); +// register(new MaterialsDataGenerator()); +// register(new RecipeDataGenerator()); - On hold until mcdata supports multiple materials for a recipe + register(new LanguageDataGenerator()); + register(new InstrumentsDataGenerator()); + } + + public static void register(IDataGenerator generator) { + GENERATORS.add(generator); + } + + public static boolean runDataGenerators(Path outputDirectory) { + try { + Files.createDirectories(outputDirectory); + } catch (IOException exception) { + logger.error("Failed to create data generator output directory at {}", outputDirectory, exception); + return false; + } + + int generatorsFailed = 0; + logger.info("Running minecraft data generators, output at {}", outputDirectory); + + for (IDataGenerator dataGenerator : GENERATORS) { + logger.info("Running generator {}", dataGenerator.getDataName()); + try { + String outputFileName = String.format("%s.json", dataGenerator.getDataName()); + JsonElement outputElement = dataGenerator.generateDataJson(); + Path outputFilePath = outputDirectory.resolve(outputFileName); + + try (Writer writer = Files.newBufferedWriter(outputFilePath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + JsonWriter jsonWriter = new JsonWriter(writer); + jsonWriter.setIndent(" "); + Streams.write(outputElement, jsonWriter); + } + logger.info("Generator: {} -> {}", dataGenerator.getDataName(), outputFileName); + + } catch (Throwable exception) { + logger.error("Failed to run data generator {}", dataGenerator.getDataName(), exception); + generatorsFailed++; + } + } + + logger.info("Finishing running data generators"); + return generatorsFailed == 0; + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java new file mode 100644 index 00000000..1c9849e6 --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java @@ -0,0 +1,46 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.stream.Collectors; + +public class EffectsDataGenerator implements IDataGenerator { + + public static JsonObject generateEffect(Registry registry, StatusEffect statusEffect) { + JsonObject effectDesc = new JsonObject(); + Identifier registryKey = registry.getKey(statusEffect).orElseThrow().getValue(); + + effectDesc.addProperty("id", registry.getRawId(statusEffect)); + if (statusEffect == StatusEffects.UNLUCK) { + effectDesc.addProperty("name", "BadLuck"); + effectDesc.addProperty("displayName", "Bad Luck"); + } else { + effectDesc.addProperty("name", Arrays.stream(registryKey.getPath().split("_")).map(StringUtils::capitalize).collect(Collectors.joining())); + effectDesc.addProperty("displayName", DGU.translateText(statusEffect.getTranslationKey())); + } + + effectDesc.addProperty("type", statusEffect.isBeneficial() ? "good" : "bad"); + return effectDesc; + } + + @Override + public String getDataName() { + return "effects"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry statusEffectRegistry = Registry.STATUS_EFFECT; + statusEffectRegistry.forEach(effect -> resultsArray.add(generateEffect(statusEffectRegistry, effect))); + return resultsArray; + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java new file mode 100644 index 00000000..a71961c9 --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java @@ -0,0 +1,108 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +public class EnchantmentsDataGenerator implements IDataGenerator { + + private static final ImmutableMap ENCHANTMENT_TARGET_NAMES = ImmutableMap.builder() + .put(EnchantmentTarget.ARMOR, "armor") + .put(EnchantmentTarget.ARMOR_FEET, "armor_feet") + .put(EnchantmentTarget.ARMOR_LEGS, "armor_legs") + .put(EnchantmentTarget.ARMOR_CHEST, "armor_chest") + .put(EnchantmentTarget.ARMOR_HEAD, "armor_head") + .put(EnchantmentTarget.WEAPON, "weapon") + .put(EnchantmentTarget.DIGGER, "digger") + .put(EnchantmentTarget.FISHING_ROD, "fishing_rod") + .put(EnchantmentTarget.TRIDENT, "trident") + .put(EnchantmentTarget.BREAKABLE, "breakable") + .put(EnchantmentTarget.BOW, "bow") + .put(EnchantmentTarget.WEARABLE, "wearable") + .put(EnchantmentTarget.CROSSBOW, "crossbow") + .put(EnchantmentTarget.VANISHABLE, "vanishable") + .build(); + + public static String getEnchantmentTargetName(EnchantmentTarget target) { + return ENCHANTMENT_TARGET_NAMES.getOrDefault(target, target.name().toLowerCase(Locale.ROOT)); + } + + //Equation enchantment costs follow is a * level + b, so we can easily retrieve a and b by passing zero level + private static JsonObject generateEnchantmentMinPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMinPower(0); + int a = enchantment.getMinPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + private static JsonObject generateEnchantmentMaxPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMaxPower(0); + int a = enchantment.getMaxPower(1) - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + public static JsonObject generateEnchantment(Registry registry, Enchantment enchantment) { + JsonObject enchantmentDesc = new JsonObject(); + Identifier registryKey = registry.getKey(enchantment).orElseThrow().getValue(); + + enchantmentDesc.addProperty("id", registry.getRawId(enchantment)); + enchantmentDesc.addProperty("name", registryKey.getPath()); + enchantmentDesc.addProperty("displayName", DGU.translateText(enchantment.getTranslationKey())); + + enchantmentDesc.addProperty("maxLevel", enchantment.getMaxLevel()); + enchantmentDesc.add("minCost", generateEnchantmentMinPowerCoefficients(enchantment)); + enchantmentDesc.add("maxCost", generateEnchantmentMaxPowerCoefficients(enchantment)); + + enchantmentDesc.addProperty("treasureOnly", enchantment.isTreasure()); + enchantmentDesc.addProperty("curse", enchantment.isCursed()); + + List incompatibleEnchantments = registry.stream() + .filter(other -> !enchantment.canCombine(other)) + .filter(other -> other != enchantment) + .collect(Collectors.toList()); + + JsonArray excludes = new JsonArray(); + for (Enchantment excludedEnchantment : incompatibleEnchantments) { + Identifier otherKey = registry.getKey(excludedEnchantment).orElseThrow().getValue(); + excludes.add(otherKey.getPath()); + } + enchantmentDesc.add("exclude", excludes); + + enchantmentDesc.addProperty("category", getEnchantmentTargetName(enchantment.type)); + enchantmentDesc.addProperty("weight", enchantment.getRarity().getWeight()); + enchantmentDesc.addProperty("tradeable", enchantment.isAvailableForEnchantedBookOffer()); + enchantmentDesc.addProperty("discoverable", enchantment.isAvailableForRandomSelection()); + + return enchantmentDesc; + } + + @Override + public String getDataName() { + return "enchantments"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry enchantmentRegistry = Registry.ENCHANTMENT; + enchantmentRegistry.stream() + .forEach(enchantment -> resultsArray.add(generateEnchantment(enchantmentRegistry, enchantment))); + return resultsArray; + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java new file mode 100644 index 00000000..c11cb03c --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java @@ -0,0 +1,124 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.mob.AmbientEntity; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.mob.WaterCreatureEntity; +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.passive.PassiveEntity; +import net.minecraft.entity.projectile.ProjectileEntity; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.lang.reflect.ParameterizedType; + +public class EntitiesDataGenerator implements IDataGenerator { + + public static JsonObject generateEntity(Registry> entityRegistry, EntityType entityType) { + JsonObject entityDesc = new JsonObject(); + Identifier registryKey = entityRegistry.getKey(entityType).orElseThrow().getValue(); + int entityRawId = entityRegistry.getRawId(entityType); + + entityDesc.addProperty("id", entityRawId); + entityDesc.addProperty("internalId", entityRawId); + entityDesc.addProperty("name", registryKey.getPath()); + + entityDesc.addProperty("displayName", DGU.translateText(entityType.getTranslationKey())); + entityDesc.addProperty("width", entityType.getDimensions().width); + entityDesc.addProperty("height", entityType.getDimensions().height); + + String entityTypeString = "UNKNOWN"; + MinecraftServer minecraftServer = DGU.getCurrentlyRunningServer(); + + if (minecraftServer != null) { + Entity entityObject = entityType.create(minecraftServer.getOverworld()); + entityTypeString = entityObject != null ? getEntityTypeForClass(entityObject.getClass()) : "player"; + } + entityDesc.addProperty("type", entityTypeString); + entityDesc.addProperty("category", getCategoryFrom(entityType)); + + return entityDesc; + } + + private static String getCategoryFrom(EntityType entityType) { + if (entityType == EntityType.PLAYER) return "other"; // fail early for player entities + Class entityClazz = null; + try { + for (var field : EntityType.class.getFields()) + if (entityType == field.get(EntityType.class)) + entityClazz = (Class) ((ParameterizedType) TypeToken.get(field.getGenericType()).getType()).getActualTypeArguments()[0]; + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + if (entityClazz == null) throw new RuntimeException("Shouldn't be null..."); + return switch (entityClazz.getPackageName()) { + case "net.minecraft.entity.decoration", "net.minecraft.entity.decoration.painting" -> "Immobile"; + case "net.minecraft.entity.boss", "net.minecraft.entity.mob", "net.minecraft.entity.boss.dragon" -> + "Hostile mobs"; + case "net.minecraft.entity.projectile", "net.minecraft.entity.projectile.thrown" -> "Projectiles"; + case "net.minecraft.entity.passive" -> "Passive mobs"; + case "net.minecraft.entity.vehicle" -> "Vehicles"; + case "net.minecraft.entity" -> "UNKNOWN"; + default -> throw new Error("Unexpected entity type: " + entityClazz.getPackageName()); + }; + } + + //Honestly, both "type" and "category" fields in the schema and examples do not contain any useful information + //Since category is optional, I will just leave it out, and for type I will assume general entity classification + //by the Entity class hierarchy (which has some weirdness too by the way) + private static String getEntityTypeForClass(Class entityClass) { + //Top-level classifications + if (WaterCreatureEntity.class.isAssignableFrom(entityClass)) { + return "water_creature"; + } + if (AnimalEntity.class.isAssignableFrom(entityClass)) { + return "animal"; + } + if (HostileEntity.class.isAssignableFrom(entityClass)) { + return "hostile"; + } + if (AmbientEntity.class.isAssignableFrom(entityClass)) { + return "ambient"; + } + + //Second level classifications. PathAwareEntity is not included because it + //doesn't really make much sense to categorize by it + if (PassiveEntity.class.isAssignableFrom(entityClass)) { + return "passive"; + } + if (MobEntity.class.isAssignableFrom(entityClass)) { + return "mob"; + } + + //Other classifications only include living entities and projectiles. everything else is categorized as other + if (LivingEntity.class.isAssignableFrom(entityClass)) { + return "living"; + } + if (ProjectileEntity.class.isAssignableFrom(entityClass)) { + return "projectile"; + } + return "other"; + } + + @Override + public String getDataName() { + return "entities"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry> entityTypeRegistry = Registry.ENTITY_TYPE; + entityTypeRegistry.forEach(entity -> resultArray.add(generateEntity(entityTypeRegistry, entity))); + return resultArray; + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java new file mode 100644 index 00000000..45a0b35f --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java @@ -0,0 +1,51 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.item.FoodComponent; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Objects; + +public class FoodsDataGenerator implements IDataGenerator { + + public static JsonObject generateFoodDescriptor(Registry registry, Item foodItem) { + JsonObject foodDesc = new JsonObject(); + Identifier registryKey = registry.getKey(foodItem).orElseThrow().getValue(); + + foodDesc.addProperty("id", registry.getRawId(foodItem)); + foodDesc.addProperty("name", registryKey.getPath()); + + foodDesc.addProperty("stackSize", foodItem.getMaxCount()); + foodDesc.addProperty("displayName", DGU.translateText(foodItem.getTranslationKey())); + + FoodComponent foodComponent = Objects.requireNonNull(foodItem.getFoodComponent()); + float foodPoints = foodComponent.getHunger(); + float saturationRatio = foodComponent.getSaturationModifier() * 2.0F; + float saturation = foodPoints * saturationRatio; + + foodDesc.addProperty("foodPoints", foodPoints); + foodDesc.addProperty("saturation", saturation); + + foodDesc.addProperty("effectiveQuality", foodPoints + saturation); + foodDesc.addProperty("saturationRatio", saturationRatio); + return foodDesc; + } + + @Override + public String getDataName() { + return "foods"; + } + + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry itemRegistry = Registry.ITEM; + itemRegistry.stream() + .filter(Item::isFood) + .forEach(food -> resultsArray.add(generateFoodDescriptor(itemRegistry, food))); + return resultsArray; + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java new file mode 100644 index 00000000..81fe4e20 --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/IDataGenerator.java @@ -0,0 +1,10 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonElement; + +public interface IDataGenerator { + + String getDataName(); + + JsonElement generateDataJson(); +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java new file mode 100644 index 00000000..e7b9b54f --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java @@ -0,0 +1,25 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.minecraft.block.enums.Instrument; + +public class InstrumentsDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "instruments"; + } + + @Override + public JsonElement generateDataJson() { + JsonArray array = new JsonArray(); + for (Instrument instrument : Instrument.values()) { + JsonObject object = new JsonObject(); + object.addProperty("id", instrument.ordinal()); + object.addProperty("name", instrument.asString()); + array.add(object); + } + return array; + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java new file mode 100644 index 00000000..b0c959dc --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java @@ -0,0 +1,81 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class ItemsDataGenerator implements IDataGenerator { + + private static List calculateItemsToRepairWith(Registry itemRegistry, Item sourceItem) { + ItemStack sourceItemStack = sourceItem.getDefaultStack(); + return itemRegistry.stream() + .filter(otherItem -> sourceItem.canRepair(sourceItemStack, otherItem.getDefaultStack())) + .collect(Collectors.toList()); + } + + private static List getApplicableEnchantmentTargets(Item sourceItem) { + return Arrays.stream(EnchantmentTarget.values()) + .filter(target -> target.isAcceptableItem(sourceItem)) + .collect(Collectors.toList()); + } + + public static JsonObject generateItem(Registry itemRegistry, Item item) { + JsonObject itemDesc = new JsonObject(); + Identifier registryKey = itemRegistry.getKey(item).orElseThrow().getValue(); + + itemDesc.addProperty("id", itemRegistry.getRawId(item)); + itemDesc.addProperty("name", registryKey.getPath()); + + itemDesc.addProperty("displayName", DGU.translateText(item.getTranslationKey())); + itemDesc.addProperty("stackSize", item.getMaxCount()); + + List enchantmentTargets = getApplicableEnchantmentTargets(item); + + JsonArray enchantCategoriesArray = new JsonArray(); + for (EnchantmentTarget target : enchantmentTargets) { + enchantCategoriesArray.add(EnchantmentsDataGenerator.getEnchantmentTargetName(target)); + } + if (enchantCategoriesArray.size() > 0) { + itemDesc.add("enchantCategories", enchantCategoriesArray); + } + + if (item.isDamageable()) { + List repairWithItems = calculateItemsToRepairWith(itemRegistry, item); + + JsonArray fixedWithArray = new JsonArray(); + for (Item repairWithItem : repairWithItems) { + Identifier repairWithName = itemRegistry.getKey(repairWithItem).orElseThrow().getValue(); + fixedWithArray.add(repairWithName.getPath()); + } + if (fixedWithArray.size() > 0) { + itemDesc.add("repairWith", fixedWithArray); + } + + int maxDurability = item.getMaxDamage(); + itemDesc.addProperty("maxDurability", maxDurability); + } + return itemDesc; + } + + @Override + public String getDataName() { + return "items"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry itemRegistry = Registry.ITEM; + itemRegistry.stream().forEach(item -> resultArray.add(generateItem(itemRegistry, item))); + return resultArray; + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java new file mode 100644 index 00000000..7a3c6ced --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +public class LanguageDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "language"; + } + + @Override + public JsonElement generateDataJson() { + try { + InputStream inputStream = Objects.requireNonNull(this.getClass().getResourceAsStream("/assets/minecraft/lang/en_us.json")); + return new Gson().fromJson(new InputStreamReader(inputStream, StandardCharsets.UTF_8), JsonObject.class); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to generate language file"); + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java new file mode 100644 index 00000000..4253ae69 --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java @@ -0,0 +1,197 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.mixin.MiningToolItemAccessor; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.Material; +import net.minecraft.item.Item; +import net.minecraft.item.Items; +import net.minecraft.item.MiningToolItem; +import net.minecraft.item.SwordItem; +import net.minecraft.tag.BlockTags; +import net.minecraft.tag.TagKey; +import net.minecraft.util.registry.Registry; + +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +//TODO entire idea of linking materials to tool speeds is obsolete and just wrong now, +//TODO but we kinda have to support it to let old code work for computing digging times, +//TODO so for now we will handle materials as "virtual" ones based on which tools can break blocks +public class MaterialsDataGenerator implements IDataGenerator { + + private static final List> COMPOSITE_MATERIALS = ImmutableList.>builder() + .add(ImmutableList.of("plant", makeMaterialNameForTag(BlockTags.AXE_MINEABLE))) + .add(ImmutableList.of("gourd", makeMaterialNameForTag(BlockTags.AXE_MINEABLE))) + .add(ImmutableList.of(makeMaterialNameForTag(BlockTags.LEAVES), makeMaterialNameForTag(BlockTags.HOE_MINEABLE))) + .add(ImmutableList.of(makeMaterialNameForTag(BlockTags.LEAVES), makeMaterialNameForTag(BlockTags.AXE_MINEABLE), makeMaterialNameForTag(BlockTags.HOE_MINEABLE))) + .add(ImmutableList.of("vine_or_glow_lichen", "plant", makeMaterialNameForTag(BlockTags.AXE_MINEABLE) + )).build(); + + private static String makeMaterialNameForTag(TagKey tag) { + return tag.id().getPath(); + } + + private static void createCompositeMaterialInfo(List allMaterials, List combinedMaterials) { + String compositeMaterialName = Joiner.on(';').join(combinedMaterials); + + List mappedMaterials = combinedMaterials.stream() + .map(otherName -> allMaterials.stream() + .filter(other -> other.getMaterialName().equals(otherName)) + .findFirst().orElseThrow(() -> new RuntimeException("Material not found with name " + otherName))) + .collect(Collectors.toList()); + + Predicate compositePredicate = blockState -> + mappedMaterials.stream().allMatch(it -> it.getPredicate().test(blockState)); + + MaterialInfo materialInfo = new MaterialInfo(compositeMaterialName, compositePredicate).includes(mappedMaterials); + allMaterials.add(0, materialInfo); + } + + private static void createCompositeMaterial(Map> allMaterials, List combinedMaterials) { + String compositeMaterialName = Joiner.on(';').join(combinedMaterials); + + Map resultingToolSpeeds = new HashMap<>(); + combinedMaterials.stream() + .map(allMaterials::get) + .forEach(resultingToolSpeeds::putAll); + allMaterials.put(compositeMaterialName, resultingToolSpeeds); + } + + public static List getGlobalMaterialInfo() { + ArrayList resultList = new ArrayList<>(); + + resultList.add(new MaterialInfo("vine_or_glow_lichen", blockState -> blockState.isOf(Blocks.VINE) || blockState.isOf(Blocks.GLOW_LICHEN))); + resultList.add(new MaterialInfo("coweb", blockState -> blockState.isOf(Blocks.COBWEB))); + + resultList.add(new MaterialInfo("leaves", blockState -> blockState.isIn(BlockTags.LEAVES))); + resultList.add(new MaterialInfo("wool", blockState -> blockState.isIn(BlockTags.WOOL))); + + resultList.add(new MaterialInfo("gourd", blockState -> blockState.getMaterial() == Material.GOURD)); + resultList.add(new MaterialInfo("plant", blockState -> blockState.getMaterial() == Material.PLANT || blockState.getMaterial() == Material.REPLACEABLE_PLANT)); + + HashSet uniqueMaterialNames = new HashSet<>(); + + Registry itemRegistry = Registry.ITEM; + itemRegistry.forEach(item -> { + if (item instanceof MiningToolItem toolItem) { + TagKey effectiveBlocks = ((MiningToolItemAccessor) toolItem).getEffectiveBlocks(); + String materialName = makeMaterialNameForTag(effectiveBlocks); + + if (!uniqueMaterialNames.contains(materialName)) { + uniqueMaterialNames.add(materialName); + resultList.add(new MaterialInfo(materialName, blockState -> blockState.isIn(effectiveBlocks))); + } + } + }); + + COMPOSITE_MATERIALS.forEach(values -> createCompositeMaterialInfo(resultList, values)); + return resultList; + } + + @Override + public String getDataName() { + return "materials"; + } + + @Override + public JsonElement generateDataJson() { + Registry itemRegistry = Registry.ITEM; + + Map> materialMiningSpeeds = new HashMap<>(); + materialMiningSpeeds.put("default", ImmutableMap.of()); + + //Special materials used for shears and swords special mining speed logic + Map leavesMaterialSpeeds = new HashMap<>(); + Map cowebMaterialSpeeds = new HashMap<>(); + Map plantMaterialSpeeds = new HashMap<>(); + Map gourdMaterialSpeeds = new HashMap<>(); + + materialMiningSpeeds.put(makeMaterialNameForTag(BlockTags.LEAVES), leavesMaterialSpeeds); + materialMiningSpeeds.put("coweb", cowebMaterialSpeeds); + materialMiningSpeeds.put("plant", plantMaterialSpeeds); + materialMiningSpeeds.put("gourd", gourdMaterialSpeeds); + + //Shears need special handling because they do not follow normal rules like tools + leavesMaterialSpeeds.put(Items.SHEARS, 15.0f); + cowebMaterialSpeeds.put(Items.SHEARS, 15.0f); + materialMiningSpeeds.put("vine_or_glow_lichen", ImmutableMap.of(Items.SHEARS, 2.0f)); + materialMiningSpeeds.put("wool", ImmutableMap.of(Items.SHEARS, 5.0f)); + + itemRegistry.forEach(item -> { + //Tools are handled rather easily and do not require anything else + if (item instanceof MiningToolItem toolItem) { + TagKey effectiveBlocks = ((MiningToolItemAccessor) toolItem).getEffectiveBlocks(); + String materialName = makeMaterialNameForTag(effectiveBlocks); + + Map materialSpeeds = materialMiningSpeeds.computeIfAbsent(materialName, k -> new HashMap<>()); + float miningSpeed = ((MiningToolItemAccessor) toolItem).getMiningSpeed(); + materialSpeeds.put(item, miningSpeed); + } + + //Swords require special treatment + if (item instanceof SwordItem) { + cowebMaterialSpeeds.put(item, 15.0f); + plantMaterialSpeeds.put(item, 1.5f); + leavesMaterialSpeeds.put(item, 1.5f); + gourdMaterialSpeeds.put(item, 1.5f); + } + }); + + COMPOSITE_MATERIALS.forEach(values -> createCompositeMaterial(materialMiningSpeeds, values)); + + JsonObject resultObject = new JsonObject(); + + for (var entry : materialMiningSpeeds.entrySet()) { + JsonObject toolSpeedsObject = new JsonObject(); + + for (var toolEntry : entry.getValue().entrySet()) { + int rawItemId = itemRegistry.getRawId(toolEntry.getKey()); + toolSpeedsObject.addProperty(Integer.toString(rawItemId), toolEntry.getValue()); + } + resultObject.add(entry.getKey(), toolSpeedsObject); + } + + return resultObject; + } + + public static class MaterialInfo { + private final String materialName; + private final Predicate predicate; + private final List includedMaterials = new ArrayList<>(); + + public MaterialInfo(String materialName, Predicate predicate) { + this.materialName = materialName; + this.predicate = predicate; + } + + protected MaterialInfo includes(List otherMaterials) { + this.includedMaterials.addAll(otherMaterials); + return this; + } + + public String getMaterialName() { + return materialName; + } + + public Predicate getPredicate() { + return predicate; + } + + public boolean includesMaterial(MaterialInfo materialInfo) { + return includedMaterials.contains(materialInfo); + } + + @Override + public String toString() { + return materialName; + } + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java new file mode 100644 index 00000000..5a6b1081 --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java @@ -0,0 +1,32 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.minecraft.particle.ParticleType; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +public class ParticlesDataGenerator implements IDataGenerator { + + public static JsonObject generateParticleType(Registry> registry, ParticleType particleType) { + JsonObject effectDesc = new JsonObject(); + Identifier registryKey = registry.getKey(particleType).orElseThrow().getValue(); + + effectDesc.addProperty("id", registry.getRawId(particleType)); + effectDesc.addProperty("name", registryKey.getPath()); + return effectDesc; + } + + @Override + public String getDataName() { + return "particles"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry> particleTypeRegistry = Registry.PARTICLE_TYPE; + particleTypeRegistry.forEach(particleType -> resultsArray.add(generateParticleType(particleTypeRegistry, particleType))); + return resultsArray; + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java new file mode 100644 index 00000000..389bcc4e --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java @@ -0,0 +1,132 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.item.Item; +import net.minecraft.recipe.Ingredient; +import net.minecraft.recipe.Recipe; +import net.minecraft.recipe.ShapedRecipe; +import net.minecraft.recipe.ShapelessRecipe; +import net.minecraft.util.registry.Registry; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class RecipeDataGenerator implements IDataGenerator { + + private static int getRawIdFor(Item item) { + return Registry.ITEM.getRawId(item); + } + + @Override + public String getDataName() { + return "recipes"; + } + + @Override + public JsonElement generateDataJson() { + JsonObject finalObj = new JsonObject(); + Multimap recipes = ArrayListMultimap.create(); + for (Recipe recipe : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) { + if (recipe instanceof ShapedRecipe sr) { + var ingredients = sr.getIngredients(); + List ingr = new ArrayList<>(); + for (int i = 0; i < 9; i++) { + if (i >= ingredients.size()) { + ingr.add(-1); + continue; + } + var stacks = ingredients.get(i); + var matching = stacks.getMatchingStacks(); + if (matching.length == 0) { + ingr.add(-1); + } else { + ingr.add(getRawIdFor(matching[0].getItem())); + } + } + Lists.reverse(ingr); + + JsonArray inShape = new JsonArray(); + + var iter = ingr.iterator(); + for (int y = 0; y < 3; y++) { + var jsonRow = new JsonArray(); + int one = iter.next(); + int two = iter.next(); + int three = iter.next(); + if (y > 0 && one == -1 && two == -1 && three == -1) continue; + jsonRow.add(one); + jsonRow.add(two); + jsonRow.add(three); + inShape.add(jsonRow); + } + + JsonObject finalRecipe = new JsonObject(); + finalRecipe.add("inShape", inShape); + + var resultObject = new JsonObject(); + resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); + resultObject.addProperty("count", sr.getOutput().getCount()); + finalRecipe.add("result", resultObject); + + String id = ((Integer) getRawIdFor(sr.getOutput().getItem())).toString(); + + if (!finalObj.has(id)) { + finalObj.add(id, new JsonArray()); + } + finalObj.get(id).getAsJsonArray().add(finalRecipe); +// var input = new JsonArray(); +// var ingredients = sr.getIngredients().stream().toList(); +// for (int y = 0; y < sr.getHeight(); y++) { +// var arr = new JsonArray(); +// for (int x = 0; x < sr.getWidth(); x++) { +// if ((y*3)+x >= ingredients.size()) { +// arr.add(JsonNull.INSTANCE); +// continue; +// } +// var ingredient = ingredients.get((y*3)+x).getMatchingStacks(); // FIXME: fix when there are more than one matching stack +// if (ingredient.length == 0) { +// arr.add(JsonNull.INSTANCE); +// } else { +// arr.add(getRawIdFor(ingredient[0].getItem())); +// } +// } +// input.add(arr); +// } +// var rootRecipeObject = new JsonObject(); +// rootRecipeObject.add("inShape", input); +// var resultObject = new JsonObject(); +// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem())); +// resultObject.addProperty("count", sr.getOutput().getCount()); +// rootRecipeObject.add("result", resultObject); +// recipes.put(getRawIdFor(sr.getOutput().getItem()), rootRecipeObject); + } else if (recipe instanceof ShapelessRecipe sl) { + var ingredients = new JsonArray(); + for (Ingredient ingredient : sl.getIngredients()) { + if (ingredient.isEmpty()) continue; + ingredients.add(getRawIdFor(ingredient.getMatchingStacks()[0].getItem())); + } + var rootRecipeObject = new JsonObject(); + rootRecipeObject.add("ingredients", ingredients); + var resultObject = new JsonObject(); + resultObject.addProperty("id", getRawIdFor(sl.getOutput().getItem())); + resultObject.addProperty("count", sl.getOutput().getCount()); + rootRecipeObject.add("result", resultObject); + recipes.put(getRawIdFor(sl.getOutput().getItem()), rootRecipeObject); + } + } + recipes.forEach((a, b) -> { + if (!finalObj.has(a.toString())) { + finalObj.add(a.toString(), new JsonArray()); + } + finalObj.get(a.toString()).getAsJsonArray().add(b); + }); + return finalObj; + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java new file mode 100644 index 00000000..283cc437 --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java @@ -0,0 +1,168 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.EmptyRenderBlockView; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.block.RedstoneWireBlock; +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.client.color.world.FoliageColors; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; + +import java.util.*; + +public class TintsDataGenerator implements IDataGenerator { + + public static BiomeTintColors generateBiomeTintColors(Registry biomeRegistry) { + BiomeTintColors colors = new BiomeTintColors(); + + biomeRegistry.forEach(biome -> { + int biomeGrassColor = biome.getGrassColorAt(0.0, 0.0); + int biomeFoliageColor = biome.getFoliageColor(); + int biomeWaterColor = biome.getWaterColor(); + + colors.grassColoursMap.computeIfAbsent(biomeGrassColor, k -> new ArrayList<>()).add(biome); + colors.foliageColoursMap.computeIfAbsent(biomeFoliageColor, k -> new ArrayList<>()).add(biome); + colors.waterColourMap.computeIfAbsent(biomeWaterColor, k -> new ArrayList<>()).add(biome); + }); + return colors; + } + + public static Map generateRedstoneTintColors() { + Map resultColors = new HashMap<>(); + + for (int redstoneLevel : RedstoneWireBlock.POWER.getValues()) { + int color = RedstoneWireBlock.getWireColor(redstoneLevel); + resultColors.put(redstoneLevel, color); + } + return resultColors; + } + + @Environment(EnvType.CLIENT) + private static int getBlockColor(Block block, BlockColors blockColors) { + return blockColors.getColor(block.getDefaultState(), EmptyRenderBlockView.INSTANCE, BlockPos.ORIGIN, 0xFFFFFF); + } + + @Environment(EnvType.CLIENT) + public static Map generateConstantTintColors() { + Map resultColors = new HashMap<>(); + BlockColors blockColors = BlockColors.create(); + + resultColors.put(Blocks.BIRCH_LEAVES, FoliageColors.getBirchColor()); + resultColors.put(Blocks.SPRUCE_LEAVES, FoliageColors.getSpruceColor()); + + resultColors.put(Blocks.LILY_PAD, getBlockColor(Blocks.LILY_PAD, blockColors)); + resultColors.put(Blocks.ATTACHED_MELON_STEM, getBlockColor(Blocks.ATTACHED_MELON_STEM, blockColors)); + resultColors.put(Blocks.ATTACHED_PUMPKIN_STEM, getBlockColor(Blocks.ATTACHED_PUMPKIN_STEM, blockColors)); + + //not really constant, depend on the block age, but kinda have to be handled since textures are literally white without them + resultColors.put(Blocks.MELON_STEM, getBlockColor(Blocks.MELON_STEM, blockColors)); + resultColors.put(Blocks.PUMPKIN_STEM, getBlockColor(Blocks.PUMPKIN_STEM, blockColors)); + + return resultColors; + } + + private static JsonObject encodeBiomeColorMap(Registry biomeRegistry, Map> colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + for (Biome biome : entry.getValue()) { + Identifier registryKey = biomeRegistry.getKey(biome).orElseThrow().getValue(); + keysArray.add(registryKey.getPath()); + } + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getKey()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeRedstoneColorMap(Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + keysArray.add(entry.getKey()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + private static JsonObject encodeBlocksColorMap(Registry blockRegistry, Map colorsMap) { + JsonArray resultColorsArray = new JsonArray(); + for (var entry : colorsMap.entrySet()) { + JsonObject entryObject = new JsonObject(); + + JsonArray keysArray = new JsonArray(); + Identifier registryKey = blockRegistry.getKey(entry.getKey()).orElseThrow().getValue(); + keysArray.add(registryKey.getPath()); + + entryObject.add("keys", keysArray); + entryObject.addProperty("color", entry.getValue()); + resultColorsArray.add(entryObject); + } + + JsonObject resultObject = new JsonObject(); + resultObject.add("data", resultColorsArray); + return resultObject; + } + + @Override + public String getDataName() { + return "tints"; + } + + @Override + public JsonObject generateDataJson() { + DynamicRegistryManager registryManager = DynamicRegistryManager.BUILTIN.get(); + Registry biomeRegistry = registryManager.get(Registry.BIOME_KEY); + Registry blockRegistry = registryManager.get(Registry.BLOCK_KEY); + + BiomeTintColors biomeTintColors = generateBiomeTintColors(biomeRegistry); + Map redstoneColors = generateRedstoneTintColors(); + Map constantTintColors = Collections.emptyMap(); + + EnvType currentEnvironment = FabricLoader.getInstance().getEnvironmentType(); + if (currentEnvironment == EnvType.CLIENT) { + constantTintColors = generateConstantTintColors(); + } + + JsonObject resultObject = new JsonObject(); + + resultObject.add("grass", encodeBiomeColorMap(biomeRegistry, biomeTintColors.grassColoursMap)); + resultObject.add("foliage", encodeBiomeColorMap(biomeRegistry, biomeTintColors.foliageColoursMap)); + resultObject.add("water", encodeBiomeColorMap(biomeRegistry, biomeTintColors.waterColourMap)); + + resultObject.add("redstone", encodeRedstoneColorMap(redstoneColors)); + resultObject.add("constant", encodeBlocksColorMap(blockRegistry, constantTintColors)); + + return resultObject; + } + + public static class BiomeTintColors { + final Map> grassColoursMap = new HashMap<>(); + final Map> foliageColoursMap = new HashMap<>(); + final Map> waterColourMap = new HashMap<>(); + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ClientPlayerInteractionManagerMixin.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ClientPlayerInteractionManagerMixin.java new file mode 100644 index 00000000..40e926af --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ClientPlayerInteractionManagerMixin.java @@ -0,0 +1,57 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import com.google.common.base.Preconditions; +import net.minecraft.block.BlockState; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerInteractionManager; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.World; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +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.CallbackInfoReturnable; + +@Mixin(ClientPlayerInteractionManager.class) +public abstract class ClientPlayerInteractionManagerMixin { + @Unique + private long blockBreakingStartTicks; + @Unique + private BlockState stateBeingBroken; + + @Inject(method = "attackBlock", at = @At(value = "FIELD", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;currentBreakingProgress:F", opcode = Opcodes.PUTFIELD)) + private void onBlockAttacked(BlockPos pos, Direction direction, CallbackInfoReturnable callbackInfo) { + World world = Preconditions.checkNotNull(MinecraftClient.getInstance().world); + this.blockBreakingStartTicks = world.getTime(); + } + + @Inject(method = "cancelBlockBreaking", at = @At("HEAD")) + private void onBlockBreakingCanceled(CallbackInfo callbackInfo) { + this.blockBreakingStartTicks = -1L; + } + + @Inject(method = "breakBlock", at = @At("HEAD")) + private void onBreakBlockHead(BlockPos pos, CallbackInfoReturnable callbackInfo) { + World world = Preconditions.checkNotNull(MinecraftClient.getInstance().world); + this.stateBeingBroken = world.getBlockState(pos); + } + + @Inject(method = "breakBlock", at = @At("RETURN")) + private void onBreakBlockReturn(BlockPos pos, CallbackInfoReturnable callbackInfo) { + World world = Preconditions.checkNotNull(MinecraftClient.getInstance().world); + if (callbackInfo.getReturnValue()) { + Preconditions.checkNotNull(stateBeingBroken); + if (blockBreakingStartTicks != -1L) { + long totalTicksElapsed = world.getTime() - this.blockBreakingStartTicks; + long totalTimeElapsed = totalTicksElapsed * 50L; + System.out.printf("Breaking block %s took %dms%n", stateBeingBroken.getBlock(), totalTimeElapsed); + } else { + System.out.printf("Breaking block %s took 0ms [INSTANT BREAK]%n", stateBeingBroken.getBlock()); + } + } + this.blockBreakingStartTicks = -1L; + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java new file mode 100644 index 00000000..0d297e7a --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.server.dedicated.EulaReader; +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; + +@Mixin(EulaReader.class) +public class EULAMixin { + @Inject(method = "isEulaAgreedTo()Z", at = @At("TAIL"), cancellable = true) + public void init(CallbackInfoReturnable cir) { + cir.setReturnValue(true); + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java new file mode 100644 index 00000000..cb7f8a1c --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/MiningToolItemAccessor.java @@ -0,0 +1,17 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.block.Block; +import net.minecraft.item.MiningToolItem; +import net.minecraft.tag.TagKey; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(MiningToolItem.class) +public interface MiningToolItemAccessor { + + @Accessor + TagKey getEffectiveBlocks(); + + @Accessor + float getMiningSpeed(); +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java new file mode 100644 index 00000000..6d9415eb --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java @@ -0,0 +1,27 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import dev.u9g.minecraftdatagenerator.Main; +import dev.u9g.minecraftdatagenerator.generators.DataGenerators; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.MinecraftVersion; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; +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.nio.file.Path; + +@Mixin(MinecraftDedicatedServer.class) +public class ReadyMixin { + @Inject(method = "setupServer()Z", at = @At("TAIL")) + private void init(CallbackInfoReturnable cir) { + Main.LOGGER.info("Starting data generation!"); + String versionName = MinecraftVersion.CURRENT.getName(); + Path serverRootDirectory = DGU.getCurrentlyRunningServer().getRunDirectory().toPath().toAbsolutePath(); + Path dataDumpDirectory = serverRootDirectory.resolve("minecraft-data").resolve(versionName); + DataGenerators.runDataGenerators(dataDumpDirectory); + Main.LOGGER.info("Done data generation!"); + DGU.getCurrentlyRunningServer().stop(false); + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/TheEndBiomeDataAccessor.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/TheEndBiomeDataAccessor.java new file mode 100644 index 00000000..4a75ab37 --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/mixin/TheEndBiomeDataAccessor.java @@ -0,0 +1,28 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.fabricmc.fabric.impl.biome.TheEndBiomeData; +import net.fabricmc.fabric.impl.biome.WeightedPicker; +import net.minecraft.util.registry.RegistryKey; +import net.minecraft.world.biome.Biome; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(TheEndBiomeData.class) +public interface TheEndBiomeDataAccessor { + @Accessor("END_BIOMES_MAP") + static Map, WeightedPicker>> END_BIOMES_MAP() { + throw new IllegalStateException("Should never be called."); + } + + @Accessor("END_MIDLANDS_MAP") + static Map, WeightedPicker>> END_MIDLANDS_MAP() { + throw new IllegalStateException("Should never be called."); + } + + @Accessor("END_BARRENS_MAP") + static Map, WeightedPicker>> END_BARRENS_MAP() { + throw new IllegalStateException("Should never be called."); + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java new file mode 100644 index 00000000..5945fa02 --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java @@ -0,0 +1,60 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Language; +import net.minecraft.world.World; + +public class DGU { + + private static final Language language = Language.getInstance(); + + @Environment(EnvType.CLIENT) + private static MinecraftServer getCurrentlyRunningServerClient() { + return MinecraftClient.getInstance().getServer(); + } + + @SuppressWarnings("deprecation") + private static MinecraftServer getCurrentlyRunningServerDedicated() { + return (MinecraftServer) FabricLoader.getInstance().getGameInstance(); + } + + public static MinecraftServer getCurrentlyRunningServer() { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return getCurrentlyRunningServerClient(); + } else if (environmentType == EnvType.SERVER) { + return getCurrentlyRunningServerDedicated(); + } + throw new UnsupportedOperationException(); + } + + @Environment(EnvType.CLIENT) + private static String translateTextClient(String translationKey) { + return I18n.translate(translationKey); + } + + private static String translateTextFallback(String translationKey) { + try { + return language.get(translationKey); + } catch (Exception ignored) { + } + throw new RuntimeException("Failed to translate: '" + translationKey + "'"); + } + + public static String translateText(String translationKey) { + EnvType environmentType = FabricLoader.getInstance().getEnvironmentType(); + if (environmentType == EnvType.CLIENT) { + return translateTextClient(translationKey); + } + return translateTextFallback(translationKey); + } + + public static World getWorld() { + return getCurrentlyRunningServer().getOverworld(); + } +} diff --git a/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java new file mode 100644 index 00000000..1cff92d4 --- /dev/null +++ b/22w19a/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java @@ -0,0 +1,73 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.fluid.FluidState; +import net.minecraft.fluid.Fluids; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.BlockRenderView; +import net.minecraft.world.LightType; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.BiomeKeys; +import net.minecraft.world.chunk.light.LightingProvider; +import net.minecraft.world.level.ColorResolver; +import org.jetbrains.annotations.Nullable; + +public enum EmptyRenderBlockView implements BlockRenderView { + INSTANCE; + + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + return null; + } + + public BlockState getBlockState(BlockPos pos) { + return Blocks.AIR.getDefaultState(); + } + + public FluidState getFluidState(BlockPos pos) { + return Fluids.EMPTY.getDefaultState(); + } + + public int getBottomY() { + return 0; + } + + public int getHeight() { + return 0; + } + + + @Override + public float getBrightness(Direction direction, boolean shaded) { + return 0.0f; + } + + @Override + public LightingProvider getLightingProvider() { + return null; + } + + @Override + public int getColor(BlockPos pos, ColorResolver colorResolver) { + DynamicRegistryManager registryManager = DynamicRegistryManager.BUILTIN.get(); + Registry biomeRegistry = registryManager.get(Registry.BIOME_KEY); + Biome plainsBiome = biomeRegistry.get(BiomeKeys.PLAINS); + + return colorResolver.getColor(plainsBiome, pos.getX(), pos.getY()); + } + + @Override + public int getLightLevel(LightType type, BlockPos pos) { + return type == LightType.SKY ? getMaxLightLevel() : 0; + } + + @Override + public int getBaseLightLevel(BlockPos pos, int ambientDarkness) { + return ambientDarkness; + } +} diff --git a/22w19a/src/main/resources/fabric.mod.json b/22w19a/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..9f6ef321 --- /dev/null +++ b/22w19a/src/main/resources/fabric.mod.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "id": "minecraft-data-generator", + "version": "${version}", + "name": "Minecraft Data Generator", + "description": "", + "authors": [ + "Archengius", + "U9G" + ], + "contact": {}, + "license": "MIT", + "environment": "server", + "entrypoints": { + "main": [ + "dev.u9g.minecraftdatagenerator.Main" + ] + }, + "mixins": [ + "minecraft-data-generator.mixins.json" + ], + "depends": { + "fabricloader": ">=0.14.5", + "fabric": "*", + "minecraft": ">=1.18" + } +} diff --git a/22w19a/src/main/resources/minecraft-data-generator.mixins.json b/22w19a/src/main/resources/minecraft-data-generator.mixins.json new file mode 100644 index 00000000..9e8d4163 --- /dev/null +++ b/22w19a/src/main/resources/minecraft-data-generator.mixins.json @@ -0,0 +1,20 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "dev.u9g.minecraftdatagenerator.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "ClientPlayerInteractionManagerMixin", + "MiningToolItemAccessor", + "ReadyMixin", + "TheEndBiomeDataAccessor" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "server": [ + "EULAMixin" + ] +} diff --git a/build.gradle b/build.gradle index 476f9ecc..c638bd14 100644 --- a/build.gradle +++ b/build.gradle @@ -1,92 +1,4 @@ -plugins { - id 'fabric-loom' version '1.0-SNAPSHOT' - id 'maven-publish' +allprojects { + version = "1.0.0-SNAPSHOT" + group = "dev.u9g" } - -version = project.mod_version -group = project.maven_group - -repositories { - // Add repositories to retrieve artifacts from in here. - // You should only use this when depending on other mods because - // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. - // See https://docs.gradle.org/current/userguide/declaring_repositories.html - // for more information about repositories. -} - -dependencies { - // To change the versions see the gradle.properties file - minecraft "com.mojang:minecraft:${project.minecraft_version}" - mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" - modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" - - // Fabric API. This is technically optional, but you probably want it anyway. - modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" -} - -processResources { - inputs.property "version", project.version - filteringCharset "UTF-8" - - filesMatching("fabric.mod.json") { - expand "version": project.version - } -} - -def targetJavaVersion = 17 -tasks.withType(JavaCompile).configureEach { - // ensure that the encoding is set to UTF-8, no matter what the system default is - // this fixes some edge cases with special characters not displaying correctly - // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html - // If Javadoc is generated, this must be specified in that task too. - it.options.encoding = "UTF-8" - if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) { - it.options.release = targetJavaVersion - } -} - -java { - def javaVersion = JavaVersion.toVersion(targetJavaVersion) - if (JavaVersion.current() < javaVersion) { - toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) - } - archivesBaseName = project.archives_base_name - // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task - // if it is present. - // If you remove this line, sources will not be generated. - withSourcesJar() -} - -jar { - from("LICENSE") { - rename { "${it}_${project.archivesBaseName}"} - } -} - -// configure the maven publication -publishing { - publications { - mavenJava(MavenPublication) { - from components.java - } - } - - // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. - repositories { - // Add repositories to publish to here. - // Notice: This block does NOT have the same function as the block in the top level. - // The repositories here will be used for publishing your artifact, not for - // retrieving dependencies. - } -} - -loom { - splitEnvironmentSourceSets() - - mods { - modid { - sourceSet sourceSets.main -// sourceSet sourceSets.client - } - } -} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 81fa9bf5..bde2fd14 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,14 +1,11 @@ # Done to increase the memory available to gradle. -org.gradle.jvmargs=-Xmx1G -# Fabric Properties -# check these on https://modmuss50.me/fabric.html -minecraft_version=1.20.2 -yarn_mappings=1.20.2+build.4 -loader_version=0.14.24 -# Mod Properties -mod_version=1.0-SNAPSHOT -maven_group=dev.u9g -archives_base_name=minecraft-data-generator-server -# Dependencies -# check this on https://modmuss50.me/fabric.html -fabric_version=0.90.4+1.20.2 +org.gradle.jvmargs=-Xmx4G -Xms256m -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +org.gradle.daemon=true + +org.gradle.configureondemand=true +org.gradle.cache=true +org.gradle.caching=true +org.gradle.parallel=true + +org.gradle.vfs.watch=true diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f398c33c..3499ded5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle b/settings.gradle index f91a4fe7..8551948a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,4 +6,44 @@ pluginManagement { } gradlePluginPortal() } + plugins { + id 'fabric-loom' version "1.4.5" + } +} + +dependencyResolutionManagement { + repositories { + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + maven { + name = "legacy-fabric" + url = "https://maven.legacyfabric.net" + } + maven { + name = "u9g" + url = "https://maven.u9g.dev" + } + mavenCentral() + } } + +[ + "1.7", + "1.8.9", + "1.9.4", + "1.10.2", + "1.11.2", + "1.12.2", + "1.13", + "1.14", + "1.15", + "1.16", + "1.17", + "1.18", + "1.19", + "1.19.2", + "22w19a", + "1.20", +].forEach { include it }