Isolate each extractor

This commit is contained in:
Ryan 2022-07-21 04:06:19 -07:00
parent e0b371d23c
commit c932b6cd71
8 changed files with 234 additions and 175 deletions

View file

@ -13,7 +13,7 @@ public class DummyPlayerEntity extends PlayerEntity {
public static final DummyPlayerEntity INSTANCE;
static {
INSTANCE = Util.magicallyInstantiate(DummyPlayerEntity.class);
INSTANCE = Main.magicallyInstantiate(DummyPlayerEntity.class);
try {
var dataTrackerField = Entity.class.getDeclaredField("dataTracker");

View file

@ -38,7 +38,7 @@ public class DummyWorld extends World {
public static final DummyWorld INSTANCE;
static {
INSTANCE = Util.magicallyInstantiate(DummyWorld.class);
INSTANCE = Main.magicallyInstantiate(DummyWorld.class);
try {
var randomField = World.class.getDeclaredField("random");

View file

@ -0,0 +1,78 @@
package dev._00a.valence_extractor;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import dev._00a.valence_extractor.extractors.Blocks;
import dev._00a.valence_extractor.extractors.Entities;
import dev._00a.valence_extractor.extractors.EntityStatuses;
import net.fabricmc.api.ModInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.reflect.ReflectionFactory;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Main implements ModInitializer {
public static final String MOD_ID = "valence_extractor";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
/**
* Magically creates an instance of a <i>concrete</i> class without calling its constructor.
*/
public static <T> T magicallyInstantiate(Class<T> clazz) {
var rf = ReflectionFactory.getReflectionFactory();
try {
var objCon = Object.class.getDeclaredConstructor();
var con = rf.newConstructorForSerialization(clazz, objCon);
return clazz.cast(con.newInstance());
} catch (Throwable e) {
throw new IllegalArgumentException("Failed to magically instantiate " + clazz.getName(), e);
}
}
@Override
public void onInitialize() {
LOGGER.info("Starting extractors...");
var extractors = new Extractor[]{new Blocks(), new Entities(), new EntityStatuses(),};
Path outputDirectory;
try {
outputDirectory = Files.createDirectories(Paths.get("valence_extractor_output"));
} catch (IOException e) {
LOGGER.info("Failed to create output directory.", e);
return;
}
var gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().serializeNulls().create();
for (var ext : extractors) {
try {
var out = outputDirectory.resolve(ext.fileName());
var fileWriter = new FileWriter(out.toFile(), StandardCharsets.UTF_8);
gson.toJson(ext.extract(), fileWriter);
fileWriter.close();
LOGGER.info("Wrote " + out.toAbsolutePath());
} catch (Exception e) {
LOGGER.error("Extractor for \"" + ext.fileName() + "\" failed.", e);
}
}
LOGGER.info("Done.");
System.exit(0);
}
public interface Extractor {
String fileName();
JsonElement extract() throws Exception;
}
public record Pair<T, U>(T left, U right) {
}
}

View file

@ -1,19 +0,0 @@
package dev._00a.valence_extractor;
import sun.reflect.ReflectionFactory;
public class Util {
/**
* Magically creates an instance of a <i>concrete</i> class without calling its constructor.
*/
public static <T> T magicallyInstantiate(Class<T> clazz) {
var rf = ReflectionFactory.getReflectionFactory();
try {
var objCon = Object.class.getDeclaredConstructor();
var con = rf.newConstructorForSerialization(clazz, objCon);
return clazz.cast(con.newInstance());
} catch (Throwable e) {
throw new IllegalArgumentException("Failed to magically instantiate " + clazz.getName(), e);
}
}
}

View file

@ -0,0 +1,81 @@
package dev._00a.valence_extractor.extractors;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import dev._00a.valence_extractor.Main;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.EmptyBlockView;
public class Blocks implements Main.Extractor {
public Blocks() {
}
@Override
public String fileName() {
return "blocks.json";
}
@Override
public JsonElement extract() {
var blocksJson = new JsonArray();
var stateIdCounter = 0;
for (var block : Registry.BLOCK) {
var blockJson = new JsonObject();
// blockJson.addProperty("id", Registry.BLOCK.getRawId(block));
blockJson.addProperty("translation_key", block.getTranslationKey());
// blockJson.addProperty("min_state_id", stateIdCounter);
// blockJson.addProperty("max_state_id", stateIdCounter + block.getStateManager().getStates().size() - 1);
var propsJson = new JsonArray();
for (var prop : block.getStateManager().getProperties()) {
var propJson = new JsonObject();
propJson.addProperty("name", prop.getName());
var valuesJson = new JsonArray();
for (var value : prop.getValues()) {
valuesJson.add(value.toString());
}
propJson.add("values", valuesJson);
propsJson.add(propJson);
}
blockJson.add("properties", propsJson);
var statesJson = new JsonArray();
for (var state : block.getStateManager().getStates()) {
var stateJson = new JsonObject();
var id = stateIdCounter++;
stateJson.addProperty("id", id);
stateJson.addProperty("luminance", state.getLuminance());
stateJson.addProperty("opaque", state.isOpaque());
if (block.getDefaultState().equals(state)) {
blockJson.addProperty("default_state_id", id);
}
var collisionShapesJson = new JsonArray();
for (var box : state.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN).getBoundingBoxes()) {
var boxJson = new JsonObject();
boxJson.addProperty("min_x", box.minX);
boxJson.addProperty("min_y", box.minY);
boxJson.addProperty("min_z", box.minZ);
boxJson.addProperty("max_x", box.maxX);
boxJson.addProperty("max_y", box.maxY);
boxJson.addProperty("max_z", box.maxZ);
collisionShapesJson.add(boxJson);
}
stateJson.add("collision_shapes", collisionShapesJson);
statesJson.add(stateJson);
}
blockJson.add("states", statesJson);
blocksJson.add(blockJson);
}
return blocksJson;
}
}

View file

@ -1,9 +1,11 @@
package dev._00a.valence_extractor;
package dev._00a.valence_extractor.extractors;
import com.google.gson.*;
import net.fabricmc.api.ModInitializer;
import dev._00a.valence_extractor.DummyPlayerEntity;
import dev._00a.valence_extractor.DummyWorld;
import dev._00a.valence_extractor.Main;
import dev._00a.valence_extractor.Main.Pair;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityStatuses;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.data.DataTracker;
import net.minecraft.entity.data.TrackedData;
@ -19,67 +21,56 @@ import net.minecraft.util.math.GlobalPos;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryEntry;
import net.minecraft.village.VillagerData;
import net.minecraft.world.EmptyBlockView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Locale;
import java.util.Optional;
import java.util.OptionalInt;
public class Extractor implements ModInitializer {
public static final String MOD_ID = "valence_extractor";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
private Gson gson;
private Path outputDirectory;
public class Entities implements Main.Extractor {
public Entities() {
}
private static TD2JResult trackedDataToJson(TrackedData<?> data, DataTracker tracker) {
private static Pair<String, JsonElement> trackedDataToJson(TrackedData<?> data, DataTracker tracker) {
final var handler = data.getType();
final var val = tracker.get(data);
if (handler == TrackedDataHandlerRegistry.BYTE) {
return new TD2JResult("byte", new JsonPrimitive((Byte) val));
return new Pair<>("byte", new JsonPrimitive((Byte) val));
} else if (handler == TrackedDataHandlerRegistry.INTEGER) {
return new TD2JResult("integer", new JsonPrimitive((Integer) val));
return new Pair<>("integer", new JsonPrimitive((Integer) val));
} else if (handler == TrackedDataHandlerRegistry.FLOAT) {
return new TD2JResult("float", new JsonPrimitive((Float) val));
return new Pair<>("float", new JsonPrimitive((Float) val));
} else if (handler == TrackedDataHandlerRegistry.STRING) {
return new TD2JResult("string", new JsonPrimitive((String) val));
return new Pair<>("string", new JsonPrimitive((String) val));
} else if (handler == TrackedDataHandlerRegistry.TEXT_COMPONENT) {
// TODO: return text as json element.
return new TD2JResult("text_component", new JsonPrimitive(((Text) val).getString()));
return new Pair<>("text_component", new JsonPrimitive(((Text) val).getString()));
} else if (handler == TrackedDataHandlerRegistry.OPTIONAL_TEXT_COMPONENT) {
var res = ((Optional<?>) val).map(o -> (JsonElement) new JsonPrimitive(((Text) o).getString())).orElse(JsonNull.INSTANCE);
return new TD2JResult("optional_text_component", res);
return new Pair<>("optional_text_component", res);
} else if (handler == TrackedDataHandlerRegistry.ITEM_STACK) {
// TODO
return new TD2JResult("item_stack", new JsonPrimitive(((ItemStack) val).toString()));
return new Pair<>("item_stack", new JsonPrimitive(((ItemStack) val).toString()));
} else if (handler == TrackedDataHandlerRegistry.BOOLEAN) {
return new TD2JResult("boolean", new JsonPrimitive((Boolean) val));
return new Pair<>("boolean", new JsonPrimitive((Boolean) val));
} else if (handler == TrackedDataHandlerRegistry.ROTATION) {
var json = new JsonObject();
var ea = (EulerAngle) val;
json.addProperty("pitch", ea.getPitch());
json.addProperty("yaw", ea.getYaw());
json.addProperty("roll", ea.getRoll());
return new TD2JResult("rotation", json);
return new Pair<>("rotation", json);
} else if (handler == TrackedDataHandlerRegistry.BLOCK_POS) {
var bp = (BlockPos) val;
var json = new JsonObject();
json.addProperty("x", bp.getX());
json.addProperty("y", bp.getY());
json.addProperty("z", bp.getZ());
return new TD2JResult("block_pos", json);
return new Pair<>("block_pos", json);
} else if (handler == TrackedDataHandlerRegistry.OPTIONAL_BLOCK_POS) {
return new TD2JResult("optional_block_pos", ((Optional<?>) val).map(o -> {
return new Pair<>("optional_block_pos", ((Optional<?>) val).map(o -> {
var bp = (BlockPos) o;
var json = new JsonObject();
json.addProperty("x", bp.getX());
@ -88,37 +79,37 @@ public class Extractor implements ModInitializer {
return (JsonElement) json;
}).orElse(JsonNull.INSTANCE));
} else if (handler == TrackedDataHandlerRegistry.FACING) {
return new TD2JResult("facing", new JsonPrimitive(val.toString()));
return new Pair<>("facing", new JsonPrimitive(val.toString()));
} else if (handler == TrackedDataHandlerRegistry.OPTIONAL_UUID) {
var res = ((Optional<?>) val).map(o -> (JsonElement) new JsonPrimitive(o.toString())).orElse(JsonNull.INSTANCE);
return new TD2JResult("optional_uuid", res);
return new Pair<>("optional_uuid", res);
} else if (handler == TrackedDataHandlerRegistry.OPTIONAL_BLOCK_STATE) {
// TODO: get raw block state ID.
var res = ((Optional<?>) val).map(o -> (JsonElement) new JsonPrimitive(o.toString())).orElse(JsonNull.INSTANCE);
return new TD2JResult("optional_block_state", res);
return new Pair<>("optional_block_state", res);
} else if (handler == TrackedDataHandlerRegistry.NBT_COMPOUND) {
// TODO: base64 binary representation or SNBT?
return new TD2JResult("nbt_compound", new JsonPrimitive(val.toString()));
return new Pair<>("nbt_compound", new JsonPrimitive(val.toString()));
} else if (handler == TrackedDataHandlerRegistry.PARTICLE) {
return new TD2JResult("particle", new JsonPrimitive(((ParticleEffect) val).asString()));
return new Pair<>("particle", new JsonPrimitive(((ParticleEffect) val).asString()));
} else if (handler == TrackedDataHandlerRegistry.VILLAGER_DATA) {
var vd = (VillagerData) val;
var json = new JsonObject();
json.addProperty("type", vd.getType().toString());
json.addProperty("profession", vd.getProfession().toString());
json.addProperty("level", vd.getLevel());
return new TD2JResult("villager_data", json);
return new Pair<>("villager_data", json);
} else if (handler == TrackedDataHandlerRegistry.OPTIONAL_INT) {
var opt = (OptionalInt) val;
return new TD2JResult("optional_int", opt.isPresent() ? new JsonPrimitive(opt.getAsInt()) : JsonNull.INSTANCE);
return new Pair<>("optional_int", opt.isPresent() ? new JsonPrimitive(opt.getAsInt()) : JsonNull.INSTANCE);
} else if (handler == TrackedDataHandlerRegistry.ENTITY_POSE) {
return new TD2JResult("entity_pose", new JsonPrimitive(val.toString()));
return new Pair<>("entity_pose", new JsonPrimitive(val.toString()));
} else if (handler == TrackedDataHandlerRegistry.CAT_VARIANT) {
return new TD2JResult("cat_variant", new JsonPrimitive(Registry.CAT_VARIANT.getId((CatVariant) val).getPath()));
return new Pair<>("cat_variant", new JsonPrimitive(Registry.CAT_VARIANT.getId((CatVariant) val).getPath()));
} else if (handler == TrackedDataHandlerRegistry.FROG_VARIANT) {
return new TD2JResult("frog_variant", new JsonPrimitive(Registry.FROG_VARIANT.getId((FrogVariant) val).getPath()));
return new Pair<>("frog_variant", new JsonPrimitive(Registry.FROG_VARIANT.getId((FrogVariant) val).getPath()));
} else if (handler == TrackedDataHandlerRegistry.OPTIONAL_GLOBAL_POS) {
return new TD2JResult("optional_global_pos", ((Optional<?>) val).map(o -> {
return new Pair<>("optional_global_pos", ((Optional<?>) val).map(o -> {
var gp = (GlobalPos) o;
var json = new JsonObject();
json.addProperty("dimension", gp.getDimension().getValue().toString());
@ -133,96 +124,20 @@ public class Extractor implements ModInitializer {
}).orElse(JsonNull.INSTANCE));
} else if (handler == TrackedDataHandlerRegistry.PAINTING_VARIANT) {
var variant = ((RegistryEntry<?>) val).getKey().map(k -> k.getValue().getPath()).orElse("");
return new TD2JResult("painting_variant", new JsonPrimitive(variant));
return new Pair<>("painting_variant", new JsonPrimitive(variant));
} else {
throw new IllegalArgumentException("Unexpected tracked data type " + handler);
}
}
@Override
public void onInitialize() {
LOGGER.info("Starting extractor...");
try {
outputDirectory = Files.createDirectories(Paths.get("valence_extractor_output"));
gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().serializeNulls().create();
extractBlocks();
extractEntities();
extractEntityStatuses();
} catch (Throwable e) {
LOGGER.error("Extraction failed", e);
System.exit(1);
}
LOGGER.info("Extractor finished successfully.");
System.exit(0);
}
private void extractBlocks() throws IOException {
var blocksJson = new JsonArray();
var stateIdCounter = 0;
for (var block : Registry.BLOCK) {
var blockJson = new JsonObject();
// blockJson.addProperty("id", Registry.BLOCK.getRawId(block));
blockJson.addProperty("translation_key", block.getTranslationKey());
// blockJson.addProperty("min_state_id", stateIdCounter);
// blockJson.addProperty("max_state_id", stateIdCounter + block.getStateManager().getStates().size() - 1);
var propsJson = new JsonArray();
for (var prop : block.getStateManager().getProperties()) {
var propJson = new JsonObject();
propJson.addProperty("name", prop.getName());
var valuesJson = new JsonArray();
for (var value : prop.getValues()) {
valuesJson.add(value.toString());
}
propJson.add("values", valuesJson);
propsJson.add(propJson);
}
blockJson.add("properties", propsJson);
var statesJson = new JsonArray();
for (var state : block.getStateManager().getStates()) {
var stateJson = new JsonObject();
var id = stateIdCounter++;
stateJson.addProperty("id", id);
stateJson.addProperty("luminance", state.getLuminance());
stateJson.addProperty("opaque", state.isOpaque());
if (block.getDefaultState().equals(state)) {
blockJson.addProperty("default_state_id", id);
}
var collisionShapesJson = new JsonArray();
for (var box : state.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN).getBoundingBoxes()) {
var boxJson = new JsonObject();
boxJson.addProperty("min_x", box.minX);
boxJson.addProperty("min_y", box.minY);
boxJson.addProperty("min_z", box.minZ);
boxJson.addProperty("max_x", box.maxX);
boxJson.addProperty("max_y", box.maxY);
boxJson.addProperty("max_z", box.maxZ);
collisionShapesJson.add(boxJson);
}
stateJson.add("collision_shapes", collisionShapesJson);
statesJson.add(stateJson);
}
blockJson.add("states", statesJson);
blocksJson.add(blockJson);
}
writeJsonFile("blocks.json", blocksJson);
public String fileName() {
return "entities.json";
}
@Override
@SuppressWarnings("unchecked")
private void extractEntities() throws IOException, IllegalAccessException, NoSuchFieldException {
public JsonElement extract() throws IllegalAccessException, NoSuchFieldException {
final var entitiesJson = new JsonArray();
final var entityClasses = new HashSet<Class<? extends Entity>>();
@ -258,9 +173,9 @@ public class Extractor implements ModInitializer {
fieldJson.addProperty("index", data.getId());
var dataTracker = (DataTracker) dataTrackerField.get(entityInstance);
var res = Extractor.trackedDataToJson(data, dataTracker);
fieldJson.addProperty("type", res.type_name);
fieldJson.add("default_value", res.data);
var res = Entities.trackedDataToJson(data, dataTracker);
fieldJson.addProperty("type", res.left());
fieldJson.add("default_value", res.right());
fieldsJson.add(fieldJson);
}
@ -282,34 +197,6 @@ public class Extractor implements ModInitializer {
}
}
writeJsonFile("entities.json", entitiesJson);
}
private void extractEntityStatuses() throws IllegalAccessException, IOException {
var statusesJson = new JsonObject();
for (var field : EntityStatuses.class.getDeclaredFields()) {
if (field.canAccess(null) && field.get(null) instanceof Byte code) {
if (field.getName().equals("field_30030")) {
// TODO: temp
statusesJson.addProperty("stop_attack", code);
} else {
statusesJson.addProperty(field.getName().toLowerCase(Locale.ROOT), code);
}
}
}
writeJsonFile("entity_statuses.json", statusesJson);
}
private void writeJsonFile(String fileName, JsonElement element) throws IOException {
var out = outputDirectory.resolve(fileName);
var fileWriter = new FileWriter(out.toFile(), StandardCharsets.UTF_8);
gson.toJson(element, fileWriter);
fileWriter.close();
LOGGER.info("Wrote " + out.toAbsolutePath());
}
private record TD2JResult(String type_name, JsonElement data) {
return entitiesJson;
}
}

View file

@ -0,0 +1,32 @@
package dev._00a.valence_extractor.extractors;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import dev._00a.valence_extractor.Main;
import java.util.Locale;
public class EntityStatuses implements Main.Extractor {
@Override
public String fileName() {
return "entity_statuses.json";
}
@Override
public JsonElement extract() throws Exception {
var statusesJson = new JsonObject();
for (var field : net.minecraft.entity.EntityStatuses.class.getDeclaredFields()) {
if (field.canAccess(null) && field.get(null) instanceof Byte code) {
if (field.getName().equals("field_30030")) {
// TODO: temp
statusesJson.addProperty("stop_attack", code);
} else {
statusesJson.addProperty(field.getName().toLowerCase(Locale.ROOT), code);
}
}
}
return statusesJson;
}
}

View file

@ -14,7 +14,7 @@
"environment": "*",
"entrypoints": {
"main": [
"dev._00a.valence_extractor.Extractor"
"dev._00a.valence_extractor.Main"
]
},
"mixins": [],