generalize data file handling

This commit is contained in:
Lulu13022002 2025-04-27 17:06:02 +02:00
parent 5fa30c43da
commit c6e3d1b0b5
No known key found for this signature in database
GPG Key ID: 491C8F0B8ACDEB01
36 changed files with 608 additions and 662 deletions

View File

@ -16,7 +16,7 @@ dependencies {
implementation("com.squareup:javapoet:1.13.0") implementation("com.squareup:javapoet:1.13.0")
implementation("io.papermc.typewriter:typewriter:1.0.1") implementation("io.papermc.typewriter:typewriter:1.0.1")
implementation("info.picocli:picocli:4.7.6") implementation("info.picocli:picocli:4.7.6")
implementation("io.github.classgraph:classgraph:4.8.47") implementation("io.github.classgraph:classgraph:4.8.179")
implementation("org.jetbrains:annotations:26.0.2") implementation("org.jetbrains:annotations:26.0.2")
implementation("org.jspecify:jspecify:1.0.0") implementation("org.jspecify:jspecify:1.0.0")
@ -185,6 +185,9 @@ abstract class GenerationArgumentProvider : CommandLineArgumentProvider {
tasks.test { tasks.test {
useJUnitPlatform() useJUnitPlatform()
} }
tasks.compileTestJava {
options.compilerArgs.add("-parameters")
}
group = "io.papermc.paper" group = "io.papermc.paper"
version = "1.0-SNAPSHOT" version = "1.0-SNAPSHOT"

View File

@ -2,6 +2,7 @@ package io.papermc.generator;
import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.MoreExecutors;
import com.mojang.logging.LogUtils; import com.mojang.logging.LogUtils;
import io.papermc.generator.resources.DataFileLoader;
import io.papermc.generator.rewriter.registration.PaperPatternSourceSetRewriter; import io.papermc.generator.rewriter.registration.PaperPatternSourceSetRewriter;
import io.papermc.generator.rewriter.registration.PatternSourceSetRewriter; import io.papermc.generator.rewriter.registration.PatternSourceSetRewriter;
import io.papermc.generator.types.SourceGenerator; import io.papermc.generator.types.SourceGenerator;
@ -107,8 +108,9 @@ public class Main implements Callable<Integer> {
public Integer call() { public Integer call() {
bootStrap(this.tagBootstrap).join(); bootStrap(this.tagBootstrap).join();
ROOT_DIR = this.rootDir;
DataFileLoader.init();
try { try {
ROOT_DIR = this.rootDir;
if (this.isRewrite) { if (this.isRewrite) {
rewrite(this.sourceSet, Rewriters.VALUES.get(this.side)); rewrite(this.sourceSet, Rewriters.VALUES.get(this.side));
} else { } else {

View File

@ -3,6 +3,7 @@ package io.papermc.generator;
import com.squareup.javapoet.ClassName; import com.squareup.javapoet.ClassName;
import io.papermc.generator.registry.RegistryBootstrapper; import io.papermc.generator.registry.RegistryBootstrapper;
import io.papermc.generator.registry.RegistryEntries; import io.papermc.generator.registry.RegistryEntries;
import io.papermc.generator.resources.DataFileLoader;
import io.papermc.generator.rewriter.registration.PatternSourceSetRewriter; import io.papermc.generator.rewriter.registration.PatternSourceSetRewriter;
import io.papermc.generator.rewriter.types.Types; import io.papermc.generator.rewriter.types.Types;
import io.papermc.generator.rewriter.types.registry.EnumRegistryRewriter; import io.papermc.generator.rewriter.types.registry.EnumRegistryRewriter;
@ -25,7 +26,6 @@ import io.papermc.generator.rewriter.types.simple.MemoryKeyRewriter;
import io.papermc.generator.rewriter.types.simple.StatisticRewriter; import io.papermc.generator.rewriter.types.simple.StatisticRewriter;
import io.papermc.generator.rewriter.types.simple.trial.PoseRewriter; import io.papermc.generator.rewriter.types.simple.trial.PoseRewriter;
import io.papermc.generator.rewriter.types.simple.trial.VillagerProfessionRewriter; import io.papermc.generator.rewriter.types.simple.trial.VillagerProfessionRewriter;
import io.papermc.generator.types.goal.MobGoalNames;
import io.papermc.generator.rewriter.types.registry.RegistriesArgumentProviderRewriter; import io.papermc.generator.rewriter.types.registry.RegistriesArgumentProviderRewriter;
import io.papermc.generator.utils.Formatting; import io.papermc.generator.utils.Formatting;
import io.papermc.typewriter.preset.EnumCloneRewriter; import io.papermc.typewriter.preset.EnumCloneRewriter;
@ -189,7 +189,7 @@ public final class Rewriters {
.register("MobGoalHelper#bukkitMap", Types.MOB_GOAL_HELPER, new SearchReplaceRewriter() { .register("MobGoalHelper#bukkitMap", Types.MOB_GOAL_HELPER, new SearchReplaceRewriter() {
@Override @Override
protected void insert(SearchMetadata metadata, StringBuilder builder) { protected void insert(SearchMetadata metadata, StringBuilder builder) {
for (Map.Entry<Class<? extends Mob>, ClassName> entry : MobGoalNames.ENTITY_CLASS_NAMES.entrySet()) { for (Map.Entry<Class<? extends Mob>, ClassName> entry : DataFileLoader.ENTITY_CLASS_NAMES.get().entrySet()) {
builder.append(metadata.indent()).append("bukkitMap.put(%s.class, %s.class);".formatted( builder.append(metadata.indent()).append("bukkitMap.put(%s.class, %s.class);".formatted(
entry.getKey().getCanonicalName(), this.importCollector.getShortName(Types.typed(entry.getValue())) entry.getKey().getCanonicalName(), this.importCollector.getShortName(Types.typed(entry.getValue()))
)); ));

View File

@ -1,16 +1,15 @@
package io.papermc.generator.registry; package io.papermc.generator.registry;
import com.google.gson.JsonObject; import com.google.common.collect.Multimap;
import com.mojang.serialization.JsonOps; import com.google.common.collect.MultimapBuilder;
import io.papermc.generator.resources.DataFileLoader;
import io.papermc.generator.resources.RegistryData;
import io.papermc.generator.utils.ClassHelper; import io.papermc.generator.utils.ClassHelper;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.List; import java.util.List;
@ -19,13 +18,12 @@ import java.util.Objects;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import io.papermc.generator.utils.SourceCodecs; import net.minecraft.Util;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.component.DataComponents; import net.minecraft.core.component.DataComponents;
import net.minecraft.core.particles.ParticleTypes; import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.registries.Registries; import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.damagesource.DamageTypes; import net.minecraft.world.damagesource.DamageTypes;
import net.minecraft.world.effect.MobEffects; import net.minecraft.world.effect.MobEffects;
@ -63,11 +61,11 @@ import org.jspecify.annotations.NullMarked;
@NullMarked @NullMarked
public final class RegistryEntries { public final class RegistryEntries {
private static <T> RegistryEntry.Builder<T> entry(ResourceKey<? extends Registry<T>> registryKey, Class<?> holderElementsClass) { private static <T> RegistryIntern<T> entry(ResourceKey<? extends Registry<T>> registryKey, Class<?> holderElementsClass) {
return new RegistryEntry.Builder<>(registryKey, holderElementsClass); return new RegistryIntern<>(registryKey, holderElementsClass);
} }
private static final Map<ResourceKey<? extends Registry<?>>, RegistryKeyField<?>> REGISTRY_KEY_FIELDS; public static final Map<ResourceKey<? extends Registry<?>>, RegistryKeyField<?>> REGISTRY_KEY_FIELDS;
static { static {
Map<ResourceKey<? extends Registry<?>>, RegistryKeyField<?>> registryKeyFields = new IdentityHashMap<>(); Map<ResourceKey<? extends Registry<?>>, RegistryKeyField<?>> registryKeyFields = new IdentityHashMap<>();
try { try {
@ -89,7 +87,7 @@ public final class RegistryEntries {
REGISTRY_KEY_FIELDS = Collections.unmodifiableMap(registryKeyFields); REGISTRY_KEY_FIELDS = Collections.unmodifiableMap(registryKeyFields);
} }
private static final Map<ResourceKey<? extends Registry<?>>, RegistryEntry.Builder<?>> EXPOSED_REGISTRIES = Stream.of( private static final Map<ResourceKey<? extends Registry<?>>, RegistryIntern<?>> EXPOSED_REGISTRIES = Stream.of(
entry(Registries.GAME_EVENT, GameEvent.class), entry(Registries.GAME_EVENT, GameEvent.class),
entry(Registries.STRUCTURE_TYPE, StructureType.class), entry(Registries.STRUCTURE_TYPE, StructureType.class),
entry(Registries.MOB_EFFECT, MobEffects.class), entry(Registries.MOB_EFFECT, MobEffects.class),
@ -124,57 +122,49 @@ public final class RegistryEntries {
entry(Registries.PARTICLE_TYPE, ParticleTypes.class), entry(Registries.PARTICLE_TYPE, ParticleTypes.class),
entry(Registries.POTION, Potions.class), entry(Registries.POTION, Potions.class),
entry(Registries.MEMORY_MODULE_TYPE, MemoryModuleType.class) entry(Registries.MEMORY_MODULE_TYPE, MemoryModuleType.class)
).collect(Collectors.toMap(RegistryEntry.Builder::getRegistryKey, entry -> entry)); ).collect(Collectors.toMap(RegistryIntern::getRegistryKey, entry -> entry));
public static final List<RegistryEntry<?>> BUILT_IN = new ArrayList<>();
public static final List<RegistryEntry<?>> DATA_DRIVEN = new ArrayList<>();
@Deprecated @Deprecated
public static final List<RegistryEntry<?>> API_ONLY = new ArrayList<>(); public static final List<RegistryEntry<?>> API_ONLY = new ArrayList<>();
@Deprecated @Deprecated
public static final List<ResourceKey<? extends Registry<?>>> API_ONLY_KEYS = List.of( public static final List<ResourceKey<? extends Registry<?>>> API_ONLY_KEYS = List.of(
Registries.ENTITY_TYPE, Registries.PARTICLE_TYPE, Registries.POTION, Registries.MEMORY_MODULE_TYPE Registries.ENTITY_TYPE, Registries.PARTICLE_TYPE, Registries.POTION, Registries.MEMORY_MODULE_TYPE
); );
static { public static final Multimap<RegistryEntry.Type, RegistryEntry<?>> REGISTRIES = Util.make(MultimapBuilder.enumKeys(RegistryEntry.Type.class).arrayListValues().build(), map -> {
List<ResourceKey<? extends Registry<?>>> remainingRegistries = new ArrayList<>(EXPOSED_REGISTRIES.keySet()); List<ResourceKey<? extends Registry<?>>> remainingRegistries = new ArrayList<>(EXPOSED_REGISTRIES.keySet());
for (RegistryEntry.Type type : RegistryEntry.Type.values()) { DataFileLoader.REGISTRIES.forEach((type, file) -> {
try (Reader input = new BufferedReader(new InputStreamReader(RegistryEntries.class.getClassLoader().getResourceAsStream("data/registry/%s.json".formatted(type.getSerializedName()))))) { Map<ResourceKey<? extends Registry<?>>, RegistryData> registries = file.get();
JsonObject registries = SourceCodecs.GSON.fromJson(input, JsonObject.class); for (Map.Entry<ResourceKey<? extends Registry<?>>, RegistryData> registry : registries.entrySet()) {
for (String rawRegistryKey : registries.keySet()) { ResourceKey<? extends Registry<?>> registryKey = registry.getKey();
ResourceKey<? extends Registry<?>> registryKey = ResourceKey.createRegistryKey(ResourceLocation.parse(rawRegistryKey)); RegistryEntry<?> entry = EXPOSED_REGISTRIES.get(registryKey).bind(registry.getValue());
RegistryData data = type.getDataCodec().parse(JsonOps.INSTANCE, registries.get(rawRegistryKey)).getOrThrow(); entry.validate(type);
RegistryEntry<?> entry = EXPOSED_REGISTRIES.get(registryKey) if (remainingRegistries.remove(registryKey)) {
.type(type) if (API_ONLY_KEYS.contains(registryKey)) {
.registryKeyField((RegistryKeyField) REGISTRY_KEY_FIELDS.get(registryKey)) API_ONLY.add(entry);
.data(data)
.build();
entry.validate();
if (remainingRegistries.remove(registryKey)) {
if (API_ONLY_KEYS.contains(registryKey)) {
API_ONLY.add(entry);
} else {
type.getEntries().add(entry);
}
} else { } else {
throw new IllegalStateException("Duplicate registry found in data files: " + registryKey); map.put(type, entry);
} }
} }
} catch (IOException ex) {
throw new RuntimeException(ex);
} }
} });
if (!remainingRegistries.isEmpty()) { if (!remainingRegistries.isEmpty()) {
throw new IllegalStateException("Registry not found in data files: " + remainingRegistries); throw new IllegalStateException("Registry not found in data files: " + remainingRegistries);
} }
});
public static Collection<RegistryEntry<?>> byType(RegistryEntry.Type type) {
return REGISTRIES.get(type);
} }
public static final Map<ResourceKey<? extends Registry<?>>, RegistryEntry<?>> BY_REGISTRY_KEY; public static final Map<ResourceKey<? extends Registry<?>>, RegistryEntry<?>> BY_REGISTRY_KEY;
static { static {
Map<ResourceKey<? extends Registry<?>>, RegistryEntry<?>> byRegistryKey = new IdentityHashMap<>(BUILT_IN.size() + DATA_DRIVEN.size() + API_ONLY.size()); Map<ResourceKey<? extends Registry<?>>, RegistryEntry<?>> byRegistryKey = new IdentityHashMap<>(REGISTRIES.size() + API_ONLY.size());
forEach(entry -> { forEach(entry -> {
byRegistryKey.put(entry.getRegistryKey(), entry); byRegistryKey.put(entry.getRegistryKey(), entry);
}, RegistryEntries.BUILT_IN, RegistryEntries.DATA_DRIVEN, RegistryEntries.API_ONLY); }, REGISTRIES.values(), API_ONLY);
BY_REGISTRY_KEY = Collections.unmodifiableMap(byRegistryKey); BY_REGISTRY_KEY = Collections.unmodifiableMap(byRegistryKey);
} }
@ -185,12 +175,12 @@ public final class RegistryEntries {
// real registries // real registries
public static void forEach(Consumer<RegistryEntry<?>> callback) { public static void forEach(Consumer<RegistryEntry<?>> callback) {
forEach(callback, RegistryEntries.BUILT_IN, RegistryEntries.DATA_DRIVEN); forEach(callback, REGISTRIES.values());
} }
@SafeVarargs @SafeVarargs
public static void forEach(Consumer<RegistryEntry<?>> callback, List<RegistryEntry<?>>... datas) { public static void forEach(Consumer<RegistryEntry<?>> callback, Collection<RegistryEntry<?>>... datas) {
for (List<RegistryEntry<?>> data : datas) { for (Collection<RegistryEntry<?>> data : datas) {
for (RegistryEntry<?> entry : data) { for (RegistryEntry<?> entry : data) {
callback.accept(entry); callback.accept(entry);
} }

View File

@ -1,16 +1,14 @@
package io.papermc.generator.registry; package io.papermc.generator.registry;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import io.papermc.generator.Main; import io.papermc.generator.Main;
import io.papermc.generator.resources.RegistryData;
import io.papermc.generator.utils.ClassHelper; import io.papermc.generator.utils.ClassHelper;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.util.Collections; import java.util.Collections;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
@ -18,68 +16,23 @@ import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.util.StringRepresentable; import net.minecraft.util.StringRepresentable;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
@NullMarked @NullMarked
public final class RegistryEntry<T> implements RegistryIdentifiable<T> { public class RegistryEntry<T> implements RegistryIdentifiable<T> {
public static class Builder<T> implements RegistryIdentifiable<T> {
private final ResourceKey<? extends Registry<T>> registryKey;
private final Class<?> holderElementsClass;
private @MonotonicNonNull RegistryKeyField<T> registryKeyField;
private @MonotonicNonNull Type type;
private @MonotonicNonNull RegistryData data;
protected Builder(ResourceKey<? extends Registry<T>> registryKey, Class<?> holderElementsClass) {
this.registryKey = registryKey;
this.holderElementsClass = holderElementsClass;
}
@Override
public ResourceKey<? extends Registry<T>> getRegistryKey() {
return registryKey;
}
public Builder<T> registryKeyField(RegistryKeyField<T> registryKeyField) {
this.registryKeyField = registryKeyField;
return this;
}
public Builder<T> type(Type type) {
this.type = type;
return this;
}
public Builder<T> data(RegistryData data) {
this.data = data;
return this;
}
public RegistryEntry<T> build() {
Preconditions.checkState(this.type != null && this.data != null && this.registryKeyField != null, "Type, data and registry key field cannot be nulls");
return new RegistryEntry<>(this.registryKey, this.registryKeyField, this.holderElementsClass, this.type, this.data);
}
}
private final ResourceKey<? extends Registry<T>> registryKey; private final ResourceKey<? extends Registry<T>> registryKey;
private final RegistryKeyField<T> registryKeyField; private final RegistryKeyField<T> registryKeyField;
private final Class<T> elementClass;
private final Class<?> holderElementsClass; private final Class<?> holderElementsClass;
private final Type type;
private final RegistryData data; private final RegistryData data;
private @Nullable Map<ResourceKey<T>, String> fieldNames; private @Nullable Map<ResourceKey<T>, String> fieldNames;
private RegistryEntry(ResourceKey<? extends Registry<T>> registryKey, RegistryKeyField<T> registryKeyField, Class<?> holderElementsClass, Type type, RegistryData data) { protected RegistryEntry(ResourceKey<? extends Registry<T>> registryKey, RegistryKeyField<T> registryKeyField, Class<?> holderElementsClass, RegistryData data) {
this.registryKey = registryKey; this.registryKey = registryKey;
this.registryKeyField = registryKeyField; this.registryKeyField = registryKeyField;
this.elementClass = registryKeyField.elementClass();
this.holderElementsClass = holderElementsClass; this.holderElementsClass = holderElementsClass;
this.type = type;
this.data = data; this.data = data;
} }
@ -93,7 +46,7 @@ public final class RegistryEntry<T> implements RegistryIdentifiable<T> {
} }
public Class<T> elementClass() { public Class<T> elementClass() {
return this.elementClass; return this.registryKeyField.elementClass();
} }
public String registryKeyField() { public String registryKeyField() {
@ -104,11 +57,13 @@ public final class RegistryEntry<T> implements RegistryIdentifiable<T> {
return this.data; return this.data;
} }
public void validate() { protected void validate(Type type) {
boolean isBuiltIn = this.type == Type.BUILT_IN; Preconditions.checkState(type != Type.BUILT_IN || !this.data.impl().delayed(), "Built-in registry '%s' cannot be delayed!", this.registryKey.location());
Type realType = this.type == Type.BUILT_IN ? Type.DATA_DRIVEN : Type.BUILT_IN;
boolean isBuiltIn = type == Type.BUILT_IN;
Type realType = type == Type.BUILT_IN ? Type.DATA_DRIVEN : Type.BUILT_IN;
Preconditions.checkState(isBuiltIn == BuiltInRegistries.REGISTRY.containsKey(this.registryKey.location()), // type is checked at runtime and not guessed in case api/impl change is needed Preconditions.checkState(isBuiltIn == BuiltInRegistries.REGISTRY.containsKey(this.registryKey.location()), // type is checked at runtime and not guessed in case api/impl change is needed
"Mismatch type, registry %s is %s but was in registry/%s.json".formatted(this.registryKey.location(), realType.getSerializedName(), this.type.getSerializedName()) "Mismatch type, registry '%s' is %s but was in registry/%s.json".formatted(this.registryKey.location(), realType.getSerializedName(), type.getSerializedName())
); );
} }
@ -117,28 +72,29 @@ public final class RegistryEntry<T> implements RegistryIdentifiable<T> {
return this.data.api().klass().simpleName(); return this.data.api().klass().simpleName();
} }
return this.elementClass.getSimpleName(); return this.elementClass().getSimpleName();
} }
public boolean allowCustomKeys() { public boolean allowCustomKeys() {
return this.data.builder().isPresent() || RegistryEntries.DATA_DRIVEN.contains(this); return this.data.builder().isPresent() || RegistryEntries.byType(Type.DATA_DRIVEN).contains(this);
} }
private <TO> Map<ResourceKey<T>, TO> getFields(Map<ResourceKey<T>, TO> map, Function<Field, @Nullable TO> transform) { private <TO> Map<ResourceKey<T>, TO> getFields(Map<ResourceKey<T>, TO> map, Function<Field, @Nullable TO> transform) {
Registry<T> registry = this.registry(); Registry<T> registry = this.registry();
Class<T> elementClass = this.elementClass();
try { try {
for (Field field : this.holderElementsClass.getDeclaredFields()) { for (Field field : this.holderElementsClass.getDeclaredFields()) {
if (!ResourceKey.class.isAssignableFrom(field.getType()) && !Holder.Reference.class.isAssignableFrom(field.getType()) && !this.elementClass.isAssignableFrom(field.getType())) { if (!ResourceKey.class.isAssignableFrom(field.getType()) && !Holder.Reference.class.isAssignableFrom(field.getType()) && !elementClass.isAssignableFrom(field.getType())) {
continue; continue;
} }
if (ClassHelper.isStaticConstant(field, Modifier.PUBLIC)) { if (ClassHelper.isStaticConstant(field, Modifier.PUBLIC)) {
ResourceKey<T> key = null; ResourceKey<T> key = null;
if (this.elementClass.isAssignableFrom(field.getType())) { if (elementClass.isAssignableFrom(field.getType())) {
key = registry.getResourceKey(this.elementClass.cast(field.get(null))).orElseThrow(); key = registry.getResourceKey(elementClass.cast(field.get(null))).orElseThrow();
} else { } else {
if (field.getGenericType() instanceof ParameterizedType complexType && complexType.getActualTypeArguments().length == 1 && if (field.getGenericType() instanceof ParameterizedType complexType && complexType.getActualTypeArguments().length == 1 &&
complexType.getActualTypeArguments()[0] == this.elementClass) { complexType.getActualTypeArguments()[0] == elementClass) {
if (Holder.Reference.class.isAssignableFrom(field.getType())) { if (Holder.Reference.class.isAssignableFrom(field.getType())) {
key = ((Holder.Reference<T>) field.get(null)).key(); key = ((Holder.Reference<T>) field.get(null)).key();
@ -183,31 +139,12 @@ public final class RegistryEntry<T> implements RegistryIdentifiable<T> {
public enum Type implements StringRepresentable { public enum Type implements StringRepresentable {
DATA_DRIVEN("data_driven", RegistryEntries.DATA_DRIVEN), BUILT_IN("built_in"),
BUILT_IN("built_in", RegistryEntries.BUILT_IN, RegistryData.UNSAFE_CODEC.validate(data -> { DATA_DRIVEN("data_driven");
return data.impl().delayed() ? DataResult.error(() -> "Built-in registry cannot be delayed!") : DataResult.success(data);
}));
private final String name; private final String name;
private final List<RegistryEntry<?>> entries; Type(String name) {
private final Codec<RegistryData> dataCodec;
Type(String name, List<RegistryEntry<?>> entries) {
this(name, entries, RegistryData.UNSAFE_CODEC);
}
Type(String name, List<RegistryEntry<?>> entries, Codec<RegistryData> dataCodec) {
this.name = name; this.name = name;
this.entries = entries;
this.dataCodec = dataCodec;
}
public List<RegistryEntry<?>> getEntries() {
return this.entries;
}
public Codec<RegistryData> getDataCodec() {
return this.dataCodec;
} }
@Override @Override

View File

@ -2,7 +2,9 @@ package io.papermc.generator.registry;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface RegistryIdentifiable<T> { public interface RegistryIdentifiable<T> {
ResourceKey<? extends Registry<T>> getRegistryKey(); ResourceKey<? extends Registry<T>> getRegistryKey();

View File

@ -0,0 +1,22 @@
package io.papermc.generator.registry;
import io.papermc.generator.resources.RegistryData;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import org.jspecify.annotations.NullMarked;
@NullMarked
public record RegistryIntern<T>(ResourceKey<? extends Registry<T>> registryKey, RegistryKeyField<T> registryKeyField, Class<?> holderElementsClass) implements RegistryIdentifiable<T> {
public RegistryIntern(ResourceKey<? extends Registry<T>> registryKey, Class<?> holderElementsClass) {
this(registryKey, (RegistryKeyField<T>) RegistryEntries.REGISTRY_KEY_FIELDS.get(registryKey), holderElementsClass);
}
@Override
public ResourceKey<? extends Registry<T>> getRegistryKey() {
return this.registryKey;
}
public RegistryEntry<?> bind(RegistryData data) {
return new RegistryEntry<>(this.registryKey, this.registryKeyField, this.holderElementsClass, data);
}
}

View File

@ -0,0 +1,136 @@
package io.papermc.generator.resources;
import com.google.gson.FormattingStyle;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DynamicOps;
import com.mojang.serialization.JsonOps;
import io.papermc.generator.Main;
import io.papermc.generator.types.SimpleGenerator;
import net.minecraft.resources.RegistryOps;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.jetbrains.annotations.VisibleForTesting;
import org.jspecify.annotations.NullMarked;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@NullMarked
public abstract class DataFile<V, A, R> {
public static final Gson GSON = new GsonBuilder().setFormattingStyle(FormattingStyle.PRETTY.withIndent(SimpleGenerator.INDENT_UNIT)).create();
private final String path;
private final Codec<V> codec;
private final Transmuter<V, A, R> transmuter;
private final boolean requireRegistry;
private @MonotonicNonNull V cached;
protected DataFile(String path, Codec<V> codec, Transmuter<V, A, R> transmuter, boolean requireRegistry) {
this.path = path;
this.codec = codec;
this.transmuter = transmuter;
this.requireRegistry = requireRegistry;
}
protected DataFile(String path, Codec<V> codec, Transmuter<V, A, R> transmuter) {
this(path, codec, transmuter, false);
}
public static class Map<K, V> extends DataFile<java.util.Map<K, V>, java.util.Map.Entry<K, V>, K> {
public Map(String path, Codec<java.util.Map<K, V>> codec, Transmuter<java.util.Map<K, V>, java.util.Map.Entry<K, V>, K> transmuter, boolean requireRegistry) {
super(path, codec, transmuter, requireRegistry);
}
public Map(String path, Codec<java.util.Map<K, V>> codec, Transmuter<java.util.Map<K, V>, java.util.Map.Entry<K, V>, K> transmuter) {
super(path, codec, transmuter);
}
}
public static class List<E> extends DataFile<java.util.List<E>, E, E> {
public List(String path, Codec<java.util.List<E>> codec, Transmuter<java.util.List<E>, E, E> transmuter, boolean requireRegistry) {
super(path, codec, transmuter, requireRegistry);
}
public List(String path, Codec<java.util.List<E>> codec, Transmuter<java.util.List<E>, E, E> transmuter) {
super(path, codec, transmuter);
}
}
public boolean isMutable() {
return this.transmuter instanceof Transmuter.Mutable<V, A, R>;
}
@VisibleForTesting
public Transmuter<V, A, R> transmuter() {
return this.transmuter;
}
public V get() {
if (this.cached == null) {
this.cached = this.readUnchecked();
}
return this.cached;
}
public V readUnchecked() {
try {
return this.read();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
public V read() throws IOException {
try (Reader input = new BufferedReader(new InputStreamReader(DataFile.class.getClassLoader().getResourceAsStream(this.path)))) {
JsonElement predicates = GSON.fromJson(input, JsonElement.class);
return this.codec.parse(this.readOps(), predicates).getOrThrow();
}
}
public MutationResult<V, A, R> upgrade(Path destination) throws IOException {
V value = this.read();
MutationResult<V, A, R> result = this.transmuter.transmute(value);
if (this.transmuter instanceof Transmuter.Mutable<V, A, R> mutableTransmuter) {
value = mutableTransmuter.applyChanges(result);
} else {
value = result.value(); // sorted values
}
Files.writeString(destination, this.toJsonString(value), StandardCharsets.UTF_8);
return result;
}
public String toJsonString(V object) {
JsonElement element = this.codec.encodeStart(this.writeOps(), object).getOrThrow();
return GSON.toJson(element) + "\n";
}
public String path() {
return this.path;
}
private DynamicOps<JsonElement> readOps() {
DynamicOps<JsonElement> ops = JsonOps.INSTANCE;
if (this.requireRegistry) {
ops = RegistryOps.create(ops, Main.REGISTRY_ACCESS);
}
return ops;
}
private DynamicOps<JsonElement> writeOps() {
return JsonOps.INSTANCE;
}
@Override
public String toString() {
return "DataFile[path=" + this.path + ']';
}
}

View File

@ -0,0 +1,218 @@
package io.papermc.generator.resources;
import com.google.common.collect.Sets;
import com.google.common.reflect.TypeToken;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.squareup.javapoet.ClassName;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ScanResult;
import io.papermc.generator.registry.RegistryEntries;
import io.papermc.generator.registry.RegistryEntry;
import io.papermc.generator.utils.ClassHelper;
import io.papermc.generator.utils.Formatting;
import io.papermc.generator.utils.SourceCodecs;
import io.papermc.generator.utils.predicate.BlockPredicate;
import io.papermc.generator.utils.predicate.ItemPredicate;
import io.papermc.typewriter.ClassNamed;
import net.minecraft.Util;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.vehicle.AbstractBoat;
import net.minecraft.world.entity.vehicle.AbstractMinecart;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.jspecify.annotations.NullMarked;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@NullMarked
public class DataFileLoader {
private static <K, V> Transmuter<Map<K, V>, Map.Entry<K, V>, K> sortedMap(Comparator<K> comparator) {
return transmuteMap(MutationResult::constant, comparator);
}
private static <K, V> Transmuter<Map<K, V>, Map.Entry<K, V>, K> transmuteMap(Supplier<Set<K>> typesProvider, Function<K, V> onMissing, Comparator<K> comparator) {
return transmuteMap(map -> {
Set<K> types = typesProvider.get();
Set<K> registeredTypes = map.keySet();
Set<Map.Entry<K, V>> added = new HashSet<>();
Sets.difference(types, registeredTypes).forEach(missingType -> added.add(Map.entry(missingType, onMissing.apply(missingType))));
Set<K> removed = new HashSet<>(Sets.difference(registeredTypes, types));
return new MutationResult<>(map, added, removed);
}, comparator);
}
private static <K, V> Transmuter<Map<K, V>, Map.Entry<K, V>, K> transmuteMap(Function<Map<K, V>, io.papermc.generator.resources.MutationResult<Map<K, V>, Map.Entry<K, V>, K>> mutation, Comparator<K> comparator) {
return new Transmuter.Mutable<>() {
@Override
public MutationResult<Map<K, V>, Map.Entry<K, V>, K> transmute(Map<K, V> value) {
Map<K, V> map = new TreeMap<>(comparator);
map.putAll(value);
return mutation.apply(map);
}
@Override
public Map<K, V> applyChanges(MutationResult<Map<K, V>, Map.Entry<K, V>, K> result) {
result.added().forEach(entry -> result.value().put(entry.getKey(), entry.getValue()));
result.removed().forEach(result.value()::remove);
return result.value();
}
};
}
public static class BlockState {
private static final Codec<Map<Class<? extends Enum<? extends StringRepresentable>>, ClassName>> ENUM_PROPERTY_TYPES_CODEC = Codec.unboundedMap(
SourceCodecs.classCodec(new TypeToken<Enum<? extends StringRepresentable>>() {}), SourceCodecs.CLASS_NAME
);
public static final DataFile.Map<Class<? extends Enum<? extends StringRepresentable>>, ClassName> ENUM_PROPERTY_TYPES = new DataFile.Map<>(
"data/block_state/enum_property_types.json", ENUM_PROPERTY_TYPES_CODEC,
transmuteMap(() -> {
try {
Set<Class<? extends Enum<? extends StringRepresentable>>> enumPropertyTypes = Collections.newSetFromMap(new IdentityHashMap<>());
for (Field field : BlockStateProperties.class.getDeclaredFields()) {
if (ClassHelper.isStaticConstant(field, Modifier.PUBLIC)) {
if (!EnumProperty.class.isAssignableFrom(field.getType())) {
continue;
}
enumPropertyTypes.add(((EnumProperty<?>) field.get(null)).getValueClass());
}
}
return Collections.unmodifiableSet(enumPropertyTypes);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
},
missingType -> ClassName.get("org.bukkit.block.data.type", missingType.getSimpleName()),
Comparator.comparing(Class::getCanonicalName))
);
// add classes that extends MultipleFacing and RedstoneWire
// no real rule here some has some don't mossy carpet and wall could have it
private static final Codec<List<ClassNamed>> EXTRA_ALLOWED_METHOD_CODEC = SourceCodecs.CLASS_NAMED.listOf();
public static final DataFile.List<ClassNamed> EXTRA_ALLOWED_METHOD = new DataFile.List<>(
"data/block_state/extra_allowed_method.json", EXTRA_ALLOWED_METHOD_CODEC, MutationResult::constant
);
private static final Codec<Map<ClassNamed, List<BlockPredicate>>> PREDICATES_CODEC = Codec.unboundedMap(
SourceCodecs.CLASS_NAMED, ExtraCodecs.nonEmptyList(BlockPredicate.CODEC.listOf())
);
public static final DataFile.Map<ClassNamed, List<BlockPredicate>> PREDICATES = new DataFile.Map<>(
"data/block_state/predicates.json", PREDICATES_CODEC, MutationResult::constant, true
);
}
@Deprecated
public static class ItemMeta {
public record BridgeData(ClassNamed api, String field) {
public static final Codec<BridgeData> CODEC = RecordCodecBuilder.create(instance -> instance.group(
SourceCodecs.CLASS_NAMED.fieldOf("api").forGetter(BridgeData::api),
SourceCodecs.IDENTIFIER.fieldOf("field").forGetter(BridgeData::field)
).apply(instance, BridgeData::new));
}
private static final Codec<Map<ClassNamed, BridgeData>> BRIDGE_CODEC = Codec.unboundedMap(SourceCodecs.CLASS_NAMED, BridgeData.CODEC);
public static final DataFile.Map<ClassNamed, BridgeData> BRIDGE = new DataFile.Map<>(
"data/item_meta/bridge.json", BRIDGE_CODEC, sortedMap(Comparator.comparing(ClassNamed::canonicalName))
);
private static final Codec<Map<ClassNamed, List<ItemPredicate>>> PREDICATES_CODEC = Codec.unboundedMap(SourceCodecs.CLASS_NAMED, ExtraCodecs.nonEmptyList(ItemPredicate.CODEC.listOf()));
public static final DataFile.Map<ClassNamed, List<ItemPredicate>> PREDICATES = new DataFile.Map<>(
"data/item_meta/predicates.json", PREDICATES_CODEC, MutationResult::constant, true
);
}
private static final Codec<Map<ResourceKey<? extends Registry<?>>, RegistryData>> REGISTRIES_CODEC = Codec.unboundedMap(
SourceCodecs.REGISTRY_KEY, RegistryData.CODEC
);
public static final Map<RegistryEntry.Type, DataFile.Map<ResourceKey<? extends Registry<?>>, RegistryData>> REGISTRIES = Collections.unmodifiableMap(Util.makeEnumMap(RegistryEntry.Type.class, type -> {
return new DataFile.Map<>("data/registry/%s.json".formatted(type.getSerializedName()), REGISTRIES_CODEC, MutationResult::constant);
}));
private static final Map<ResourceKey<EntityType<?>>, Class<?>> ENTITY_TYPE_GENERICS = RegistryEntries.byRegistryKey(Registries.ENTITY_TYPE).getFields(field -> {
if (field.getGenericType() instanceof ParameterizedType complexType && complexType.getActualTypeArguments().length == 1) {
return (Class<?>) complexType.getActualTypeArguments()[0];
}
return null;
});
private static final Codec<Map<ResourceKey<EntityType<?>>, EntityTypeData>> ENTITY_TYPES_CODEC = Codec.unboundedMap(ResourceKey.codec(Registries.ENTITY_TYPE), EntityTypeData.CODEC);
public static final DataFile.Map<ResourceKey<EntityType<?>>, EntityTypeData> ENTITY_TYPES = new DataFile.Map<>(
"data/entity_types.json", ENTITY_TYPES_CODEC,
transmuteMap(() -> BuiltInRegistries.ENTITY_TYPE.listElementIds().collect(Collectors.toSet()),
missingType -> {
Class<?> genericType = ENTITY_TYPE_GENERICS.get(missingType);
String packageName = "org.bukkit.entity";
if (AbstractBoat.class.isAssignableFrom(genericType)) {
packageName += ".boat";
} else if (AbstractMinecart.class.isAssignableFrom(genericType)) {
packageName += ".minecart";
}
return new EntityTypeData(ClassNamed.of(packageName, genericType.getSimpleName()));
},
Formatting.alphabeticKeyOrder(key -> key.location().getPath()))
);
private static final Codec<Map<Class<? extends Mob>, ClassName>> ENTITY_CLASS_NAMES_CODEC = Codec.unboundedMap(
SourceCodecs.classCodec(Mob.class), SourceCodecs.CLASS_NAME
);
public static final DataFile.Map<Class<? extends Mob>, ClassName> ENTITY_CLASS_NAMES = new DataFile.Map<>(
"data/entity_class_names.json", ENTITY_CLASS_NAMES_CODEC,
transmuteMap(() -> {
try (ScanResult scanResult = new ClassGraph().enableClassInfo().acceptPackages(Entity.class.getPackageName()).scan()) {
Set<Class<? extends Mob>> classes = new HashSet<>(scanResult.getSubclasses(Mob.class.getName()).loadClasses(Mob.class));
if (classes.isEmpty()) {
throw new IllegalStateException("There are supposed to be more than 0 mob classes!");
}
classes.add(Mob.class);
return Collections.unmodifiableSet(classes);
}
},
missingType -> ClassName.get("org.bukkit.entity", missingType.getSimpleName()),
Comparator.comparing(Class::getCanonicalName))
);
public static final @MonotonicNonNull List<DataFile<?, ?, ?>> DATA_FILES = Collections.unmodifiableList(Util.make(new ArrayList<>(), list -> {
list.add(BlockState.ENUM_PROPERTY_TYPES);
list.add(BlockState.EXTRA_ALLOWED_METHOD);
list.add(BlockState.PREDICATES);
list.add(ItemMeta.BRIDGE);
list.add(ItemMeta.PREDICATES);
list.addAll(REGISTRIES.values());
list.add(ENTITY_CLASS_NAMES);
list.add(ENTITY_TYPES);
}));
public static void init() {
}
}

View File

@ -0,0 +1,33 @@
package io.papermc.generator.resources;
import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.papermc.generator.utils.SourceCodecs;
import io.papermc.typewriter.ClassNamed;
import net.minecraft.util.ExtraCodecs;
import org.jspecify.annotations.NullMarked;
@NullMarked
public record EntityTypeData(ClassNamed api, int legacyId) {
private static final int NO_LEGACY_ID = -1;
public EntityTypeData(ClassNamed api) {
this(api, NO_LEGACY_ID);
}
public static final Codec<EntityTypeData> DIRECT_CODEC = RecordCodecBuilder.create(instance -> instance.group(
SourceCodecs.CLASS_NAMED.fieldOf("api").forGetter(EntityTypeData::api),
ExtraCodecs.intRange(-1, Integer.MAX_VALUE).optionalFieldOf("legacy_id", NO_LEGACY_ID).deprecated(13).forGetter(EntityTypeData::legacyId)
).apply(instance, EntityTypeData::new));
private static final Codec<EntityTypeData> CLASS_ONLY_CODEC = SourceCodecs.CLASS_NAMED.xmap(EntityTypeData::new, EntityTypeData::api);
public static final Codec<EntityTypeData> CODEC = Codec.either(CLASS_ONLY_CODEC, DIRECT_CODEC).xmap(Either::unwrap, data -> {
if (data.legacyId() != NO_LEGACY_ID) {
return Either.right(data);
}
return Either.left(data);
});
}

View File

@ -0,0 +1,13 @@
package io.papermc.generator.resources;
import org.jspecify.annotations.NullMarked;
import java.util.Collections;
import java.util.Set;
@NullMarked
public record MutationResult<V, A, R>(V value, Set<A> added, Set<R> removed) {
public static <V, A, R> MutationResult<V, A, R> constant(V value) {
return new MutationResult<>(value, Collections.emptySet(), Collections.emptySet());
}
}

View File

@ -1,4 +1,4 @@
package io.papermc.generator.registry; package io.papermc.generator.resources;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder; import com.mojang.serialization.codecs.RecordCodecBuilder;
@ -18,7 +18,7 @@ public record RegistryData(
boolean allowInline boolean allowInline
) { ) {
public static final Codec<RegistryData> UNSAFE_CODEC = RecordCodecBuilder.create(instance -> instance.group( public static final Codec<RegistryData> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Api.CODEC.fieldOf("api").forGetter(RegistryData::api), Api.CODEC.fieldOf("api").forGetter(RegistryData::api),
Impl.CODEC.fieldOf("impl").forGetter(RegistryData::impl), Impl.CODEC.fieldOf("impl").forGetter(RegistryData::impl),
Builder.CODEC.optionalFieldOf("builder").forGetter(RegistryData::builder), Builder.CODEC.optionalFieldOf("builder").forGetter(RegistryData::builder),

View File

@ -0,0 +1,14 @@
package io.papermc.generator.resources;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface Transmuter<V, A, R> {
MutationResult<V, A, R> transmute(V value);
interface Mutable<V, A, R> extends Transmuter<V, A, R> {
V applyChanges(MutationResult<V, A, R> result);
}
}

View File

@ -1,7 +1,7 @@
package io.papermc.generator.rewriter.types.registry; package io.papermc.generator.rewriter.types.registry;
import com.google.common.base.CaseFormat; import com.google.common.base.CaseFormat;
import io.papermc.generator.registry.RegistryData; import io.papermc.generator.resources.RegistryData;
import io.papermc.generator.registry.RegistryEntries; import io.papermc.generator.registry.RegistryEntries;
import io.papermc.generator.registry.RegistryEntry; import io.papermc.generator.registry.RegistryEntry;
import io.papermc.generator.rewriter.types.Types; import io.papermc.generator.rewriter.types.Types;
@ -12,7 +12,7 @@ import net.minecraft.core.registries.Registries;
public class PaperRegistriesRewriter extends SearchReplaceRewriter { public class PaperRegistriesRewriter extends SearchReplaceRewriter {
private void appendEntry(String indent, StringBuilder builder, RegistryEntry<?> entry, boolean canBeDelayed, boolean apiOnly) { private void appendEntry(String indent, StringBuilder builder, RegistryEntry<?> entry, boolean apiOnly) {
builder.append(indent); builder.append(indent);
builder.append("start"); builder.append("start");
builder.append('('); builder.append('(');
@ -54,7 +54,7 @@ public class PaperRegistriesRewriter extends SearchReplaceRewriter {
builder.append(')'); builder.append(')');
}, () -> builder.append(".build()")); }, () -> builder.append(".build()"));
} }
if (canBeDelayed && data.impl().delayed()) { if (data.impl().delayed()) {
builder.append(".delayed()"); builder.append(".delayed()");
} }
builder.append(','); builder.append(',');
@ -63,27 +63,22 @@ public class PaperRegistriesRewriter extends SearchReplaceRewriter {
@Override @Override
public void insert(SearchMetadata metadata, StringBuilder builder) { public void insert(SearchMetadata metadata, StringBuilder builder) {
builder.append(metadata.indent()).append("// built-in"); for (RegistryEntry.Type type : RegistryEntry.Type.values()) {
builder.append('\n'); builder.append(metadata.indent()).append("// ").append(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, type.getSerializedName()));
builder.append('\n');
for (RegistryEntry<?> entry : RegistryEntries.BUILT_IN) { for (RegistryEntry<?> entry : RegistryEntries.byType(type)) {
appendEntry(metadata.indent(), builder, entry, false, false); appendEntry(metadata.indent(), builder, entry, false);
}
builder.append('\n');
} }
builder.append('\n');
builder.append(metadata.indent()).append("// data-driven");
builder.append('\n');
for (RegistryEntry<?> entry : RegistryEntries.DATA_DRIVEN) {
appendEntry(metadata.indent(), builder, entry, true, false);
}
builder.append('\n');
builder.append(metadata.indent()).append("// api-only"); builder.append(metadata.indent()).append("// api-only");
builder.append('\n'); builder.append('\n');
for (RegistryEntry<?> entry : RegistryEntries.API_ONLY) { for (RegistryEntry<?> entry : RegistryEntries.API_ONLY) {
appendEntry(metadata.indent(), builder, entry, false, true); appendEntry(metadata.indent(), builder, entry, true);
} }
builder.deleteCharAt(builder.length() - 2); // delete extra comma... builder.deleteCharAt(builder.length() - 2); // delete extra comma...

View File

@ -1,6 +1,6 @@
package io.papermc.generator.rewriter.types.registry; package io.papermc.generator.rewriter.types.registry;
import io.papermc.generator.registry.RegistryData; import io.papermc.generator.resources.RegistryData;
import io.papermc.generator.registry.RegistryEntries; import io.papermc.generator.registry.RegistryEntries;
import io.papermc.generator.rewriter.types.Types; import io.papermc.generator.rewriter.types.Types;
import io.papermc.typewriter.replace.SearchMetadata; import io.papermc.typewriter.replace.SearchMetadata;
@ -16,17 +16,17 @@ public class RegistryEventsRewriter extends SearchReplaceRewriter {
public void insert(SearchMetadata metadata, StringBuilder builder) { public void insert(SearchMetadata metadata, StringBuilder builder) {
RegistryEntries.forEach(entry -> { RegistryEntries.forEach(entry -> {
RegistryData data = entry.data(); RegistryData data = entry.data();
if (data.builder().isPresent()) { data.builder().ifPresent(b -> {
builder.append(metadata.indent()); builder.append(metadata.indent());
builder.append("%s %s %s ".formatted(PUBLIC, STATIC, FINAL)); builder.append("%s %s %s ".formatted(PUBLIC, STATIC, FINAL));
builder.append(Types.REGISTRY_EVENT_PROVIDER.simpleName()); builder.append(Types.REGISTRY_EVENT_PROVIDER.simpleName());
builder.append("<").append(this.importCollector.getShortName(data.api().klass())).append(", ").append(this.importCollector.getShortName(data.builder().get().api())).append('>'); builder.append("<").append(this.importCollector.getShortName(data.api().klass())).append(", ").append(this.importCollector.getShortName(b.api())).append('>');
builder.append(' '); builder.append(' ');
builder.append(entry.registryKeyField()); builder.append(entry.registryKeyField());
builder.append(" = "); builder.append(" = ");
builder.append("create(").append(Types.REGISTRY_KEY.simpleName()).append('.').append(entry.registryKeyField()).append(");"); builder.append("create(").append(Types.REGISTRY_KEY.simpleName()).append('.').append(entry.registryKeyField()).append(");");
builder.append('\n'); builder.append('\n');
} });
}); });
} }
} }

View File

@ -1,7 +1,7 @@
package io.papermc.generator.rewriter.types.registry; package io.papermc.generator.rewriter.types.registry;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
import io.papermc.generator.registry.RegistryData; import io.papermc.generator.resources.RegistryData;
import io.papermc.generator.registry.RegistryEntries; import io.papermc.generator.registry.RegistryEntries;
import io.papermc.generator.registry.RegistryEntry; import io.papermc.generator.registry.RegistryEntry;
import io.papermc.generator.registry.RegistryIdentifiable; import io.papermc.generator.registry.RegistryIdentifiable;

View File

@ -1,9 +1,9 @@
package io.papermc.generator.rewriter.types.simple; package io.papermc.generator.rewriter.types.simple;
import io.papermc.generator.registry.RegistryEntries; import io.papermc.generator.registry.RegistryEntries;
import io.papermc.generator.resources.DataFileLoader;
import io.papermc.generator.rewriter.types.Types; import io.papermc.generator.rewriter.types.Types;
import io.papermc.generator.utils.Formatting; import io.papermc.generator.utils.Formatting;
import io.papermc.generator.utils.ItemMetaData;
import io.papermc.generator.utils.predicate.ItemPredicate; import io.papermc.generator.utils.predicate.ItemPredicate;
import io.papermc.typewriter.ClassNamed; import io.papermc.typewriter.ClassNamed;
import io.papermc.typewriter.context.IndentUnit; import io.papermc.typewriter.context.IndentUnit;
@ -25,8 +25,8 @@ public class CraftItemMetasRewriter extends SearchReplaceRewriter {
protected void insert(SearchMetadata metadata, StringBuilder builder) { protected void insert(SearchMetadata metadata, StringBuilder builder) {
IndentUnit indentUnit = this.indentUnit(); IndentUnit indentUnit = this.indentUnit();
ClassNamed itemType = RegistryEntries.byRegistryKey(Registries.ITEM).data().api().klass(); ClassNamed itemType = RegistryEntries.byRegistryKey(Registries.ITEM).data().api().klass();
for (Map.Entry<ClassNamed, List<ItemPredicate>> entry : ItemMetaData.PREDICATES.entrySet()) { for (Map.Entry<ClassNamed, List<ItemPredicate>> entry : DataFileLoader.ItemMeta.PREDICATES.get().entrySet()) {
String field = ItemMetaData.BRIDGE.get(entry.getKey()).field(); String field = DataFileLoader.ItemMeta.BRIDGE.get().get(entry.getKey()).field();
Iterator<ItemPredicate> predicateIterator = entry.getValue().iterator(); Iterator<ItemPredicate> predicateIterator = entry.getValue().iterator();
builder.append(metadata.indent()); builder.append(metadata.indent());

View File

@ -1,73 +1,27 @@
package io.papermc.generator.rewriter.types.simple; package io.papermc.generator.rewriter.types.simple;
import com.google.gson.JsonObject; import io.papermc.generator.resources.DataFileLoader;
import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Codec;
import com.mojang.serialization.JsonOps;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.papermc.generator.rewriter.types.registry.EnumRegistryRewriter; import io.papermc.generator.rewriter.types.registry.EnumRegistryRewriter;
import io.papermc.generator.utils.SourceCodecs; import io.papermc.generator.resources.EntityTypeData;
import io.papermc.typewriter.ClassNamed;
import io.papermc.typewriter.preset.model.EnumValue; import io.papermc.typewriter.preset.model.EnumValue;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries; import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
import static io.papermc.generator.utils.Formatting.quoted; import static io.papermc.generator.utils.Formatting.quoted;
public class EntityTypeRewriter extends EnumRegistryRewriter<EntityType<?>> { public class EntityTypeRewriter extends EnumRegistryRewriter<EntityType<?>> {
public record Data(ClassNamed api, int legacyId) {
private static final int NO_LEGACY_ID = -1;
public Data(ClassNamed api) {
this(api, NO_LEGACY_ID);
}
public static final Codec<Data> DIRECT_CODEC = RecordCodecBuilder.create(instance -> instance.group(
SourceCodecs.CLASS_NAMED.fieldOf("api").forGetter(Data::api),
ExtraCodecs.intRange(-1, Integer.MAX_VALUE).optionalFieldOf("legacy_id", NO_LEGACY_ID).deprecated(13).forGetter(Data::legacyId)
).apply(instance, Data::new));
private static final Codec<Data> CLASS_ONLY_CODEC = SourceCodecs.CLASS_NAMED.xmap(Data::new, Data::api);
public static final Codec<Data> CODEC = Codec.either(CLASS_ONLY_CODEC, DIRECT_CODEC).xmap(Either::unwrap, data -> {
if (data.legacyId() != NO_LEGACY_ID) {
return Either.right(data);
}
return Either.left(data);
});
}
private static final Map<ResourceKey<EntityType<?>>, Data> DATA;
public static final Codec<Map<ResourceKey<EntityType<?>>, Data>> DATA_CODEC = Codec.unboundedMap(ResourceKey.codec(Registries.ENTITY_TYPE), Data.CODEC);
static {
try (Reader input = new BufferedReader(new InputStreamReader(EntityTypeRewriter.class.getClassLoader().getResourceAsStream("data/entity_types.json")))) {
JsonObject registries = SourceCodecs.GSON.fromJson(input, JsonObject.class);
DATA = DATA_CODEC.parse(JsonOps.INSTANCE, registries).getOrThrow();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
public EntityTypeRewriter() { public EntityTypeRewriter() {
super(Registries.ENTITY_TYPE, false); super(Registries.ENTITY_TYPE, false);
} }
@Override @Override
protected EnumValue.Builder rewriteEnumValue(Holder.Reference<EntityType<?>> reference) { protected EnumValue.Builder rewriteEnumValue(Holder.Reference<EntityType<?>> reference) {
Data data = Objects.requireNonNull(DATA.get(reference.key()), () -> "Missing entity type data for " + reference); EntityTypeData data = Objects.requireNonNull(DataFileLoader.ENTITY_TYPES.get().get(reference.key()), () -> "Missing entity type data for " + reference);
String path = reference.key().location().getPath(); String path = reference.key().location().getPath();
List<String> arguments = new ArrayList<>(4); List<String> arguments = new ArrayList<>(4);
arguments.add(quoted(path)); arguments.add(quoted(path));

View File

@ -1,8 +1,8 @@
package io.papermc.generator.rewriter.types.simple; package io.papermc.generator.rewriter.types.simple;
import io.papermc.generator.resources.DataFileLoader;
import io.papermc.generator.rewriter.types.Types; import io.papermc.generator.rewriter.types.Types;
import io.papermc.generator.rewriter.types.registry.RegistryFieldRewriter; import io.papermc.generator.rewriter.types.registry.RegistryFieldRewriter;
import io.papermc.generator.utils.ItemMetaData;
import io.papermc.generator.utils.predicate.ItemPredicate; import io.papermc.generator.utils.predicate.ItemPredicate;
import io.papermc.typewriter.ClassNamed; import io.papermc.typewriter.ClassNamed;
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
@ -27,7 +27,7 @@ public class ItemTypeRewriter extends RegistryFieldRewriter<Item> {
ClassNamed implMetaName = null; ClassNamed implMetaName = null;
mainLoop: mainLoop:
for (Map.Entry<ClassNamed, List<ItemPredicate>> entry : ItemMetaData.PREDICATES.entrySet()) { for (Map.Entry<ClassNamed, List<ItemPredicate>> entry : DataFileLoader.ItemMeta.PREDICATES.get().entrySet()) {
for (ItemPredicate predicate : entry.getValue()) { for (ItemPredicate predicate : entry.getValue()) {
if (predicate.test(reference)) { if (predicate.test(reference)) {
implMetaName = entry.getKey(); implMetaName = entry.getKey();
@ -38,7 +38,7 @@ public class ItemTypeRewriter extends RegistryFieldRewriter<Item> {
ClassNamed metaName = null; ClassNamed metaName = null;
if (implMetaName != null) { if (implMetaName != null) {
metaName = ItemMetaData.BRIDGE.get(implMetaName).api(); metaName = DataFileLoader.ItemMeta.BRIDGE.get().get(implMetaName).api();
} }
return "%s<%s>".formatted(Types.ITEM_TYPE_TYPED.dottedNestedName(), metaName != null ? this.importCollector.getShortName(metaName) : Types.ITEM_META.simpleName()); return "%s<%s>".formatted(Types.ITEM_TYPE_TYPED.dottedNestedName(), metaName != null ? this.importCollector.getShortName(metaName) : Types.ITEM_META.simpleName());
} }

View File

@ -1,55 +1,13 @@
package io.papermc.generator.tasks; package io.papermc.generator.tasks;
import com.google.common.collect.Sets;
import com.google.gson.JsonElement;
import com.mojang.logging.LogUtils; import com.mojang.logging.LogUtils;
import com.mojang.serialization.Codec;
import com.mojang.serialization.JsonOps;
import com.squareup.javapoet.ClassName;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ScanResult;
import io.papermc.generator.Main; import io.papermc.generator.Main;
import io.papermc.generator.registry.RegistryEntries; import io.papermc.generator.resources.DataFile;
import io.papermc.generator.rewriter.types.simple.EntityTypeRewriter; import io.papermc.generator.resources.DataFileLoader;
import io.papermc.generator.types.goal.MobGoalNames; import io.papermc.generator.resources.MutationResult;
import io.papermc.generator.utils.BlockStateData;
import io.papermc.generator.utils.ClassHelper;
import io.papermc.generator.utils.Formatting;
import io.papermc.generator.utils.ItemMetaData;
import io.papermc.generator.utils.SourceCodecs;
import io.papermc.typewriter.ClassNamed;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.vehicle.AbstractBoat;
import net.minecraft.world.entity.vehicle.AbstractMinecart;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import org.slf4j.Logger; import org.slf4j.Logger;
import java.io.IOException; import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public class PrepareInputFiles { public class PrepareInputFiles {
@ -59,140 +17,23 @@ public class PrepareInputFiles {
private static final Logger LOGGER = LogUtils.getLogger(); private static final Logger LOGGER = LogUtils.getLogger();
private interface MutationResult<A> {
A value();
Set<?> added();
Set<?> removed();
}
private record SimpleMutationResult<A>(A value, Set<?> added, Set<?> removed) implements MutationResult<A> {
public static <A> MutationResult<A> noMutation(A value) {
return new SimpleMutationResult<>(value, Collections.emptySet(), Collections.emptySet());
}
}
private record MapMutationResult<K, V>(Map<K, V> value, Set<Map.Entry<K, V>> added, Set<K> removed) implements MutationResult<Map<K, V>> {
}
@FunctionalInterface
private interface Transmuter<A> {
MutationResult<A> transmute(A value);
}
private record DataFile<A>(String path, Codec<A> codec, Transmuter<A> transmuter) {
public MutationResult<A> transmuteRaw(Object value) {
return this.transmuter.transmute((A) value);
}
}
private static <K, V> Transmuter<Map<K, V>> transmuteMap(Comparator<? super K> comparator) {
return transmuteMap(SimpleMutationResult::noMutation, comparator);
}
private static <K, V> Transmuter<Map<K, V>> transmuteDeltaMap(Supplier<Set<K>> typesProvider, Function<K, V> onMissing, Comparator<? super K> comparator) {
return transmuteMap(map -> {
Set<K> types = typesProvider.get();
Set<K> registeredTypes = map.keySet();
Set<Map.Entry<K, V>> added = new HashSet<>();
Sets.difference(types, registeredTypes).forEach(missingType -> added.add(Map.entry(missingType, onMissing.apply(missingType))));
Set<K> removed = new HashSet<>(Sets.difference(registeredTypes, types));
added.forEach(entry -> map.put(entry.getKey(), entry.getValue()));
removed.forEach(map::remove);
return new MapMutationResult<>(map, added, removed);
}, comparator);
}
private static <K, V> Transmuter<Map<K, V>> transmuteMap(Function<Map<K, V>, MutationResult<Map<K, V>>> mutation, Comparator<? super K> comparator) {
return value -> {
TreeMap<K, V> map = new TreeMap<>(comparator);
map.putAll(value);
return mutation.apply(map);
};
}
private static final Map<ResourceKey<EntityType<?>>, Class<?>> ENTITY_TYPE_GENERICS = RegistryEntries.byRegistryKey(Registries.ENTITY_TYPE).getFields(field -> {
if (field.getGenericType() instanceof ParameterizedType complexType && complexType.getActualTypeArguments().length == 1) {
return (Class<?>) complexType.getActualTypeArguments()[0];
}
return null;
});
private static final List<DataFile<?>> DATA_FILES = List.of(
new DataFile<>("block_state/enum_property_types.json", BlockStateData.ENUM_PROPERTY_TYPES_CODEC, transmuteDeltaMap(() -> {
try {
Set<Class<? extends Enum<? extends StringRepresentable>>> enumPropertyTypes = Collections.newSetFromMap(new IdentityHashMap<>());
for (Field field : BlockStateProperties.class.getDeclaredFields()) {
if (ClassHelper.isStaticConstant(field, Modifier.PUBLIC)) {
if (!EnumProperty.class.isAssignableFrom(field.getType())) {
continue;
}
enumPropertyTypes.add(((EnumProperty<?>) field.get(null)).getValueClass());
}
}
return Collections.unmodifiableSet(enumPropertyTypes);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
},
missingType -> ClassName.get("org.bukkit.block.data.type", missingType.getSimpleName()),
Comparator.comparing(Class::getCanonicalName))
),
new DataFile<>("entity_types.json", EntityTypeRewriter.DATA_CODEC, transmuteDeltaMap(() -> BuiltInRegistries.ENTITY_TYPE.listElementIds().collect(Collectors.toSet()),
missingType -> {
Holder.Reference<EntityType<?>> reference = Main.REGISTRY_ACCESS.get(missingType).orElseThrow();
Class<?> genericType = ENTITY_TYPE_GENERICS.get(missingType);
String packageName = "org.bukkit.entity";
if (AbstractBoat.class.isAssignableFrom(genericType)) {
packageName += ".boat";
} else if (AbstractMinecart.class.isAssignableFrom(genericType)) {
packageName += ".minecart";
}
return new EntityTypeRewriter.Data(ClassNamed.of(packageName, genericType.getSimpleName()));
},
Formatting.alphabeticKeyOrder(key -> key.location().getPath()))
),
new DataFile<>("entity_class_names.json", MobGoalNames.ENTITY_CLASS_NAMES_CODEC, transmuteDeltaMap(() -> {
try (ScanResult scanResult = new ClassGraph().enableClassInfo().whitelistPackages(Entity.class.getPackageName()).scan()) {
Set<Class<? extends Mob>> classes = new HashSet<>(scanResult.getSubclasses(Mob.class.getName()).loadClasses(Mob.class));
classes.add(Mob.class);
return Collections.unmodifiableSet(classes);
}
},
missingType -> ClassName.get("org.bukkit.entity", missingType.getSimpleName()),
Comparator.comparing(Class::getCanonicalName))
),
new DataFile<>("item_meta/bridge.json", ItemMetaData.BRIDGE_CODEC, transmuteMap(Comparator.comparing(ClassNamed::canonicalName)))
);
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
Path resourceDir = Path.of(args[0]); Path resourceDir = Path.of(args[0]);
for (DataFile<?> file : DATA_FILES) { for (DataFile<?, ?, ?> file : DataFileLoader.DATA_FILES) {
Path filePath = Path.of("data", file.path()); if (!file.isMutable()) {
Path resourcePath = resourceDir.resolve(filePath); continue;
try (Reader input = Files.newBufferedReader(resourcePath, StandardCharsets.UTF_8)) { }
JsonElement element = SourceCodecs.GSON.fromJson(input, JsonElement.class);
MutationResult<?> result = file.transmuteRaw(file.codec().parse(JsonOps.INSTANCE, element).getOrThrow());
element = ((Codec<Object>) file.codec()).encodeStart(JsonOps.INSTANCE, result.value()).getOrThrow();
Files.writeString(resourcePath, SourceCodecs.GSON.toJson(element) + "\n", StandardCharsets.UTF_8);
if (!result.added().isEmpty()) { Path filePath = Path.of(file.path());
LOGGER.info("Added the following elements in {}:", filePath); MutationResult<?, ?, ?> result = file.upgrade(resourceDir.resolve(filePath));
result.added().stream().map(Object::toString).forEach(LOGGER::info);
} if (!result.added().isEmpty()) {
if (!result.removed().isEmpty()) { LOGGER.info("Added the following elements in {}:", filePath);
LOGGER.warn("Removed the following keys in {}:", filePath); result.added().stream().map(o -> "- " + o).forEach(LOGGER::info);
result.removed().stream().map(Object::toString).forEach(LOGGER::info); }
} if (!result.removed().isEmpty()) {
LOGGER.warn("Removed the following keys in {}:", filePath);
result.removed().stream().map(o -> "- " + o).forEach(LOGGER::warn);
} }
} }
} }

View File

@ -4,7 +4,7 @@ import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeName;
import io.papermc.generator.utils.BlockStateData; import io.papermc.generator.resources.DataFileLoader;
import net.minecraft.util.StringRepresentable; import net.minecraft.util.StringRepresentable;
import net.minecraft.world.level.block.state.properties.EnumProperty; import net.minecraft.world.level.block.state.properties.EnumProperty;
import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.NullMarked;
@ -27,7 +27,7 @@ public class EnumPropertyWriter<T extends Enum<T> & StringRepresentable> extends
@Override @Override
protected TypeName processApiType() { protected TypeName processApiType() {
Class<?> rawClass = this.property.getValueClass(); Class<?> rawClass = this.property.getValueClass();
return BlockStateData.ENUM_PROPERTY_TYPES.get(rawClass); return DataFileLoader.BlockState.ENUM_PROPERTY_TYPES.get().get(rawClass);
} }
@Override @Override

View File

@ -11,9 +11,9 @@ import io.papermc.generator.types.Types;
import io.papermc.generator.types.craftblockdata.CraftBlockDataGenerator; import io.papermc.generator.types.craftblockdata.CraftBlockDataGenerator;
import io.papermc.generator.types.craftblockdata.property.converter.ConverterBase; import io.papermc.generator.types.craftblockdata.property.converter.ConverterBase;
import io.papermc.generator.types.craftblockdata.property.holder.appender.DataAppenders; import io.papermc.generator.types.craftblockdata.property.holder.appender.DataAppenders;
import io.papermc.generator.utils.BlockStateData;
import io.papermc.generator.utils.ClassHelper; import io.papermc.generator.utils.ClassHelper;
import io.papermc.generator.utils.CommonVariable; import io.papermc.generator.utils.CommonVariable;
import io.papermc.generator.resources.DataFileLoader;
import io.papermc.generator.utils.NamingManager; import io.papermc.generator.utils.NamingManager;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
@ -80,7 +80,7 @@ public class DataPropertyWriter extends DataPropertyWriterBase {
this.type = DataHolderType.MAP; this.type = DataHolderType.MAP;
this.internalIndexClass = ClassHelper.eraseType(complexType.getActualTypeArguments()[0]); this.internalIndexClass = ClassHelper.eraseType(complexType.getActualTypeArguments()[0]);
if (this.internalIndexClass.isEnum()) { if (this.internalIndexClass.isEnum()) {
TypeName indexClass = BlockStateData.ENUM_PROPERTY_TYPES.get(this.internalIndexClass); TypeName indexClass = DataFileLoader.BlockState.ENUM_PROPERTY_TYPES.get().get(this.internalIndexClass);
if (indexClass == null) { if (indexClass == null) {
indexClass = TypeName.get(this.internalIndexClass); indexClass = TypeName.get(this.internalIndexClass);
} }

View File

@ -11,6 +11,7 @@ import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Stream;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.properties.Property; import net.minecraft.world.level.block.state.properties.Property;
import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.NullMarked;
@ -18,18 +19,14 @@ import org.jspecify.annotations.NullMarked;
@NullMarked @NullMarked
public abstract class DataPropertyWriterBase implements DataPropertyMaker { public abstract class DataPropertyWriterBase implements DataPropertyMaker {
protected final Collection<? extends Property<?>> properties; protected final Stream<? extends Property<?>> properties;
protected final Class<? extends Block> blockClass; protected final Class<? extends Block> blockClass;
protected DataPropertyWriterBase(Collection<? extends Property<?>> properties, Class<? extends Block> blockClass) { protected DataPropertyWriterBase(Collection<? extends Property<?>> properties, Class<? extends Block> blockClass) {
this.properties = properties; this.properties = properties.stream().sorted(Comparator.comparing(Property::getName));
this.blockClass = blockClass; this.blockClass = blockClass;
} }
private Iterator<? extends Property<?>> sortedIterator() {
return this.properties.stream().sorted(Comparator.comparing(Property::getName)).iterator();
}
protected void createSyntheticCollection(CodeBlock.Builder code, boolean isArray, Map<Property<?>, Field> fields) { protected void createSyntheticCollection(CodeBlock.Builder code, boolean isArray, Map<Property<?>, Field> fields) {
if (isArray) { if (isArray) {
code.add("{\n"); code.add("{\n");
@ -37,12 +34,12 @@ public abstract class DataPropertyWriterBase implements DataPropertyMaker {
code.add("$T.of(\n", List.class); code.add("$T.of(\n", List.class);
} }
code.indent(); code.indent();
Iterator<? extends Property<?>> it = this.sortedIterator(); Iterator<? extends Property<?>> properties = this.properties.iterator();
while (it.hasNext()) { while (properties.hasNext()) {
Property<?> property = it.next(); Property<?> property = properties.next();
Pair<Class<?>, String> fieldName = PropertyWriter.referenceField(this.blockClass, property, fields); Pair<Class<?>, String> fieldName = PropertyWriter.referenceField(this.blockClass, property, fields);
code.add("$T.$L", fieldName.left(), fieldName.right()); code.add("$T.$L", fieldName.left(), fieldName.right());
if (it.hasNext()) { if (properties.hasNext()) {
code.add(","); code.add(",");
} }
code.add("\n"); code.add("\n");
@ -53,13 +50,13 @@ public abstract class DataPropertyWriterBase implements DataPropertyMaker {
protected void createSyntheticMap(CodeBlock.Builder code, TypeName indexClass, Map<Property<?>, Field> fields) { protected void createSyntheticMap(CodeBlock.Builder code, TypeName indexClass, Map<Property<?>, Field> fields) {
// assume indexClass is an enum with its values matching the property names // assume indexClass is an enum with its values matching the property names
code.add("$T.of(\n", Map.class).indent(); code.add("$T.of(\n", Map.class).indent();
Iterator<? extends Property<?>> it = this.sortedIterator(); Iterator<? extends Property<?>> properties = this.properties.iterator();
while (it.hasNext()) { while (properties.hasNext()) {
Property<?> property = it.next(); Property<?> property = properties.next();
String name = Formatting.formatKeyAsField(property.getName()); String name = Formatting.formatKeyAsField(property.getName());
Pair<Class<?>, String> fieldName = PropertyWriter.referenceField(this.blockClass, property, fields); Pair<Class<?>, String> fieldName = PropertyWriter.referenceField(this.blockClass, property, fields);
code.add("$T.$L, $T.$L", indexClass, name, fieldName.left(), fieldName.right()); code.add("$T.$L, $T.$L", indexClass, name, fieldName.left(), fieldName.right());
if (it.hasNext()) { if (properties.hasNext()) {
code.add(","); code.add(",");
} }
code.add("\n"); code.add("\n");

View File

@ -10,7 +10,7 @@ import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.TypeSpec;
import io.papermc.generator.types.craftblockdata.CraftBlockDataGenerator; import io.papermc.generator.types.craftblockdata.CraftBlockDataGenerator;
import io.papermc.generator.types.craftblockdata.property.converter.ConverterBase; import io.papermc.generator.types.craftblockdata.property.converter.ConverterBase;
import io.papermc.generator.utils.BlockStateData; import io.papermc.generator.resources.DataFileLoader;
import io.papermc.generator.utils.NamingManager; import io.papermc.generator.utils.NamingManager;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Collection; import java.util.Collection;
@ -54,7 +54,7 @@ public class VirtualDataPropertyWriter extends DataPropertyWriterBase {
} else { } else {
Class<?> valueClass = this.properties.iterator().next().getValueClass(); Class<?> valueClass = this.properties.iterator().next().getValueClass();
if (valueClass.isEnum()) { if (valueClass.isEnum()) {
this.indexClass = BlockStateData.ENUM_PROPERTY_TYPES.get(valueClass); this.indexClass = DataFileLoader.BlockState.ENUM_PROPERTY_TYPES.get().get(valueClass);
} else { } else {
this.indexClass = TypeName.get(valueClass); this.indexClass = TypeName.get(valueClass);
} }

View File

@ -2,9 +2,6 @@ package io.papermc.generator.types.craftblockdata.property.holder.appender;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.gson.JsonArray;
import com.mojang.serialization.Codec;
import com.mojang.serialization.JsonOps;
import com.squareup.javapoet.ClassName; import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.MethodSpec;
@ -12,23 +9,16 @@ import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.TypeSpec;
import io.papermc.generator.resources.DataFileLoader;
import io.papermc.generator.types.Types; import io.papermc.generator.types.Types;
import io.papermc.generator.types.craftblockdata.CraftBlockDataGenerator; import io.papermc.generator.types.craftblockdata.CraftBlockDataGenerator;
import io.papermc.generator.types.craftblockdata.property.converter.ConverterBase; import io.papermc.generator.types.craftblockdata.property.converter.ConverterBase;
import io.papermc.generator.types.craftblockdata.property.holder.DataHolderType; import io.papermc.generator.types.craftblockdata.property.holder.DataHolderType;
import io.papermc.generator.utils.BlockStateMapping;
import io.papermc.generator.utils.CommonVariable; import io.papermc.generator.utils.CommonVariable;
import io.papermc.generator.utils.NamingManager; import io.papermc.generator.utils.NamingManager;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import io.papermc.generator.utils.SourceCodecs;
import io.papermc.typewriter.ClassNamed;
import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.block.state.properties.BooleanProperty;
import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.NullMarked;
@ -38,21 +28,7 @@ public class MapAppender implements DataAppender {
private static final Map<String, String> INDEX_NAMES = ImmutableMap.<String, String>builder() private static final Map<String, String> INDEX_NAMES = ImmutableMap.<String, String>builder()
.put(Types.BLOCK_FACE.simpleName(), "Face") .put(Types.BLOCK_FACE.simpleName(), "Face")
.buildOrThrow(); .buildOrThrow();
// todo find another way for extra method and clean up those hardcoded things ^
// no real rule here some has some don't mossy carpet and wall could have it
// todo find another way and clean up those hardcoced things ^
public static final List<ClassNamed> SUPPORT_ALLOWED_METHOD;
public static final Codec<List<ClassNamed>> SUPPORT_ALLOWED_METHOD_CODEC = SourceCodecs.CLASS_NAMED.listOf(1, Integer.MAX_VALUE);
static {
// add classes that extends MultipleFacing and RedstoneWire
try (Reader input = new BufferedReader(new InputStreamReader(BlockStateMapping.class.getClassLoader().getResourceAsStream("data/block_state/extra_allowed_method.json")))) {
JsonArray classes = SourceCodecs.GSON.fromJson(input, JsonArray.class);
SUPPORT_ALLOWED_METHOD = SUPPORT_ALLOWED_METHOD_CODEC.parse(JsonOps.INSTANCE, classes).getOrThrow();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
@Override @Override
public DataHolderType getType() { public DataHolderType getType() {
@ -80,7 +56,7 @@ public class MapAppender implements DataAppender {
builder.addMethod(methodBuilder.build()); builder.addMethod(methodBuilder.build());
} }
if (SUPPORT_ALLOWED_METHOD.contains(generator.getBaseClass()) && if (DataFileLoader.BlockState.EXTRA_ALLOWED_METHOD.get().contains(generator.getBaseClass()) &&
indexParameter.type instanceof ClassName className && !className.isBoxedPrimitive()) { indexParameter.type instanceof ClassName className && !className.isBoxedPrimitive()) {
NamingManager.NameWrapper indexNaming = NamingManager.NameWrapper.wrap("get", INDEX_NAMES.getOrDefault(className.simpleName(), className.simpleName())); NamingManager.NameWrapper indexNaming = NamingManager.NameWrapper.wrap("get", INDEX_NAMES.getOrDefault(className.simpleName(), className.simpleName()));

View File

@ -61,7 +61,7 @@ public class MobGoalGenerator extends SimpleGenerator {
.returns(ParameterizedTypeName.get(Types.GOAL_KEY, type)); .returns(ParameterizedTypeName.get(Types.GOAL_KEY, type));
List<Class<Goal>> classes; List<Class<Goal>> classes;
try (ScanResult scanResult = new ClassGraph().enableAllInfo().whitelistPackages("net.minecraft").scan()) { try (ScanResult scanResult = new ClassGraph().enableAllInfo().acceptPackages("net.minecraft").scan()) {
classes = scanResult.getSubclasses(Goal.class.getName()).loadClasses(Goal.class); classes = scanResult.getSubclasses(Goal.class.getName()).loadClasses(Goal.class);
} }

View File

@ -1,22 +1,14 @@
package io.papermc.generator.types.goal; package io.papermc.generator.types.goal;
import com.google.common.base.CaseFormat; import com.google.common.base.CaseFormat;
import com.google.gson.JsonObject;
import com.mojang.serialization.Codec;
import com.mojang.serialization.JsonOps;
import com.squareup.javapoet.ClassName; import com.squareup.javapoet.ClassName;
import io.papermc.generator.resources.DataFileLoader;
import io.papermc.generator.types.Types; import io.papermc.generator.types.Types;
import io.papermc.generator.utils.SourceCodecs;
import io.papermc.generator.utils.Formatting; import io.papermc.generator.utils.Formatting;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.goal.Goal; import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.monster.RangedAttackMob; import net.minecraft.world.entity.monster.RangedAttackMob;
import org.apache.commons.lang3.math.NumberUtils; import org.apache.commons.lang3.math.NumberUtils;
@ -26,20 +18,6 @@ import org.jspecify.annotations.NullMarked;
public final class MobGoalNames { // todo sync with MobGoalHelper ideally this should not be duplicated public final class MobGoalNames { // todo sync with MobGoalHelper ideally this should not be duplicated
private static final Map<Class<? extends Goal>, ClassName> entityClassCache = new HashMap<>(); private static final Map<Class<? extends Goal>, ClassName> entityClassCache = new HashMap<>();
public static final Map<Class<? extends Mob>, ClassName> ENTITY_CLASS_NAMES;
public static final Codec<Map<Class<? extends Mob>, ClassName>> ENTITY_CLASS_NAMES_CODEC = Codec.unboundedMap(
SourceCodecs.classCodec(Mob.class), SourceCodecs.CLASS_NAME
);
static {
try (Reader input = new BufferedReader(new InputStreamReader(MobGoalNames.class.getClassLoader().getResourceAsStream("data/entity_class_names.json")))) {
JsonObject names = SourceCodecs.GSON.fromJson(input, JsonObject.class);
ENTITY_CLASS_NAMES = ENTITY_CLASS_NAMES_CODEC.parse(JsonOps.INSTANCE, names).getOrThrow();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
private static final Map<String, String> deobfuscationMap = new HashMap<>(); private static final Map<String, String> deobfuscationMap = new HashMap<>();
static { static {
@ -102,7 +80,7 @@ public final class MobGoalNames { // todo sync with MobGoalHelper ideally this s
} }
private static ClassName toBukkitClass(Class<? extends net.minecraft.world.entity.Mob> nmsClass) { private static ClassName toBukkitClass(Class<? extends net.minecraft.world.entity.Mob> nmsClass) {
ClassName bukkitClass = ENTITY_CLASS_NAMES.get(nmsClass); ClassName bukkitClass = DataFileLoader.ENTITY_CLASS_NAMES.get().get(nmsClass);
if (bukkitClass == null) { if (bukkitClass == null) {
throw new RuntimeException("Can't figure out applicable bukkit entity for nms entity " + nmsClass); // maybe just return Mob? throw new RuntimeException("Can't figure out applicable bukkit entity for nms entity " + nmsClass); // maybe just return Mob?
} }

View File

@ -1,52 +0,0 @@
package io.papermc.generator.utils;
import com.google.common.reflect.TypeToken;
import com.google.gson.JsonObject;
import com.mojang.serialization.Codec;
import com.mojang.serialization.JsonOps;
import com.squareup.javapoet.ClassName;
import io.papermc.generator.Main;
import io.papermc.generator.utils.predicate.BlockPredicate;
import io.papermc.typewriter.ClassNamed;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.List;
import java.util.Map;
import net.minecraft.resources.RegistryOps;
import net.minecraft.util.StringRepresentable;
public final class BlockStateData {
public static final Map<ClassNamed, List<BlockPredicate>> PREDICATES;
public static final Codec<Map<ClassNamed, List<BlockPredicate>>> PREDICATES_CODEC = Codec.unboundedMap(
SourceCodecs.CLASS_NAMED, BlockPredicate.CODEC.listOf()
);
static {
try (Reader input = new BufferedReader(new InputStreamReader(BlockStateMapping.class.getClassLoader().getResourceAsStream("data/block_state/predicates.json")))) {
JsonObject predicates = SourceCodecs.GSON.fromJson(input, JsonObject.class);
PREDICATES = PREDICATES_CODEC.parse(RegistryOps.create(JsonOps.INSTANCE, Main.REGISTRY_ACCESS), predicates).getOrThrow();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
public static final Map<Class<? extends Enum<? extends StringRepresentable>>, ClassName> ENUM_PROPERTY_TYPES;
public static final Codec<Map<Class<? extends Enum<? extends StringRepresentable>>, ClassName>> ENUM_PROPERTY_TYPES_CODEC = Codec.unboundedMap(
SourceCodecs.classCodec(new TypeToken<Enum<? extends StringRepresentable>>() {}), SourceCodecs.CLASS_NAME
);
static {
try (Reader input = new BufferedReader(new InputStreamReader(BlockStateMapping.class.getClassLoader().getResourceAsStream("data/block_state/enum_property_types.json")))) {
JsonObject properties = SourceCodecs.GSON.fromJson(input, JsonObject.class);
ENUM_PROPERTY_TYPES = ENUM_PROPERTY_TYPES_CODEC.parse(JsonOps.INSTANCE, properties).getOrThrow();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
private BlockStateData() {
}
}

View File

@ -8,6 +8,7 @@ import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.mojang.datafixers.util.Either; import com.mojang.datafixers.util.Either;
import io.papermc.generator.Main; import io.papermc.generator.Main;
import io.papermc.generator.resources.DataFileLoader;
import io.papermc.generator.types.craftblockdata.property.holder.VirtualField; import io.papermc.generator.types.craftblockdata.property.holder.VirtualField;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
@ -148,7 +149,7 @@ public final class BlockStateMapping {
return Optional.empty(); return Optional.empty();
}).orElseGet(() -> { }).orElseGet(() -> {
Set<Property<?>> propertySet = new HashSet<>(entry.getValue()); Set<Property<?>> propertySet = new HashSet<>(entry.getValue());
for (Map.Entry<ClassNamed, List<BlockPredicate>> predicateEntry : BlockStateData.PREDICATES.entrySet()) { for (Map.Entry<ClassNamed, List<BlockPredicate>> predicateEntry : DataFileLoader.BlockState.PREDICATES.get().entrySet()) {
for (BlockPredicate predicate : predicateEntry.getValue()) { for (BlockPredicate predicate : predicateEntry.getValue()) {
if (predicate.test(specialBlock, propertySet)) { if (predicate.test(specialBlock, propertySet)) {
return predicateEntry.getKey(); return predicateEntry.getKey();

View File

@ -1,53 +0,0 @@
package io.papermc.generator.utils;
import com.google.gson.JsonObject;
import com.mojang.serialization.Codec;
import com.mojang.serialization.JsonOps;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.papermc.generator.Main;
import io.papermc.generator.utils.predicate.ItemPredicate;
import io.papermc.typewriter.ClassNamed;
import net.minecraft.resources.RegistryOps;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.List;
import java.util.Map;
@Deprecated
public final class ItemMetaData {
public record BridgeData(ClassNamed api, String field) {
public static final Codec<BridgeData> CODEC = RecordCodecBuilder.create(instance -> instance.group(
SourceCodecs.CLASS_NAMED.fieldOf("api").forGetter(BridgeData::api),
SourceCodecs.IDENTIFIER.fieldOf("field").forGetter(BridgeData::field)
).apply(instance, BridgeData::new));
}
public static final Map<ClassNamed, BridgeData> BRIDGE;
public static final Codec<Map<ClassNamed, BridgeData>> BRIDGE_CODEC = Codec.unboundedMap(SourceCodecs.CLASS_NAMED, BridgeData.CODEC);
static {
try (Reader input = new BufferedReader(new InputStreamReader(ItemMetaData.class.getClassLoader().getResourceAsStream("data/item_meta/bridge.json")))) {
JsonObject metas = SourceCodecs.GSON.fromJson(input, JsonObject.class);
BRIDGE = BRIDGE_CODEC.parse(JsonOps.INSTANCE, metas).getOrThrow();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
public static final Map<ClassNamed, List<ItemPredicate>> PREDICATES;
public static final Codec<Map<ClassNamed, List<ItemPredicate>>> PREDICATES_CODEC = Codec.unboundedMap(SourceCodecs.CLASS_NAMED, ItemPredicate.CODEC.listOf(1, Integer.MAX_VALUE));
static {
try (Reader input = new BufferedReader(new InputStreamReader(ItemMetaData.class.getClassLoader().getResourceAsStream("data/item_meta/predicates.json")))) {
JsonObject predicates = SourceCodecs.GSON.fromJson(input, JsonObject.class);
PREDICATES = PREDICATES_CODEC.parse(RegistryOps.create(JsonOps.INSTANCE, Main.REGISTRY_ACCESS), predicates).getOrThrow();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
private ItemMetaData() {
}
}

View File

@ -1,16 +1,12 @@
package io.papermc.generator.utils; package io.papermc.generator.utils;
import com.google.common.reflect.TypeToken; import com.google.common.reflect.TypeToken;
import com.google.gson.FormattingStyle;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.mojang.datafixers.util.Either; import com.mojang.datafixers.util.Either;
import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult; import com.mojang.serialization.DataResult;
import com.mojang.serialization.DynamicOps; import com.mojang.serialization.DynamicOps;
import com.squareup.javapoet.ClassName; import com.squareup.javapoet.ClassName;
import io.papermc.generator.types.SimpleGenerator;
import io.papermc.typewriter.ClassNamed; import io.papermc.typewriter.ClassNamed;
import javax.lang.model.SourceVersion; import javax.lang.model.SourceVersion;
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
@ -18,12 +14,13 @@ import net.minecraft.core.Registry;
import net.minecraft.resources.RegistryFixedCodec; import net.minecraft.resources.RegistryFixedCodec;
import net.minecraft.resources.RegistryOps; import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import org.jspecify.annotations.NullMarked;
@NullMarked
public final class SourceCodecs { public final class SourceCodecs {
public static final Gson GSON = new GsonBuilder().setFormattingStyle(FormattingStyle.PRETTY.withIndent(SimpleGenerator.INDENT_UNIT)).create();
private SourceCodecs() { private SourceCodecs() {
} }
@ -74,6 +71,8 @@ public final class SourceCodecs {
io.papermc.generator.types.Types::typed, io.papermc.generator.rewriter.types.Types::typed io.papermc.generator.types.Types::typed, io.papermc.generator.rewriter.types.Types::typed
); );
public static final Codec<ResourceKey<? extends Registry<?>>> REGISTRY_KEY = ResourceLocation.CODEC.xmap(ResourceKey::createRegistryKey, ResourceKey::location);
public static <E> Codec<Either<TagKey<E>, Holder<E>>> elementOrTagCodec(ResourceKey<? extends Registry<E>> registryKey) { public static <E> Codec<Either<TagKey<E>, Holder<E>>> elementOrTagCodec(ResourceKey<? extends Registry<E>> registryKey) {
return Codec.either(RegistryAwareTagKeyCodec.hashedCodec(registryKey), RegistryFixedCodec.create(registryKey)); return Codec.either(RegistryAwareTagKeyCodec.hashedCodec(registryKey), RegistryFixedCodec.create(registryKey));
} }

View File

@ -5,6 +5,8 @@ import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec; import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder; import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.papermc.generator.utils.BlockStateMapping; import io.papermc.generator.utils.BlockStateMapping;
import io.papermc.generator.utils.SourceCodecs;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.util.StringRepresentable; import net.minecraft.util.StringRepresentable;
import net.minecraft.world.level.block.state.properties.Property; import net.minecraft.world.level.block.state.properties.Property;
import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.NullMarked;
@ -20,7 +22,7 @@ public sealed interface BlockPropertyPredicate permits BlockPropertyPredicate.Is
Codec<BlockPropertyPredicate> CODEC = Codec.either(BlockPropertyPredicate.IsNamePredicate.COMPACT_CODEC, DIRECT_CODEC).xmap(Either::unwrap, Either::right); Codec<BlockPropertyPredicate> CODEC = Codec.either(BlockPropertyPredicate.IsNamePredicate.COMPACT_CODEC, DIRECT_CODEC).xmap(Either::unwrap, Either::right);
Codec<Set<BlockPropertyPredicate>> SET_CODEC = CODEC.listOf().xmap(Set::copyOf, List::copyOf); Codec<Set<BlockPropertyPredicate>> SET_CODEC = CODEC.listOf().xmap(Set::copyOf, List::copyOf);
Codec<Set<BlockPropertyPredicate>> NON_EMPTY_SET_CODEC = CODEC.listOf(1, Integer.MAX_VALUE).xmap(Set::copyOf, List::copyOf); Codec<Set<BlockPropertyPredicate>> NON_EMPTY_SET_CODEC = ExtraCodecs.nonEmptyList(CODEC.listOf()).xmap(Set::copyOf, List::copyOf);
Type type(); Type type();
@ -79,7 +81,7 @@ public sealed interface BlockPropertyPredicate permits BlockPropertyPredicate.Is
record IsFieldPredicate(String value) implements BlockPropertyPredicate { record IsFieldPredicate(String value) implements BlockPropertyPredicate {
public static final MapCodec<IsFieldPredicate> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( public static final MapCodec<IsFieldPredicate> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
Codec.STRING.fieldOf("value").forGetter(IsFieldPredicate::value) SourceCodecs.IDENTIFIER.fieldOf("value").forGetter(IsFieldPredicate::value)
).apply(instance, IsFieldPredicate::new)); ).apply(instance, IsFieldPredicate::new));
@Override @Override

View File

@ -1,52 +1,16 @@
package io.papermc.generator; package io.papermc.generator;
import io.papermc.generator.utils.BlockStateData;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import io.papermc.generator.utils.ClassHelper;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.level.block.ChiseledBookShelfBlock; import net.minecraft.world.level.block.ChiseledBookShelfBlock;
import net.minecraft.world.level.block.MossyCarpetBlock; import net.minecraft.world.level.block.MossyCarpetBlock;
import net.minecraft.world.level.block.PipeBlock; import net.minecraft.world.level.block.PipeBlock;
import net.minecraft.world.level.block.WallBlock; import net.minecraft.world.level.block.WallBlock;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
public class BlockStatePropertyTest extends BootstrapTest { public class BlockStatePropertyTest extends BootstrapTest {
private static Set<Class<? extends Enum<? extends StringRepresentable>>> ENUM_PROPERTY_TYPES;
@BeforeAll
public static void getAllProperties() {
// get all properties
Set<Class<? extends Enum<? extends StringRepresentable>>> enumPropertyTypes = Collections.newSetFromMap(new IdentityHashMap<>());
try {
for (Field field : BlockStateProperties.class.getDeclaredFields()) {
if (ClassHelper.isStaticConstant(field, Modifier.PUBLIC)) {
if (!EnumProperty.class.isAssignableFrom(field.getType())) {
continue;
}
enumPropertyTypes.add(((EnumProperty<?>) field.get(null)).getValueClass());
}
}
ENUM_PROPERTY_TYPES = Collections.unmodifiableSet(enumPropertyTypes);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
@Test @Test
public void testReferences() throws NoSuchFieldException, IllegalAccessException { public void testReferences() throws NoSuchFieldException, IllegalAccessException {
// if renamed should change DataPropertyWriter#FIELD_TO_BASE_NAME/FIELD_TO_BASE_NAME_SPECIFICS // if renamed should change DataPropertyWriter#FIELD_TO_BASE_NAME/FIELD_TO_BASE_NAME_SPECIFICS
@ -56,16 +20,4 @@ public class BlockStatePropertyTest extends BootstrapTest {
lookup.findStaticVarHandle(WallBlock.class, "PROPERTY_BY_DIRECTION", Map.class); lookup.findStaticVarHandle(WallBlock.class, "PROPERTY_BY_DIRECTION", Map.class);
lookup.findStaticVarHandle(MossyCarpetBlock.class, "PROPERTY_BY_DIRECTION", Map.class); lookup.findStaticVarHandle(MossyCarpetBlock.class, "PROPERTY_BY_DIRECTION", Map.class);
} }
@Test
public void testBridge() {
Set<Class<? extends Enum<? extends StringRepresentable>>> missingApiEquivalents = new HashSet<>();
for (Class<? extends Enum<? extends StringRepresentable>> type : ENUM_PROPERTY_TYPES) {
if (!BlockStateData.ENUM_PROPERTY_TYPES.containsKey(type)) {
missingApiEquivalents.add(type);
}
}
Assertions.assertTrue(missingApiEquivalents.isEmpty(), () -> "Missing some api equivalent in the block state data enum types (in block_state/enum_property_types.json) : " + missingApiEquivalents.stream().map(Class::getCanonicalName).collect(Collectors.joining(", ")));
}
} }

View File

@ -1,13 +1,8 @@
package io.papermc.generator; package io.papermc.generator;
import net.minecraft.SharedConstants; public abstract class BootstrapTest {
import net.minecraft.server.Bootstrap;
public class BootstrapTest {
static { static {
SharedConstants.tryDetectVersion(); Main.bootStrap(true);
Bootstrap.bootStrap();
Bootstrap.validate();
} }
} }

View File

@ -0,0 +1,26 @@
package io.papermc.generator;
import io.papermc.generator.resources.DataFile;
import io.papermc.generator.resources.DataFileLoader;
import io.papermc.generator.resources.MutationResult;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class DataFileTest extends BootstrapTest {
public static List<DataFile<?, ?, ?>> files() {
return DataFileLoader.DATA_FILES;
}
@ParameterizedTest
@MethodSource("files")
public <V, A, R> void testFile(DataFile<V, A, R> file) {
MutationResult<V, A, R> result = file.transmuter().transmute(file.readUnchecked());
assertTrue(result.added().isEmpty(), () -> "Missing some data in " + file.path() + ":\n" + String.join("\n", result.added().stream().map(Object::toString).collect(Collectors.toSet())));
assertTrue(result.removed().isEmpty(), () -> "Extra data found in " + file.path() + ":\n" + String.join("\n", result.removed().stream().map(Object::toString).collect(Collectors.toSet())));
}
}

View File

@ -1,35 +0,0 @@
package io.papermc.generator;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ScanResult;
import io.papermc.generator.types.goal.MobGoalNames;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Mob;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class MobGoalConverterTest extends BootstrapTest {
@Test
public void testBukkitMap() {
final List<Class<Mob>> classes;
try (ScanResult scanResult = new ClassGraph().enableClassInfo().whitelistPackages(Entity.class.getPackageName()).scan()) {
classes = scanResult.getSubclasses(Mob.class.getName()).loadClasses(Mob.class);
}
assertFalse(classes.isEmpty(), "There are supposed to be more than 0 mob classes!");
List<String> missingClasses = new ArrayList<>();
for (Class<Mob> nmsClass : classes) {
if (!MobGoalNames.ENTITY_CLASS_NAMES.containsKey(nmsClass)) {
missingClasses.add(nmsClass.getCanonicalName());
}
}
assertTrue(missingClasses.isEmpty(), () -> "Missing some entity classes in the bukkit map: " + String.join(", ", missingClasses));
}
}