* 1.20.5 recipes: null over -1

* add 1.21

* 1.21 (#26)

* update classname

* update moved class

* updated to equivelant method since gettranslatekey removed

* added isCursed list as bandaid alternative for now since isCursed() removed

* added isTreasure list as bandaid alternative for now since isTreasure() removed

* found alternative method to canCombine() since removed

* modified getEnchantmentTarget name to handle registryEntryList since getApplicableItems dosnt return tagKey anymore

* bandaid list solution for "tradeable" property since isTradeable removed and no alternative in mappings

* bandaid solution for discoverable property since removed and no alternative to isAvailiableForRandomSelection in 1.21 mappings

* removed .toPath since apparently it dosnt exist

* no longer outputs in tagkey format

* fixed not adding enchantcategories for items properly

* added SuperGamerTron tag solution

* Update build.yml

---------

Co-authored-by: Madlykeanu <96554689+Madlykeanu@users.noreply.github.com>
Co-authored-by: Romain Beaumont <romain.rom1@gmail.com>
This commit is contained in:
extremeheat
2024-10-27 11:34:53 +01:00
committed by GitHub
co-authored by Madlykeanu Romain Beaumont
parent 131218ce79
commit 2c423f77f8
25 changed files with 1681 additions and 2 deletions
+2 -1
View File
@@ -29,7 +29,8 @@ jobs:
"1.19.2", "1.19.2",
"1.20", "1.20",
"1.20.4", "1.20.4",
"1.20.5" "1.20.5",
"1.21"
] ]
name: Build ${{ matrix.version }} name: Build ${{ matrix.version }}
@@ -85,7 +85,52 @@ public class RecipeDataGenerator implements IDataGenerator {
for (RecipeEntry<?> recipeE : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) { for (RecipeEntry<?> recipeE : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) {
Recipe<?> recipe = recipeE.value(); Recipe<?> recipe = recipeE.value();
if (recipe instanceof ShapedRecipe sr) { if (recipe instanceof ShapedRecipe sr) {
generateShapedRecipe(registryManager, finalObj, sr, 0);
var ingredients = sr.getIngredients();
List<Integer> ingr = new ArrayList<>();
for (int i = 0; i < 9; i++) {
if (i >= ingredients.size()) {
ingr.add(null);
continue;
}
var stacks = ingredients.get(i);
var matching = stacks.getMatchingStacks();
if (matching.length == 0) {
ingr.add(null);
} 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 input = new JsonArray();
// var ingredients = sr.getIngredients().stream().toList(); // var ingredients = sr.getIngredients().stream().toList();
// for (int y = 0; y < sr.getHeight(); y++) { // for (int y = 0; y < sr.getHeight(); y++) {
+47
View File
@@ -0,0 +1,47 @@
import xyz.wagyourtail.unimined.api.minecraft.task.RemapJarTask
import xyz.wagyourtail.unimined.internal.minecraft.MinecraftProvider
plugins {
id 'xyz.wagyourtail.unimined'
}
unimined.minecraft {
version "1.21"
mappings {
intermediary()
yarn(1)
devFallbackNamespace "intermediary"
}
runs.config("server") {
javaVersion = JavaVersion.VERSION_21
}
customPatcher(new CustomOfficialFabricMinecraftTransformer(project, delegate as MinecraftProvider)) {
it.loader libs.versions.fabric.loader.get()
}
defaultRemapJar = true
}
dependencies {
implementation project(":common")
}
processResources {
filteringCharset "UTF-8"
filesMatching("fabric.mod.json") {
expand "version": project.version
}
}
tasks.withType(RemapJarTask).configureEach {
onlyIf { false}
}
tasks.withType(JavaCompile).configureEach {
it.options.encoding = "UTF-8"
}
@@ -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.registry.DynamicRegistryManager;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.tag.BiomeTags;
import net.minecraft.util.Identifier;
import net.minecraft.world.biome.Biome;
public class BiomesDataGenerator implements IDataGenerator {
private static String guessBiomeDimensionFromCategory(Biome biome) {
var biomeRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.BIOME);
if (biomeRegistry.getEntry(biome).isIn(BiomeTags.IS_NETHER)) {
return "nether";
} else if (biomeRegistry.getEntry(biome).isIn(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<Biome> 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);
biomeDesc.addProperty("category", guessCategoryBasedOnName(name, dimension));
biomeDesc.addProperty("temperature", biome.getTemperature());
//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();
DynamicRegistryManager registryManager = DGU.getWorld().getRegistryManager();
Registry<Biome> biomeRegistry = registryManager.get(RegistryKeys.BIOME);
biomeRegistry.stream()
.map(biome -> generateBiomeInfo(biomeRegistry, biome))
.forEach(biomesArray::add);
return biomesArray;
}
}
@@ -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.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
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<Block> 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 final Map<VoxelShape, Integer> uniqueBlockShapes = new LinkedHashMap<>();
public final Map<Block, List<Integer>> blockCollisionShapes = new LinkedHashMap<>();
private int lastCollisionShapeId = 0;
public void processBlock(Block block) {
List<BlockState> blockStates = block.getStateManager().getStates();
List<Integer> 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<Block> blockRegistry) {
JsonObject resultObject = new JsonObject();
for (var entry : blockCollisionShapes.entrySet()) {
List<Integer> 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);
}
}
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;
}
}
}
@@ -0,0 +1,197 @@
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.LootContextParameterSet;
import net.minecraft.loot.context.LootContextParameters;
import net.minecraft.registry.Registries;
import net.minecraft.registry.RegistryKeys;
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.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<Item> getItemsEffectiveForBlock(BlockState blockState) {
return DGU.getWorld().getRegistryManager().get(RegistryKeys.ITEM).stream()
.filter(item -> item.getDefaultStack().isSuitableFor(blockState))
.collect(Collectors.toList());
}
private static void populateDropsIfPossible(BlockState blockState, Item firstToolItem, List<ItemStack> 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();
LootContextParameterSet.Builder lootContextParameterSet = new LootContextParameterSet.Builder(serverWorld)
.add(LootContextParameters.BLOCK_STATE, blockState)
.add(LootContextParameters.ORIGIN, Vec3d.ZERO)
.add(LootContextParameters.TOOL, firstToolItem.getDefaultStack());
outDrops.addAll(blockState.getDroppedStacks(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.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 <T extends Comparable<T>> JsonObject generateStateProperty(Property<T> property) {
JsonObject propertyObject = new JsonObject();
Collection<T> 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<MaterialsDataGenerator.MaterialInfo> materials) {
List<MaterialsDataGenerator.MaterialInfo> 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<MaterialsDataGenerator.MaterialInfo> materials, Block block) {
JsonObject blockDesc = new JsonObject();
List<BlockState> blockStates = block.getStateManager().getStates();
BlockState defaultState = block.getDefaultState();
Identifier registryKey = Registries.BLOCK.getKey(block).orElseThrow().getValue();
String localizationKey = block.getTranslationKey();
List<Item> effectiveTools = getItemsEffectiveForBlock(defaultState);
blockDesc.addProperty("id", Registries.BLOCK.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.getFirst()));
blockDesc.addProperty("maxStateId", Block.getRawIdFromState(blockStates.getLast()));
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<ItemStack> actualBlockDrops = new ArrayList<>();
populateDropsIfPossible(defaultState, effectiveTools.isEmpty() ? Items.AIR : effectiveTools.getFirst(), 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();
List<MaterialsDataGenerator.MaterialInfo> availableMaterials = MaterialsDataGenerator.getGlobalMaterialInfo();
Registries.BLOCK.forEach(block -> resultBlocksArray.add(generateBlock(availableMaterials, block)));
return resultBlocksArray;
}
}
@@ -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.registry.Registry;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.util.Identifier;
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<StatusEffect> 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<StatusEffect> statusEffectRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.STATUS_EFFECT);
statusEffectRegistry.forEach(effect -> resultsArray.add(generateEffect(statusEffectRegistry, effect)));
return resultsArray;
}
}
@@ -0,0 +1,112 @@
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.Enchantment;
import net.minecraft.item.Item;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.registry.entry.RegistryEntryList;
import net.minecraft.util.Identifier;
import net.minecraft.registry.entry.RegistryEntry;
import java.util.List;
import java.util.Set;
public class EnchantmentsDataGenerator implements IDataGenerator {
public static String getEnchantmentTargetName(RegistryEntryList<Item> target) {
TagKey<Item> tagKey = target.getTagKey().orElseThrow();
return tagKey.id().getPath().split("/")[1];
}
private static boolean isEnchantmentInTag(Enchantment enchantment, String tag) {
return DGU.getWorld()
.getRegistryManager()
.get(RegistryKeys.ENCHANTMENT)
.streamTagsAndEntries()
.filter(tagKeyNamedPair -> tagKeyNamedPair.getFirst().id().equals(Identifier.of(tag)))
.flatMap(tagKeyNamedPair -> tagKeyNamedPair.getSecond().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.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<Enchantment> 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());
String displayName = Enchantment.getName(registry.getEntry(enchantment), 1).getString();
displayName = displayName.replaceAll(" I$", "");
enchantmentDesc.addProperty("displayName", displayName);
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<Enchantment> incompatibleEnchantments = registry.stream()
.filter(other -> {
RegistryEntry<Enchantment> enchantmentEntry = registry.getEntry(enchantment);
RegistryEntry<Enchantment> otherEntry = registry.getEntry(other);
return !Enchantment.canBeCombined(enchantmentEntry, otherEntry);
})
.filter(other -> other != enchantment)
.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.getApplicableItems()));
enchantmentDesc.addProperty("weight", enchantment.getWeight());
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<Enchantment> enchantmentRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.ENCHANTMENT);
enchantmentRegistry.stream()
.forEach(enchantment -> resultsArray.add(generateEnchantment(enchantmentRegistry, enchantment)));
return resultsArray;
}
}
@@ -0,0 +1,119 @@
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.registry.Registry;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.Identifier;
public class EntitiesDataGenerator implements IDataGenerator {
public static JsonObject generateEntity(Registry<EntityType<?>> 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()) : "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());
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<? extends Entity> 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<EntityType<?>> entityTypeRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.ENTITY_TYPE);
entityTypeRegistry.forEach(entity -> resultArray.add(generateEntity(entityTypeRegistry, entity)));
return resultArray;
}
}
@@ -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.component.DataComponentTypes;
import net.minecraft.component.type.FoodComponent;
import net.minecraft.item.Item;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.util.Identifier;
import java.util.Objects;
public class FoodsDataGenerator implements IDataGenerator {
public static JsonObject generateFoodDescriptor(Registry<Item> 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.getComponents().get(DataComponentTypes.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<Item> itemRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.ITEM);
itemRegistry.stream()
.filter(i -> i.getComponents().contains(DataComponentTypes.FOOD))
.forEach(food -> resultsArray.add(generateFoodDescriptor(itemRegistry, food)));
return resultsArray;
}
}
@@ -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.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.asString());
array.add(object);
}
return array;
}
}
@@ -0,0 +1,85 @@
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.component.DataComponentTypes;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.util.Identifier;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class ItemsDataGenerator implements IDataGenerator {
private static List<Item> calculateItemsToRepairWith(Registry<Item> itemRegistry, Item sourceItem) {
ItemStack sourceItemStack = sourceItem.getDefaultStack();
return itemRegistry.stream()
.filter(otherItem -> sourceItem.canRepair(sourceItemStack, otherItem.getDefaultStack()))
.collect(Collectors.toList());
}
public static JsonObject generateItem(Registry<Item> 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());
JsonArray enchantCategoriesArray = new JsonArray();
Registry<Enchantment> enchants = DGU.getWorld().getRegistryManager().get(RegistryKeys.ENCHANTMENT);
for (Enchantment enchant : enchants) {
if (enchant.getApplicableItems().contains(item.getRegistryEntry())) {
String enchantTarget = enchant.getApplicableItems().getTagKey().get().id().getPath().split("/")[1];
if (!enchantCategoriesArray.contains(new JsonPrimitive(enchantTarget))) {
enchantCategoriesArray.add(enchantTarget);
}
}
}
if (enchantCategoriesArray.size() > 0) {
itemDesc.add("enchantCategories", enchantCategoriesArray);
}
if (item.getComponents().contains(DataComponentTypes.DAMAGE)) {
List<Item> 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 = Objects.requireNonNull(item.getComponents().get(DataComponentTypes.MAX_DAMAGE));
itemDesc.addProperty("maxDurability", maxDurability);
}
return itemDesc;
}
@Override
public String getDataName() {
return "items";
}
@Override
public JsonArray generateDataJson() {
JsonArray resultArray = new JsonArray();
Registry<Item> itemRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.ITEM);
itemRegistry.stream().forEach(item -> resultArray.add(generateItem(itemRegistry, item)));
return resultArray;
}
}
@@ -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");
}
}
@@ -0,0 +1,211 @@
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.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.item.Item;
import net.minecraft.item.Items;
import net.minecraft.item.MiningToolItem;
import net.minecraft.item.SwordItem;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.registry.tag.TagKey;
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<ImmutableList<String>> COMPOSITE_MATERIALS = ImmutableList.<ImmutableList<String>>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<Block> tag) {
return tag.id().getPath();
}
private static void createCompositeMaterialInfo(List<MaterialInfo> allMaterials, List<String> combinedMaterials) {
String compositeMaterialName = String.join(";", combinedMaterials);
List<MaterialInfo> 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<BlockState> 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<String, Map<Item, Float>> allMaterials, List<String> combinedMaterials) {
String compositeMaterialName = String.join(";", combinedMaterials);
Map<Item, Float> resultingToolSpeeds = new LinkedHashMap<>();
combinedMaterials.stream()
.map(allMaterials::get)
.forEach(resultingToolSpeeds::putAll);
allMaterials.put(compositeMaterialName, resultingToolSpeeds);
}
public static List<MaterialInfo> getGlobalMaterialInfo() {
ArrayList<MaterialInfo> 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)));
// Block Materials were removed in 1.20 in favor of block tags
resultList.add(new MaterialInfo("gourd", blockState -> blockState.isOf(Blocks.MELON) || blockState.isOf(Blocks.PUMPKIN) || blockState.isOf(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.isIn(BlockTags.SWORD_EFFICIENT)));
HashSet<String> uniqueMaterialNames = new HashSet<>();
Registry<Item> itemRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.ITEM);
itemRegistry.forEach(item -> {
if (item instanceof MiningToolItem toolItem) {
item.getComponents().get(DataComponentTypes.TOOL).rules()
.stream().map(rule -> rule.blocks())
.forEach(blocks -> {
Optional<TagKey<Block>> tagKey = blocks.getTagKey();
if (tagKey.isPresent()) {
String materialName = makeMaterialNameForTag((tagKey.get()));
if (!uniqueMaterialNames.contains(materialName)) {
uniqueMaterialNames.add(materialName);
resultList.add(new MaterialInfo(materialName, blockState -> blockState.isIn(blocks)));
}
}
});
}
});
COMPOSITE_MATERIALS.forEach(values -> createCompositeMaterialInfo(resultList, values));
return resultList;
}
@Override
public String getDataName() {
return "materials";
}
@Override
public JsonElement generateDataJson() {
Registry<Item> itemRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.ITEM);
Map<String, Map<Item, Float>> materialMiningSpeeds = new LinkedHashMap<>();
materialMiningSpeeds.put("default", ImmutableMap.of());
//Special materials used for shears and swords special mining speed logic
Map<Item, Float> leavesMaterialSpeeds = new LinkedHashMap<>();
Map<Item, Float> cowebMaterialSpeeds = new LinkedHashMap<>();
Map<Item, Float> plantMaterialSpeeds = new LinkedHashMap<>();
Map<Item, Float> 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);
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) {
item.getComponents().get(DataComponentTypes.TOOL).rules()
.stream().map(rule -> rule.blocks())
.forEach(blocks -> {
Optional<TagKey<Block>> tagKey = blocks.getTagKey();
if (tagKey.isPresent()) {
String materialName = makeMaterialNameForTag(tagKey.get());
Map<Item, Float> materialSpeeds = materialMiningSpeeds.computeIfAbsent(materialName, k -> new LinkedHashMap<>());
float miningSpeed = item.getComponents().get(DataComponentTypes.TOOL).defaultMiningSpeed();
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<BlockState> predicate;
private final List<MaterialInfo> includedMaterials = new ArrayList<>();
public MaterialInfo(String materialName, Predicate<BlockState> predicate) {
this.materialName = materialName;
this.predicate = predicate;
}
protected MaterialInfo includes(List<MaterialInfo> otherMaterials) {
this.includedMaterials.addAll(otherMaterials);
return this;
}
public String getMaterialName() {
return materialName;
}
public Predicate<BlockState> getPredicate() {
return predicate;
}
public boolean includesMaterial(MaterialInfo materialInfo) {
return includedMaterials.contains(materialInfo);
}
@Override
public String toString() {
return materialName;
}
}
}
@@ -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.particle.ParticleType;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.util.Identifier;
public class ParticlesDataGenerator implements IDataGenerator {
public static JsonObject generateParticleType(Registry<ParticleType<?>> 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<ParticleType<?>> particleTypeRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.PARTICLE_TYPE);
particleTypeRegistry.forEach(particleType -> resultsArray.add(generateParticleType(particleTypeRegistry, particleType)));
return resultsArray;
}
}
@@ -0,0 +1,128 @@
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<Integer, JsonObject> 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<Integer> 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;
}
}
@@ -0,0 +1,158 @@
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.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.block.RedstoneWireBlock;
import net.minecraft.client.color.block.BlockColors;
import net.minecraft.world.biome.FoliageColors;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKeys;
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(Registry<Biome> 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<Integer, Integer> generateRedstoneTintColors() {
Map<Integer, Integer> resultColors = new LinkedHashMap<>();
for (int redstoneLevel : RedstoneWireBlock.POWER.getValues()) {
int color = RedstoneWireBlock.getWireColor(redstoneLevel);
resultColors.put(redstoneLevel, color);
}
return resultColors;
}
private static int getBlockColor(Block block) {
return BlockColors.create().getColor(block.getDefaultState(), EmptyRenderBlockView.INSTANCE, BlockPos.ORIGIN, 0xFFFFFF);
}
public static Map<Block, Integer> generateConstantTintColors() {
Map<Block, Integer> resultColors = new LinkedHashMap<>();
resultColors.put(Blocks.BIRCH_LEAVES, FoliageColors.getBirchColor());
resultColors.put(Blocks.SPRUCE_LEAVES, FoliageColors.getSpruceColor());
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<Biome> biomeRegistry, Map<Integer, List<Biome>> 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<Integer, Integer> 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<Block> blockRegistry, Map<Block, Integer> 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 = DGU.getWorld().getRegistryManager();
Registry<Biome> biomeRegistry = registryManager.get(RegistryKeys.BIOME);
Registry<Block> blockRegistry = registryManager.get(RegistryKeys.BLOCK);
BiomeTintColors biomeTintColors = generateBiomeTintColors(biomeRegistry);
Map<Integer, Integer> redstoneColors = generateRedstoneTintColors();
Map<Block, Integer> 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<Integer, List<Biome>> grassColoursMap = new LinkedHashMap<>();
final Map<Integer, List<Biome>> foliageColoursMap = new LinkedHashMap<>();
final Map<Integer, List<Biome>> waterColourMap = new LinkedHashMap<>();
}
}
@@ -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<Boolean> cir) {
cir.setReturnValue(true);
}
}
@@ -0,0 +1,22 @@
package dev.u9g.minecraftdatagenerator.mixin;
import dev.u9g.minecraftdatagenerator.MinecraftDataGenerator;
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;
@Mixin(MinecraftDedicatedServer.class)
public class ReadyMixin {
@Inject(method = "setupServer()Z", at = @At("TAIL"))
private void init(CallbackInfoReturnable<Boolean> cir) {
MinecraftDataGenerator.start(
MinecraftVersion.CURRENT.getName(),
DGU.getCurrentlyRunningServer().getRunDirectory()
);
}
}
@@ -0,0 +1,21 @@
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 {
@SuppressWarnings("deprecation")
public static MinecraftServer getCurrentlyRunningServer() {
return (MinecraftServer) FabricLoader.getInstance().getGameInstance();
}
public static String translateText(String translationKey) {
return Language.getInstance().get(translationKey);
}
public static World getWorld() {
return getCurrentlyRunningServer().getOverworld();
}
}
@@ -0,0 +1,72 @@
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.registry.Registry;
import net.minecraft.registry.RegistryKeys;
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.Biome;
import net.minecraft.world.biome.BiomeKeys;
import net.minecraft.world.biome.ColorResolver;
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 float getBrightness(Direction direction, boolean shaded) {
return 0.0f;
}
@Override
public LightingProvider getLightingProvider() {
return null;
}
@Override
public int getColor(BlockPos pos, ColorResolver colorResolver) {
Registry<Biome> biomeRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.BIOME);
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;
}
}
+20
View File
@@ -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": "*"
}
}
@@ -0,0 +1,15 @@
{
"required": true,
"minVersion": "0.8",
"package": "dev.u9g.minecraftdatagenerator.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"EULAMixin",
"ReadyMixin"
],
"client": [
],
"injectors": {
"defaultRequire": 1
}
}
+2
View File
@@ -31,6 +31,8 @@ Then also add it to the `.github/workflows/build.yml` file.
Then copy the code of the most recently released version into the new directory. Then copy the code of the most recently released version into the new directory.
Then you need to change the values in the `gradle.properties` file. Then you need to change the values in the `gradle.properties` file.
At last, you need to fix all code issues that are caused by the new version. At last, you need to fix all code issues that are caused by the new version.
if there are any new curse, treasure, or non-tradeable, or non-discoverable enchantments in the new version, you need to add them manually to the `EnchantmentsDataGenerator` class.
For that, use an IDE like IntelliJ IDEA to fix the issues. For that, use an IDE like IntelliJ IDEA to fix the issues.
Once everything compiles, you can commit the changes, push them to your fork and create a pull request. Once everything compiles, you can commit the changes, push them to your fork and create a pull request.
Once your PR was accepted and merged, the new version will be available in the next release. Once your PR was accepted and merged, the new version will be available in the next release.
+1
View File
@@ -31,4 +31,5 @@ include "common"
"1.20", "1.20",
"1.20.4", "1.20.4",
"1.20.5", "1.20.5",
"1.21"
].forEach { include it } ].forEach { include it }