diff --git a/mc/1.21.11/build.gradle b/mc/1.21.11/build.gradle new file mode 100644 index 00000000..81db180f --- /dev/null +++ b/mc/1.21.11/build.gradle @@ -0,0 +1,25 @@ +import xyz.wagyourtail.unimined.internal.minecraft.MinecraftProvider + +plugins { + id 'dg-mc-conventions' +} + +unimined.minecraft { + version "1.21.11" + + mappings { + mojmap() + + devFallbackNamespace "mojmap" + } + + customPatcher(new CustomOfficialFabricMinecraftTransformer(project, delegate as MinecraftProvider)) { + it.loader libs.versions.fabric.loader.get() + } + + runs.config("server") { + javaVersion = JavaVersion.VERSION_21 + } + + defaultRemapJar = true +} diff --git a/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java new file mode 100644 index 00000000..ce22714a --- /dev/null +++ b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/AttributesDataGenerator.java @@ -0,0 +1,38 @@ +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 net.minecraft.core.registries.Registries; +import net.minecraft.world.entity.ai.attributes.Attribute; +import net.minecraft.world.entity.ai.attributes.RangedAttribute; + +import java.util.Objects; + +public class AttributesDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "attributes"; + } + + @Override + public JsonElement generateDataJson() { + JsonArray arr = new JsonArray(); + var registry = DGU.getWorld().registryAccess().lookupOrThrow(Registries.ATTRIBUTE); + for (Attribute attribute : registry) { + JsonObject obj = new JsonObject(); + String name = Objects.requireNonNull(registry.getKey(attribute)).getPath(); + while(name.contains("_")) { + name = name.replaceFirst("_[a-z]", String.valueOf(Character.toUpperCase(name.charAt(name.indexOf("_") + 1)))); + } + obj.addProperty("name", name); + obj.addProperty("resource", Objects.requireNonNull(registry.getKey(attribute)).toString()); + obj.addProperty("min", ((RangedAttribute) attribute).getMinValue()); + obj.addProperty("max", ((RangedAttribute) attribute).getMaxValue()); + obj.addProperty("default", attribute.getDefaultValue()); + arr.add(obj); + } + return arr; + } +} diff --git a/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.java new file mode 100644 index 00000000..150ffe86 --- /dev/null +++ b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/BiomesDataGenerator.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.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.BiomeTags; +import net.minecraft.world.level.biome.Biome; + +public class BiomesDataGenerator implements IDataGenerator { + private static String guessBiomeDimensionFromCategory(Biome biome) { + var biomeRegistry = DGU.getWorld().registryAccess().lookupOrThrow(Registries.BIOME); + if (biomeRegistry.wrapAsHolder(biome).is(BiomeTags.IS_NETHER)) { + return "nether"; + } else if (biomeRegistry.wrapAsHolder(biome).is(BiomeTags.IS_END)) { + return "end"; + } else { + 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"; + } + } + + public static JsonObject generateBiomeInfo(Registry registry, Biome biome) { + JsonObject biomeDesc = new JsonObject(); + ResourceLocation registryKey = registry.getKey(biome); + String localizationKey = String.format("biome.%s.%s", registryKey.getNamespace(), registryKey.getPath()); + String name = registryKey.getPath(); + biomeDesc.addProperty("id", registry.getId(biome)); + biomeDesc.addProperty("name", name); + String dimension = guessBiomeDimensionFromCategory(biome); + biomeDesc.addProperty("category", guessCategoryBasedOnName(name, dimension)); + biomeDesc.addProperty("temperature", biome.getBaseTemperature()); + //biomeDesc.addProperty("precipitation", biome.getPrecipitation().getName());// - removed in 1.19.4 + biomeDesc.addProperty("has_precipitation", biome.hasPrecipitation()); + //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", biome.getSkyColor()); + //biomeDesc.addProperty("rainfall", biome.getDownfall());// - removed in 1.19.4 + + return biomeDesc; + } + + @Override + public String getDataName() { + return "biomes"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray biomesArray = new JsonArray(); + RegistryAccess registryManager = DGU.getWorld().registryAccess(); + Registry biomeRegistry = registryManager.lookupOrThrow(Registries.BIOME); + + biomeRegistry.stream() + .map(biome -> generateBiomeInfo(biomeRegistry, biome)) + .forEach(biomesArray::add); + return biomesArray; + } +} diff --git a/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java new file mode 100644 index 00000000..96293756 --- /dev/null +++ b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlockCollisionShapesDataGenerator.java @@ -0,0 +1,110 @@ +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 net.minecraft.core.BlockPos; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.EmptyBlockGetter; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.shapes.VoxelShape; + +import java.util.*; + +public class BlockCollisionShapesDataGenerator implements IDataGenerator { + + @Override + public String getDataName() { + return "blockCollisionShapes"; + } + + @Override + public JsonObject generateDataJson() { + Registry blockRegistry = DGU.getWorld().registryAccess().lookupOrThrow(Registries.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 LinkedHashMap<>(); + public final Map> blockCollisionShapes = new LinkedHashMap<>(); + private int lastCollisionShapeId = 0; + + public void processBlock(Block block) { + List blockStates = block.getStateDefinition().getPossibleStates(); + List blockCollisionShapes = new ArrayList<>(); + + for (BlockState blockState : blockStates) { + VoxelShape blockShape = blockState.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO); + 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.getFirst()); + } else { + blockCollision = new JsonArray(); + for (int collisionId : blockCollisions) { + ((JsonArray) blockCollision).add(collisionId); + } + } + + ResourceLocation registryKey = blockRegistry.getKey(entry.getKey()); + 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().forAllBoxes((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/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java new file mode 100644 index 00000000..787fe432 --- /dev/null +++ b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/BlocksDataGenerator.java @@ -0,0 +1,201 @@ +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.core.BlockPos; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.EmptyBlockGetter; +import net.minecraft.world.level.block.AirBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.block.state.properties.EnumProperty; +import net.minecraft.world.level.block.state.properties.IntegerProperty; +import net.minecraft.world.level.block.state.properties.Property; +import net.minecraft.world.level.storage.loot.LootParams; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.VoxelShape; +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 DGU.getWorld().registryAccess().lookupOrThrow(Registries.ITEM).stream() + .filter(item -> item.getDefaultInstance().isCorrectToolForDrops(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 + ServerLevel serverWorld = minecraftServer.overworld(); + LootParams.Builder lootContextParameterSet = new LootParams.Builder(serverWorld) + .withParameter(LootContextParams.BLOCK_STATE, blockState) + .withParameter(LootContextParams.ORIGIN, Vec3.ZERO) + .withParameter(LootContextParams.TOOL, firstToolItem.getDefaultInstance()); + outDrops.addAll(blockState.getDrops(lootContextParameterSet)); + } 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.getDefaultInstance()); + } + } + } + + 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.getPossibleValues(); + + 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.getName(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.getFirst(); + 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.getFirst().getMaterialName(); + } + + public static JsonObject generateBlock(List materials, Block block) { + JsonObject blockDesc = new JsonObject(); + Registry blockRegistry = DGU.getWorld().registryAccess().lookupOrThrow(Registries.BLOCK); + + List blockStates = block.getStateDefinition().getPossibleStates(); + BlockState defaultState = block.defaultBlockState(); + ResourceLocation registryKey = blockRegistry.getKey(block); + String localizationKey = block.getDescriptionId(); + List effectiveTools = getItemsEffectiveForBlock(defaultState); + + blockDesc.addProperty("id", blockRegistry.getId(block)); + blockDesc.addProperty("name", registryKey.getPath()); + blockDesc.addProperty("displayName", DGU.translateText(localizationKey)); + + blockDesc.addProperty("hardness", block.defaultDestroyTime()); + blockDesc.addProperty("resistance", block.getExplosionResistance()); + blockDesc.addProperty("stackSize", block.asItem().getDefaultMaxStackSize()); + blockDesc.addProperty("diggable", block.defaultDestroyTime() != -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.canOcclude()); + blockDesc.addProperty("emitLight", defaultState.getLightEmission()); + blockDesc.addProperty("filterLight", defaultState.getLightBlock()); + + blockDesc.addProperty("defaultState", Block.getId(defaultState)); + blockDesc.addProperty("minStateId", Block.getId(blockStates.getFirst())); + blockDesc.addProperty("maxStateId", Block.getId(blockStates.getLast())); + + JsonArray stateProperties = new JsonArray(); + for (Property property : block.getStateDefinition().getProperties()) { + stateProperties.add(generateStateProperty(property)); + } + blockDesc.add("states", stateProperties); + + //Only add harvest tools if tool is required for harvesting this block + if (defaultState.requiresCorrectToolForDrops()) { + Registry itemRegistry = DGU.getWorld().registryAccess().lookupOrThrow(Registries.ITEM); + JsonObject effectiveToolsObject = new JsonObject(); + for (Item effectiveItem : effectiveTools) { + effectiveToolsObject.addProperty(Integer.toString(itemRegistry.getId(effectiveItem)), true); + } + blockDesc.add("harvestTools", effectiveToolsObject); + } + + List actualBlockDrops = new ArrayList<>(); + populateDropsIfPossible(defaultState, effectiveTools.isEmpty() ? Items.AIR : effectiveTools.getFirst(), actualBlockDrops); + + JsonArray dropsArray = new JsonArray(); + Registry itemRegistry = DGU.getWorld().registryAccess().lookupOrThrow(Registries.ITEM); + for (ItemStack dropStack : actualBlockDrops) { + dropsArray.add(itemRegistry.getId(dropStack.getItem())); + } + blockDesc.add("drops", dropsArray); + + VoxelShape blockCollisionShape = defaultState.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO); + blockDesc.addProperty("boundingBox", blockCollisionShape.isEmpty() ? "empty" : "block"); + + return blockDesc; + } + + @Override + public String getDataName() { + return "blocks"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultBlocksArray = new JsonArray(); + List availableMaterials = MaterialsDataGenerator.getGlobalMaterialInfo(); + Registry blockRegistry = DGU.getWorld().registryAccess().lookupOrThrow(Registries.BLOCK); + + blockRegistry.forEach(block -> resultBlocksArray.add(generateBlock(availableMaterials, block))); + return resultBlocksArray; + } +} diff --git a/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/EffectsDataGenerator.java new file mode 100644 index 00000000..4ed06c31 --- /dev/null +++ b/mc/1.21.11/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.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffects; +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, MobEffect mobEffect) { + JsonObject effectDesc = new JsonObject(); + ResourceLocation registryKey = registry.getKey(mobEffect); + + effectDesc.addProperty("id", registry.getId(mobEffect)); + if (mobEffect == MobEffects.UNLUCK.value()) { + 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(mobEffect.getDescriptionId())); + } + + effectDesc.addProperty("type", mobEffect.isBeneficial() ? "good" : "bad"); + return effectDesc; + } + + @Override + public String getDataName() { + return "effects"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry mobEffectRegistry = DGU.getWorld().registryAccess().lookupOrThrow(Registries.MOB_EFFECT); + mobEffectRegistry.forEach(effect -> resultsArray.add(generateEffect(mobEffectRegistry, effect))); + return resultsArray; + } +} diff --git a/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java new file mode 100644 index 00000000..1391c856 --- /dev/null +++ b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/EnchantmentsDataGenerator.java @@ -0,0 +1,105 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.enchantment.Enchantment; + +import java.util.List; + +public class EnchantmentsDataGenerator implements IDataGenerator { + public static String getEnchantmentTargetName(HolderSet target) { + TagKey tagKey = target.unwrapKey().orElseThrow(); + return tagKey.location().getPath().split("/")[1]; + } + + private static boolean isEnchantmentInTag(Enchantment enchantment, String tag) { + return DGU.getWorld() + .registryAccess() + .lookupOrThrow(Registries.ENCHANTMENT) + .getOrThrow(TagKey.create(Registries.ENCHANTMENT, ResourceLocation.parse(tag))) + .stream() + .anyMatch(enchantmentRegistryEntry -> enchantmentRegistryEntry.value() == enchantment); + } + + //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.getMinLevel(); + int a = enchantment.getMaxLevel() - b; + + JsonObject resultObject = new JsonObject(); + resultObject.addProperty("a", a); + resultObject.addProperty("b", b); + return resultObject; + } + + private static JsonObject generateEnchantmentMaxPowerCoefficients(Enchantment enchantment) { + int b = enchantment.getMinLevel(); + int a = enchantment.getMaxLevel() - 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(); + ResourceLocation registryKey = registry.getKey(enchantment); + + enchantmentDesc.addProperty("id", registry.getId(enchantment)); + enchantmentDesc.addProperty("name", registryKey.getPath()); + enchantmentDesc.addProperty("displayName", enchantment.description().getString()); + + enchantmentDesc.addProperty("maxLevel", enchantment.getMaxLevel()); + enchantmentDesc.add("minCost", generateEnchantmentMinPowerCoefficients(enchantment)); + enchantmentDesc.add("maxCost", generateEnchantmentMaxPowerCoefficients(enchantment)); + + enchantmentDesc.addProperty("treasureOnly", isEnchantmentInTag(enchantment, "treasure")); + + enchantmentDesc.addProperty("curse", isEnchantmentInTag(enchantment, "curse")); + + List incompatibleEnchantments = registry.stream() + .filter(other -> { + Holder enchantmentEntry = registry.wrapAsHolder(enchantment); + Holder otherEntry = registry.wrapAsHolder(other); + return !Enchantment.areCompatible(enchantmentEntry, otherEntry); + }) + .filter(other -> other != enchantment) + .toList(); + + JsonArray excludes = new JsonArray(); + for (Enchantment excludedEnchantment : incompatibleEnchantments) { + ResourceLocation otherKey = registry.getKey(excludedEnchantment); + excludes.add(otherKey.getPath()); + } + enchantmentDesc.add("exclude", excludes); + enchantmentDesc.addProperty("category", getEnchantmentTargetName(enchantment.definition().supportedItems())); + enchantmentDesc.addProperty("weight", enchantment.definition().weight()); + enchantmentDesc.addProperty("tradeable", isEnchantmentInTag(enchantment, "tradeable")); + enchantmentDesc.addProperty("discoverable", isEnchantmentInTag(enchantment, "on_random_loot")); + + return enchantmentDesc; + } + + @Override + public String getDataName() { + return "enchantments"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry enchantmentRegistry = DGU.getWorld().registryAccess().lookupOrThrow(Registries.ENCHANTMENT); + enchantmentRegistry.stream() + .forEach(enchantment -> resultsArray.add(generateEnchantment(enchantmentRegistry, enchantment))); + return resultsArray; + } +} diff --git a/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java new file mode 100644 index 00000000..0c0e6c19 --- /dev/null +++ b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/EntitiesDataGenerator.java @@ -0,0 +1,136 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.entity.AgeableMob; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntitySpawnReason; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.ambient.AmbientCreature; +import net.minecraft.world.entity.animal.Animal; +import net.minecraft.world.entity.animal.WaterAnimal; +import net.minecraft.world.entity.monster.Monster; +import net.minecraft.world.entity.projectile.Projectile; + +public class EntitiesDataGenerator implements IDataGenerator { + public static JsonObject generateEntity(Registry> entityRegistry, EntityType entityType) { + JsonObject entityDesc = new JsonObject(); + ResourceLocation registryKey = entityRegistry.getKey(entityType); + int entityRawId = entityRegistry.getId(entityType); + + entityDesc.addProperty("id", entityRawId); + entityDesc.addProperty("internalId", entityRawId); + entityDesc.addProperty("name", registryKey.getPath()); + + entityDesc.addProperty("displayName", DGU.translateText(entityType.getDescriptionId())); + 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.overworld(), EntitySpawnReason.NATURAL); + entityTypeString = entityObject != null ? getEntityTypeForClass(entityObject.getClass()) : "unknown"; + } + if (entityType == EntityType.PLAYER) { + entityTypeString = "player"; + } + + entityDesc.addProperty("type", entityTypeString); + entityDesc.addProperty("category", getCategoryFrom(entityType)); + + return entityDesc; + } + + private static String getCategoryFrom(EntityType entityType) { + if (entityType == EntityType.PLAYER) return "UNKNOWN"; + Entity entity = entityType.create(DGU.getWorld(), EntitySpawnReason.NATURAL); + if (entity == null) + throw new Error("Entity was null after trying to create a: " + DGU.translateText(entityType.getDescriptionId())); + entity.discard(); + String packageName = entity.getClass().getPackageName(); + + // Use a more flexible approach to handle sub-packages + if (packageName.equals("net.minecraft.world.entity.decoration") || + packageName.startsWith("net.minecraft.world.entity.decoration.")) { + return "Immobile"; + } else if (packageName.equals("net.minecraft.world.entity.boss") || + packageName.equals("net.minecraft.world.entity.monster") || + packageName.startsWith("net.minecraft.world.entity.boss.") || + packageName.startsWith("net.minecraft.world.entity.monster.")) { + return "Hostile mobs"; + } else if (packageName.equals("net.minecraft.world.entity.projectile") || + packageName.startsWith("net.minecraft.world.entity.projectile.")) { + return "Projectiles"; + } else if (packageName.equals("net.minecraft.world.entity.animal") || + packageName.startsWith("net.minecraft.world.entity.animal.")) { + return "Passive mobs"; + } else if (packageName.equals("net.minecraft.world.entity.vehicle") || + packageName.startsWith("net.minecraft.world.entity.vehicle.")) { + return "Vehicles"; + } else if (packageName.equals("net.minecraft.world.entity")) { + return "UNKNOWN"; + } else { + // Instead of throwing an error, return UNKNOWN for unexpected packages + return "UNKNOWN"; + } + } + + //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 (WaterAnimal.class.isAssignableFrom(entityClass)) { + return "water_creature"; + } + if (Animal.class.isAssignableFrom(entityClass)) { + return "animal"; + } + if (Monster.class.isAssignableFrom(entityClass)) { + return "hostile"; + } + if (AmbientCreature.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 (AgeableMob.class.isAssignableFrom(entityClass)) { + return "passive"; + } + if (Mob.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 = DGU.getWorld().registryAccess().lookupOrThrow(Registries.ENTITY_TYPE); + entityTypeRegistry.forEach(entity -> resultArray.add(generateEntity(entityTypeRegistry, entity))); + return resultArray; + } +} diff --git a/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java new file mode 100644 index 00000000..de640ee2 --- /dev/null +++ b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/FoodsDataGenerator.java @@ -0,0 +1,52 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.core.Registry; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.food.FoodProperties; +import net.minecraft.world.item.Item; + +import java.util.Objects; + +public class FoodsDataGenerator implements IDataGenerator { + public static JsonObject generateFoodDescriptor(Registry registry, Item foodItem) { + JsonObject foodDesc = new JsonObject(); + ResourceLocation registryKey = registry.getKey(foodItem); + + foodDesc.addProperty("id", registry.getId(foodItem)); + foodDesc.addProperty("name", registryKey.getPath()); + + foodDesc.addProperty("stackSize", foodItem.getDefaultMaxStackSize()); + foodDesc.addProperty("displayName", DGU.translateText(foodItem.getDescriptionId())); + + FoodProperties foodComponent = Objects.requireNonNull(foodItem.components().get(DataComponents.FOOD)); + float foodPoints = foodComponent.nutrition(); + float saturationRatio = foodComponent.saturation() * 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 = DGU.getWorld().registryAccess().lookupOrThrow(Registries.ITEM); + itemRegistry.stream() + .filter(i -> i.components().has(DataComponents.FOOD)) + .forEach(food -> resultsArray.add(generateFoodDescriptor(itemRegistry, food))); + return resultsArray; + } +} diff --git a/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/InstrumentsDataGenerator.java new file mode 100644 index 00000000..40d0b4c9 --- /dev/null +++ b/mc/1.21.11/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.world.level.block.state.properties.NoteBlockInstrument; + +public class InstrumentsDataGenerator implements IDataGenerator { + @Override + public String getDataName() { + return "instruments"; + } + + @Override + public JsonElement generateDataJson() { + JsonArray array = new JsonArray(); + for (NoteBlockInstrument instrument : NoteBlockInstrument.values()) { + JsonObject object = new JsonObject(); + object.addProperty("id", instrument.ordinal()); + object.addProperty("name", instrument.getSerializedName()); + array.add(object); + } + return array; + } +} diff --git a/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/ItemsDataGenerator.java new file mode 100644 index 00000000..dca29aba --- /dev/null +++ b/mc/1.21.11/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 com.google.gson.JsonPrimitive; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.enchantment.Enchantment; + +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 = new ItemStack(sourceItem); + return itemRegistry.stream() + .filter(otherItem -> sourceItemStack.isValidRepairItem(new ItemStack(otherItem))) + .collect(Collectors.toList()); + } + + public static JsonObject generateItem(Registry itemRegistry, Item item) { + JsonObject itemDesc = new JsonObject(); + ResourceLocation registryKey = itemRegistry.getKey(item); + + itemDesc.addProperty("id", itemRegistry.getId(item)); + itemDesc.addProperty("name", registryKey.getPath()); + + itemDesc.addProperty("displayName", DGU.translateText(item.getDescriptionId())); + itemDesc.addProperty("stackSize", item.getDefaultMaxStackSize()); + + JsonArray enchantCategoriesArray = new JsonArray(); + DGU.getWorld().registryAccess().lookupOrThrow(Registries.ENCHANTMENT).stream() + .map(Enchantment::getSupportedItems) + .filter(applicableItems -> applicableItems.contains(itemRegistry.wrapAsHolder(item))) + .map(EnchantmentsDataGenerator::getEnchantmentTargetName) + .distinct() + .forEach(enchantCategoriesArray::add); + + if (enchantCategoriesArray.size() > 0) { + itemDesc.add("enchantCategories", enchantCategoriesArray); + } + + if (item.components().has(DataComponents.MAX_DAMAGE)) { + List repairWithItems = calculateItemsToRepairWith(itemRegistry, item); + + JsonArray fixedWithArray = new JsonArray(); + for (Item repairWithItem : repairWithItems) { + ResourceLocation repairWithName = itemRegistry.getKey(repairWithItem); + fixedWithArray.add(repairWithName.getPath()); + } + if (fixedWithArray.size() > 0) { + itemDesc.add("repairWith", fixedWithArray); + } + + int maxDurability = Objects.requireNonNull(item.components().get(DataComponents.MAX_DAMAGE)); + itemDesc.addProperty("maxDurability", maxDurability); + } + return itemDesc; + } + + @Override + public String getDataName() { + return "items"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultArray = new JsonArray(); + Registry itemRegistry = DGU.getWorld().registryAccess().lookupOrThrow(Registries.ITEM); + itemRegistry.stream().forEach(item -> resultArray.add(generateItem(itemRegistry, item))); + return resultArray; + } +} diff --git a/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/LanguageDataGenerator.java new file mode 100644 index 00000000..7a3c6ced --- /dev/null +++ b/mc/1.21.11/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/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java new file mode 100644 index 00000000..eccac53e --- /dev/null +++ b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/MaterialsDataGenerator.java @@ -0,0 +1,239 @@ +package dev.u9g.minecraftdatagenerator.generators; + +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.util.DGU; +import net.minecraft.core.Registry; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.Registries; +import net.minecraft.tags.BlockTags; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; + +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.MINEABLE_WITH_AXE))) + .add(ImmutableList.of("gourd", makeMaterialNameForTag(BlockTags.MINEABLE_WITH_AXE))) + .add(ImmutableList.of(makeMaterialNameForTag(BlockTags.LEAVES), makeMaterialNameForTag(BlockTags.MINEABLE_WITH_HOE))) + .add(ImmutableList.of(makeMaterialNameForTag(BlockTags.LEAVES), makeMaterialNameForTag(BlockTags.MINEABLE_WITH_AXE), makeMaterialNameForTag(BlockTags.MINEABLE_WITH_HOE))) + .add(ImmutableList.of("vine_or_glow_lichen", "plant", makeMaterialNameForTag(BlockTags.MINEABLE_WITH_AXE) + )).build(); + + private static final Map TOOL_SPEEDS = new HashMap<>() {{ + // Base speeds for each tool type + put("wooden", 2.0f); + put("stone", 4.0f); + put("iron", 6.0f); + put("diamond", 8.0f); + put("netherite", 9.0f); + put("golden", 12.0f); + }}; + + private static Float getToolSpeed(Item item) { + String itemName = item.toString().toLowerCase(); + // Remove minecraft: prefix if present + if (itemName.startsWith("minecraft:")) { + itemName = itemName.substring("minecraft:".length()); + } + for (Map.Entry entry : TOOL_SPEEDS.entrySet()) { + if (itemName.startsWith(entry.getKey())) { + return entry.getValue(); + } + } + return 1.0f; + } + + private static String makeMaterialNameForTag(TagKey tag) { + return tag.location().getPath(); + } + + private static void createCompositeMaterialInfo(List allMaterials, List combinedMaterials) { + String compositeMaterialName = String.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.addFirst(materialInfo); + } + + private static void createCompositeMaterial(Map> allMaterials, List combinedMaterials) { + String compositeMaterialName = String.join(";", combinedMaterials); + + Map resultingToolSpeeds = new LinkedHashMap<>(); + 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.is(Blocks.VINE) || blockState.is(Blocks.GLOW_LICHEN))); + resultList.add(new MaterialInfo("coweb", blockState -> blockState.is(Blocks.COBWEB))); + + resultList.add(new MaterialInfo("leaves", blockState -> blockState.is(BlockTags.LEAVES))); + resultList.add(new MaterialInfo("wool", blockState -> blockState.is(BlockTags.WOOL))); + + // Block Materials were removed in 1.20 in favor of block tags + resultList.add(new MaterialInfo("gourd", blockState -> blockState.is(Blocks.MELON) || blockState.is(Blocks.PUMPKIN) || blockState.is(Blocks.JACK_O_LANTERN))); + // 'sword_efficient' tag is for all plants, and includes everything from the old PLANT and REPLACEABLE_PLANT materials (see https://minecraft.fandom.com/wiki/Tag#Blocks) + resultList.add(new MaterialInfo("plant", blockState -> blockState.is(BlockTags.SWORD_EFFICIENT))); + + HashSet uniqueMaterialNames = new HashSet<>(); + + Registry itemRegistry = DGU.getWorld().registryAccess().lookupOrThrow(Registries.ITEM); + itemRegistry.forEach(item -> { + if (item.components().get(DataComponents.TOOL) != null) { + item.components().get(DataComponents.TOOL).rules() + .stream().map(rule -> rule.blocks()) + .forEach(blocks -> { + Optional> tagKey = blocks.unwrapKey(); + if (tagKey.isPresent()) { + String materialName = makeMaterialNameForTag((tagKey.get())); + + if (!uniqueMaterialNames.contains(materialName)) { + uniqueMaterialNames.add(materialName); + resultList.add(new MaterialInfo(materialName, blockState -> blockState.is(blocks))); + } + } + }); + } + }); + + COMPOSITE_MATERIALS.forEach(values -> createCompositeMaterialInfo(resultList, values)); + return resultList; + } + + @Override + public String getDataName() { + return "materials"; + } + + @Override + public JsonElement generateDataJson() { + Registry itemRegistry = DGU.getWorld().registryAccess().lookupOrThrow(Registries.ITEM); + + Map> materialMiningSpeeds = new LinkedHashMap<>(); + materialMiningSpeeds.put("default", new LinkedHashMap<>()); + + //Special materials used for shears and swords special mining speed logic + Map leavesMaterialSpeeds = new LinkedHashMap<>(); + Map cowebMaterialSpeeds = new LinkedHashMap<>(); + Map plantMaterialSpeeds = new LinkedHashMap<>(); + Map gourdMaterialSpeeds = new LinkedHashMap<>(); + + 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); + + Map vineOrGlowLichenSpeeds = new LinkedHashMap<>(); + vineOrGlowLichenSpeeds.put(Items.SHEARS, 2.0f); + materialMiningSpeeds.put("vine_or_glow_lichen", vineOrGlowLichenSpeeds); + + Map woolSpeeds = new LinkedHashMap<>(); + woolSpeeds.put(Items.SHEARS, 5.0f); + materialMiningSpeeds.put("wool", woolSpeeds); + + itemRegistry.forEach(item -> { + //Tools are handled rather easily and do not require anything else + if (item.components().get(DataComponents.TOOL) != null) { + item.components().get(DataComponents.TOOL).rules() + .stream().map(rule -> rule.blocks()) + .forEach(blocks -> { + Optional> tagKey = blocks.unwrapKey(); + if (tagKey.isPresent()) { + String materialName = makeMaterialNameForTag(tagKey.get()); + + Map materialSpeeds = materialMiningSpeeds.computeIfAbsent(materialName, k -> new LinkedHashMap<>()); + float baseSpeed = getToolSpeed(item); + materialSpeeds.put(item, baseSpeed); + } + } + ); + + //Swords require special treatment + if (itemRegistry.getKey(item).getPath().contains("sword")) { + 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.getId(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/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java new file mode 100644 index 00000000..6f3991cf --- /dev/null +++ b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/ParticlesDataGenerator.java @@ -0,0 +1,33 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.core.Registry; +import net.minecraft.core.particles.ParticleType; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; + +public class ParticlesDataGenerator implements IDataGenerator { + public static JsonObject generateParticleType(Registry> registry, ParticleType particleType) { + JsonObject effectDesc = new JsonObject(); + ResourceLocation registryKey = registry.getKey(particleType); + + effectDesc.addProperty("id", registry.getId(particleType)); + effectDesc.addProperty("name", registryKey.getPath()); + return effectDesc; + } + + @Override + public String getDataName() { + return "particles"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry> particleTypeRegistry = DGU.getWorld().registryAccess().lookupOrThrow(Registries.PARTICLE_TYPE); + particleTypeRegistry.forEach(particleType -> resultsArray.add(generateParticleType(particleTypeRegistry, particleType))); + return resultsArray; + } +} diff --git a/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/RecipeDataGenerator.java new file mode 100644 index 00000000..88d27933 --- /dev/null +++ b/mc/1.21.11/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.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.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; +import net.minecraft.util.context.ContextMap; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.crafting.*; +import net.minecraft.world.item.crafting.CraftingInput; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.item.crafting.display.ShapelessCraftingRecipeDisplay; +import net.minecraft.world.item.crafting.display.SlotDisplay; +import net.minecraft.world.item.crafting.display.SlotDisplayContext; + +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().registryAccess().lookupOrThrow(Registries.ITEM).getId(item); + } + + @Override + public String getDataName() { + return "recipes"; + } + + @Override + public JsonElement generateDataJson() { + RegistryAccess registryManager = DGU.getWorld().registryAccess(); + JsonObject finalObj = new JsonObject(); + Multimap recipes = ArrayListMultimap.create(); + for (RecipeHolder recipeE : Objects.requireNonNull(DGU.getWorld()).getServer().getRecipeManager().getRecipes()) { + Recipe recipe = recipeE.value(); + if (recipe instanceof ShapedRecipe sr) { + generateShapedRecipe(registryManager, finalObj, sr, 0); + } else if (recipe instanceof ShapelessRecipe sl) { + var ingredients = new JsonArray(); + var displays = sl.display(); + if (!displays.isEmpty() && displays.get(0) instanceof ShapelessCraftingRecipeDisplay shapelessDisplay) { + for (SlotDisplay slotDisplay : shapelessDisplay.ingredients()) { + var itemStack = slotDisplay.resolveForFirstStack(SlotDisplayContext.fromLevel(DGU.getWorld())); + if (!itemStack.isEmpty()) { + ingredients.add(getRawIdFor(itemStack.getItem())); + } + } + } + var rootRecipeObject = new JsonObject(); + rootRecipeObject.add("ingredients", ingredients); + var resultObject = new JsonObject(); + resultObject.addProperty("id", getRawIdFor(sl.assemble(CraftingInput.EMPTY, registryManager).getItem())); + resultObject.addProperty("count", sl.assemble(CraftingInput.EMPTY, registryManager).getCount()); + rootRecipeObject.add("result", resultObject); + recipes.put(getRawIdFor(sl.assemble(CraftingInput.EMPTY, 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 void generateShapedRecipe(RegistryAccess registryManager, JsonObject finalObj, ShapedRecipe sr, int n) { + boolean hasIncremented = false; + var ingredients = sr.getIngredients(); + List ingr = new ArrayList<>(); + for (int i = 0; i < 9; i++) { + if (i >= ingredients.size()) { + ingr.add(null); + continue; + } + var stacks = ingredients.get(i); + if (stacks.isEmpty()) { + ingr.add(null); + continue; + } + var matchingList = stacks.get().items().toList(); + if (matchingList.isEmpty()) { + ingr.add(null); + continue; + } else { + // we already have matchingList from above + if (matchingList.size() > n) { + ingr.add(getRawIdFor(matchingList.get(n).value())); + } else { + ingr.add(getRawIdFor(matchingList.get(0).value())); + } + if (matchingList.size() - 1 > n && !hasIncremented) { + generateShapedRecipe(registryManager, finalObj, sr, n+1); + hasIncremented = true; + } + } + } + + 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.assemble(CraftingInput.EMPTY, registryManager).getItem())); + resultObject.addProperty("count", sr.assemble(CraftingInput.EMPTY, registryManager).getCount()); + finalRecipe.add("result", resultObject); + + String id = ((Integer) getRawIdFor(sr.assemble(CraftingInput.EMPTY, registryManager).getItem())).toString(); + + if (!finalObj.has(id)) { + finalObj.add(id, new JsonArray()); + } + finalObj.get(id).getAsJsonArray().add(finalRecipe); + } +} diff --git a/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/SoundsDataGenerator.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/SoundsDataGenerator.java new file mode 100644 index 00000000..d18ed157 --- /dev/null +++ b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/SoundsDataGenerator.java @@ -0,0 +1,33 @@ +package dev.u9g.minecraftdatagenerator.generators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.sounds.SoundEvent; + +public class SoundsDataGenerator implements IDataGenerator { + public static JsonObject generateSound(SoundEvent soundEvent) { + JsonObject soundDesc = new JsonObject(); + + soundDesc.addProperty("id", BuiltInRegistries.SOUND_EVENT.getId(soundEvent) + 1); // the plus 1 is required for 1.19.2+ due to Mojang using 0 in the packet to say that you should read a string id instead. + soundDesc.addProperty("name", soundEvent.location().getPath()); + + return soundDesc; + } + + @Override + public String getDataName() { + return "sounds"; + } + + @Override + public JsonArray generateDataJson() { + JsonArray resultsArray = new JsonArray(); + Registry soundEventRegistry = DGU.getWorld().registryAccess().lookupOrThrow(Registries.SOUND_EVENT); + soundEventRegistry.forEach(sound -> resultsArray.add(generateSound(sound))); + return resultsArray; + } +} diff --git a/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/generators/TintsDataGenerator.java new file mode 100644 index 00000000..d367c21d --- /dev/null +++ b/mc/1.21.11/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.util.DGU; +import dev.u9g.minecraftdatagenerator.util.EmptyRenderBlockView; +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.FoliageColor; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.RedStoneWireBlock; + +import java.util.*; + +public class TintsDataGenerator implements IDataGenerator { + public static BiomeTintColors generateBiomeTintColors(Registry biomeRegistry) { + BiomeTintColors colors = new BiomeTintColors(); + + biomeRegistry.forEach(biome -> { + int biomeGrassColor = biome.getGrassColor(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 LinkedHashMap<>(); + + for (int redstoneLevel : RedStoneWireBlock.POWER.getPossibleValues()) { + // Remove the unintended alpha channel from the redstone tint color + int color = removeAlphaChannel(RedStoneWireBlock.getColorForPower(redstoneLevel)); + resultColors.put(redstoneLevel, color); + } + return resultColors; + } + + private static int removeAlphaChannel(int color) { + float r = (float) (color >> 16 & 0xFF) / 255; + float g = (float) (color >> 8 & 0xFF) / 255; + float b = (float) (color & 0xFF) / 255; + return ((int)(r * 255) << 16) | ((int)(g * 255) << 8) | (int)(b * 255); + } + + private static int getBlockColor(Block block) { + return BlockColors.createDefault().getColor(block.defaultBlockState(), EmptyRenderBlockView.INSTANCE, BlockPos.ZERO, 0xFFFFFF); + } + + public static Map generateConstantTintColors() { + Map resultColors = new LinkedHashMap<>(); + + resultColors.put(Blocks.BIRCH_LEAVES, FoliageColor.FOLIAGE_BIRCH); + resultColors.put(Blocks.SPRUCE_LEAVES, FoliageColor.FOLIAGE_EVERGREEN); + + resultColors.put(Blocks.LILY_PAD, getBlockColor(Blocks.LILY_PAD)); + resultColors.put(Blocks.ATTACHED_MELON_STEM, getBlockColor(Blocks.ATTACHED_MELON_STEM)); + resultColors.put(Blocks.ATTACHED_PUMPKIN_STEM, getBlockColor(Blocks.ATTACHED_PUMPKIN_STEM)); + + //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(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()) { + ResourceLocation registryKey = biomeRegistry.getKey(biome); + 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(); + ResourceLocation registryKey = blockRegistry.getKey(entry.getKey()); + 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() { + RegistryAccess registryManager = DGU.getWorld().registryAccess(); + Registry biomeRegistry = registryManager.lookupOrThrow(Registries.BIOME); + Registry blockRegistry = registryManager.lookupOrThrow(Registries.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 LinkedHashMap<>(); + final Map> foliageColoursMap = new LinkedHashMap<>(); + final Map> waterColourMap = new LinkedHashMap<>(); + } +} diff --git a/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/mixin/DataPackSettingsMixin.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/mixin/DataPackSettingsMixin.java new file mode 100644 index 00000000..38aacbf2 --- /dev/null +++ b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/mixin/DataPackSettingsMixin.java @@ -0,0 +1,36 @@ +/* + * SoulFire + * Copyright (C) 2024 AlexProgrammerDE + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.repository.PackRepository; +import net.minecraft.world.flag.FeatureFlags; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.List; + +@Mixin(PackRepository.class) +public class DataPackSettingsMixin { + @Inject(method = "getSelectedIds", at = @At("HEAD"), cancellable = true) + public void getEnabled(CallbackInfoReturnable> cir) { + cir.setReturnValue(FeatureFlags.REGISTRY.toNames(FeatureFlags.REGISTRY.allFlags()).stream().map(ResourceLocation::getPath).toList()); + } +} diff --git a/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java new file mode 100644 index 00000000..0be76d9c --- /dev/null +++ b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/mixin/EULAMixin.java @@ -0,0 +1,15 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import net.minecraft.server.Eula; +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(Eula.class) +public class EULAMixin { + @Inject(method = "hasAgreedToEULA()Z", at = @At("TAIL"), cancellable = true) + public void init(CallbackInfoReturnable cir) { + cir.setReturnValue(true); + } +} diff --git a/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java new file mode 100644 index 00000000..657acc76 --- /dev/null +++ b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/mixin/ReadyMixin.java @@ -0,0 +1,22 @@ +package dev.u9g.minecraftdatagenerator.mixin; + +import dev.u9g.minecraftdatagenerator.MinecraftDataGenerator; +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.SharedConstants; +import net.minecraft.server.dedicated.DedicatedServer; +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(DedicatedServer.class) +public class ReadyMixin { + + @Inject(method = "initServer()Z", at = @At("TAIL")) + private void init(CallbackInfoReturnable cir) { + MinecraftDataGenerator.start( + SharedConstants.getCurrentVersion().name(), + DGU.getCurrentlyRunningServer().getServerDirectory() + ); + } +} diff --git a/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java new file mode 100644 index 00000000..20336aee --- /dev/null +++ b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/util/DGU.java @@ -0,0 +1,21 @@ +package dev.u9g.minecraftdatagenerator.util; + +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.locale.Language; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.level.Level; + +public class DGU { + @SuppressWarnings("deprecation") + public static MinecraftServer getCurrentlyRunningServer() { + return (MinecraftServer) FabricLoader.getInstance().getGameInstance(); + } + + public static String translateText(String translationKey) { + return Language.getInstance().getOrDefault(translationKey); + } + + public static Level getWorld() { + return getCurrentlyRunningServer().overworld(); + } +} diff --git a/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java new file mode 100644 index 00000000..1a43e7bf --- /dev/null +++ b/mc/1.21.11/src/main/java/dev/u9g/minecraftdatagenerator/util/EmptyRenderBlockView.java @@ -0,0 +1,77 @@ +package dev.u9g.minecraftdatagenerator.util; + +import dev.u9g.minecraftdatagenerator.util.DGU; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeSpecialEffects.GrassColorModifier; +import net.minecraft.world.level.biome.Biomes; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.lighting.LevelLightEngine; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import org.jetbrains.annotations.Nullable; + +public enum EmptyRenderBlockView implements BlockAndTintGetter { + INSTANCE; + + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + return null; + } + + public BlockState getBlockState(BlockPos pos) { + return Blocks.AIR.defaultBlockState(); + } + + public FluidState getFluidState(BlockPos pos) { + return Fluids.EMPTY.defaultFluidState(); + } + + public int getBottomY() { + return 0; + } + + @Override + public int getHeight() { + return 384; + } + + @Override + public float getShade(Direction direction, boolean shaded) { + return 0.0f; + } + + @Override + public LevelLightEngine getLightEngine() { + return null; + } + + @Override + public int getBlockTint(BlockPos pos, net.minecraft.world.level.ColorResolver colorResolver) { + Registry biomeRegistry = DGU.getWorld().registryAccess().lookupOrThrow(Registries.BIOME); + Biome plainsBiome = biomeRegistry.get(Biomes.PLAINS).orElse(null).value(); + + return colorResolver.getColor(plainsBiome, pos.getX(), pos.getY()); + } + + @Override + public int getBrightness(LightLayer type, BlockPos pos) { + return type == LightLayer.SKY ? 15 : 0; + } + + public int getBaseLightLevel(BlockPos pos, int ambientDarkness) { + return ambientDarkness; + } + + @Override + public int getMinY() { + return -64; + } +} diff --git a/mc/1.21.11/src/main/resources/fabric.mod.json b/mc/1.21.11/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..ffac96b0 --- /dev/null +++ b/mc/1.21.11/src/main/resources/fabric.mod.json @@ -0,0 +1,20 @@ +{ + "schemaVersion": 1, + "id": "minecraft-data-generator", + "version": "${version}", + "name": "Minecraft Data Generator", + "description": "", + "authors": [ + "Archengius", + "U9G" + ], + "contact": {}, + "license": "MIT", + "mixins": [ + "minecraft-data-generator.mixins.json" + ], + "depends": { + "fabricloader": "*", + "minecraft": "*" + } +} diff --git a/mc/1.21.11/src/main/resources/minecraft-data-generator.mixins.json b/mc/1.21.11/src/main/resources/minecraft-data-generator.mixins.json new file mode 100644 index 00000000..ed926d16 --- /dev/null +++ b/mc/1.21.11/src/main/resources/minecraft-data-generator.mixins.json @@ -0,0 +1,16 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "dev.u9g.minecraftdatagenerator.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "DataPackSettingsMixin", + "EULAMixin", + "ReadyMixin" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/versions.json b/versions.json index 0a5dd23f..e033026e 100644 --- a/versions.json +++ b/versions.json @@ -23,5 +23,6 @@ "1.21.6", "1.21.7", "1.21.8", - "1.21.9" + "1.21.9", + "1.21.11" ] \ No newline at end of file