Fix handling FeatureSet related entities in 1.20.3/4 (#16)

* Fix handling FeatureSet related entities.

* Add 1.20.4 to build.yml

* Turns out jsut need to enable the feature set in the server.properties

* Remove the server.properties

Lets not enable feature sets since they could change alot of stuff on use.

The other fix in the PR still works as it makes it so that the entities don't show up as player type, just as unknow if we can't get them.

* Use the same way of getting the entity in all parts of the generator.

This should help deal with entities from features, since using the factory directly gets around the feature check.

* Would be helpful to call the function correctly...
This commit is contained in:
William Gaylord
2024-03-17 16:13:09 +01:00
committed by GitHub
parent 0f1c6bdd04
commit 665bf9b7c7
33 changed files with 2153 additions and 1 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
build:
strategy:
matrix:
version: ["1.7", "1.8.9", "1.9.4", "1.10.2", "1.11.2", "1.12.2", "1.13", "1.14", "1.15", "1.16", "1.17", "1.18", "22w19a", "1.19", "1.19.2", "1.20"]
version: ["1.7", "1.8.9", "1.9.4", "1.10.2", "1.11.2", "1.12.2", "1.13", "1.14", "1.15", "1.16", "1.17", "1.18", "22w19a", "1.19", "1.19.2", "1.20", "1.20.4"]
name: Build ${{ matrix.version }}
timeout-minutes: 10
+31
View File
@@ -0,0 +1,31 @@
plugins {
id 'fabric-loom'
}
dependencies {
// To change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
}
processResources {
filteringCharset "UTF-8"
filesMatching("fabric.mod.json") {
expand "version": project.version
}
}
tasks.withType(JavaCompile).configureEach {
it.options.encoding = "UTF-8"
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
+8
View File
@@ -0,0 +1,8 @@
# Fabric Properties
# check these on https://modmuss50.me/fabric.html
minecraft_version=1.20.4
yarn_mappings=1.20.4+build.3
loader_version=0.15.3
# Dependencies
# check this on https://modmuss50.me/fabric.html
fabric_version=0.92.0+1.20.4
@@ -0,0 +1,14 @@
package dev.u9g.minecraftdatagenerator;
import net.fabricmc.api.ModInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main implements ModInitializer {
public static final Logger LOGGER = LoggerFactory.getLogger("mc-data-gen-serv");
@Override
public void onInitialize() {
}
}
@@ -0,0 +1,317 @@
package dev.u9g.minecraftdatagenerator.generators;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import dev.u9g.minecraftdatagenerator.mixin.TheEndBiomeDataAccessor;
import dev.u9g.minecraftdatagenerator.util.DGU;
import net.fabricmc.fabric.api.biome.v1.NetherBiomes;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.util.Identifier;
import net.minecraft.world.biome.Biome;
import java.util.HashSet;
import java.util.Set;
public class BiomesDataGenerator implements IDataGenerator {
private static final Set<RegistryKey<Biome>> END_BIOMES = new HashSet<>();
static {
END_BIOMES.addAll(TheEndBiomeDataAccessor.END_BIOMES_MAP().keySet());
END_BIOMES.addAll(TheEndBiomeDataAccessor.END_BARRENS_MAP().keySet());
END_BIOMES.addAll(TheEndBiomeDataAccessor.END_MIDLANDS_MAP().keySet());
}
private static String guessBiomeDimensionFromCategory(Biome biome, String biomeName) {
var key = DGU.getWorld().getRegistryManager().get(RegistryKeys.BIOME).getKey(biome).orElseThrow();
if (NetherBiomes.canGenerateInNether(key)) {
return "nether";
} else if (END_BIOMES.contains(key) || biomeName.startsWith("end_")) {
return "end";
}
return "overworld";
}
private static String guessCategoryBasedOnName(String name, String dimension) {
if (dimension.equals("nether")) {
return "nether";
} else if (dimension.equals("end")) {
return "the_end";
}
if (name.contains("end")) {
System.out.println();
}
if (name.contains("hills")) {
return "extreme_hills";
} else if (name.contains("ocean")) {
return "ocean";
} else if (name.contains("plains")) {
return "plains";
} else if (name.contains("ice") || name.contains("frozen")) {
return "ice";
} else if (name.contains("jungle")) {
return "jungle";
} else if (name.contains("desert")) {
return "desert";
} else if (name.contains("forest") || name.contains("grove")) {
return "forest";
} else if (name.contains("taiga")) {
return "taiga";
} else if (name.contains("swamp")) {
return "swamp";
} else if (name.contains("river")) {
return "river";
} else if (name.equals("the_end")) {
return "the_end";
} else if (name.contains("mushroom")) {
return "mushroom";
} else if (name.contains("beach") || name.equals("stony_shore")) {
return "beach";
} else if (name.contains("savanna")) {
return "savanna";
} else if (name.contains("badlands")) {
return "mesa";
} else if (name.contains("peaks") || name.equals("snowy_slopes") || name.equals("meadow")) {
return "mountain";
} else if (name.equals("the_void")) {
return "none";
} else if (name.contains("cave") || name.equals("deep_dark")) {
return "underground";
} else {
System.out.println("Unable to find biome category for biome with name: '" + name + "'");
return "none";
}
}
private static int getBiomeColorFor(String biomeName) {
switch (biomeName) {
case "the_void" -> {
return 0;
}
case "plains" -> {
return 9286496;
}
case "sunflower_plains" -> {
return 11918216;
}
case "snowy_plains" -> {
return 16777215;
}
case "ice_spikes" -> {
return 11853020;
}
case "desert" -> {
return 16421912;
}
case "swamp" -> {
return 522674;
}
case "forest" -> {
return 353825;
}
case "flower_forest" -> {
return 2985545;
}
case "birch_forest" -> {
return 3175492;
}
case "dark_forest" -> {
return 4215066;
}
case "old_growth_birch_forest" -> {
return 5807212;
}
case "old_growth_pine_taiga" -> {
return 5858897;
}
case "old_growth_spruce_taiga" -> {
return 8490617;
}
case "taiga" -> {
return 747097;
}
case "snowy_taiga" -> {
return 3233098;
}
case "savanna" -> {
return 12431967;
}
case "savanna_plateau" -> {
return 10984804;
}
case "windswept_hills" -> {
return 6316128;
}
case "windswept_gravelly_hills" -> {
return 8947848;
}
case "windswept_forest" -> {
return 2250012;
}
case "windswept_savanna" -> {
return 15063687;
}
case "jungle" -> {
return 5470985;
}
case "sparse_jungle" -> {
return 6458135;
}
case "bamboo_jungle" -> {
return 7769620;
}
case "badlands" -> {
return 14238997;
}
case "eroded_badlands" -> {
return 16739645;
}
case "wooded_badlands" -> {
return 11573093;
}
case "meadow" -> {
return 9217136;
}
case "grove" -> {
return 14675173;
}
case "snowy_slopes" -> {
return 14348785;
}
case "frozen_peaks" -> {
return 15399931;
}
case "jagged_peaks" -> {
return 14937325;
}
case "stony_peaks" -> {
return 13750737;
}
case "river" -> {
return 255;
}
case "frozen_river" -> {
return 10526975;
}
case "beach" -> {
return 16440917;
}
case "snowy_beach" -> {
return 16445632;
}
case "stony_shore" -> {
return 10658436;
}
case "warm_ocean" -> {
return 172;
}
case "lukewarm_ocean" -> {
return 144;
}
case "deep_lukewarm_ocean" -> {
return 64;
}
case "ocean" -> {
return 112;
}
case "deep_ocean" -> {
return 48;
}
case "cold_ocean" -> {
return 2105456;
}
case "deep_cold_ocean" -> {
return 2105400;
}
case "frozen_ocean" -> {
return 7368918;
}
case "deep_frozen_ocean" -> {
return 4210832;
}
case "mushroom_fields" -> {
return 16711935;
}
case "dripstone_caves" -> {
return 12690831;
}
case "lush_caves" -> {
return 14652980;
}
case "nether_wastes" -> {
return 12532539;
}
case "warped_forest" -> {
return 4821115;
}
case "crimson_forest" -> {
return 14485512;
}
case "soul_sand_valley" -> {
return 6174768;
}
case "basalt_deltas" -> {
return 4208182;
}
case "the_end" -> {
return 8421631;
}
case "end_highlands" -> {
return 12828041;
}
case "end_midlands" -> {
return 15464630;
}
case "small_end_islands" -> {
return 42;
}
case "end_barrens" -> {
return 9474162;
}
}
System.out.println("Don't know the color of biome: '" + biomeName + "'");
return 0;
}
public static JsonObject generateBiomeInfo(Registry<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, name);
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", getBiomeColorFor(registryKey.getPath()));
//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,113 @@
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.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BlockCollisionShapesDataGenerator implements IDataGenerator {
@Override
public String getDataName() {
return "blockCollisionShapes";
}
@Override
public JsonObject generateDataJson() {
Registry<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 HashMap<>();
public final Map<Block, List<Integer>> blockCollisionShapes = new HashMap<>();
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.get(0));
} else {
blockCollision = new JsonArray();
for (int collisionId : blockCollisions) {
((JsonArray) blockCollision).add(collisionId);
}
}
Identifier registryKey = blockRegistry.getKey(entry.getKey()).orElseThrow().getValue();
resultObject.add(registryKey.getPath(), blockCollision);
}
return resultObject;
}
public JsonObject dumpShapesObject() {
JsonObject shapesObject = new JsonObject();
for (var entry : uniqueBlockShapes.entrySet()) {
JsonArray boxesArray = new JsonArray();
entry.getKey().forEachBox((x1, y1, z1, x2, y2, z2) -> {
JsonArray oneBoxJsonArray = new JsonArray();
oneBoxJsonArray.add(x1);
oneBoxJsonArray.add(y1);
oneBoxJsonArray.add(z1);
oneBoxJsonArray.add(x2);
oneBoxJsonArray.add(y2);
oneBoxJsonArray.add(z2);
boxesArray.add(oneBoxJsonArray);
});
shapesObject.add(Integer.toString(entry.getValue()), boxesArray);
}
return shapesObject;
}
}
}
@@ -0,0 +1,198 @@
package dev.u9g.minecraftdatagenerator.generators;
import com.google.common.base.CaseFormat;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import dev.u9g.minecraftdatagenerator.util.DGU;
import net.minecraft.block.AirBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.loot.context.LootContextParameterSet;
import net.minecraft.loot.context.LootContextParameters;
import net.minecraft.registry.Registry;
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.get(0);
var otherMaterials = matchingMaterials.subList(1, matchingMaterials.size());
if (!otherMaterials.stream().allMatch(firstMaterial::includesMaterial)) {
logger.error("Block {} matches multiple materials: {}", blockState.getBlock(), matchingMaterials);
}
}
if (matchingMaterials.isEmpty()) {
return "default";
}
return matchingMaterials.get(0).getMaterialName();
}
public static JsonObject generateBlock(Registry<Block> blockRegistry, List<MaterialsDataGenerator.MaterialInfo> materials, Block block) {
JsonObject blockDesc = new JsonObject();
List<BlockState> blockStates = block.getStateManager().getStates();
BlockState defaultState = block.getDefaultState();
Identifier registryKey = blockRegistry.getKey(block).orElseThrow().getValue();
String localizationKey = block.getTranslationKey();
List<Item> effectiveTools = getItemsEffectiveForBlock(defaultState);
blockDesc.addProperty("id", blockRegistry.getRawId(block));
blockDesc.addProperty("name", registryKey.getPath());
blockDesc.addProperty("displayName", DGU.translateText(localizationKey));
blockDesc.addProperty("hardness", block.getHardness());
blockDesc.addProperty("resistance", block.getBlastResistance());
blockDesc.addProperty("stackSize", block.asItem().getMaxCount());
blockDesc.addProperty("diggable", block.getHardness() != -1.0f && !(block instanceof AirBlock));
// JsonObject effTools = new JsonObject();
// effectiveTools.forEach(item -> effTools.addProperty(
// String.valueOf(Registry.ITEM.getRawId(item)), // key
// item.getMiningSpeedMultiplier(item.getDefaultStack(), defaultState) // value
// ));
// blockDesc.add("effectiveTools", effTools);
blockDesc.addProperty("material", findMatchingBlockMaterial(defaultState, materials));
blockDesc.addProperty("transparent", !defaultState.isOpaque());
blockDesc.addProperty("emitLight", defaultState.getLuminance());
blockDesc.addProperty("filterLight", defaultState.getOpacity(EmptyBlockView.INSTANCE, BlockPos.ORIGIN));
blockDesc.addProperty("defaultState", Block.getRawIdFromState(defaultState));
blockDesc.addProperty("minStateId", Block.getRawIdFromState(blockStates.get(0)));
blockDesc.addProperty("maxStateId", Block.getRawIdFromState(blockStates.get(blockStates.size() - 1)));
JsonArray stateProperties = new JsonArray();
for (Property<?> property : block.getStateManager().getProperties()) {
stateProperties.add(generateStateProperty(property));
}
blockDesc.add("states", stateProperties);
//Only add harvest tools if tool is required for harvesting this block
if (defaultState.isToolRequired()) {
JsonObject effectiveToolsObject = new JsonObject();
for (Item effectiveItem : effectiveTools) {
effectiveToolsObject.addProperty(Integer.toString(Item.getRawId(effectiveItem)), true);
}
blockDesc.add("harvestTools", effectiveToolsObject);
}
List<ItemStack> actualBlockDrops = new ArrayList<>();
populateDropsIfPossible(defaultState, effectiveTools.isEmpty() ? Items.AIR : effectiveTools.get(0), actualBlockDrops);
JsonArray dropsArray = new JsonArray();
for (ItemStack dropStack : actualBlockDrops) {
dropsArray.add(Item.getRawId(dropStack.getItem()));
}
blockDesc.add("drops", dropsArray);
VoxelShape blockCollisionShape = defaultState.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN);
blockDesc.addProperty("boundingBox", blockCollisionShape.isEmpty() ? "empty" : "block");
return blockDesc;
}
@Override
public String getDataName() {
return "blocks";
}
@Override
public JsonArray generateDataJson() {
JsonArray resultBlocksArray = new JsonArray();
Registry<Block> blockRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.BLOCK);
List<MaterialsDataGenerator.MaterialInfo> availableMaterials = MaterialsDataGenerator.getGlobalMaterialInfo();
blockRegistry.forEach(block -> resultBlocksArray.add(generateBlock(blockRegistry, availableMaterials, block)));
return resultBlocksArray;
}
}
@@ -0,0 +1,77 @@
package dev.u9g.minecraftdatagenerator.generators;
import com.google.gson.JsonElement;
import com.google.gson.internal.Streams;
import com.google.gson.stream.JsonWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
public class DataGenerators {
private static final Logger logger = LoggerFactory.getLogger(DataGenerators.class);
private static final List<IDataGenerator> GENERATORS = new ArrayList<>();
static {
register(new BiomesDataGenerator());
register(new BlockCollisionShapesDataGenerator());
register(new BlocksDataGenerator());
register(new EffectsDataGenerator());
register(new EnchantmentsDataGenerator());
register(new EntitiesDataGenerator());
register(new FoodsDataGenerator());
register(new ItemsDataGenerator());
register(new ParticlesDataGenerator());
register(new TintsDataGenerator());
register(new MaterialsDataGenerator());
// register(new RecipeDataGenerator()); - On hold until mcdata supports multiple materials for a recipe
register(new LanguageDataGenerator());
register(new InstrumentsDataGenerator());
}
public static void register(IDataGenerator generator) {
GENERATORS.add(generator);
}
public static boolean runDataGenerators(Path outputDirectory) {
try {
Files.createDirectories(outputDirectory);
} catch (IOException exception) {
logger.error("Failed to create data generator output directory at {}", outputDirectory, exception);
return false;
}
int generatorsFailed = 0;
logger.info("Running minecraft data generators, output at {}", outputDirectory);
for (IDataGenerator dataGenerator : GENERATORS) {
logger.info("Running generator {}", dataGenerator.getDataName());
try {
String outputFileName = String.format("%s.json", dataGenerator.getDataName());
JsonElement outputElement = dataGenerator.generateDataJson();
Path outputFilePath = outputDirectory.resolve(outputFileName);
try (Writer writer = Files.newBufferedWriter(outputFilePath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
JsonWriter jsonWriter = new JsonWriter(writer);
jsonWriter.setIndent(" ");
Streams.write(outputElement, jsonWriter);
}
logger.info("Generator: {} -> {}", dataGenerator.getDataName(), outputFileName);
} catch (Throwable exception) {
logger.error("Failed to run data generator {}", dataGenerator.getDataName(), exception);
generatorsFailed++;
}
}
logger.info("Finishing running data generators");
return generatorsFailed == 0;
}
}
@@ -0,0 +1,47 @@
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,109 @@
package dev.u9g.minecraftdatagenerator.generators;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import dev.u9g.minecraftdatagenerator.util.DGU;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.EnchantmentTarget;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.util.Identifier;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
public class EnchantmentsDataGenerator implements IDataGenerator {
private static final ImmutableMap<EnchantmentTarget, String> ENCHANTMENT_TARGET_NAMES = ImmutableMap.<EnchantmentTarget, String>builder()
.put(EnchantmentTarget.ARMOR, "armor")
.put(EnchantmentTarget.ARMOR_FEET, "armor_feet")
.put(EnchantmentTarget.ARMOR_LEGS, "armor_legs")
.put(EnchantmentTarget.ARMOR_CHEST, "armor_chest")
.put(EnchantmentTarget.ARMOR_HEAD, "armor_head")
.put(EnchantmentTarget.WEAPON, "weapon")
.put(EnchantmentTarget.DIGGER, "digger")
.put(EnchantmentTarget.FISHING_ROD, "fishing_rod")
.put(EnchantmentTarget.TRIDENT, "trident")
.put(EnchantmentTarget.BREAKABLE, "breakable")
.put(EnchantmentTarget.BOW, "bow")
.put(EnchantmentTarget.WEARABLE, "wearable")
.put(EnchantmentTarget.CROSSBOW, "crossbow")
.put(EnchantmentTarget.VANISHABLE, "vanishable")
.build();
public static String getEnchantmentTargetName(EnchantmentTarget target) {
return ENCHANTMENT_TARGET_NAMES.getOrDefault(target, target.name().toLowerCase(Locale.ROOT));
}
//Equation enchantment costs follow is a * level + b, so we can easily retrieve a and b by passing zero level
private static JsonObject generateEnchantmentMinPowerCoefficients(Enchantment enchantment) {
int b = enchantment.getMinPower(0);
int a = enchantment.getMinPower(1) - b;
JsonObject resultObject = new JsonObject();
resultObject.addProperty("a", a);
resultObject.addProperty("b", b);
return resultObject;
}
private static JsonObject generateEnchantmentMaxPowerCoefficients(Enchantment enchantment) {
int b = enchantment.getMaxPower(0);
int a = enchantment.getMaxPower(1) - b;
JsonObject resultObject = new JsonObject();
resultObject.addProperty("a", a);
resultObject.addProperty("b", b);
return resultObject;
}
public static JsonObject generateEnchantment(Registry<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());
enchantmentDesc.addProperty("displayName", DGU.translateText(enchantment.getTranslationKey()));
enchantmentDesc.addProperty("maxLevel", enchantment.getMaxLevel());
enchantmentDesc.add("minCost", generateEnchantmentMinPowerCoefficients(enchantment));
enchantmentDesc.add("maxCost", generateEnchantmentMaxPowerCoefficients(enchantment));
enchantmentDesc.addProperty("treasureOnly", enchantment.isTreasure());
enchantmentDesc.addProperty("curse", enchantment.isCursed());
List<Enchantment> incompatibleEnchantments = registry.stream()
.filter(other -> !enchantment.canCombine(other))
.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.target));
enchantmentDesc.addProperty("weight", enchantment.getRarity().getWeight());
enchantmentDesc.addProperty("tradeable", enchantment.isAvailableForEnchantedBookOffer());
enchantmentDesc.addProperty("discoverable", enchantment.isAvailableForRandomSelection());
return enchantmentDesc;
}
@Override
public String getDataName() {
return "enchantments";
}
@Override
public JsonArray generateDataJson() {
JsonArray resultsArray = new JsonArray();
Registry<Enchantment> enchantmentRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.ENCHANTMENT);
enchantmentRegistry.stream()
.forEach(enchantment -> resultsArray.add(generateEnchantment(enchantmentRegistry, enchantment)));
return resultsArray;
}
}
@@ -0,0 +1,126 @@
package dev.u9g.minecraftdatagenerator.generators;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import dev.u9g.minecraftdatagenerator.mixin.EntityTypeAccessor;
import dev.u9g.minecraftdatagenerator.util.DGU;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.SpawnReason;
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.registry.tag.TagKey;
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 = ((EntityTypeAccessor) entityType).factory().create(entityType, 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"; // fail early for player entities
/*public T create(World world) {
return this.factory.create(this, world);
}*/
Entity entity = ((EntityTypeAccessor) entityType).factory().create(entityType, DGU.getWorld());
if (entity == null)
throw new Error("Entity was null after trying to create a: " + DGU.translateText(entityType.getTranslationKey()));
entity.discard();
return switch (entity.getClass().getPackageName()) {
case "net.minecraft.entity.decoration", "net.minecraft.entity.decoration.painting" -> "Immobile";
case "net.minecraft.entity.boss", "net.minecraft.entity.mob", "net.minecraft.entity.boss.dragon" ->
"Hostile mobs";
case "net.minecraft.entity.projectile", "net.minecraft.entity.projectile.thrown" -> "Projectiles";
case "net.minecraft.entity.passive" -> "Passive mobs";
case "net.minecraft.entity.vehicle" -> "Vehicles";
case "net.minecraft.entity" -> "UNKNOWN";
default -> throw new Error("Unexpected entity type: " + entity.getClass().getPackageName());
};
}
//Honestly, both "type" and "category" fields in the schema and examples do not contain any useful information
//Since category is optional, I will just leave it out, and for type I will assume general entity classification
//by the Entity class hierarchy (which has some weirdness too by the way)
private static String getEntityTypeForClass(Class<? 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.item.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.getFoodComponent());
float foodPoints = foodComponent.getHunger();
float saturationRatio = foodComponent.getSaturationModifier() * 2.0F;
float saturation = foodPoints * saturationRatio;
foodDesc.addProperty("foodPoints", foodPoints);
foodDesc.addProperty("saturation", saturation);
foodDesc.addProperty("effectiveQuality", foodPoints + saturation);
foodDesc.addProperty("saturationRatio", saturationRatio);
return foodDesc;
}
@Override
public String getDataName() {
return "foods";
}
public JsonArray generateDataJson() {
JsonArray resultsArray = new JsonArray();
Registry<Item> itemRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.ITEM);
itemRegistry.stream()
.filter(Item::isFood)
.forEach(food -> resultsArray.add(generateFoodDescriptor(itemRegistry, food)));
return resultsArray;
}
}
@@ -0,0 +1,10 @@
package dev.u9g.minecraftdatagenerator.generators;
import com.google.gson.JsonElement;
public interface IDataGenerator {
String getDataName();
JsonElement generateDataJson();
}
@@ -0,0 +1,25 @@
package dev.u9g.minecraftdatagenerator.generators;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import net.minecraft.block.enums.Instrument;
public class InstrumentsDataGenerator implements IDataGenerator {
@Override
public String getDataName() {
return "instruments";
}
@Override
public JsonElement generateDataJson() {
JsonArray array = new JsonArray();
for (Instrument instrument : Instrument.values()) {
JsonObject object = new JsonObject();
object.addProperty("id", instrument.ordinal());
object.addProperty("name", instrument.asString());
array.add(object);
}
return array;
}
}
@@ -0,0 +1,82 @@
package dev.u9g.minecraftdatagenerator.generators;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import dev.u9g.minecraftdatagenerator.util.DGU;
import net.minecraft.enchantment.EnchantmentTarget;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.util.Identifier;
import java.util.Arrays;
import java.util.List;
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());
}
private static List<EnchantmentTarget> getApplicableEnchantmentTargets(Item sourceItem) {
return Arrays.stream(EnchantmentTarget.values())
.filter(target -> target.isAcceptableItem(sourceItem))
.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());
List<EnchantmentTarget> enchantmentTargets = getApplicableEnchantmentTargets(item);
JsonArray enchantCategoriesArray = new JsonArray();
for (EnchantmentTarget target : enchantmentTargets) {
enchantCategoriesArray.add(EnchantmentsDataGenerator.getEnchantmentTargetName(target));
}
if (enchantCategoriesArray.size() > 0) {
itemDesc.add("enchantCategories", enchantCategoriesArray);
}
if (item.isDamageable()) {
List<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 = item.getMaxDamage();
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,200 @@
package dev.u9g.minecraftdatagenerator.generators;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import dev.u9g.minecraftdatagenerator.mixin.MiningToolItemAccessor;
import dev.u9g.minecraftdatagenerator.util.DGU;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
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 = Joiner.on(';').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.add(0, materialInfo);
}
private static void createCompositeMaterial(Map<String, Map<Item, Float>> allMaterials, List<String> combinedMaterials) {
String compositeMaterialName = Joiner.on(';').join(combinedMaterials);
Map<Item, Float> resultingToolSpeeds = new HashMap<>();
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) {
TagKey<Block> effectiveBlocks = ((MiningToolItemAccessor) toolItem).getEffectiveBlocks();
String materialName = makeMaterialNameForTag(effectiveBlocks);
if (!uniqueMaterialNames.contains(materialName)) {
uniqueMaterialNames.add(materialName);
resultList.add(new MaterialInfo(materialName, blockState -> blockState.isIn(effectiveBlocks)));
}
}
});
COMPOSITE_MATERIALS.forEach(values -> createCompositeMaterialInfo(resultList, values));
return resultList;
}
@Override
public String getDataName() {
return "materials";
}
@Override
public JsonElement generateDataJson() {
Registry<Item> itemRegistry = DGU.getWorld().getRegistryManager().get(RegistryKeys.ITEM);
Map<String, Map<Item, Float>> materialMiningSpeeds = new HashMap<>();
materialMiningSpeeds.put("default", ImmutableMap.of());
//Special materials used for shears and swords special mining speed logic
Map<Item, Float> leavesMaterialSpeeds = new HashMap<>();
Map<Item, Float> cowebMaterialSpeeds = new HashMap<>();
Map<Item, Float> plantMaterialSpeeds = new HashMap<>();
Map<Item, Float> gourdMaterialSpeeds = new HashMap<>();
materialMiningSpeeds.put(makeMaterialNameForTag(BlockTags.LEAVES), leavesMaterialSpeeds);
materialMiningSpeeds.put("coweb", cowebMaterialSpeeds);
materialMiningSpeeds.put("plant", plantMaterialSpeeds);
materialMiningSpeeds.put("gourd", gourdMaterialSpeeds);
//Shears need special handling because they do not follow normal rules like tools
leavesMaterialSpeeds.put(Items.SHEARS, 15.0f);
cowebMaterialSpeeds.put(Items.SHEARS, 15.0f);
materialMiningSpeeds.put("vine_or_glow_lichen", ImmutableMap.of(Items.SHEARS, 2.0f));
materialMiningSpeeds.put("wool", ImmutableMap.of(Items.SHEARS, 5.0f));
itemRegistry.forEach(item -> {
//Tools are handled rather easily and do not require anything else
if (item instanceof MiningToolItem toolItem) {
TagKey<Block> effectiveBlocks = ((MiningToolItemAccessor) toolItem).getEffectiveBlocks();
String materialName = makeMaterialNameForTag(effectiveBlocks);
Map<Item, Float> materialSpeeds = materialMiningSpeeds.computeIfAbsent(materialName, k -> new HashMap<>());
float miningSpeed = ((MiningToolItemAccessor) toolItem).getMiningSpeed();
materialSpeeds.put(item, miningSpeed);
}
//Swords require special treatment
if (item instanceof SwordItem) {
cowebMaterialSpeeds.put(item, 15.0f);
plantMaterialSpeeds.put(item, 1.5f);
leavesMaterialSpeeds.put(item, 1.5f);
gourdMaterialSpeeds.put(item, 1.5f);
}
});
COMPOSITE_MATERIALS.forEach(values -> createCompositeMaterial(materialMiningSpeeds, values));
JsonObject resultObject = new JsonObject();
for (var entry : materialMiningSpeeds.entrySet()) {
JsonObject toolSpeedsObject = new JsonObject();
for (var toolEntry : entry.getValue().entrySet()) {
int rawItemId = itemRegistry.getRawId(toolEntry.getKey());
toolSpeedsObject.addProperty(Integer.toString(rawItemId), toolEntry.getValue());
}
resultObject.add(entry.getKey(), toolSpeedsObject);
}
return resultObject;
}
public static class MaterialInfo {
private final String materialName;
private final Predicate<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,34 @@
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,163 @@
package dev.u9g.minecraftdatagenerator.generators;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import dev.u9g.minecraftdatagenerator.mojangannoyances.BlockColors;
import dev.u9g.minecraftdatagenerator.util.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.world.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.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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 HashMap<>();
for (int redstoneLevel : RedstoneWireBlock.POWER.getValues()) {
int color = RedstoneWireBlock.getWireColor(redstoneLevel);
resultColors.put(redstoneLevel, color);
}
return resultColors;
}
private static int getBlockColor(Block block, BlockColors blockColors) {
return blockColors.getColor(block.getDefaultState(), EmptyRenderBlockView.INSTANCE, BlockPos.ORIGIN, 0xFFFFFF);
}
public static Map<Block, Integer> generateConstantTintColors() {
Map<Block, Integer> resultColors = new HashMap<>();
BlockColors blockColors = BlockColors.create();
resultColors.put(Blocks.BIRCH_LEAVES, FoliageColors.getBirchColor());
resultColors.put(Blocks.SPRUCE_LEAVES, FoliageColors.getSpruceColor());
resultColors.put(Blocks.LILY_PAD, getBlockColor(Blocks.LILY_PAD, blockColors));
resultColors.put(Blocks.ATTACHED_MELON_STEM, getBlockColor(Blocks.ATTACHED_MELON_STEM, blockColors));
resultColors.put(Blocks.ATTACHED_PUMPKIN_STEM, getBlockColor(Blocks.ATTACHED_PUMPKIN_STEM, blockColors));
//not really constant, depend on the block age, but kinda have to be handled since textures are literally white without them
resultColors.put(Blocks.MELON_STEM, getBlockColor(Blocks.MELON_STEM, blockColors));
resultColors.put(Blocks.PUMPKIN_STEM, getBlockColor(Blocks.PUMPKIN_STEM, blockColors));
return resultColors;
}
private static JsonObject encodeBiomeColorMap(Registry<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 HashMap<>();
final Map<Integer, List<Biome>> foliageColoursMap = new HashMap<>();
final Map<Integer, List<Biome>> waterColourMap = new HashMap<>();
}
}
@@ -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,12 @@
package dev.u9g.minecraftdatagenerator.mixin;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(EntityType.class)
public interface EntityTypeAccessor<T extends Entity> {
@Accessor("factory")
EntityType.EntityFactory<T> factory();
}
@@ -0,0 +1,17 @@
package dev.u9g.minecraftdatagenerator.mixin;
import net.minecraft.block.Block;
import net.minecraft.item.MiningToolItem;
import net.minecraft.registry.tag.TagKey;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(MiningToolItem.class)
public interface MiningToolItemAccessor {
@Accessor
TagKey<Block> getEffectiveBlocks();
@Accessor
float getMiningSpeed();
}
@@ -0,0 +1,28 @@
package dev.u9g.minecraftdatagenerator.mixin;
import dev.u9g.minecraftdatagenerator.Main;
import dev.u9g.minecraftdatagenerator.generators.DataGenerators;
import dev.u9g.minecraftdatagenerator.util.DGU;
import net.minecraft.MinecraftVersion;
import net.minecraft.server.dedicated.MinecraftDedicatedServer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.nio.file.Path;
@Mixin(MinecraftDedicatedServer.class)
public class ReadyMixin {
@Inject(method = "setupServer()Z", at = @At("TAIL"))
private void init(CallbackInfoReturnable<Boolean> cir) {
Main.LOGGER.info("Starting data generation!");
String versionName = MinecraftVersion.CURRENT.getName();
Path serverRootDirectory = DGU.getCurrentlyRunningServer().getRunDirectory().toPath().toAbsolutePath();
Path dataDumpDirectory = serverRootDirectory.resolve("minecraft-data").resolve(versionName);
DataGenerators.runDataGenerators(dataDumpDirectory);
Main.LOGGER.info("Done data generation!");
Runtime.getRuntime().halt(0);
}
}
@@ -0,0 +1,29 @@
package dev.u9g.minecraftdatagenerator.mixin;
import net.fabricmc.fabric.impl.biome.TheEndBiomeData;
import net.fabricmc.fabric.impl.biome.WeightedPicker;
import net.minecraft.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.Map;
@Mixin(TheEndBiomeData.class)
public interface TheEndBiomeDataAccessor {
@Accessor(value = "END_BIOMES_MAP", remap = false)
static Map<RegistryKey<Biome>, WeightedPicker<RegistryKey<Biome>>> END_BIOMES_MAP() {
throw new IllegalStateException("Should never be called.");
}
@Accessor(value = "END_MIDLANDS_MAP", remap = false)
static Map<RegistryKey<Biome>, WeightedPicker<RegistryKey<Biome>>> END_MIDLANDS_MAP() {
throw new IllegalStateException("Should never be called.");
}
@Accessor(value = "END_BARRENS_MAP", remap = false)
static Map<RegistryKey<Biome>, WeightedPicker<RegistryKey<Biome>>> END_BARRENS_MAP() {
throw new IllegalStateException("Should never be called.");
}
}
@@ -0,0 +1,35 @@
package dev.u9g.minecraftdatagenerator.mojangannoyances;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockRenderView;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.ColorResolver;
@Environment(EnvType.CLIENT)
public class BiomeColors {
public static final ColorResolver GRASS_COLOR = Biome::getGrassColorAt;
public static final ColorResolver FOLIAGE_COLOR = (biome, x, z) -> biome.getFoliageColor();
public static final ColorResolver WATER_COLOR = (biome, x, z) -> biome.getWaterColor();
public BiomeColors() {
}
private static int getColor(BlockRenderView world, BlockPos pos, ColorResolver resolver) {
return world.getColor(pos, resolver);
}
public static int getGrassColor(BlockRenderView world, BlockPos pos) {
return getColor(world, pos, GRASS_COLOR);
}
public static int getFoliageColor(BlockRenderView world, BlockPos pos) {
return getColor(world, pos, FOLIAGE_COLOR);
}
public static int getWaterColor(BlockRenderView world, BlockPos pos) {
return getColor(world, pos, WATER_COLOR);
}
}
@@ -0,0 +1,11 @@
package dev.u9g.minecraftdatagenerator.mojangannoyances;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockRenderView;
import org.jetbrains.annotations.Nullable;
public interface BlockColorProvider {
int getColor(BlockState state, @Nullable BlockRenderView world, @Nullable BlockPos pos, int tintIndex);
}
@@ -0,0 +1,94 @@
package dev.u9g.minecraftdatagenerator.mojangannoyances;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import dev.u9g.minecraftdatagenerator.util.DGU;
import net.minecraft.block.*;
import net.minecraft.block.enums.DoubleBlockHalf;
import net.minecraft.client.color.world.FoliageColors;
import net.minecraft.client.color.world.GrassColors;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.state.property.Property;
import net.minecraft.util.collection.IdList;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockRenderView;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Set;
public class BlockColors {
private static final int NO_COLOR = -1;
private final IdList<BlockColorProvider> providers = new IdList<>(32);
private final Map<Block, Set<Property<?>>> properties = Maps.newHashMap();
public BlockColors() {
}
public static BlockColors create() {
BlockColors blockColors = new BlockColors();
blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getGrassColor(world, state.get(TallPlantBlock.HALF) == DoubleBlockHalf.UPPER ? pos.down() : pos) : -1, Blocks.LARGE_FERN, Blocks.TALL_GRASS);
blockColors.registerColorProperty(TallPlantBlock.HALF, Blocks.LARGE_FERN, Blocks.TALL_GRASS);
blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getGrassColor(world, pos) : GrassColors.getColor(0.5, 1.0), Blocks.GRASS_BLOCK, Blocks.FERN, Blocks.SHORT_GRASS, Blocks.POTTED_FERN);
blockColors.registerColorProvider((state, world, pos, tintIndex) -> FoliageColors.getSpruceColor(), Blocks.SPRUCE_LEAVES);
blockColors.registerColorProvider((state, world, pos, tintIndex) -> FoliageColors.getBirchColor(), Blocks.BIRCH_LEAVES);
blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getFoliageColor(world, pos) : FoliageColors.getDefaultColor(), Blocks.OAK_LEAVES, Blocks.JUNGLE_LEAVES, Blocks.ACACIA_LEAVES, Blocks.DARK_OAK_LEAVES, Blocks.VINE, Blocks.MANGROVE_LEAVES);
blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getWaterColor(world, pos) : -1, Blocks.WATER, Blocks.BUBBLE_COLUMN, Blocks.WATER_CAULDRON);
blockColors.registerColorProvider((state, world, pos, tintIndex) -> RedstoneWireBlock.getWireColor(state.get(RedstoneWireBlock.POWER)), Blocks.REDSTONE_WIRE);
blockColors.registerColorProperty(RedstoneWireBlock.POWER, Blocks.REDSTONE_WIRE);
blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? BiomeColors.getGrassColor(world, pos) : -1, Blocks.SUGAR_CANE);
blockColors.registerColorProvider((state, world, pos, tintIndex) -> 14731036, Blocks.ATTACHED_MELON_STEM, Blocks.ATTACHED_PUMPKIN_STEM);
blockColors.registerColorProvider((state, world, pos, tintIndex) -> {
int i = state.get(StemBlock.AGE);
int j = i * 32;
int k = 255 - i * 8;
int l = i * 4;
return j << 16 | k << 8 | l;
}, Blocks.MELON_STEM, Blocks.PUMPKIN_STEM);
blockColors.registerColorProperty(StemBlock.AGE, Blocks.MELON_STEM, Blocks.PUMPKIN_STEM);
blockColors.registerColorProvider((state, world, pos, tintIndex) -> world != null && pos != null ? 2129968 : 7455580, Blocks.LILY_PAD);
return blockColors;
}
public int getParticleColor(BlockState state, World world, BlockPos pos) {
BlockColorProvider blockColorProvider = this.providers.get(DGU.getWorld().getRegistryManager().get(RegistryKeys.BLOCK).getRawId(state.getBlock()));
if (blockColorProvider != null) {
return blockColorProvider.getColor(state, null, null, 0);
} else {
MapColor mapColor = state.getMapColor(world, pos);
return mapColor != null ? mapColor.color : -1;
}
}
public int getColor(BlockState state, @Nullable BlockRenderView world, @Nullable BlockPos pos, int tintIndex) {
BlockColorProvider blockColorProvider = this.providers.get(DGU.getWorld().getRegistryManager().get(RegistryKeys.BLOCK).getRawId(state.getBlock()));
return blockColorProvider == null ? -1 : blockColorProvider.getColor(state, world, pos, tintIndex);
}
public void registerColorProvider(BlockColorProvider provider, Block... blocks) {
int var4 = blocks.length;
for (Block block : blocks) {
this.providers.set(provider, DGU.getWorld().getRegistryManager().get(RegistryKeys.BLOCK).getRawId(block));
}
}
private void registerColorProperties(Set<Property<?>> properties, Block... blocks) {
int var4 = blocks.length;
for (Block block : blocks) {
this.properties.put(block, properties);
}
}
private void registerColorProperty(Property<?> property, Block... blocks) {
this.registerColorProperties(ImmutableSet.of(property), blocks);
}
public Set<Property<?>> getProperties(Block block) {
return this.properties.getOrDefault(block, ImmutableSet.of());
}
}
@@ -0,0 +1,32 @@
package dev.u9g.minecraftdatagenerator.util;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.Language;
import net.minecraft.world.World;
public class DGU {
private static final Language language = Language.getInstance();
@SuppressWarnings("deprecation")
private static MinecraftServer getCurrentlyRunningServerDedicated() {
return (MinecraftServer) FabricLoader.getInstance().getGameInstance();
}
public static MinecraftServer getCurrentlyRunningServer() {
return getCurrentlyRunningServerDedicated();
}
private static String translateTextFallback(String translationKey) {
return language.get(translationKey);
}
public static String translateText(String translationKey) {
return translateTextFallback(translationKey);
}
public static World getWorld() {
return getCurrentlyRunningServer().getOverworld();
}
}
@@ -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;
}
}
+27
View File
@@ -0,0 +1,27 @@
{
"schemaVersion": 1,
"id": "minecraft-data-generator",
"version": "${version}",
"name": "Minecraft Data Generator",
"description": "",
"authors": [
"Archengius",
"U9G"
],
"contact": {},
"license": "MIT",
"environment": "server",
"entrypoints": {
"main": [
"dev.u9g.minecraftdatagenerator.Main"
]
},
"mixins": [
"minecraft-data-generator.mixins.json"
],
"depends": {
"fabricloader": ">=0.14.4",
"fabric": "*",
"minecraft": "*"
}
}
@@ -0,0 +1,18 @@
{
"required": true,
"minVersion": "0.8",
"package": "dev.u9g.minecraftdatagenerator.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"EntityTypeAccessor",
"EULAMixin",
"MiningToolItemAccessor",
"ReadyMixin",
"TheEndBiomeDataAccessor"
],
"client": [
],
"injectors": {
"defaultRequire": 1
}
}
+1
View File
@@ -46,4 +46,5 @@ dependencyResolutionManagement {
"1.19",
"1.19.2",
"1.20",
"1.20.4",
].forEach { include it }