make it work

This commit is contained in:
u9g
2022-05-05 16:09:16 -04:00
commit 64250071a2
33 changed files with 2280 additions and 0 deletions
+118
View File
@@ -0,0 +1,118 @@
# User-specific stuff
.idea/
*.iml
*.ipr
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
.gradle
build/
# Ignore Gradle GUI config
gradle-app.setting
# Cache of project
.gradletasknamecache
**/build/
# Common working directory
run/
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2022 Archengius, U9G
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+81
View File
@@ -0,0 +1,81 @@
plugins {
id 'fabric-loom' version '0.12-SNAPSHOT'
id 'maven-publish'
}
version = project.mod_version
group = project.maven_group
repositories {
// Add repositories to retrieve artifacts from in here.
// You should only use this when depending on other mods because
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
// for more information about repositories.
}
dependencies {
// To change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
}
processResources {
inputs.property "version", project.version
filteringCharset "UTF-8"
filesMatching("fabric.mod.json") {
expand "version": project.version
}
}
def targetJavaVersion = 17
tasks.withType(JavaCompile).configureEach {
// ensure that the encoding is set to UTF-8, no matter what the system default is
// this fixes some edge cases with special characters not displaying correctly
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
// If Javadoc is generated, this must be specified in that task too.
it.options.encoding = "UTF-8"
if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) {
it.options.release = targetJavaVersion
}
}
java {
def javaVersion = JavaVersion.toVersion(targetJavaVersion)
if (JavaVersion.current() < javaVersion) {
toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion)
}
archivesBaseName = project.archives_base_name
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this line, sources will not be generated.
withSourcesJar()
}
jar {
from("LICENSE") {
rename { "${it}_${project.archivesBaseName}" }
}
}
// configure the maven publication
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
}
}
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
repositories {
// Add repositories to publish to here.
// Notice: This block does NOT have the same function as the block in the top level.
// The repositories here will be used for publishing your artifact, not for
// retrieving dependencies.
}
}
+14
View File
@@ -0,0 +1,14 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G
# Fabric Properties
# check these on https://modmuss50.me/fabric.html
minecraft_version=1.18
yarn_mappings=1.18+build.1
loader_version=0.14.4
# Mod Properties
mod_version=1.0-SNAPSHOT
maven_group=dev.u9g
archives_base_name=minecrft-data-generator-server
# Dependencies
# check this on https://modmuss50.me/fabric.html
fabric_version=0.44.0+1.18
Binary file not shown.
+5
View File
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+234
View File
@@ -0,0 +1,234 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
Vendored
+89
View File
@@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+9
View File
@@ -0,0 +1,9 @@
pluginManagement {
repositories {
maven {
name = 'Fabric'
url = 'https://maven.fabricmc.net/'
}
gradlePluginPortal()
}
}
@@ -0,0 +1,13 @@
package dev.u9g.minecrftdatagenerator;
import net.fabricmc.api.ModInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main implements ModInitializer {
public static Logger LOGGER = LoggerFactory.getLogger("mc-data-gen-serv");
@Override
public void onInitialize() {
}
}
@@ -0,0 +1,57 @@
package dev.u9g.minecrftdatagenerator.generators;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import dev.u9g.minecrftdatagenerator.util.DGU;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.DynamicRegistryManager;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.biome.Biome;
public class BiomesDataGenerator implements IDataGenerator {
private static String guessBiomeDimensionFromCategory(Biome biome) {
return switch (biome.getCategory()) {
case NETHER -> "nether";
case THEEND -> "end";
default -> "overworld";
};
}
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());
biomeDesc.addProperty("id", registry.getRawId(biome));
biomeDesc.addProperty("name", registryKey.getPath());
biomeDesc.addProperty("category", biome.getCategory().getName());
biomeDesc.addProperty("temperature", biome.getTemperature());
biomeDesc.addProperty("precipitation", biome.getPrecipitation().getName());
//biomeDesc.addProperty("depth", biome.getDepth()); - Doesn't exist anymore in minecraft source
biomeDesc.addProperty("dimension", guessBiomeDimensionFromCategory(biome));
biomeDesc.addProperty("displayName", DGU.translateText(localizationKey));
biomeDesc.addProperty("color", biome.getSkyColor());
biomeDesc.addProperty("rainfall", biome.getDownfall());
return biomeDesc;
}
@Override
public String getDataName() {
return "biomes";
}
@Override
public JsonArray generateDataJson() {
JsonArray biomesArray = new JsonArray();
DynamicRegistryManager registryManager = DynamicRegistryManager.create();
Registry<Biome> biomeRegistry = registryManager.get(Registry.BIOME_KEY);
biomeRegistry.stream()
.map(biome -> generateBiomeInfo(biomeRegistry, biome))
.forEach(biomesArray::add);
return biomesArray;
}
}
@@ -0,0 +1,111 @@
package dev.u9g.minecrftdatagenerator.generators;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.EmptyBlockView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BlockCollisionShapesDataGenerator implements IDataGenerator {
private static class BlockShapesCache {
public Map<VoxelShape, Integer> uniqueBlockShapes = new HashMap<>();
public 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;
}
}
@Override
public String getDataName() {
return "blockCollisionShapes";
}
@Override
public JsonObject generateDataJson() {
Registry<Block> blockRegistry = Registry.BLOCK;
BlockShapesCache blockShapesCache = new BlockShapesCache();
blockRegistry.forEach(blockShapesCache::processBlock);
JsonObject resultObject = new JsonObject();
resultObject.add("blocks", blockShapesCache.dumpBlockShapeIndices(blockRegistry));
resultObject.add("shapes", blockShapesCache.dumpShapesObject());
return resultObject;
}
}
@@ -0,0 +1,195 @@
package dev.u9g.minecrftdatagenerator.generators;
import com.google.common.base.CaseFormat;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import dev.u9g.minecrftdatagenerator.util.DGU;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.loot.context.LootContext;
import net.minecraft.loot.context.LootContextParameters;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.state.property.BooleanProperty;
import net.minecraft.state.property.EnumProperty;
import net.minecraft.state.property.IntProperty;
import net.minecraft.state.property.Property;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.EmptyBlockView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
public class BlocksDataGenerator implements IDataGenerator {
private static Logger logger = LoggerFactory.getLogger(BlocksDataGenerator.class);
private static List<Item> getItemsEffectiveForBlock(BlockState blockState) {
return Registry.ITEM.stream()
.filter(item -> item.getDefaultStack().isSuitableFor(blockState))
.collect(Collectors.toList());
}
private static void populateDropsIfPossible(BlockState blockState, Item firstToolItem, List<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();
LootContext.Builder lootContext = new LootContext.Builder(serverWorld)
.parameter(LootContextParameters.BLOCK_STATE, blockState)
.parameter(LootContextParameters.ORIGIN, Vec3d.ZERO)
.parameter(LootContextParameters.TOOL, firstToolItem.getDefaultStack())
.random(0L);
outDrops.addAll(blockState.getDroppedStacks(lootContext));
} else {
//If we're lacking world context to correctly determine drops, assume that default drop is ItemBlock stack in quantity of 1
Item itemBlock = blockState.getBlock().asItem();
if (itemBlock != Items.AIR) {
outDrops.add(itemBlock.getDefaultStack());
}
}
}
private static String getPropertyTypeName(Property<?> property) {
//Explicitly handle default minecraft properties
if (property instanceof BooleanProperty) {
return "bool";
}
if (property instanceof IntProperty) {
return "int";
}
if (property instanceof EnumProperty) {
return "enum";
}
//Use simple class name as fallback, this code will give something like
//example_type for ExampleTypeProperty class name
String rawPropertyName = property.getClass().getSimpleName().replace("Property", "");
return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, rawPropertyName);
}
private static <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;
}
@Override
public String getDataName() {
return "blocks";
}
@Override
public JsonArray generateDataJson() {
JsonArray resultBlocksArray = new JsonArray();
Registry<Block> blockRegistry = Registry.BLOCK;
List<MaterialsDataGenerator.MaterialInfo> availableMaterials = MaterialsDataGenerator.getGlobalMaterialInfo();
blockRegistry.forEach(block -> resultBlocksArray.add(generateBlock(blockRegistry, availableMaterials, block)));
return resultBlocksArray;
}
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();
blockDesc.addProperty("id", blockRegistry.getRawId(block));
blockDesc.addProperty("name", registryKey.getPath());
// if (Objects.equals(registryKey.getPath(), "red_candle_cake")) {
// System.out.println();
// }
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.getHardness() == 0f);
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);
List<Item> effectiveTools = getItemsEffectiveForBlock(defaultState);
//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;
}
}
@@ -0,0 +1,77 @@
package dev.u9g.minecrftdatagenerator.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 List<IDataGenerator> GENERATORS = new ArrayList<>();
private static final Logger logger = LoggerFactory.getLogger(DataGenerators.class);
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;
}
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());
}
}
@@ -0,0 +1,36 @@
package dev.u9g.minecrftdatagenerator.generators;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import dev.u9g.minecrftdatagenerator.util.DGU;
import net.minecraft.entity.effect.StatusEffect;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
public class EffectsDataGenerator implements IDataGenerator {
@Override
public String getDataName() {
return "effects";
}
@Override
public JsonArray generateDataJson() {
JsonArray resultsArray = new JsonArray();
Registry<StatusEffect> statusEffectRegistry = Registry.STATUS_EFFECT;
statusEffectRegistry.forEach(effect -> resultsArray.add(generateEffect(statusEffectRegistry, effect)));
return resultsArray;
}
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));
effectDesc.addProperty("name", registryKey.getPath());
effectDesc.addProperty("displayName", DGU.translateText(statusEffect.getTranslationKey()));
effectDesc.addProperty("type", statusEffect.isBeneficial() ? "good" : "bad");
return effectDesc;
}
}
@@ -0,0 +1,108 @@
package dev.u9g.minecrftdatagenerator.generators;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import dev.u9g.minecrftdatagenerator.util.DGU;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.EnchantmentTarget;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
public class EnchantmentsDataGenerator implements IDataGenerator {
private static final ImmutableMap<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;
}
@Override
public String getDataName() {
return "enchantments";
}
@Override
public JsonArray generateDataJson() {
JsonArray resultsArray = new JsonArray();
Registry<Enchantment> enchantmentRegistry = Registry.ENCHANTMENT;
enchantmentRegistry.stream()
.forEach(enchantment -> resultsArray.add(generateEnchantment(enchantmentRegistry, enchantment)));
return resultsArray;
}
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)
.collect(Collectors.toList());
JsonArray excludes = new JsonArray();
for (Enchantment excludedEnchantment : incompatibleEnchantments) {
Identifier otherKey = registry.getKey(excludedEnchantment).orElseThrow().getValue();
excludes.add(otherKey.getPath());
}
enchantmentDesc.add("exclude", excludes);
enchantmentDesc.addProperty("category", getEnchantmentTargetName(enchantment.type));
enchantmentDesc.addProperty("weight", enchantment.getRarity().getWeight());
enchantmentDesc.addProperty("tradeable", enchantment.isAvailableForEnchantedBookOffer());
enchantmentDesc.addProperty("discoverable", enchantment.isAvailableForRandomSelection());
return enchantmentDesc;
}
}
@@ -0,0 +1,113 @@
package dev.u9g.minecrftdatagenerator.generators;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import dev.u9g.minecrftdatagenerator.util.DGU;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.mob.AmbientEntity;
import net.minecraft.entity.mob.HostileEntity;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.mob.WaterCreatureEntity;
import net.minecraft.entity.passive.AnimalEntity;
import net.minecraft.entity.passive.PassiveEntity;
import net.minecraft.entity.projectile.ProjectileEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
public class EntitiesDataGenerator implements IDataGenerator {
@Override
public String getDataName() {
return "entities";
}
@Override
public JsonArray generateDataJson() {
JsonArray resultArray = new JsonArray();
Registry<EntityType<?>> entityTypeRegistry = Registry.ENTITY_TYPE;
entityTypeRegistry.forEach(entity -> resultArray.add(generateEntity(entityTypeRegistry, entity)));
return resultArray;
}
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()) : "player";
}
entityDesc.addProperty("type", entityTypeString);
entityDesc.addProperty("category", getCategoryFrom(entityType));
return entityDesc;
}
private static String getCategoryFrom(EntityType<?> entityType) {
if (entityType == EntityType.PLAYER) return "UNKNOWN"; // fail early for player entities
Entity entity = EntityType.createInstanceFromId(Registry.ENTITY_TYPE.getRawId(entityType), DGU.getWorld());
if (entity == null) throw new Error("Entity was null after trying to create a: " + DGU.translateText(entityType.getTranslationKey()));
entity.discard();
return switch (entity.getClass().getPackageName()) {
case "net.minecraft.entity.decoration", "net.minecraft.entity.decoration.painting" -> "Immobile";
case "net.minecraft.entity.boss", "net.minecraft.entity.mob", "net.minecraft.entity.boss.dragon" -> "Hostile Mobs";
case "net.minecraft.entity.projectile", "net.minecraft.entity.projectile.thrown" -> "Projectiles";
case "net.minecraft.entity.passive" -> "Passive Mobs";
case "net.minecraft.entity.vehicle" -> "Vehicles";
case "net.minecraft.entity" -> "UNKNOWN";
default -> throw new Error("Unexpected entity type: " + entity.getClass().getPackageName());
};
}
//Honestly, both "type" and "category" fields in the schema and examples do not contain any useful information
//Since category is optional, I will just leave it out, and for type I will assume general entity classification
//by the Entity class hierarchy (which has some weirdness too by the way)
private static String getEntityTypeForClass(Class<? 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";
}
}
@@ -0,0 +1,51 @@
package dev.u9g.minecrftdatagenerator.generators;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import dev.u9g.minecrftdatagenerator.util.DGU;
import net.minecraft.item.FoodComponent;
import net.minecraft.item.Item;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
import java.util.Objects;
public class FoodsDataGenerator implements IDataGenerator {
@Override
public String getDataName() {
return "foods";
}
public JsonArray generateDataJson() {
JsonArray resultsArray = new JsonArray();
Registry<Item> itemRegistry = Registry.ITEM;
itemRegistry.stream()
.filter(Item::isFood)
.forEach(food -> resultsArray.add(generateFoodDescriptor(itemRegistry, food)));
return resultsArray;
}
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;
}
}
@@ -0,0 +1,10 @@
package dev.u9g.minecrftdatagenerator.generators;
import com.google.gson.JsonElement;
public interface IDataGenerator {
String getDataName();
JsonElement generateDataJson();
}
@@ -0,0 +1,25 @@
package dev.u9g.minecrftdatagenerator.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,78 @@
package dev.u9g.minecrftdatagenerator.generators;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import dev.u9g.minecrftdatagenerator.util.DGU;
import net.minecraft.enchantment.EnchantmentTarget;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class ItemsDataGenerator implements IDataGenerator {
private static List<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());
}
@Override
public String getDataName() {
return "items";
}
@Override
public JsonArray generateDataJson() {
JsonArray resultArray = new JsonArray();
Registry<Item> itemRegistry = Registry.ITEM;
itemRegistry.stream().forEach(item -> resultArray.add(generateItem(itemRegistry, item)));
return resultArray;
}
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));
}
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());
}
itemDesc.add("fixedWith", fixedWithArray);
int maxDurability = item.getMaxDamage();
itemDesc.addProperty("durability", maxDurability);
itemDesc.addProperty("maxDurability", maxDurability);
}
return itemDesc;
}
}
@@ -0,0 +1,26 @@
package dev.u9g.minecrftdatagenerator.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,198 @@
package dev.u9g.minecrftdatagenerator.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.minecrftdatagenerator.mixin.MiningToolItemAccessor;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.Material;
import net.minecraft.item.Item;
import net.minecraft.item.Items;
import net.minecraft.item.MiningToolItem;
import net.minecraft.item.SwordItem;
import net.minecraft.tag.BlockTags;
import net.minecraft.tag.Tag;
import net.minecraft.util.registry.Registry;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
//TODO entire idea of linking materials to tool speeds is obsolete and just wrong now,
//TODO but we kinda have to support it to let old code work for computing digging times,
//TODO so for now we will handle materials as "virtual" ones based on which tools can break blocks
public class MaterialsDataGenerator implements IDataGenerator {
private static final List<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();
@Override
public String getDataName() {
return "materials";
}
private static String makeMaterialNameForTag(Tag<Block> tag) {
Tag.Identified<Block> identifiedTag = (Tag.Identified<Block>) tag;
return identifiedTag.getId().getPath();
}
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;
}
}
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)));
resultList.add(new MaterialInfo("gourd", blockState -> blockState.getMaterial() == Material.GOURD));
resultList.add(new MaterialInfo("plant", blockState -> blockState.getMaterial() == Material.PLANT || blockState.getMaterial() == Material.REPLACEABLE_PLANT));
HashSet<String> uniqueMaterialNames = new HashSet<>();
Registry<Item> itemRegistry = Registry.ITEM;
itemRegistry.forEach(item -> {
if (item instanceof MiningToolItem toolItem) {
Tag<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 JsonElement generateDataJson() {
Registry<Item> itemRegistry = Registry.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) {
Tag<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;
}
}
@@ -0,0 +1,32 @@
package dev.u9g.minecrftdatagenerator.generators;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import net.minecraft.particle.ParticleType;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
public class ParticlesDataGenerator implements IDataGenerator {
@Override
public String getDataName() {
return "particles";
}
@Override
public JsonArray generateDataJson() {
JsonArray resultsArray = new JsonArray();
Registry<ParticleType<?>>particleTypeRegistry = Registry.PARTICLE_TYPE;
particleTypeRegistry.forEach(particleType -> resultsArray.add(generateParticleType(particleTypeRegistry, particleType)));
return resultsArray;
}
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;
}
}
@@ -0,0 +1,133 @@
package dev.u9g.minecrftdatagenerator.generators;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import dev.u9g.minecrftdatagenerator.util.DGU;
import net.minecraft.client.MinecraftClient;
import net.minecraft.item.Item;
import net.minecraft.recipe.Ingredient;
import net.minecraft.recipe.Recipe;
import net.minecraft.recipe.ShapedRecipe;
import net.minecraft.recipe.ShapelessRecipe;
import net.minecraft.util.registry.Registry;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class RecipeDataGenerator implements IDataGenerator {
@Override
public String getDataName() {
return "recipes";
}
@Override
public JsonElement generateDataJson() {
JsonObject finalObj = new JsonObject();
Multimap<Integer, JsonObject> recipes = ArrayListMultimap.create();
for (Recipe<?> recipe : Objects.requireNonNull(DGU.getWorld()).getRecipeManager().values()) {
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 < 3; y++) {
var jsonRow = new JsonArray();
int one = iter.next();
int two = iter.next();
int three = iter.next();
if (y > 0 && one == -1 && two == -1 && three == -1) continue;
jsonRow.add(one);
jsonRow.add(two);
jsonRow.add(three);
inShape.add(jsonRow);
}
JsonObject finalRecipe = new JsonObject();
finalRecipe.add("inShape", inShape);
var resultObject = new JsonObject();
resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem()));
resultObject.addProperty("count", sr.getOutput().getCount());
finalRecipe.add("result", resultObject);
String id = ((Integer) getRawIdFor(sr.getOutput().getItem())).toString();
if (!finalObj.has(id)) {
finalObj.add(id, new JsonArray());
}
finalObj.get(id).getAsJsonArray().add(finalRecipe);
// var input = new JsonArray();
// var ingredients = sr.getIngredients().stream().toList();
// for (int y = 0; y < sr.getHeight(); y++) {
// var arr = new JsonArray();
// for (int x = 0; x < sr.getWidth(); x++) {
// if ((y*3)+x >= ingredients.size()) {
// arr.add(JsonNull.INSTANCE);
// continue;
// }
// var ingredient = ingredients.get((y*3)+x).getMatchingStacks(); // FIXME: fix when there are more than one matching stack
// if (ingredient.length == 0) {
// arr.add(JsonNull.INSTANCE);
// } else {
// arr.add(getRawIdFor(ingredient[0].getItem()));
// }
// }
// input.add(arr);
// }
// var rootRecipeObject = new JsonObject();
// rootRecipeObject.add("inShape", input);
// var resultObject = new JsonObject();
// resultObject.addProperty("id", getRawIdFor(sr.getOutput().getItem()));
// resultObject.addProperty("count", sr.getOutput().getCount());
// rootRecipeObject.add("result", resultObject);
// recipes.put(getRawIdFor(sr.getOutput().getItem()), rootRecipeObject);
} else if (recipe instanceof ShapelessRecipe sl) {
var ingredients = new JsonArray();
for (Ingredient ingredient : sl.getIngredients()) {
if (ingredient.isEmpty()) continue;
ingredients.add(getRawIdFor(ingredient.getMatchingStacks()[0].getItem()));
}
var rootRecipeObject = new JsonObject();
rootRecipeObject.add("ingredients", ingredients);
var resultObject = new JsonObject();
resultObject.addProperty("id", getRawIdFor(sl.getOutput().getItem()));
resultObject.addProperty("count", sl.getOutput().getCount());
rootRecipeObject.add("result", resultObject);
recipes.put(getRawIdFor(sl.getOutput().getItem()), rootRecipeObject);
}
}
recipes.forEach((a, b) -> {
if (!finalObj.has(a.toString())) {
finalObj.add(a.toString(), new JsonArray());
}
finalObj.get(a.toString()).getAsJsonArray().add(b);
});
return finalObj;
}
private static int getRawIdFor (Item item) {
return Registry.ITEM.getRawId(item);
}
}
@@ -0,0 +1,168 @@
package dev.u9g.minecrftdatagenerator.generators;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import dev.u9g.minecrftdatagenerator.util.EmptyRenderBlockView;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.block.RedstoneWireBlock;
import net.minecraft.client.color.block.BlockColors;
import net.minecraft.client.color.world.FoliageColors;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.registry.DynamicRegistryManager;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.biome.Biome;
import java.util.*;
public class TintsDataGenerator implements IDataGenerator {
public static class BiomeTintColors {
Map<Integer, List<Biome>> grassColoursMap = new HashMap<>();
Map<Integer, List<Biome>> foliageColoursMap = new HashMap<>();
Map<Integer, List<Biome>> waterColourMap = new HashMap<>();
}
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;
}
@Environment(EnvType.CLIENT)
private static int getBlockColor(Block block, BlockColors blockColors) {
return blockColors.getColor(block.getDefaultState(), EmptyRenderBlockView.INSTANCE, BlockPos.ORIGIN, 0xFFFFFF);
}
@Environment(EnvType.CLIENT)
public static Map<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 = DynamicRegistryManager.create();
Registry<Biome> biomeRegistry = registryManager.get(Registry.BIOME_KEY);
Registry<Block> blockRegistry = registryManager.get(Registry.BLOCK_KEY);
BiomeTintColors biomeTintColors = generateBiomeTintColors(biomeRegistry);
Map<Integer, Integer> redstoneColors = generateRedstoneTintColors();
Map<Block, Integer> constantTintColors = Collections.emptyMap();
EnvType currentEnvironment = FabricLoader.getInstance().getEnvironmentType();
if (currentEnvironment == EnvType.CLIENT) {
constantTintColors = generateConstantTintColors();
}
JsonObject resultObject = new JsonObject();
resultObject.add("grass", encodeBiomeColorMap(biomeRegistry, biomeTintColors.grassColoursMap));
resultObject.add("foliage", encodeBiomeColorMap(biomeRegistry, biomeTintColors.foliageColoursMap));
resultObject.add("water", encodeBiomeColorMap(biomeRegistry, biomeTintColors.waterColourMap));
resultObject.add("redstone", encodeRedstoneColorMap(redstoneColors));
resultObject.add("constant", encodeBlocksColorMap(blockRegistry, constantTintColors));
return resultObject;
}
}
@@ -0,0 +1,57 @@
package dev.u9g.minecrftdatagenerator.mixin;
import com.google.common.base.Preconditions;
import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerInteractionManager;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.World;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ClientPlayerInteractionManager.class)
public abstract class ClientPlayerInteractionManagerMixin {
@Unique
private long blockBreakingStartTicks;
@Unique
private BlockState stateBeingBroken;
@Inject(method = "attackBlock", at = @At(value = "FIELD", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;currentBreakingProgress:F", opcode = Opcodes.PUTFIELD))
private void onBlockAttacked(BlockPos pos, Direction direction, CallbackInfoReturnable<Boolean> callbackInfo) {
World world = Preconditions.checkNotNull(MinecraftClient.getInstance().world);
this.blockBreakingStartTicks = world.getTime();
}
@Inject(method = "cancelBlockBreaking", at = @At("HEAD"))
private void onBlockBreakingCanceled(CallbackInfo callbackInfo) {
this.blockBreakingStartTicks = -1L;
}
@Inject(method = "breakBlock", at = @At("HEAD"))
private void onBreakBlockHead(BlockPos pos, CallbackInfoReturnable<Boolean> callbackInfo) {
World world = Preconditions.checkNotNull(MinecraftClient.getInstance().world);
this.stateBeingBroken = world.getBlockState(pos);
}
@Inject(method = "breakBlock", at = @At("RETURN"))
private void onBreakBlockReturn(BlockPos pos, CallbackInfoReturnable<Boolean> callbackInfo) {
World world = Preconditions.checkNotNull(MinecraftClient.getInstance().world);
if (callbackInfo.getReturnValue()) {
Preconditions.checkNotNull(stateBeingBroken);
if (blockBreakingStartTicks != -1L) {
long totalTicksElapsed = world.getTime() - this.blockBreakingStartTicks;
long totalTimeElapsed = totalTicksElapsed * 50L;
System.out.printf("Breaking block %s took %dms%n", stateBeingBroken.getBlock(), totalTimeElapsed);
} else {
System.out.printf("Breaking block %s took 0ms [INSTANT BREAK]%n", stateBeingBroken.getBlock());
}
}
this.blockBreakingStartTicks = -1L;
}
}
@@ -0,0 +1,17 @@
package dev.u9g.minecrftdatagenerator.mixin;
import net.minecraft.block.Block;
import net.minecraft.item.MiningToolItem;
import net.minecraft.tag.Tag;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(MiningToolItem.class)
public interface MiningToolItemAccessor {
@Accessor
Tag<Block> getEffectiveBlocks();
@Accessor
float getMiningSpeed();
}
@@ -0,0 +1,27 @@
package dev.u9g.minecrftdatagenerator.mixin;
import dev.u9g.minecrftdatagenerator.Main;
import dev.u9g.minecrftdatagenerator.generators.DataGenerators;
import dev.u9g.minecrftdatagenerator.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!");
DGU.getCurrentlyRunningServer().stop(false);
}
}
@@ -0,0 +1,60 @@
package dev.u9g.minecrftdatagenerator.util;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.server.MinecraftServer;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Language;
import net.minecraft.world.World;
public class DGU {
@Environment(EnvType.CLIENT)
private static MinecraftServer getCurrentlyRunningServerClient() {
return MinecraftClient.getInstance().getServer();
}
@SuppressWarnings("deprecation")
private static MinecraftServer getCurrentlyRunningServerDedicated() {
return (MinecraftServer) FabricLoader.getInstance().getGameInstance();
}
public static MinecraftServer getCurrentlyRunningServer() {
EnvType environmentType = FabricLoader.getInstance().getEnvironmentType();
if (environmentType == EnvType.CLIENT) {
return getCurrentlyRunningServerClient();
} else if (environmentType == EnvType.SERVER) {
return getCurrentlyRunningServerDedicated();
}
throw new UnsupportedOperationException();
}
@Environment(EnvType.CLIENT)
private static String translateTextClient(String translationKey) {
return I18n.translate(translationKey);
}
private static Language language = Language.getInstance();
private static String translateTextFallback(String translationKey) {
try {
return language.get(translationKey);
} catch (Exception ignored) {}
throw new RuntimeException("Failed to translate: '" + translationKey + "'");
}
public static String translateText(String translationKey) {
EnvType environmentType = FabricLoader.getInstance().getEnvironmentType();
if (environmentType == EnvType.CLIENT) {
return translateTextClient(translationKey);
}
return translateTextFallback(translationKey);
}
public static World getWorld() {
return getCurrentlyRunningServer().getOverworld();
}
}
@@ -0,0 +1,73 @@
package dev.u9g.minecrftdatagenerator.util;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.fluid.FluidState;
import net.minecraft.fluid.Fluids;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.registry.DynamicRegistryManager;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.BlockRenderView;
import net.minecraft.world.LightType;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeKeys;
import net.minecraft.world.chunk.light.LightingProvider;
import net.minecraft.world.level.ColorResolver;
import org.jetbrains.annotations.Nullable;
public enum EmptyRenderBlockView implements BlockRenderView {
INSTANCE;
@Nullable
public BlockEntity getBlockEntity(BlockPos pos) {
return null;
}
public BlockState getBlockState(BlockPos pos) {
return Blocks.AIR.getDefaultState();
}
public FluidState getFluidState(BlockPos pos) {
return Fluids.EMPTY.getDefaultState();
}
public int getBottomY() {
return 0;
}
public int getHeight() {
return 0;
}
@Override
public float getBrightness(Direction direction, boolean shaded) {
return 0.0f;
}
@Override
public LightingProvider getLightingProvider() {
return null;
}
@Override
public int getColor(BlockPos pos, ColorResolver colorResolver) {
DynamicRegistryManager registryManager = DynamicRegistryManager.create();
Registry<Biome> biomeRegistry = registryManager.get(Registry.BIOME_KEY);
Biome plainsBiome = biomeRegistry.get(BiomeKeys.PLAINS);
return colorResolver.getColor(plainsBiome, pos.getX(), pos.getY());
}
@Override
public int getLightLevel(LightType type, BlockPos pos) {
return type == LightType.SKY ? getMaxLightLevel() : 0;
}
@Override
public int getBaseLightLevel(BlockPos pos, int ambientDarkness) {
return ambientDarkness;
}
}
+28
View File
@@ -0,0 +1,28 @@
{
"schemaVersion": 1,
"id": "minecrft-data-generator",
"version": "${version}",
"name": "Minecrft Data Generator",
"description": "",
"authors": [
"Archengius",
"U9G"
],
"contact": {},
"license": "MIT",
"icon": "assets/minecrft-data-generator/icon.png",
"environment": "server",
"entrypoints": {
"main": [
"dev.u9g.minecrftdatagenerator.Main"
]
},
"mixins": [
"minecrft-data-generator.mixins.json"
],
"depends": {
"fabricloader": ">=0.14.4",
"fabric": "*",
"minecraft": "1.18"
}
}
@@ -0,0 +1,16 @@
{
"required": true,
"minVersion": "0.8",
"package": "dev.u9g.minecrftdatagenerator.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"ClientPlayerInteractionManagerMixin",
"MiningToolItemAccessor",
"ReadyMixin"
],
"client": [
],
"injectors": {
"defaultRequire": 1
}
}