generate tags and structure type

This commit is contained in:
Lulu13022002 2024-01-20 22:54:31 +01:00
parent 359ae082f3
commit ba1cc2c12b
No known key found for this signature in database
GPG Key ID: 491C8F0B8ACDEB01
15 changed files with 2736 additions and 105 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,46 @@
package org.bukkit.entity;
import io.papermc.paper.generated.GeneratedFrom;
@SuppressWarnings({
"unused",
"SpellCheckingInspection"
})
@GeneratedFrom("1.20.4")
public enum Pose {
STANDING,
FALL_FLYING,
SLEEPING,
SWIMMING,
SPIN_ATTACK,
CROUCHING,
LONG_JUMPING,
DYING,
CROAKING,
USING_TONGUE,
SITTING,
ROARING,
SNIFFING,
EMERGING,
DIGGING,
SLIDING,
SHOOTING,
INHALING
}

View File

@ -0,0 +1,139 @@
package org.bukkit.generator.structure;
import io.papermc.paper.generated.GeneratedFrom;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.jetbrains.annotations.NotNull;
/**
* Vanilla keys for StructureTypes.
*
* @apiNote The fields provided here are a direct representation of
* what is available from the vanilla game source. They may be
* changed (including removals) on any Minecraft version
* bump, so cross-version compatibility is not provided on the
* same level as it is on most of the other API.
*/
@SuppressWarnings({
"unused",
"SpellCheckingInspection"
})
@GeneratedFrom("1.20.4")
public abstract class StructureType implements Keyed {
/**
* {@code minecraft:buried_treasure}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final StructureType BURIED_TREASURE = create("buried_treasure");
/**
* {@code minecraft:desert_pyramid}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final StructureType DESERT_PYRAMID = create("desert_pyramid");
/**
* {@code minecraft:end_city}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final StructureType END_CITY = create("end_city");
/**
* {@code minecraft:fortress}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final StructureType FORTRESS = create("fortress");
/**
* {@code minecraft:igloo}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final StructureType IGLOO = create("igloo");
/**
* {@code minecraft:jigsaw}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final StructureType JIGSAW = create("jigsaw");
/**
* {@code minecraft:jungle_temple}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final StructureType JUNGLE_TEMPLE = create("jungle_temple");
/**
* {@code minecraft:mineshaft}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final StructureType MINESHAFT = create("mineshaft");
/**
* {@code minecraft:nether_fossil}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final StructureType NETHER_FOSSIL = create("nether_fossil");
/**
* {@code minecraft:ocean_monument}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final StructureType OCEAN_MONUMENT = create("ocean_monument");
/**
* {@code minecraft:ocean_ruin}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final StructureType OCEAN_RUIN = create("ocean_ruin");
/**
* {@code minecraft:ruined_portal}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final StructureType RUINED_PORTAL = create("ruined_portal");
/**
* {@code minecraft:shipwreck}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final StructureType SHIPWRECK = create("shipwreck");
/**
* {@code minecraft:stronghold}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final StructureType STRONGHOLD = create("stronghold");
/**
* {@code minecraft:swamp_hut}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final StructureType SWAMP_HUT = create("swamp_hut");
/**
* {@code minecraft:woodland_mansion}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final StructureType WOODLAND_MANSION = create("woodland_mansion");
private static @NotNull StructureType create(final @NotNull String key) {
return Registry.STRUCTURE_TYPE.get(NamespacedKey.minecraft(key));
}
}

View File

@ -1,15 +1,18 @@
package io.papermc.generator;
import io.papermc.generator.types.GeneratedKeyType;
import io.papermc.generator.types.registry.GeneratedKeyType;
import io.papermc.generator.types.SourceGenerator;
import io.papermc.generator.types.enumgen.BiomeGenerator;
import io.papermc.generator.types.enumgen.PoseGenerator;
import io.papermc.generator.types.enumgen.SoundGenerator;
import io.papermc.generator.types.enumgen.EnumGenerator;
import io.papermc.generator.types.registry.BiomeGenerator;
import io.papermc.generator.types.registry.SoundGenerator;
import io.papermc.generator.types.goal.MobGoalGenerator;
import io.papermc.generator.types.registry.StructureTypeGenerator;
import io.papermc.generator.types.registry.TagGenerator;
import io.papermc.paper.registry.RegistryKey;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.entity.Pose;
import org.bukkit.GameEvent;
import org.bukkit.MusicInstrument;
import org.bukkit.block.Biome;
@ -39,7 +42,9 @@ public interface Generators {
new MobGoalGenerator("VanillaGoal", "com.destroystokyo.paper.entity.ai"),
new SoundGenerator("Sound", "org.bukkit"),
new BiomeGenerator("Biome", "org.bukkit.block"),
new PoseGenerator("Pose", "org.bukkit.entity")
new StructureTypeGenerator("StructureType", "org.bukkit.generator.structure"),
new EnumGenerator<>(Pose.class, "org.bukkit.entity"),
new TagGenerator("Tag", "org.bukkit")
};
private static <T, A> SourceGenerator simpleKey(final String className, final Class<A> apiType, final ResourceKey<? extends Registry<T>> registryKey, final RegistryKey<A> apiRegistryKey, final boolean publicCreateKeyMethod) {

View File

@ -1,22 +1,26 @@
package io.papermc.generator;
import com.google.common.util.concurrent.MoreExecutors;
import com.mojang.logging.LogUtils;
import io.papermc.generator.types.SourceGenerator;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import net.minecraft.SharedConstants;
import net.minecraft.commands.Commands;
import net.minecraft.core.LayeredRegistryAccess;
import net.minecraft.core.RegistryAccess;
import net.minecraft.resources.RegistryDataLoader;
import net.minecraft.server.Bootstrap;
import net.minecraft.server.RegistryLayer;
import net.minecraft.server.ReloadableServerResources;
import net.minecraft.server.WorldLoader;
import net.minecraft.server.packs.PackType;
import net.minecraft.server.packs.repository.Pack;
import net.minecraft.server.packs.repository.PackRepository;
import net.minecraft.server.packs.repository.ServerPacksSource;
import net.minecraft.server.packs.resources.MultiPackResourceManager;
import net.minecraft.world.flag.FeatureFlags;
import org.apache.commons.io.file.PathUtils;
import org.slf4j.Logger;
@ -36,6 +40,8 @@ public final class Main {
LayeredRegistryAccess<RegistryLayer> layers = RegistryLayer.createRegistryAccess();
layers = WorldLoader.loadAndReplaceLayer(resourceManager, layers, RegistryLayer.WORLDGEN, RegistryDataLoader.WORLDGEN_REGISTRIES);
REGISTRY_ACCESS = layers.compositeAccess().freeze();
final ReloadableServerResources datapack = ReloadableServerResources.loadResources(resourceManager, layers, FeatureFlags.REGISTRY.allFlags(), Commands.CommandSelection.DEDICATED, 0, MoreExecutors.directExecutor(), MoreExecutors.directExecutor()).join();
datapack.updateRegistryTags();
}
private Main() {

View File

@ -1,83 +1,39 @@
package io.papermc.generator.types.enumgen;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import io.papermc.generator.Main;
import io.papermc.generator.types.SimpleGenerator;
import io.papermc.generator.utils.Annotations;
import io.papermc.generator.utils.Formatting;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.lang.model.element.Modifier;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
@DefaultQualifier(NonNull.class)
public abstract class EnumGenerator<T> extends SimpleGenerator {
public class EnumGenerator<T extends Enum<T>> extends SimpleGenerator {
protected final ResourceKey<Registry<T>> registryResourceKey;
private final Class<T> enumClass;
public EnumGenerator(final String keysClassName, final String pkg, ResourceKey<Registry<T>> registryResourceKey) {
super(keysClassName, pkg);
this.registryResourceKey = registryResourceKey;
public EnumGenerator(final Class<T> enumClass, final String pkg) {
super(enumClass.getSimpleName(), pkg);
this.enumClass = enumClass;
}
@Override
protected TypeSpec getTypeSpec() {
TypeSpec.Builder typeBuilder = TypeSpec.enumBuilder(this.className)
.addSuperinterface(Keyed.class)
.addModifiers(Modifier.PUBLIC)
.addAnnotations(Annotations.CLASS_HEADER);
Registry<T> event = Main.REGISTRY_ACCESS.registryOrThrow(this.registryResourceKey);
List<Map.Entry<ResourceKey<T>, T>> paths = new ArrayList<>(event.entrySet());
paths.sort(Comparator.comparing(o -> o.getKey().location().getPath()));
paths.forEach(entry -> {
String path = entry.getKey().location().getPath();
String fieldName = Formatting.formatKeyAsField(path);
boolean isExperimental = this.isExperimental(entry);
TypeSpec.Builder builder = TypeSpec.anonymousClassBuilder("$S", path);
if (isExperimental) {
builder.addAnnotations(Annotations.experimentalAnnotations(null));
}
typeBuilder.addEnumConstant(fieldName, builder.build());
});
typeBuilder.addField(FieldSpec.builder(NamespacedKey.class, "key", Modifier.PRIVATE).build());
typeBuilder.addMethod(MethodSpec.constructorBuilder()
.addParameter(String.class, "key").addCode("this.key = NamespacedKey.minecraft(key);").build());
typeBuilder.addMethod(MethodSpec.methodBuilder("getKey")
.returns(NamespacedKey.class)
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Annotations.NOT_NULL)
.addAnnotation(Annotations.OVERRIDE)
.addCode("return this.key;").build());
this.addExtras(typeBuilder);
for (T enumValue : this.enumClass.getEnumConstants()) {
typeBuilder.addEnumConstant(enumValue.name());
}
return typeBuilder.build();
}
public abstract void addExtras(TypeSpec.Builder builder);
@Override
protected JavaFile.Builder file(JavaFile.Builder builder) {
return builder
.skipJavaLangImports(true);
}
public abstract boolean isExperimental(Map.Entry<ResourceKey<T>, T> entry);
}

View File

@ -1,39 +0,0 @@
package io.papermc.generator.types.enumgen;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeSpec;
import io.papermc.generator.types.SimpleGenerator;
import io.papermc.generator.utils.Annotations;
import javax.lang.model.element.Modifier;
import net.minecraft.world.entity.Pose;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
@DefaultQualifier(NonNull.class)
public class PoseGenerator extends SimpleGenerator {
public PoseGenerator(final String keysClassName, final String pkg) {
super(keysClassName, pkg);
}
@Override
protected TypeSpec getTypeSpec() {
TypeSpec.Builder typeBuilder = TypeSpec.enumBuilder(this.className)
.addModifiers(Modifier.PUBLIC)
.addAnnotations(Annotations.CLASS_HEADER);
for (Pose nmsPose : Pose.values()) {
String fieldName = nmsPose.name();
typeBuilder.addEnumConstant(fieldName);
}
return typeBuilder.build();
}
@Override
protected JavaFile.Builder file(JavaFile.Builder builder) {
return builder
.skipJavaLangImports(true);
}
}

View File

@ -1,4 +1,4 @@
package io.papermc.generator.types.enumgen;
package io.papermc.generator.types.registry;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
@ -6,11 +6,9 @@ import io.papermc.generator.Main;
import io.papermc.generator.utils.Annotations;
import io.papermc.generator.utils.Javadocs;
import io.papermc.generator.utils.RegistryUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import javax.lang.model.element.Modifier;
import net.kyori.adventure.translation.Translatable;
import net.minecraft.core.Registry;
@ -21,8 +19,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
@DefaultQualifier(NonNull.class)
public class BiomeGenerator extends EnumGenerator<Biome> {
public class BiomeGenerator extends EnumRegistryGenerator<Biome> {
private static final String CLASS_HEADER = Javadocs.getVersionDependentClassHeader("Biomes");
private final Set<ResourceKey<Biome>> experimental;
@ -48,7 +45,6 @@ public class BiomeGenerator extends EnumGenerator<Biome> {
builder.addEnumConstant("CUSTOM", TypeSpec.anonymousClassBuilder("$S", "custom").build());
}
@Override
public boolean isExperimental(final Map.Entry<ResourceKey<Biome>, Biome> entry) {
return this.experimental.contains(entry.getKey());

View File

@ -0,0 +1,88 @@
package io.papermc.generator.types.registry;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeSpec;
import io.papermc.generator.Main;
import io.papermc.generator.types.SimpleGenerator;
import io.papermc.generator.utils.Annotations;
import io.papermc.generator.utils.Formatting;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.Modifier;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
@DefaultQualifier(NonNull.class)
public abstract class EnumRegistryGenerator<T> extends SimpleGenerator {
protected final ResourceKey<Registry<T>> registryResourceKey;
public EnumRegistryGenerator(final String keysClassName, final String pkg, ResourceKey<Registry<T>> registryResourceKey) {
super(keysClassName, pkg);
this.registryResourceKey = registryResourceKey;
}
@Override
protected TypeSpec getTypeSpec() {
TypeSpec.Builder typeBuilder = TypeSpec.enumBuilder(this.className)
.addSuperinterface(Keyed.class)
.addModifiers(Modifier.PUBLIC)
.addAnnotations(Annotations.CLASS_HEADER);
Registry<T> event = Main.REGISTRY_ACCESS.registryOrThrow(this.registryResourceKey);
List<Map.Entry<ResourceKey<T>, T>> paths = new ArrayList<>(event.entrySet());
paths.sort(Comparator.comparing(o -> o.getKey().location().getPath()));
paths.forEach(entry -> {
ResourceKey<T> resourceKey = entry.getKey();
String pathKey = resourceKey.location().getPath();
String fieldName = Formatting.formatKeyAsField(pathKey);
boolean isExperimental = this.isExperimental(entry);
TypeSpec.Builder builder = TypeSpec.anonymousClassBuilder("$S", pathKey);
if (isExperimental) {
builder.addAnnotations(Annotations.experimentalAnnotations(null));
}
typeBuilder.addEnumConstant(fieldName, builder.build());
});
FieldSpec keyField = FieldSpec.builder(NamespacedKey.class, "key", Modifier.PRIVATE).build();
typeBuilder.addField(keyField);
ParameterSpec keyParam = ParameterSpec.builder(String.class, "key").build();
typeBuilder.addMethod(MethodSpec.constructorBuilder()
.addParameter(keyParam).addCode("this.$N = $T.minecraft($N);", keyField, NamespacedKey.class, keyParam).build());
typeBuilder.addMethod(MethodSpec.methodBuilder("getKey")
.returns(NamespacedKey.class)
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Annotations.NOT_NULL)
.addAnnotation(Annotations.OVERRIDE)
.addCode("return this.$N;", keyField).build());
this.addExtras(typeBuilder);
return typeBuilder.build();
}
public abstract void addExtras(TypeSpec.Builder builder);
@Override
protected JavaFile.Builder file(JavaFile.Builder builder) {
return builder
.skipJavaLangImports(true);
}
public abstract boolean isExperimental(Map.Entry<ResourceKey<T>, T> entry);
}

View File

@ -1,4 +1,4 @@
package io.papermc.generator.types;
package io.papermc.generator.types.registry;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
@ -8,6 +8,7 @@ import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import io.papermc.generator.Main;
import io.papermc.generator.types.SimpleGenerator;
import io.papermc.generator.utils.Annotations;
import io.papermc.generator.utils.Formatting;
import io.papermc.generator.utils.Javadocs;

View File

@ -1,4 +1,4 @@
package io.papermc.generator.types.enumgen;
package io.papermc.generator.types.registry;
import com.squareup.javapoet.TypeSpec;
import io.papermc.generator.utils.Javadocs;
@ -14,7 +14,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
@DefaultQualifier(NonNull.class)
public class SoundGenerator extends EnumGenerator<SoundEvent> {
public class SoundGenerator extends EnumRegistryGenerator<SoundEvent> {
private static final String CLASS_HEADER = Javadocs.getVersionDependentClassHeader("Sounds");
private static final List<Pattern> EXPERIMENTAL_REGEX = of(

View File

@ -0,0 +1,92 @@
package io.papermc.generator.types.registry;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import io.papermc.generator.types.SimpleGenerator;
import io.papermc.generator.utils.Annotations;
import io.papermc.generator.utils.Formatting;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.Modifier;
import io.papermc.generator.utils.Javadocs;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceKey;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.generator.structure.StructureType;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
import static com.squareup.javapoet.TypeSpec.classBuilder;
import static io.papermc.generator.utils.Annotations.NOT_NULL;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
@DefaultQualifier(NonNull.class)
public class StructureTypeGenerator extends SimpleGenerator { // todo abstract this one for similar stuff like game event?
private static final String CLASS_HEADER = Javadocs.getVersionDependentClassHeader("StructureTypes");
public StructureTypeGenerator(final String keysClassName, final String pkg) {
super(keysClassName, pkg);
}
private MethodSpec.Builder createMethod(TypeName returnType) {
final TypeName keyType = TypeName.get(String.class).annotated(NOT_NULL);
final ParameterSpec keyParam = ParameterSpec.builder(keyType, "key", FINAL).build();
final MethodSpec.Builder create = MethodSpec.methodBuilder("create")
.addModifiers(PRIVATE, STATIC)
.addParameter(keyParam)
.addCode("return $T.$L.get($T.minecraft($N));", Registry.class, "STRUCTURE_TYPE", NamespacedKey.class, keyParam)
.returns(returnType.annotated(NOT_NULL));
return create;
}
private TypeSpec.Builder keyHolderType() {
return classBuilder(this.className)
.addSuperinterface(Keyed.class)
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.addAnnotations(Annotations.CLASS_HEADER)
.addJavadoc(CLASS_HEADER);
}
@Override
protected TypeSpec getTypeSpec() {
TypeSpec.Builder typeBuilder = this.keyHolderType();
MethodSpec.Builder createMethod = this.createMethod(TypeName.get(StructureType.class));
List<Map.Entry<ResourceKey<net.minecraft.world.level.levelgen.structure.StructureType<?>>, net.minecraft.world.level.levelgen.structure.StructureType<?>>> paths = new ArrayList<>(BuiltInRegistries.STRUCTURE_TYPE.entrySet());
paths.sort(Comparator.comparing(o -> o.getKey().location().getPath()));
paths.forEach(entry -> {
ResourceKey<net.minecraft.world.level.levelgen.structure.StructureType<?>> resourceKey = entry.getKey();
String pathKey = resourceKey.location().getPath();
String fieldName = Formatting.formatKeyAsField(pathKey);
FieldSpec.Builder fieldBuilder = FieldSpec.builder(StructureType.class, fieldName, PUBLIC, STATIC, FINAL)
.initializer("$N($S)", createMethod.build(), pathKey)
.addJavadoc(Javadocs.getVersionDependentField("{@code $L}"), resourceKey.location().toString());
typeBuilder.addField(fieldBuilder.build());
});
return typeBuilder.addMethod(createMethod.build()).build();
}
@Override
protected JavaFile.Builder file(JavaFile.Builder builder) {
return builder
.skipJavaLangImports(true);
}
}

View File

@ -0,0 +1,176 @@
package io.papermc.generator.types.registry;
import com.destroystokyo.paper.MaterialTags;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import io.papermc.generator.Main;
import io.papermc.generator.types.SimpleGenerator;
import io.papermc.generator.utils.Annotations;
import io.papermc.generator.utils.Formatting;
import io.papermc.generator.utils.Javadocs;
import io.papermc.paper.tag.EntityTags;
import java.util.Comparator;
import java.util.Locale;
import java.util.Set;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.tags.TagKey;
import org.bukkit.Bukkit;
import org.bukkit.Fluid;
import org.bukkit.GameEvent;
import org.bukkit.Keyed;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Tag;
import org.bukkit.entity.EntityType;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
import static com.squareup.javapoet.TypeSpec.interfaceBuilder;
import static io.papermc.generator.utils.Annotations.NOT_NULL;
import static javax.lang.model.element.Modifier.ABSTRACT;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
@DefaultQualifier(NonNull.class)
public class TagGenerator extends SimpleGenerator {
public static String CLASS_HEADER_JAVADOC = """
Represents a tag that may be defined by the server or a resource pack to
group like things together.
<p>
Note that whilst all tags defined within this interface must be present in
Implementations, their existence is not guaranteed across future versions.
<p>
Custom tags defined by Paper are not present (as constants) in this class.
To access them please refer to {@link $T}
and {@link $T}.
@param <T> the type of things grouped by this tag
""";
private static final String IS_TAGGED_JAVADOC = """
Returns whether or not this tag has an entry for the specified item.
@param item to check
@return if it is tagged
""";
private static final String GET_VALUES_JAVADOC = """
Gets an immutable set of all tagged items.
@return set of tagged items
""";
private static final String REGISTRY_FIELD_JAVADOC = "Key for the built in $L registry.";
public record TagRegistryNames(String name, Class<?> apiType, ResourceKey<? extends Registry<?>> registryKey) {}
private static TagRegistryNames names(final String name, final Class<?> apiType, final ResourceKey<? extends Registry<?>> registryKey) {
return new TagRegistryNames(name, apiType, registryKey);
}
private static final Set<TagRegistryNames> NAMES = Set.of(
names("blocks", Material.class, Registries.BLOCK),
names("items", Material.class, Registries.ITEM),
names("fluids", Fluid.class, Registries.FLUID),
names("entity_types", EntityType.class, Registries.ENTITY_TYPE),
names("game_events", GameEvent.class, Registries.GAME_EVENT)
);
private final TypeVariableName typeVariable = TypeVariableName.get("T", Keyed.class);
public TagGenerator(final String keysClassName, final String pkg) {
super(keysClassName, pkg);
}
private TypeSpec.Builder tagHolderType() {
return interfaceBuilder(this.className)
.addTypeVariable(this.typeVariable)
.addModifiers(PUBLIC)
.addSuperinterface(Keyed.class)
.addJavadoc(CLASS_HEADER_JAVADOC, MaterialTags.class, EntityTags.class)
.addAnnotations(Annotations.CLASS_HEADER);
}
@Override
protected TypeSpec getTypeSpec() {
final TypeSpec.Builder typeBuilder = this.tagHolderType();
for (TagRegistryNames names : NAMES) {
final TypeName fieldType = ParameterizedTypeName.get(Tag.class, names.apiType());
final ResourceKey<? extends Registry<?>> registryKey = names.registryKey();
final Registry<?> registry = Main.REGISTRY_ACCESS.registryOrThrow(registryKey);
final String registryFieldName = "REGISTRY_" + names.name().toUpperCase(Locale.ENGLISH);
final FieldSpec.Builder registryFieldBuilder = FieldSpec.builder(String.class, registryFieldName)
.addModifiers(PUBLIC, STATIC, FINAL)
.initializer("$S", names.name())
.addJavadoc(REGISTRY_FIELD_JAVADOC, registryKey.location().getPath());
typeBuilder.addField(registryFieldBuilder.build());
for (final TagKey<?> tagKey : registry.getTagNames().sorted(Comparator.comparing(tagKey -> tagKey.location().getPath())).toList()) {
final String keyPath = tagKey.location().getPath();
final String fieldPrefix;
if (registryKey == Registries.BLOCK) {
fieldPrefix = "";
} else if (registryKey == Registries.GAME_EVENT) {
fieldPrefix = "GAME_EVENT_"; // Paper doesn't follow the format (should be GAME_EVENTS_)
} else {
fieldPrefix = names.name().toUpperCase(Locale.ENGLISH) + "_";
}
final String fieldName = Formatting.formatKeyAsTagField(keyPath, fieldPrefix);
final FieldSpec.Builder fieldBuilder = FieldSpec.builder(fieldType, fieldName)
.addModifiers(PUBLIC, STATIC, FINAL)
.initializer("$T.getTag($L, $T.minecraft($S), $T.class)", Bukkit.class, registryFieldName, NamespacedKey.class, keyPath, names.apiType())
.addJavadoc(Javadocs.getVersionDependentField("{@code $L}"), tagKey.location().toString());
typeBuilder.addField(fieldBuilder.build());
}
}
// deprecated tags
typeBuilder.addField(
FieldSpec.builder(ParameterizedTypeName.get(Tag.class, Material.class), "CARPETS")
.addModifiers(PUBLIC, STATIC, FINAL)
.addAnnotation(Deprecated.class)
.initializer("WOOL_CARPETS")
.addJavadoc(Javadocs.DEPRECATED_FOR, "WOOL_CARPETS").build()
);
// methods
typeBuilder.addMethod(MethodSpec.methodBuilder("isTagged")
.addModifiers(PUBLIC, ABSTRACT)
.returns(boolean.class).addParameter(ParameterSpec.builder(this.typeVariable, "item")
.addAnnotation(NOT_NULL).build())
.addJavadoc(IS_TAGGED_JAVADOC)
.build());
typeBuilder.addMethod(MethodSpec.methodBuilder("getValues")
.addModifiers(PUBLIC, ABSTRACT)
.returns(ParameterizedTypeName.get(ClassName.get(Set.class), this.typeVariable))
.addJavadoc(GET_VALUES_JAVADOC)
.addAnnotation(NOT_NULL).build());
return typeBuilder.build();
}
@Override
protected JavaFile.Builder file(JavaFile.Builder builder) {
return builder
.skipJavaLangImports(true)
.indent(" ");
}
}

View File

@ -15,6 +15,10 @@ public final class Formatting {
return ILLEGAL_FIELD_CHARACTERS.matcher(path.toUpperCase(Locale.ENGLISH)).replaceAll("_");
}
public static String formatKeyAsTagField(String path, String prefix) {
return prefix + formatKeyAsField(path);
}
public static Comparator<String> ALPHABETIC_KEY_ORDER = alphabeticKeyOrder(path -> path);
public static <T> Comparator<T> alphabeticKeyOrder(Function<T, String> mapper) {

View File

@ -22,6 +22,8 @@ public final class Javadocs {
""".formatted(headerIdentifier);
}
public static final String DEPRECATED_FOR = "@deprecated in favour of {@link #$L}";
private Javadocs() {
}
}